Version 5.0.13
This commit is contained in:
1 parent
b89858b2ff
commit
5efe744c5f
1407 files changed
+69690
-30177
No files matched your search
+243
-167
@@ -20,6 +20,7 @@ use IPS\Api\Webhook;
|
||||
use IPS\Application;
|
||||
use IPS\Application\Module;
|
||||
use IPS\Content;
|
||||
use IPS\Content\Permissions as PermissionsExtension;
|
||||
use IPS\Content\Search\Index;
|
||||
use IPS\core\Achievements\Recognize;
|
||||
use IPS\core\Approval;
|
||||
@@ -84,6 +85,8 @@ use function is_null;
|
||||
use function is_numeric;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function mb_strrpos;
|
||||
use function mb_strtolower;
|
||||
use function substr;
|
||||
use const IPS\CIC;
|
||||
|
||||
@@ -195,12 +198,14 @@ abstract class Item extends Content
|
||||
* Build form to create
|
||||
*
|
||||
* @param Model|NULL $container Container (e.g. forum), if appropriate
|
||||
* @param bool $showError Bypass canCreate check? This will show the form even when the member is not allowed to create the item.
|
||||
* @return Form
|
||||
*/
|
||||
public static function create( Model|null $container=NULL ): Form
|
||||
public static function create( Model|null $container=NULL, bool $showError = true ): Form
|
||||
{
|
||||
/* Perform permission checks */
|
||||
static::canCreate( Member::loggedIn(), $container, TRUE );
|
||||
/* Perform permission checks */
|
||||
static::canCreate( Member::loggedIn(), $container, $showError );
|
||||
|
||||
|
||||
/* Build the form */
|
||||
$form = static::buildCreateForm( $container );
|
||||
@@ -566,26 +571,7 @@ abstract class Item extends Content
|
||||
/* Auto-follow */
|
||||
if( isset( $values[ static::$formLangPrefix . 'auto_follow'] ) AND $values[ static::$formLangPrefix . 'auto_follow'] )
|
||||
{
|
||||
$followArea = mb_strtolower( mb_substr( get_called_class(), mb_strrpos( get_called_class(), '\\' ) + 1 ) );
|
||||
|
||||
/* Insert */
|
||||
$idColumn = static::$databaseColumnId;
|
||||
$save = array(
|
||||
'follow_id' => md5( static::$application . ';' . $followArea . ';' . $obj->$idColumn . ';' . Member::loggedIn()->member_id ),
|
||||
'follow_app' => static::$application,
|
||||
'follow_area' => $followArea,
|
||||
'follow_rel_id' => $obj->$idColumn,
|
||||
'follow_member_id' => Member::loggedIn()->member_id,
|
||||
'follow_is_anon' => isset( $values[ 'post_anonymously' ] ) ? (bool) $values[ 'post_anonymously' ] : 0,
|
||||
'follow_added' => time() + 1, // Make sure streams show follows after content is created
|
||||
'follow_notify_do' => 1,
|
||||
'follow_notify_meta' => '',
|
||||
'follow_notify_freq' => Member::loggedIn()->auto_follow['method'],
|
||||
'follow_notify_sent' => 0,
|
||||
'follow_visible' => 1
|
||||
);
|
||||
|
||||
Db::i()->insert( 'core_follow', $save );
|
||||
$obj->follow( Member::loggedIn()->auto_follow['method'], isset( $values['post_anonymously'] ) ? !$values['post_anonymously'] : true );
|
||||
}
|
||||
|
||||
/* Auto-share */
|
||||
@@ -609,7 +595,7 @@ abstract class Item extends Content
|
||||
}
|
||||
|
||||
/* Send notifications */
|
||||
if ( $sendNotification and ( IPS::classUsesTrait( $obj, 'IPS\Content\FuturePublishing' ) AND !$obj->isFutureDate() ) )
|
||||
if ( $sendNotification and ( !IPS::classUsesTrait( $obj, 'IPS\Content\FuturePublishing' ) or !$obj->isFutureDate() ) )
|
||||
{
|
||||
if ( !$obj->hidden() )
|
||||
{
|
||||
@@ -844,7 +830,12 @@ abstract class Item extends Content
|
||||
{
|
||||
if ( isset( static::$databaseColumnMap['author_name'] ) )
|
||||
{
|
||||
$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' );
|
||||
$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" ), function( $val ){
|
||||
if( !empty( $val ) and filter_var( $val, FILTER_VALIDATE_EMAIL ) !== false )
|
||||
{
|
||||
throw new InvalidArgumentException( 'form_no_email_allowed' );
|
||||
}
|
||||
}, NULL, NULL, 'guest_name' );
|
||||
}
|
||||
}
|
||||
if ( Settings::i()->bot_antispam_type !== 'none' )
|
||||
@@ -879,7 +870,7 @@ abstract class Item extends Content
|
||||
'key' => IPS::mb_ucfirst( static::$module ),
|
||||
'autoSaveKey' => ( $item === NULL ? ( 'newContentItem-' . static::$application . '/' . static::$module . '-' . ( $container ? $container->_id : 0 ) ) : ( 'contentEdit-' . static::$application . '/' . static::$module . '-' . $item->$idColumn ) ),
|
||||
'attachIds' => ( $item === NULL ? NULL : array( $item->$idColumn, $commentObj->$commentIdColumn ) )
|
||||
), '\IPS\Helpers\Form::floodCheck', NULL, NULL, static::$formLangPrefix . 'content_editor' );
|
||||
), ( $item ? null : '\IPS\Helpers\Form::floodCheck' ), NULL, NULL, static::$formLangPrefix . 'content_editor' );
|
||||
|
||||
if ( $item AND IPS::classUsesTrait( $commentClass, 'IPS\Content\EditHistory' ) and Settings::i()->edit_log )
|
||||
{
|
||||
@@ -1049,13 +1040,9 @@ abstract class Item extends Content
|
||||
}
|
||||
|
||||
/* Data Layer Event */
|
||||
if ( Settings::i()->core_datalayer_enabled )
|
||||
if ( DataLayer::enabled() and static::dataLayerEventActive( 'content_create' ) )
|
||||
{
|
||||
/* make sure it isn't a PM Conversation */
|
||||
if ( !isset( static::$title ) or static::$title !== Conversation::$title )
|
||||
{
|
||||
DataLayer::i()->addEvent( 'content_create', $this->getDataLayerProperties() );
|
||||
}
|
||||
DataLayer::i()->addEvent( 'content_create', $this->getDataLayerProperties( createOrEditValues: $values ) );
|
||||
}
|
||||
|
||||
/* Send this URL to IndexNow if the guest can view it */
|
||||
@@ -1158,9 +1145,11 @@ abstract class Item extends Content
|
||||
/* Initial Comment */
|
||||
if ( isset( static::$commentClass ) and static::$firstCommentRequired )
|
||||
{
|
||||
/* @var Comment $commentClass */
|
||||
$commentClass = static::$commentClass;
|
||||
$commentObj = $this->firstComment();
|
||||
|
||||
/* @var Comment $commentClass */
|
||||
$commentClass = get_class( $commentObj );
|
||||
|
||||
/* @var $databaseColumnMap array */
|
||||
$column = $commentClass::$databaseColumnMap['content'];
|
||||
$idField = $commentClass::$databaseColumnId;
|
||||
@@ -1251,6 +1240,11 @@ abstract class Item extends Content
|
||||
$this->itemEdited();
|
||||
}
|
||||
|
||||
if ( DataLayer::enabled( 'analytics_full' ) and static::dataLayerEventActive( 'content_edit' ) )
|
||||
{
|
||||
DataLayer::i()->addEvent( 'content_edit', $this->getDataLayerProperties() );
|
||||
}
|
||||
|
||||
Event::fire( 'onCreateOrEdit', $this, array( $values ) );
|
||||
|
||||
$this->ui( 'formPostSave', array( $values ) );
|
||||
@@ -1517,7 +1511,7 @@ abstract class Item extends Content
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BadMethodCallException;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2064,6 +2058,9 @@ abstract class Item extends Content
|
||||
{
|
||||
Index::i()->massUpdate( $commentClass, NULL, $item->$idColumn, $extension->searchIndexPermissions(), $this->hidden() ? 2 : NULL, $extension->searchIndexContainer(), NULL, $this->$idColumn, $this->author()->member_id );
|
||||
}
|
||||
|
||||
/* Solved Index */
|
||||
Db::i()->update( 'core_solved_index', [ 'item_id' => $this->$idColumn ], [ 'app=? and comment_class=? and item_id=?', static::$application, $commentClass, $item->$idColumn ] );
|
||||
}
|
||||
if ( isset( static::$reviewClass ) )
|
||||
{
|
||||
@@ -2194,6 +2191,12 @@ abstract class Item extends Content
|
||||
$viewColumn = $item::$databaseColumnMap['views'];
|
||||
$this->$viewColumn = $this->mapped('views') + $views;
|
||||
}
|
||||
|
||||
/* Recount helpfuls */
|
||||
if( IPS::classUsesTrait( $this, Helpful::class ) )
|
||||
{
|
||||
$this->recountHelpfuls();
|
||||
}
|
||||
|
||||
$this->rebuildFirstAndLastCommentData();
|
||||
|
||||
@@ -2576,6 +2579,12 @@ abstract class Item extends Content
|
||||
Db::i()->delete( 'core_ratings', ['class=? AND item_id=?', get_called_class(), $this->$idColumn] );
|
||||
}
|
||||
|
||||
/* Pending view updates */
|
||||
if( IPS::classUsesTrait( $this, ViewUpdates::class ) )
|
||||
{
|
||||
Db::i()->delete( 'core_view_updates', [ 'classname=? and id=?', get_called_class(), $this->$idColumn ] );
|
||||
}
|
||||
|
||||
foreach ( $memberIds as $member )
|
||||
{
|
||||
Member::load( $member )->recountNotifications();
|
||||
@@ -2615,6 +2624,7 @@ abstract class Item extends Content
|
||||
|
||||
Db::i()->delete( 'core_reputation_index', array('rep_class=? AND ' . Db::i()->in( 'type_id', $ids ), $class) );
|
||||
Db::i()->delete( 'core_solved_index', array('comment_class=? AND ' . Db::i()->in( 'comment_id', $ids ), $class) );
|
||||
Db::i()->delete( 'core_approval_queue', array( 'approval_content_class=? AND ' . Db::i()->in( 'approval_content_id', $ids ), $class ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2691,7 +2701,7 @@ abstract class Item extends Content
|
||||
try
|
||||
{
|
||||
$permissions = $this->container()->permissions();
|
||||
return $permissions['perm_id'];
|
||||
return is_array( $permissions ) ? $permissions['perm_id'] : null;
|
||||
}
|
||||
catch( BadMethodCallException )
|
||||
{
|
||||
@@ -2948,7 +2958,7 @@ abstract class Item extends Content
|
||||
{
|
||||
$authorCol = static::$databaseTable . '.' . static::$databasePrefix . static::$databaseColumnMap['author'];
|
||||
/* Only fetching a count, for a single container with a item cache count, no future publishing, and there is only one where clause (the container limitation) */
|
||||
if( $countOnly === TRUE AND $skipPermission instanceof Model AND $skipPermission->_items !== NULL AND !in_array( 'IPS\Content\FuturePublishing', class_implements( get_called_class() ) ) AND count( $where ) === 1 )
|
||||
if( $countOnly === TRUE AND $skipPermission instanceof Model AND $skipPermission->_items !== NULL AND !IPS::classUsesTrait( get_called_class(), FuturePublishing::class ) AND count( $where ) === 1 )
|
||||
{
|
||||
$countShortcut = TRUE;
|
||||
$where[] = array( "({$col}=0 AND ( {$authorCol}={$member->member_id}{$extra} ) )" );
|
||||
@@ -3062,47 +3072,13 @@ abstract class Item extends Content
|
||||
{
|
||||
$containerClass = static::$containerNodeClass;
|
||||
$member = $member ?: Member::loggedIn();
|
||||
$categories = array();
|
||||
$lookupKey = md5( $containerClass::$permApp . $containerClass::$permType . $permissionKey . json_encode( $member->groups ) );
|
||||
|
||||
/* EME: Switch to use exclusion instead of inclusion */
|
||||
/* @var $permissionMap array */
|
||||
if( !isset( static::$permissionSelect[ $lookupKey ] ) )
|
||||
{
|
||||
static::$permissionSelect[ $lookupKey ] = array();
|
||||
$permQueryJoinContainer = (bool) ( count( $containerWhere ) );
|
||||
$clubWhere = "";
|
||||
|
||||
/* If we cannot access clubs, skip them */
|
||||
if ( IPS::classUsesTrait( $containerClass, 'IPS\Content\ClubContainer' ) AND !$member->canAccessModule( Module::get( 'core', 'clubs', 'front' ) ) )
|
||||
{
|
||||
$permQueryWhere = array( "core_permission_index.app='" . $containerClass::$permApp . "' AND core_permission_index.perm_type='" . $containerClass::$permType . "' AND (" . $containerClass::$databaseTable . '.' . $containerClass::$databasePrefix . $containerClass::clubIdColumn() . " IS NOT NULL OR !(" . Db::i()->findInSet( 'perm_' . $containerClass::$permissionMap[ $permissionKey ], $member->permissionArray() ) . ' OR ' . 'perm_' . $containerClass::$permissionMap[ $permissionKey ] . "='*' ))" );
|
||||
$permQueryJoinContainer = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$permQueryWhere = array( "core_permission_index.app='" . $containerClass::$permApp . "' AND core_permission_index.perm_type='" . $containerClass::$permType . "' AND !(" . Db::i()->findInSet( 'perm_' . $containerClass::$permissionMap[ $permissionKey ], $member->permissionArray() ) . ' OR ' . 'perm_' . $containerClass::$permissionMap[ $permissionKey ] . "='*' )" );
|
||||
}
|
||||
|
||||
$permQuery = Db::i()->select( 'perm_type_id', 'core_permission_index', $permQueryWhere );
|
||||
|
||||
if ( $permQueryJoinContainer )
|
||||
{
|
||||
$permQuery->join( $containerClass::$databaseTable, array_merge( $containerWhere, array( 'core_permission_index.perm_type_id=' . $containerClass::$databaseTable . '.' . $containerClass::$databasePrefix . $containerClass::$databaseColumnId ) ), 'STRAIGHT_JOIN' );
|
||||
}
|
||||
|
||||
foreach( $permQuery as $result )
|
||||
{
|
||||
static::$permissionSelect[ $lookupKey ][] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
$categories = static::$permissionSelect[ $lookupKey ];
|
||||
|
||||
if( count( $categories ) )
|
||||
{
|
||||
$where[] = array( static::$databaseTable . "." . static::$databasePrefix . static::$databaseColumnMap['container'] . ' NOT IN (' . implode( ',', $categories ) . ')' );
|
||||
}
|
||||
$permQueryWhere = PermissionsExtension::nodePermissionClause( $permissionKey, $containerClass, $member );
|
||||
$permQueryWhere[] = [ 'core_permission_index.app=?', $containerClass::$permApp ];
|
||||
$permQueryWhere[] = [ 'core_permission_index.perm_type=?', $containerClass::$permType ];
|
||||
$where[] = array(
|
||||
'(' . static::$databaseTable . '.' . static::$databasePrefix . static::$databaseColumnMap['container'] . ' IN( ' . Db::i()->select( 'perm_type_id', 'core_permission_index', $permQueryWhere )->returnFullQuery() . ') )',
|
||||
);
|
||||
}
|
||||
|
||||
$groupBy = ( $joinComments ? static::$databaseTable . '.' . static::$databasePrefix . static::$databaseColumnId : NULL );
|
||||
@@ -4639,21 +4615,23 @@ abstract class Item extends Content
|
||||
}
|
||||
|
||||
/* Data Layer stuff for comments here, not in Comment class. We track user actions, and the user action is handled here */
|
||||
$dataLayer = Settings::i()->core_datalayer_enabled ? $this->getDataLayerProperties( $comment ) : array();
|
||||
|
||||
Output::i()->json(
|
||||
array(
|
||||
'type' => 'add',
|
||||
'id' => $comment->$commentIdColumn,
|
||||
'page' => $newPageCount,
|
||||
'total' => $this->mapped('num_comments'),
|
||||
'content' => $output,
|
||||
'message' => $message,
|
||||
'postedByLoggedInMember' => true,
|
||||
/* This is used on the front end */
|
||||
"dataLayer" => $dataLayer,
|
||||
)
|
||||
$json = array(
|
||||
'type' => 'add',
|
||||
'id' => $comment->$commentIdColumn,
|
||||
'page' => $newPageCount,
|
||||
'total' => $this->mapped('num_comments'),
|
||||
'content' => $output,
|
||||
'message' => $message,
|
||||
'postedByLoggedInMember' => true,
|
||||
);
|
||||
|
||||
/* This is used on the front end */
|
||||
if ( DataLayer::enabled() and $commentClass::dataLayerEventActive( 'comment_create' ) )
|
||||
{
|
||||
$json['dataLayer'] = $this->getDataLayerProperties( $comment );
|
||||
}
|
||||
|
||||
Output::i()->json( $json );
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -4727,21 +4705,25 @@ abstract class Item extends Content
|
||||
* Most, if not all of these are the same for different events, so we can just have one method
|
||||
*
|
||||
* @param Comment|null $comment A comment item, leave null for these keys to be omitted
|
||||
* @param array $createOrEditValues=[] Values from the create or edit form, if applicable.
|
||||
* @param bool $clearCache=false Whether to clear the cached properties and regenerate them from scratch
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDataLayerProperties( ?Comment $comment = null ): array
|
||||
public function getDataLayerProperties( ?Comment $comment = null, array $createOrEditValues=[], bool $clearCache=false ): array
|
||||
{
|
||||
$commentIdColumn = $comment ? $comment::$databaseColumnId : null;
|
||||
$index = $commentIdColumn ? ( $comment->$commentIdColumn ?: 0 ) : 0;
|
||||
$index = "idx_" . ( $commentIdColumn ? ( $comment->$commentIdColumn ?: 0 ) : -1 );
|
||||
|
||||
if ( !isset( $this->_dataLayerProperties[$index] ) )
|
||||
if ( $clearCache OR !isset( $this->_dataLayerProperties[$index] ) )
|
||||
{
|
||||
$app = static::$application ?? null;
|
||||
$idColumn = static::$databaseColumnId;
|
||||
$dataLayer = array(
|
||||
'author_id' => DataLayer::i()->getSsoId( $this->author()->member_id ),
|
||||
'author_name' => $this->author()->real_name ?: null,
|
||||
$anonymous = IPS::classUsesTrait( $comment ?? $this,'IPS\\Content\\Anonymous' ) and ($comment ?? $this)->isAnonymous();
|
||||
$author = $comment ? $comment->author() : $this->author();
|
||||
$dataLayer = $comment ? $this->getDataLayerProperties() : array(
|
||||
'author_id' => $anonymous ? 0 : DataLayer::i()->getSsoId( $author->member_id ),
|
||||
'author_name' => $anonymous ? null : ( $author->real_name ?: null ),
|
||||
'content_age' => $this->mapped( 'date' ) ? intval( floor( ( time() - $this->mapped( 'date' ) ) / 86400 ) ) : null,
|
||||
'content_container_id' => null,
|
||||
'content_container_name' => null,
|
||||
@@ -4751,11 +4733,20 @@ abstract class Item extends Content
|
||||
'content_title' => $this->mapped( 'title' ),
|
||||
'content_type' => static::$contentType ?? null,
|
||||
'content_url' => (string) $this->url(),
|
||||
'content_anonymous' => $anonymous,
|
||||
'content_is_followed' => ( isset( $createOrEditValues[ static::$formLangPrefix . 'auto_follow'] ) AND $createOrEditValues[ static::$formLangPrefix . 'auto_follow'] ) or (bool) (
|
||||
Member::loggedIn()->member_id and
|
||||
$this->$idColumn and
|
||||
IPS::classUsesTrait( $this::class, 'IPS\Content\Followable' ) and
|
||||
Member::loggedIn()->following( $app, mb_strtolower( mb_substr( $this::class, mb_strrpos( $this::class, '\\' ) + 1 ) ), $this->$idColumn )
|
||||
),
|
||||
);
|
||||
|
||||
if ( $comment AND ( $commentIdColumn = $comment::$databaseColumnId ?? null ) )
|
||||
if ( $comment )
|
||||
{
|
||||
$dataLayer = array_replace( $dataLayer, array(
|
||||
'author_id' => $anonymous ? 0 : DataLayer::i()->getSsoId( $comment->author()->member_id ),
|
||||
'author_name' => $anonymous ? null : ( $comment->author()->real_name ?: null ),
|
||||
'comment_id' => $comment->$commentIdColumn,
|
||||
'comment_type' => $comment::$commentType ?? null,
|
||||
'comment_url' => (string) $comment->url()
|
||||
@@ -4763,12 +4754,18 @@ abstract class Item extends Content
|
||||
}
|
||||
|
||||
/* For QA forums, the comment_type and content_type is an exception */
|
||||
if ( Settings::i()->core_datalayer_distinguish_qa AND
|
||||
$this instanceof Topic AND
|
||||
$this->container()->_forum_type === 'qa' )
|
||||
static $isQA = null;
|
||||
if ( $isQA === null )
|
||||
{
|
||||
$isQA = ( Settings::i()->core_datalayer_distinguish_qa AND
|
||||
$this instanceof Topic AND
|
||||
$this->container()->_forum_type === 'qa');
|
||||
}
|
||||
|
||||
if ( $isQA )
|
||||
{
|
||||
$dataLayer['content_type'] = 'question';
|
||||
if ( isset( $comment ) )
|
||||
if ( $comment and $comment instanceof Topic\Post )
|
||||
{
|
||||
$dataLayer['comment_type'] = 'answer';
|
||||
}
|
||||
@@ -4776,7 +4773,7 @@ abstract class Item extends Content
|
||||
/* If we didn't find a Comment or Content type, try pulling that info from the static title fields */
|
||||
elseif ( ( !isset( $dataLayer['content_type'] ) AND isset( static::$title ) ) OR ( !isset( $dataLayer['comment_type'] ) AND isset( $comment, $comment::$title ) ) )
|
||||
{
|
||||
$contentType = ( $app ? preg_replace( "/[{$app}_]?(.*)/", '$1', static::$title ?? "" ) : ( static::$title ?? null ) ) ?: null;
|
||||
$contentType = ( $app ? preg_replace( "/^" . preg_quote( $app . "_" ) . "/", '', static::$title ?? "" ) : ( static::$title ?? null ) ) ?: null;
|
||||
if ( $contentType AND !isset( $dataLayer['content_type'] ) )
|
||||
{
|
||||
$dataLayer['content_type'] = $contentType;
|
||||
@@ -4784,7 +4781,7 @@ abstract class Item extends Content
|
||||
|
||||
if ( $comment AND isset( $comment::$title ) AND !isset( $dataLayer['comment_type'] ) )
|
||||
{
|
||||
$commentType = ($app or $contentType) ? preg_replace( "/" . ( $app ? "[{$app}_]?" : '' ) . ( $contentType ? "[{$contentType}_]?" : '' ) . "(.*)/", "$1", $comment::$title ) : $comment::$title;
|
||||
$commentType = ($app or $contentType) ? preg_replace( "/^" . ( $app ? ( '(?:' . preg_quote( $app . '_' ) . ')?' ) : '' ) . ( $contentType ? ( '(?:' . preg_quote( $contentType . '_' ) . ')?' ) : '' ) . "/", "", $comment::$title ) : $comment::$title;
|
||||
$dataLayer['comment_type'] = $commentType;
|
||||
}
|
||||
}
|
||||
@@ -4795,19 +4792,25 @@ abstract class Item extends Content
|
||||
$dataLayer['comment_type'] = $comment instanceof Review ? 'review' : 'comment';
|
||||
}
|
||||
|
||||
try
|
||||
/* Since the comment properties is based on the item's properties (the recursive call above), we only need this for non-comment properties */
|
||||
if ( !$comment )
|
||||
{
|
||||
$container = $this->container();
|
||||
$dataLayer = array_replace( $dataLayer, $container->getDataLayerProperties() );
|
||||
}
|
||||
catch ( OutOfRangeException | BadMethodCallException $e ) {}
|
||||
|
||||
if ( !isset( $dataLayer['content_area'] ) AND $app )
|
||||
{
|
||||
$lang = Lang::load( Lang::defaultLanguage() );
|
||||
if ( $lang->checkKeyExists( '__app_' . static::$application ) )
|
||||
try
|
||||
{
|
||||
$dataLayer['content_area'] = $lang->addToStack( '__app_' . static::$application );
|
||||
$container = $this->container();
|
||||
$dataLayer = array_replace( $dataLayer, $container->getDataLayerProperties() );
|
||||
}
|
||||
catch ( OutOfRangeException|BadMethodCallException $e )
|
||||
{
|
||||
}
|
||||
|
||||
if ( !isset( $dataLayer['content_area'] ) and $app )
|
||||
{
|
||||
$lang = Lang::load( Lang::defaultLanguage() );
|
||||
if ( $lang->checkKeyExists( '__app_' . static::$application ) )
|
||||
{
|
||||
$dataLayer['content_area'] = $lang->addToStack( '__app_' . static::$application );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4897,7 +4900,12 @@ abstract class Item extends Content
|
||||
{
|
||||
if ( isset( $commentClass::$databaseColumnMap['author_name'] ) )
|
||||
{
|
||||
$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') ) );
|
||||
$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') ), function( $val ){
|
||||
if( !empty( $val ) and filter_var( $val, FILTER_VALIDATE_EMAIL ) !== false )
|
||||
{
|
||||
throw new InvalidArgumentException( 'form_no_email_allowed' );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
if ( Settings::i()->bot_antispam_type !== 'none' )
|
||||
@@ -4957,7 +4965,7 @@ abstract class Item extends Content
|
||||
$commentClass = static::$commentClass;
|
||||
$idColumn = static::$databaseColumnId;
|
||||
$commentIdColumn = $commentClass::$databaseColumnId;
|
||||
$followArea = mb_strtolower( mb_substr( get_called_class(), mb_strrpos( get_called_class(), '\\' ) + 1 ) );
|
||||
$followArea = mb_strtolower( mb_substr( $this::class, mb_strrpos( $this::class, '\\' ) + 1 ) );
|
||||
|
||||
/* Moderator wants to hide the comment */
|
||||
if( isset( $values['hide'] ) AND $values['hide'] )
|
||||
@@ -4969,41 +4977,24 @@ abstract class Item extends Content
|
||||
$hidden = NULL;
|
||||
}
|
||||
|
||||
$comment = $commentClass::create( $this, $values[ static::$formLangPrefix . 'comment' . '_' . $this->$idColumn ], FALSE, $values['guest_name'] ?? NULL, NULL, NULL, NULL, NULL, $hidden, ( isset( $values[ 'post_anonymously' ] ) ? (bool) $values[ 'post_anonymously' ] : NULL ) );
|
||||
|
||||
Event::fire( 'onCreateOrEdit', $comment, array( $values, TRUE ) );
|
||||
|
||||
/* Auto-follow - If posted anonymously we should set follow to anonymous as well */
|
||||
if( isset( $values[ static::$formLangPrefix . 'auto_follow' ] ) )
|
||||
{
|
||||
if ( $values[ static::$formLangPrefix . 'auto_follow' ] and !Member::loggedIn()->following( static::$application, $followArea, $this->$idColumn ) )
|
||||
{
|
||||
/* Insert */
|
||||
$save = array(
|
||||
'follow_id' => md5( static::$application . ';' . $followArea . ';' . $this->$idColumn . ';' . Member::loggedIn()->member_id ),
|
||||
'follow_app' => static::$application,
|
||||
'follow_area' => $followArea,
|
||||
'follow_rel_id' => $this->$idColumn,
|
||||
'follow_member_id' => Member::loggedIn()->member_id,
|
||||
'follow_is_anon' => isset( $values[ 'post_anonymously' ] ) ? (bool) $values[ 'post_anonymously' ] : 0,
|
||||
'follow_added' => time() + 1, // Make sure streams show follows after content is created
|
||||
'follow_notify_do' => 1,
|
||||
'follow_notify_meta' => '',
|
||||
'follow_notify_freq' => Member::loggedIn()->auto_follow['method'],
|
||||
'follow_notify_sent' => 0,
|
||||
'follow_visible' => 1
|
||||
);
|
||||
|
||||
Db::i()->insert( 'core_follow', $save );
|
||||
Db::i()->delete( 'core_follow_count_cache', array( 'class=? AND id=?', get_called_class(), (int) $this->$idColumn ) );
|
||||
$this->follow( Member::loggedIn()->auto_follow['method'], isset( $values['post_anonymously'] ) ? !$values['post_anonymously'] : true );
|
||||
}
|
||||
else if ( $values[ static::$formLangPrefix . 'auto_follow' ] === false AND Member::loggedIn()->following( static::$application, $followArea, $this->$idColumn ) )
|
||||
{
|
||||
Db::i()->delete( 'core_follow', array( 'follow_id=?', md5( static::$application . ';' . $followArea . ';' . $this->$idColumn . ';' . Member::loggedIn()->member_id ) ) );
|
||||
Db::i()->delete( 'core_follow_count_cache', array( 'id=? AND class=?', $this->$idColumn, get_called_class() ) );
|
||||
$this->unfollow();
|
||||
}
|
||||
}
|
||||
|
||||
$this->_dataLayerProperties = [];
|
||||
$comment = $commentClass::create( $this, $values[ static::$formLangPrefix . 'comment' . '_' . $this->$idColumn ], FALSE, $values['guest_name'] ?? NULL, NULL, NULL, NULL, NULL, $hidden, ( isset( $values[ 'post_anonymously' ] ) ? (bool) $values[ 'post_anonymously' ] : NULL ) );
|
||||
|
||||
Event::fire( 'onCreateOrEdit', $comment, array( $values, TRUE ) );
|
||||
|
||||
/* Update the search index (note: we already index the comment in Comment::create()) */
|
||||
if( Content\Search\SearchContent::isSearchable( $this ) )
|
||||
{
|
||||
@@ -5059,7 +5050,12 @@ abstract class Item extends Content
|
||||
{
|
||||
if ( isset( $reviewClass::$databaseColumnMap['author_name'] ) )
|
||||
{
|
||||
$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') ) ) );
|
||||
$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') ), function( $val ){
|
||||
if( !empty( $val ) and filter_var( $val, FILTER_VALIDATE_EMAIL ) !== false )
|
||||
{
|
||||
throw new InvalidArgumentException( 'form_no_email_allowed' );
|
||||
}
|
||||
} ) );
|
||||
}
|
||||
}
|
||||
if ( Settings::i()->bot_antispam_type !== 'none' )
|
||||
@@ -5229,6 +5225,12 @@ abstract class Item extends Content
|
||||
*/
|
||||
public function canComment( ?Member $member=NULL, bool $considerPostBeforeRegistering = TRUE ): bool
|
||||
{
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( 'reply', $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
if( $this->containerWrapper() and !$this->container()->checkAction( 'comment' ) )
|
||||
{
|
||||
return FALSE;
|
||||
@@ -5246,6 +5248,12 @@ abstract class Item extends Content
|
||||
*/
|
||||
public function canReview( ?Member $member=NULL, bool $considerPostBeforeRegistering = TRUE ): bool
|
||||
{
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( 'review', $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
if( $this->containerWrapper() and !$this->container()->checkAction( 'review' ) )
|
||||
{
|
||||
return FALSE;
|
||||
@@ -6510,6 +6518,12 @@ abstract class Item extends Content
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( $permission, $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
/* If we can find the node... */
|
||||
try
|
||||
{
|
||||
@@ -6553,6 +6567,12 @@ abstract class Item extends Content
|
||||
*/
|
||||
public function canView( ?Member $member=NULL ): bool
|
||||
{
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( 'view', $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
$member = $member ?: Member::loggedIn();
|
||||
|
||||
/* Check it isn't hidden */
|
||||
@@ -6586,6 +6606,11 @@ abstract class Item extends Content
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if this is club content and if clubs are enabled */
|
||||
if ( $this->containerWrapper() and IPS::classUsesTrait( $this->container(), 'IPS\Content\ClubContainer' ) and $this->container()->club() and !Settings::i()->clubs )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/* Check node */
|
||||
return $this->containerWrapper() ? $this->container()->can( 'read', $member ) : true;
|
||||
}
|
||||
@@ -6598,6 +6623,12 @@ abstract class Item extends Content
|
||||
*/
|
||||
public function canChangeAuthor( ?Member $member = NULL ): bool
|
||||
{
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( 'edit', $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
return static::modPermission( 'edit', $member, $this->container() );
|
||||
}
|
||||
|
||||
@@ -6611,6 +6642,12 @@ abstract class Item extends Content
|
||||
*/
|
||||
public function canEdit( ?Member $member=NULL ): bool
|
||||
{
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( 'edit', $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
if( !$this->actionEnabled( 'edit', $member ) )
|
||||
{
|
||||
return false;
|
||||
@@ -6656,6 +6693,12 @@ abstract class Item extends Content
|
||||
{
|
||||
$member = $member ?: Member::loggedIn();
|
||||
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( 'edit', $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
/* Are we restricted from posting or have an unacknowledged warning? */
|
||||
if ( $member->restrict_post or ( $member->members_bitoptions['unacknowledged_warnings'] and Settings::i()->warn_on and Settings::i()->warnings_acknowledge ) )
|
||||
{
|
||||
@@ -6704,6 +6747,12 @@ abstract class Item extends Content
|
||||
*/
|
||||
public function canMove( ?Member $member=NULL ): bool
|
||||
{
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( 'move', $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
if( !$this->actionEnabled( 'move', $member ) )
|
||||
{
|
||||
return false;
|
||||
@@ -6729,6 +6778,12 @@ abstract class Item extends Content
|
||||
*/
|
||||
public function canMerge( Member|null $member=NULL ): bool
|
||||
{
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( 'merge', $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
if( !$this->actionEnabled( 'merge', $member ) )
|
||||
{
|
||||
return false;
|
||||
@@ -6750,6 +6805,12 @@ abstract class Item extends Content
|
||||
*/
|
||||
public function canDelete( Member $member=NULL ): bool
|
||||
{
|
||||
/* Extensions go first */
|
||||
if( $permCheck = Permissions::can( 'delete', $this, $member ) )
|
||||
{
|
||||
return ( $permCheck === Permissions::PERM_ALLOW );
|
||||
}
|
||||
|
||||
if( !$this->actionEnabled( 'delete', $member ) )
|
||||
{
|
||||
return false;
|
||||
@@ -7150,29 +7211,29 @@ abstract class Item extends Content
|
||||
|
||||
if( $this->canEdit( $member ) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( $this->url()->setQueryString( 'do', 'edit' ), 'edit', identifier: 'edit' ) );
|
||||
$menu->add( new ContentMenuLink( $this->url()->setQueryString( 'do', 'edit' ), 'edit', identifier: 'edit', icon: 'fa-solid fa-pen-to-square' ) );
|
||||
}
|
||||
|
||||
if( $member->modPermission('can_manage_deleted_content') AND $this->hidden() == -2 )
|
||||
{
|
||||
if( IPS::classUsesTrait( $this, Hideable::class ) and $this->canRestore() )
|
||||
{
|
||||
$restore = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'restore' ) ), languageString: 'restore_as_visible', identifier: 'restore' );
|
||||
$restore = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'restore' ) ), languageString: 'restore_as_visible', identifier: 'restore', icon: 'fa-solid fa-eye' );
|
||||
$restore->requiresConfirm( 'restore_as_visible_desc' );
|
||||
$menu->add( $restore );
|
||||
$menu->add( new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'restoreAsHidden' ) ), 'restore_as_hidden', identifier: 'restore_hidden' ) );
|
||||
$menu->add( new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'restoreAsHidden' ) ), 'restore_as_hidden', identifier: 'restore_hidden', icon: 'fa-solid fa-eye-slash' ) );
|
||||
}
|
||||
|
||||
if( $this->canDelete() )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'delete', 'immediate' => 1 ) ), 'delete_immediately', identifier: 'delete_immediately' ) );
|
||||
$menu->add( new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'delete', 'immediate' => 1 ) ), 'delete_immediately', identifier: 'delete_immediately', icon: 'fa-solid fa-trash-can' ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !$this::$firstCommentRequired and $this->canReportOrRevoke() === TRUE )
|
||||
{
|
||||
$report = new ContentMenuLink( url: $this->url('report'), languageString: 'report', identifier: 'report' );
|
||||
$report = new ContentMenuLink( url: $this->url('report'), languageString: 'report', identifier: 'report', icon: 'fa-solid fa-flag' );
|
||||
if( $member->member_id OR Captcha::supportsModal() )
|
||||
{
|
||||
$report->opensDialog(title: 'report', remoteSubmit: TRUE );
|
||||
@@ -7182,26 +7243,26 @@ abstract class Item extends Content
|
||||
|
||||
if ( IPS::classUsesTrait( $this, 'IPS\Content\FuturePublishing' ) AND $this->isFutureDate() AND static::canFuturePublish($member, $this->container()))
|
||||
{
|
||||
$publish = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'publish' ) ), languageString: 'publish', dataAttributes: [ 'title=\'{lang="publish_desc"}\'' ], identifier: 'publish' );
|
||||
$publish = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'publish' ) ), languageString: 'publish', dataAttributes: [ 'title=\'{lang="publish_desc"}\'' ], identifier: 'publish', icon: 'fa-solid fa-clock' );
|
||||
$publish->requiresConfirm();
|
||||
$menu->add( $publish );
|
||||
}
|
||||
|
||||
if( IPS::classUsesTrait( $this, 'IPS\Content\Pinnable' ) AND $this->canPin( $member ) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'pin' ) ), 'pin', identifier: 'pin' ) );
|
||||
$menu->add( new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'pin' ) ), 'pin', identifier: 'pin', icon: 'fa-solid fa-thumbtack' ) );
|
||||
}
|
||||
|
||||
if( IPS::classUsesTrait( $this, 'IPS\Content\Pinnable' ) AND $this->canUnpin( $member ) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unpin' ) ), 'unpin', identifier: 'unpin' ) );
|
||||
$menu->add( new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unpin' ) ), 'unpin', identifier: 'unpin', icon: 'fa-solid fa-thumbtack-slash' ) );
|
||||
}
|
||||
|
||||
if( IPS::classUsesTrait( $this, 'IPS\Content\Hideable' ) )
|
||||
{
|
||||
if( $this->canHide($member))
|
||||
{
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'hide' ) ), languageString: 'hide' , dataAttributes: [
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'hide' ) ), languageString: 'hide', icon: 'fa-solid fa-eye-slash', dataAttributes: [
|
||||
'data-ipsDialog' => 'true',
|
||||
'data-ipsDialog-size' => 'medium',
|
||||
'data-ipsDialog-title' => Member::loggedIn()->language()->addToStack('hide'),
|
||||
@@ -7211,7 +7272,7 @@ abstract class Item extends Content
|
||||
|
||||
if( $this->canUnhide($member) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unhide' ) ), languageString: $this->hidden() === 1 ? 'approve' : 'unhide', dataAttributes: [
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unhide' ) ), languageString: $this->hidden() === 1 ? 'approve' : 'unhide', icon: 'fa-solid fa-eye', dataAttributes: [
|
||||
'data-ipsDialog-destructOnClose' => 'true'
|
||||
], identifier: 'unhide' ) );
|
||||
}
|
||||
@@ -7221,7 +7282,7 @@ abstract class Item extends Content
|
||||
{
|
||||
if( $this->canLock( $member ) )
|
||||
{
|
||||
$lock = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'lock' ) ), languageString: 'lock', identifier: 'lock' );
|
||||
$lock = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'lock' ) ), languageString: 'lock', identifier: 'lock', icon: 'fa-solid fa-lock' );
|
||||
if( $member->modPermission('can_manage_alerts') AND $this->author()->member_id )
|
||||
{
|
||||
$lock->addAttribute( 'data-ipsDialog')
|
||||
@@ -7234,32 +7295,32 @@ abstract class Item extends Content
|
||||
|
||||
if( $this->canUnlock( $member ) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unlock' ) ), languageString: 'unlock' ) );
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'unlock' ) ), languageString: 'unlock', icon: 'fa-solid fa-unlock' ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( $this->canMove( $member ) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'move' ) ), languageString: 'move' , opensDialog: true ) );
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'move' ) ), languageString: 'move' , opensDialog: true, icon: 'fa-solid fa-arrow-right' ) );
|
||||
}
|
||||
|
||||
if( $this->canMerge( $member ) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'merge' ) ), languageString: 'merge', opensDialog: true ) );
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'merge' ) ), languageString: 'merge', opensDialog: true, icon: 'fa-solid fa-down-left-and-up-right-to-center' ) );
|
||||
}
|
||||
|
||||
if( isset( static::$archiveClass ) )
|
||||
{
|
||||
if( $this->canUnarchive( $member ) )
|
||||
{
|
||||
$unarchive = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'unarchive' ) ), languageString: 'unarchive');
|
||||
$unarchive = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'unarchive' ) ), languageString: 'unarchive', icon: 'fa-solid fa-box-open');
|
||||
$unarchive->requiresConfirm( $this->unarchiveBlurb() );
|
||||
$menu->add( $unarchive );
|
||||
}
|
||||
|
||||
if( $this->canRemoveArchiveExcludeFlag( $member ) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'removeArchiveExcludeFlag' ) ), languageString: 'remove_archive_exclude_flag') );
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'removeArchiveExcludeFlag' ) ), languageString: 'remove_archive_exclude_flag', icon: 'fa-solid fa-box') );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7274,6 +7335,7 @@ abstract class Item extends Content
|
||||
if ( !$this->isFeatured() )
|
||||
{
|
||||
$feature = new ContentMenuLink( url: $this->url()->setQueryString( array( 'do' => 'feature', 'fromItem' => 1 ) ), languageString: 'promote_social_button' );
|
||||
$feature->icon ="fa-solid fa-star";
|
||||
$feature->addAttribute( 'data-ipsDialog-flashMessage', Member::loggedIn()->language()->addToStack( 'promote_flash_msg' ) )
|
||||
->addAttribute( 'data-ipsDialog-flashMessageTimeout', 5 )
|
||||
->addAttribute( 'data-ipsDialog-flashMessageEscape', 'false' )
|
||||
@@ -7284,6 +7346,7 @@ abstract class Item extends Content
|
||||
else
|
||||
{
|
||||
$feature = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'unfeature', 'fromItem' => 1 ) ), languageString: 'demote_social_button' );
|
||||
$feature->icon ="fa-regular fa-star";
|
||||
$feature->addAttribute( 'data-ipsDialog-flashMessage', Member::loggedIn()->language()->addToStack( 'demote_flash_msg' ) )
|
||||
->addAttribute( 'data-ipsDialog-flashMessageTimeout', 5 )
|
||||
->addAttribute( 'data-ipsDialog-flashMessageEscape', 'false' )
|
||||
@@ -7297,6 +7360,7 @@ abstract class Item extends Content
|
||||
if( $this->canDelete( $member ) )
|
||||
{
|
||||
$delete = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' =>'delete' ) ), languageString: 'delete');
|
||||
$delete->icon ="fa-solid fa-trash";
|
||||
$delete->requiresConfirm();
|
||||
$menu->add( $delete );
|
||||
}
|
||||
@@ -7308,23 +7372,24 @@ abstract class Item extends Content
|
||||
if( IPS::classUsesTrait( $this, Statistics::class ) )
|
||||
{
|
||||
$analytics = new ContentMenuLink( url:$this->url()->setQueryString( array( 'do' => 'analytics' ) ), languageString: 'analytics_and_stats' );
|
||||
$analytics->icon = "fa-solid fa-chart-simple";
|
||||
$analytics->opensDialog( 'analytics_and_stats', 'large' );
|
||||
$menu->add( $analytics );
|
||||
}
|
||||
|
||||
$menu->add( new ContentMenuLink( url:$this->url()->setQueryString( array( 'do' => 'modlog' ) ), languageString: 'moderation_history' , opensDialog: true ) );
|
||||
$menu->add( new ContentMenuLink( url:$this->url()->setQueryString( array( 'do' => 'modlog' ) ), languageString: 'moderation_history' , opensDialog: true, icon: 'fa-solid fa-clock-rotate-left' ) );
|
||||
}
|
||||
|
||||
if( IPS::classUsesTrait( $this, MetaData::class ) )
|
||||
{
|
||||
if( $this->canOnMessage( 'add', $member ) )
|
||||
{
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->setQueryString( array( 'do' => 'messageForm' ) ), languageString: 'add_message' , opensDialog: true ) );
|
||||
$menu->add( new ContentMenuLink( url: $this->url()->setQueryString( array( 'do' => 'messageForm' ) ), languageString: 'add_message' , opensDialog: true, icon: 'fa-solid fa-message' ) );
|
||||
}
|
||||
|
||||
if( $this->canToggleItemModeration( $member ) )
|
||||
{
|
||||
$toggle = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'toggleItemModeration' ) ), languageString: $this->itemModerationEnabled() ?'disable_topic_moderation' : 'enable_topic_moderation' );
|
||||
$toggle = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'toggleItemModeration' ) ), languageString: $this->itemModerationEnabled() ?'disable_topic_moderation' : 'enable_topic_moderation', icon: 'fa-solid fa-traffic-light' );
|
||||
$toggle->requiresConfirm( $this->itemModerationEnabled() ? 'disable_topic_moderation_confirm' : 'enable_topic_moderation_confirm' );
|
||||
$menu->add( $toggle );
|
||||
}
|
||||
@@ -7335,7 +7400,7 @@ abstract class Item extends Content
|
||||
$menu->addSeparator();
|
||||
foreach ( $actions as $action )
|
||||
{
|
||||
$actionLink = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'savedAction', 'action' => $action->_id ) ), languageString: $action->_title );
|
||||
$actionLink = new ContentMenuLink( url: $this->url()->csrf()->setQueryString( array( 'do' => 'savedAction', 'action' => $action->_id ) ), languageString: $action->_title, icon: 'fa-solid fa-wand-magic' );
|
||||
$actionLink->requiresConfirm();
|
||||
$menu->add( $actionLink );
|
||||
}
|
||||
@@ -7474,17 +7539,28 @@ abstract class Item extends Content
|
||||
* @apiresponse datetime date Date
|
||||
* @apiresponse string content Content
|
||||
* @apiresponse string url URL
|
||||
* @apiresponse \IPS\core\Assignments\Assignment|null assignment Assignment data
|
||||
*/
|
||||
public function apiOutput( Member $authorizedMember = NULL ): array
|
||||
{
|
||||
return array(
|
||||
$data = [
|
||||
'id' => $this->id,
|
||||
'title' => $this->title ? $this->title : NULL,
|
||||
'container' => $this->containerWrapper()?->apiOutput( $authorizedMember ),
|
||||
'title' => $this->mapped( 'title' ),
|
||||
'container' => $this->containerWrapper( true )?->apiOutput( $authorizedMember ),
|
||||
'author' => $this->author()->apiOutput( $authorizedMember ),
|
||||
'date' => ( isset( static::$databaseColumnMap['date'] ) ? DateTime::ts( $this->mapped( 'date' ) )->rfc3339() : null ),
|
||||
'content' => $this->content(),
|
||||
'url' => (string) $this->url()
|
||||
);
|
||||
'url' => (string) $this->url(),
|
||||
];
|
||||
if ( IPS::classUsesTrait( $this, Assignable::class ) )
|
||||
{
|
||||
$data['assignment'] = $this->assignment ? $this->assignment->apiOutput($authorizedMember, false ) : NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$data['assignment'] = NULL;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user