Version 5.0.0 beta 9
This commit is contained in:
1 parent
214f649f1d
commit
56979f8ee8
202 files changed
+4403
-1123
No files matched your search
@@ -479,7 +479,7 @@ abstract class Comment extends Content
|
||||
if ( IPS::classUsesTrait( $this, 'IPS\Content\Anonymous' ) and isset( $item::$databaseColumnMap['last_comment_anon'] ) and isset( static::$databaseColumnMap['is_anon'] ) )
|
||||
{
|
||||
$lastCommentAnon = $item::$databaseColumnMap['last_comment_anon'];
|
||||
$item->$lastCommentAnon = $this->mapped('is_anon');
|
||||
$item->$lastCommentAnon = (int) $this->mapped('is_anon');
|
||||
}
|
||||
|
||||
$item->save();
|
||||
@@ -1035,11 +1035,6 @@ abstract class Comment extends Content
|
||||
{
|
||||
Index::i()->index( $this );
|
||||
}
|
||||
|
||||
if( Content\Search\SearchContent::isSearchable( $item ) )
|
||||
{
|
||||
$item->clearCachedStatistics();
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the content items URL to IndexNow */
|
||||
@@ -1188,7 +1183,7 @@ abstract class Comment extends Content
|
||||
|
||||
if ( $item AND IPS::classUsesTrait( $this->item(), 'IPS\Content\Statistics' ) )
|
||||
{
|
||||
$item->clearCachedStatistics();
|
||||
$item->clearCachedStatistics( TRUE );
|
||||
}
|
||||
|
||||
/* Delete any notifications telling people about this */
|
||||
@@ -1883,7 +1878,7 @@ abstract class Comment extends Content
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! Output::i()->reduceLinks() )
|
||||
if ( ! Output::i()->reduceLinks() and $this->canShare() )
|
||||
{
|
||||
if ( $this->mapped( 'first' ) )
|
||||
{
|
||||
@@ -1921,7 +1916,7 @@ abstract class Comment extends Content
|
||||
}
|
||||
}
|
||||
|
||||
if( $this->canEdit($member ) or (IPS::classUsesTrait( $this, "\\IPS\\Content\\Hideable" ) and $this->canHide( $member )) or $this->canSplit( $member ) )
|
||||
if( $this->canEdit( $member ) or ( IPS::classUsesTrait( $this, "\\IPS\\Content\\Hideable" ) and $this->canHide( $member ) ) or $this->canSplit( $member ) )
|
||||
{
|
||||
$menu->addSeparator();
|
||||
}
|
||||
@@ -1965,7 +1960,7 @@ abstract class Comment extends Content
|
||||
|
||||
if( $this->canUnhide($member) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( url: $this->url( 'unhide' )->csrf(), languageString: 'unhide', dataAttributes: [
|
||||
$menu->add( new ContentMenuLink( url: $this->url( 'unhide' )->csrf(), languageString: ( $this->hidden() === 1 ? 'approve' : 'unhide' ), dataAttributes: [
|
||||
'data-ipsDialog-destructOnClose' => 'true'
|
||||
] ) );
|
||||
}
|
||||
|
||||
@@ -1475,8 +1475,10 @@ class Controller extends CoverPhotoController
|
||||
{
|
||||
$options[ $type->id ] = $type->_title;
|
||||
}
|
||||
|
||||
$form->add( new Radio( 'report_type', NULL, FALSE, array( 'options' => $options ) ) );
|
||||
if ( count( $options ) > 1 )
|
||||
{
|
||||
$form->add( new Radio( 'report_type', NULL, FALSE, ['options' => $options] ) );
|
||||
}
|
||||
}
|
||||
|
||||
$form->add( new Editor( 'report_message', NULL, FALSE, array( 'app' => 'core', 'key' => 'Reports', 'autoSaveKey' => "report-{$class::$application}-{$class::$module}-{$item->$idColumn}", 'minimize' => Request::i()->isAjax() ? 'report_message_placeholder' : NULL ) ) );
|
||||
|
||||
@@ -42,6 +42,12 @@ trait Featurable
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Skip for hidden content */
|
||||
if( $this->hidden() !== 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Is this the first comment in an item that requires first comments? */
|
||||
if ( $this instanceof Comment )
|
||||
{
|
||||
|
||||
@@ -231,7 +231,7 @@ trait Hideable
|
||||
/* Restoring as hidden? */
|
||||
if ( $hidden )
|
||||
{
|
||||
$column = static::$databaseColumnMap['hidden'] ?? static::$databaseColumnId['approved'];
|
||||
$column = static::$databaseColumnMap['hidden'] ?? static::$databaseColumnMap['approved'];
|
||||
$this->$column = -1;
|
||||
}
|
||||
else
|
||||
@@ -461,7 +461,7 @@ trait Hideable
|
||||
{
|
||||
try
|
||||
{
|
||||
$item->clearCachedStatistics();
|
||||
$item->clearCachedStatistics( TRUE );
|
||||
}
|
||||
catch ( Exception )
|
||||
{
|
||||
@@ -1354,7 +1354,7 @@ trait Hideable
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->hidden() === -1 )
|
||||
if ( $this->hidden() <> 0 )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
@@ -1381,7 +1381,7 @@ trait Hideable
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !$this->hidden() )
|
||||
if ( $this->hidden() === 0 )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -832,7 +832,7 @@ abstract class Item extends Content
|
||||
$return['guest_name'] = new Text( 'guest_name', NULL, FALSE, array( 'minLength' => Settings::i()->min_user_name_length, 'maxLength' => Settings::i()->max_user_name_length, 'placeholder' => Member::loggedIn()->language()->addToStack('comment_guest_name'), 'htmlAutocomplete' => "username" ), NULL, NULL, NULL, 'guest_name' );
|
||||
}
|
||||
}
|
||||
if ( Settings::i()->bot_antispam_type !== 'none' and Settings::i()->guest_captcha )
|
||||
if ( Settings::i()->bot_antispam_type !== 'none' )
|
||||
{
|
||||
$return['captcha'] = new Captcha;
|
||||
}
|
||||
@@ -4871,7 +4871,7 @@ abstract class Item extends Content
|
||||
$return['guest_name'] = new Text( 'guest_name', NULL, FALSE, array( 'minLength' => Settings::i()->min_user_name_length, 'maxLength' => Settings::i()->max_user_name_length, 'placeholder' => Member::loggedIn()->language()->addToStack('comment_guest_name') ) );
|
||||
}
|
||||
}
|
||||
if ( Settings::i()->bot_antispam_type !== 'none' and Settings::i()->guest_captcha )
|
||||
if ( Settings::i()->bot_antispam_type !== 'none' )
|
||||
{
|
||||
$return['captcha'] = new Captcha;
|
||||
}
|
||||
@@ -5033,7 +5033,7 @@ abstract class Item extends Content
|
||||
$form->add( new Text( 'guest_name', NULL, FALSE, array( 'minLength' => Settings::i()->min_user_name_length, 'maxLength' => Settings::i()->max_user_name_length, 'placeholder' => Member::loggedIn()->language()->addToStack('comment_guest_name') ) ) );
|
||||
}
|
||||
}
|
||||
if ( Settings::i()->bot_antispam_type !== 'none' and Settings::i()->guest_captcha )
|
||||
if ( Settings::i()->bot_antispam_type !== 'none' )
|
||||
{
|
||||
$form->add( new Captcha );
|
||||
}
|
||||
|
||||
@@ -68,6 +68,13 @@ trait ItemTopic
|
||||
*/
|
||||
abstract function getForumId() : int;
|
||||
|
||||
/**
|
||||
* Determine if the topic sync is enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract function isTopicSyncEnabled() : bool;
|
||||
|
||||
/**
|
||||
* Create/Update Topic
|
||||
*
|
||||
@@ -75,6 +82,12 @@ trait ItemTopic
|
||||
*/
|
||||
public function syncTopic(): void
|
||||
{
|
||||
/* If the topic sync is disabled, stop here */
|
||||
if( !$this->isTopicSyncEnabled() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* If this item is pending deletion, do nothing */
|
||||
if( $this->hidden() == -2 )
|
||||
{
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @brief Bluesky share link
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 19 November 2024
|
||||
*/
|
||||
|
||||
namespace IPS\Content\ShareServices;
|
||||
|
||||
use IPS\Content\ShareServices;
|
||||
use IPS\Theme;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bluesky share link
|
||||
*/
|
||||
class Bluesky extends ShareServices
|
||||
{
|
||||
/**
|
||||
* Return the HTML code to show the share link
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return Theme::i()->getTemplate( 'sharelinks', 'core' )->bluesky( urlencode( $this->url ), $this->title ? urlencode( $this->title ) : NULL );
|
||||
}
|
||||
}
|
||||
@@ -36,14 +36,15 @@ abstract class ShareServices
|
||||
*/
|
||||
public static function services(): array
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
'Bluesky' => 'IPS\Content\ShareServices\Bluesky',
|
||||
'Facebook' => 'IPS\Content\ShareServices\Facebook',
|
||||
'Linkedin' => 'IPS\Content\ShareServices\Linkedin',
|
||||
'Pinterest' => 'IPS\Content\ShareServices\Pinterest',
|
||||
'Reddit' => 'IPS\Content\ShareServices\Reddit',
|
||||
'Email' => 'IPS\Content\ShareServices\Email',
|
||||
'X' => 'IPS\Content\ShareServices\X',
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -553,13 +553,22 @@ trait Statistics
|
||||
/**
|
||||
* Clear any cached stats
|
||||
*
|
||||
* @param bool $force Force immediate cache delete
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function clearCachedStatistics(): void
|
||||
public function clearCachedStatistics( bool $force=FALSE )
|
||||
{
|
||||
$idField = static::$databaseColumnId;
|
||||
Db::i()->delete( 'core_item_statistics_cache', array( 'cache_class=? and cache_item_id=?', get_class( $this ), $this->$idField ) );
|
||||
if( $force )
|
||||
{
|
||||
Db::i()->delete( 'core_item_statistics_cache', [ 'cache_class=? and cache_item_id=?', \get_class( $this ), $this->$idField ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set cache to time out in 10 minutes. */
|
||||
Db::i()->update( 'core_item_statistics_cache', [ 'cache_added' => ( time() - 172200 ) ], [ 'cache_class=? and cache_item_id=?', \get_class( $this ), $this->$idField ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -728,6 +737,11 @@ trait Statistics
|
||||
*/
|
||||
public static array|null $cachedActivity = NULL;
|
||||
|
||||
/**
|
||||
* @var bool|null Process has rebuild lock
|
||||
*/
|
||||
protected ?bool $hasRebuildLock = NULL;
|
||||
|
||||
/**
|
||||
* Get the cached data
|
||||
*
|
||||
@@ -738,28 +752,57 @@ trait Statistics
|
||||
protected function _getCached( string $key ): mixed
|
||||
{
|
||||
$idField = static::$databaseColumnId;
|
||||
if( $this->hasRebuildLock === FALSE )
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$class = get_class( $this );
|
||||
$arrayKey = $class.'.'.$this->$idField;
|
||||
if ( !isset( static::$cachedActivity[$arrayKey] ) )
|
||||
$arrayKey = $class . '.' . $this->$idField;
|
||||
if ( !isset( static::$cachedActivity[ $arrayKey ] ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$cache = Db::i()->select( '*', 'core_item_statistics_cache', array( 'cache_class=? and cache_item_id=?', $class, $this->$idField ) )->first();
|
||||
|
||||
if ( $cache['cache_added'] > time() - 86400 )
|
||||
$cache = Db::i()->select( '*', 'core_item_statistics_cache', [ 'cache_class=? and cache_item_id=?', $class, $this->$idField ] )->first();
|
||||
if ( $cache['cache_added'] > ( time() - 172800 ) )
|
||||
{
|
||||
static::$cachedActivity[ $arrayKey ] = json_decode( $cache['cache_contents'], TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
Db::i()->delete( 'core_item_statistics_cache', array( 'cache_class=? and cache_item_id=?', get_class( $this ), $this->$idField ) );
|
||||
$this->hasRebuildLock = TRUE;
|
||||
$affectedRows = Db::i()->delete( 'core_item_statistics_cache', [ 'cache_class=? and cache_item_id=?', \get_class( $this ), $this->$idField ] );
|
||||
if( $affectedRows == 0 )
|
||||
{
|
||||
/* We did not delete the row, allow another process to rebuild the cache */
|
||||
$this->hasRebuildLock = FALSE;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
{
|
||||
/* Try to get the lock for new cache generation */
|
||||
try
|
||||
{
|
||||
Db::i()->insert( 'core_item_statistics_cache', [
|
||||
'cache_class' => \get_class( $this ),
|
||||
'cache_item_id' => $this->$idField,
|
||||
'cache_contents' => '[]',
|
||||
'cache_added' => time()
|
||||
] );
|
||||
$this->hasRebuildLock = TRUE;
|
||||
}
|
||||
/* We didn't get the lock, return empty array and allow other process to build cache */
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
$this->hasRebuildLock = FALSE;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
catch( UnderflowException $e ) { }
|
||||
}
|
||||
|
||||
if ( isset(static::$cachedActivity[ $arrayKey ] ) and isset( static::$cachedActivity[ $arrayKey ][ $key ] ) )
|
||||
if ( isset( static::$cachedActivity[ $arrayKey ] ) and isset( static::$cachedActivity[ $arrayKey ][ $key ] ) )
|
||||
{
|
||||
return static::$cachedActivity[ $arrayKey ][ $key ];
|
||||
}
|
||||
@@ -767,10 +810,10 @@ trait Statistics
|
||||
{
|
||||
if( !isset( static::$cachedActivity[ $arrayKey ] ) )
|
||||
{
|
||||
static::$cachedActivity[ $arrayKey ] = array();
|
||||
static::$cachedActivity[ $arrayKey ] = [];
|
||||
}
|
||||
|
||||
static::$cachedActivity[ $arrayKey ][ $key ] = array();
|
||||
static::$cachedActivity[ $arrayKey ][ $key ] = [];
|
||||
throw new OutOfRangeException;
|
||||
}
|
||||
}
|
||||
@@ -819,8 +862,8 @@ trait Statistics
|
||||
{
|
||||
Db::i()->insert( 'core_item_statistics_cache', array(
|
||||
'cache_class' => get_class( $this ),
|
||||
'cache_item_id' => str_replace( get_class($this) . '.', '', $key),
|
||||
'cache_contents' => json_encode( static::$cachedActivity[$key] ),
|
||||
'cache_item_id' => str_replace( \get_class( $this ) . '.', '', $key ),
|
||||
'cache_contents' => json_encode( static::$cachedActivity[ $key ] ) ?? '[]',
|
||||
'cache_added' => time()
|
||||
), TRUE );
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user