Version 4.7.0

This commit is contained in:
Neo committed 2025-12-19 16:00:35 -08:00
1 parent 517a5e1f70
commit 8ba7d43898
1933 files changed
+65613 -11332

No files matched your search

+14 -3
View File
@@ -390,7 +390,6 @@ class _Controller extends \IPS\Dispatcher\Controller
}
}
}
/* Build form */
$form = $this->_addEditForm( $node );
@@ -1121,10 +1120,22 @@ class _Controller extends \IPS\Dispatcher\Controller
$matrix = new \IPS\Helpers\Form\Matrix;
$matrix->manageable = FALSE;
$matrix->langPrefix = $nodeClass::$permissionLangPrefix . 'perm__';
$matrix->styledRowTitle = TRUE;
$matrix->columns = array(
'label' => function( $key, $value, $data )
{
return $value;
$groupId = explode( '[', $key )[0];
$groupLink = NULL;
/* Let's try to get a link to the group's permissions */
try
{
\IPS\Member\Group::load( $groupId );
$groupLink = \IPS\Http\Url::internal( "app=core&module=members&controller=groups&do=permissions&id=" . $groupId );
}
catch ( \OutOfRangeException $e ) { }
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->titleWithLink( $value, $groupLink, 'all_permissions', \IPS\Member::loggedIn()->language()->addToStack( "all_permissions_group_hovertitle", FALSE, array( 'sprintf' => [ $value ] ) ) );
},
);
@@ -1156,7 +1167,7 @@ class _Controller extends \IPS\Dispatcher\Controller
if ( $disabled === FALSE )
{
$disabled = ( $groupId == \IPS\Settings::i()->guest_group AND \in_array( $k, array('review', 'rate' ) ) ) ? TRUE : FALSE;
$disabled = ( $groupId == \IPS\Settings::i()->guest_group AND \in_array( $k, array( 'review', 'rate' ) ) ) ? TRUE : FALSE;
}
$fieldValue = ( isset( $current[ "perm_{$v}" ] ) and ( $current[ "perm_{$v}" ] === '*' or \in_array( $groupId, explode( ',', $current[ "perm_{$v}" ] ) ) ) );
+150 -10
View File
@@ -511,6 +511,38 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
throw new \BadMethodCallException;
}
/**
* @brief Cached AdminCP urls
*/
protected $_acpUrls = array();
/**
* @brief The class of the ACP \IPS\Node\Controller that manages this node type
*/
protected static $acpController = NULL;
/**
* Get the URL of the AdminCP page for this node
*
* @param string|NULL $do The "do" query parameter of the url (e.g. 'form', 'permissions', etc).
*
* @return \IPS\Http\Url | NULL
*/
public function acpUrl( $do="form" )
{
if ( !isset( $this->acpUrls[$do] ) AND static::$acpController !== NULL )
{
/* Does the method exist? */
if ( method_exists( static::$acpController, $do ) )
{
$bits = explode( "\\", static::$acpController );
$acpUrlBase = "app={$bits[1]}&module={$bits[4]}&controller={$bits[5]}";
$this->_acpUrls[$do] = \IPS\Http\Url::internal( $acpUrlBase )->setQueryString( array( 'do' => $do, 'id' => $this->_id ) );
}
}
return $this->_acpUrls[$do] ?? NULL;
}
/**
* Columns needed to query for search result / stream view
*
@@ -738,7 +770,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
{
if ( static::$titleLangPrefix and static::$descriptionLangSuffix )
{
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $this->id . static::$descriptionLangSuffix, FALSE, array( 'striptags' => TRUE, 'escape' => TRUE ) );
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $this->id . static::$descriptionLangSuffix, FALSE, array( 'striptags' => TRUE, 'escape' => TRUE, 'removeNewlines' => TRUE ) );
}
return NULL;
}
@@ -1610,24 +1642,39 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
* @brief Constant allow anonymous comments
*/
const ANON_COMMENTS = 2;
/**
* [Node] Does the currently logged in user have permission to post anonymously in this node?
*
* @return bool
* @param int $where What are we checking, 0 => means don't check it, 2 means comments only
* @param \IPS\Member|NULL $member The member posting anonymously or NULL for logged in member
* @return bool
*/
public function canPostAnonymously( $where = 0 )
public function canPostAnonymously( $where = 0, \IPS\Member $member = NULL ): bool
{
if ( ! isset( static::$contentItemClass ) or ! \in_array( 'IPS\Content\Anonymous', class_implements( static::$contentItemClass ) ) )
{
return FALSE;
return FALSE;
}
if( ! \IPS\Member::loggedIn()->member_id or ! \IPS\Member::loggedIn()->group['gbw_can_post_anonymously'] )
$member = $member ?: \IPS\Member::loggedIn();
if( ! $member->member_id or ! $member->group['gbw_can_post_anonymously'] )
{
return FALSE;
}
return $this->isAnonymousContentSupported( $where );
}
/**
* Does the current node support anonymous content?
*
* @param int $where What are we checking, 0 => means don't check it, 2 means comments only
* @return bool
*/
public function isAnonymousContentSupported( int $where = 0 ) : bool
{
/* 1 means both items and comments */
if ( $this->allow_anonymous === 1 )
{
@@ -1639,13 +1686,12 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
{
return TRUE;
}
/* 2 means comments only */
if ( $where === static::ANON_COMMENTS and $this->allow_anonymous === 2 )
{
return TRUE;
}
return FALSE;
}
@@ -2206,6 +2252,16 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
return $this->can( 'view', $member );
}
/**
* Return if this node has custom permissions
*
* @return null|array
*/
public static function customPermissionNodes()
{
return NULL;
}
/**
* @brief Cached permission checks
*/
@@ -3472,7 +3528,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
protected static $deleteOrMoveQueue = NULL;
/**
* Is this node currently queued for deleting or moving content?
* Is this node currently queued for deleting or moving content OR is it the target of content queued to be moved from another node?
*
* @return bool
*/
@@ -3500,6 +3556,15 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
{
$this->queued = TRUE;
}
elseif ( isset( $data['moveTo'] ) )
{
$moveToClass = $data['moveToClass'] ?? \get_class( $this );
if ( $moveToClass === \get_class( $this ) AND $data['moveTo'] == $this->_id )
{
$this->queued = TRUE;
}
}
}
}
@@ -3687,6 +3752,11 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
{
$return['permissions'] = \in_array( 'IPS\Node\Permissions', class_implements( \get_class( $this ) ) ) ? $this->permissions() : NULL;
}
if ( \IPS\IPS::classUsesTrait( \get_called_class(), 'IPS\Content\ClubContainer' ) AND $this->club() )
{
$return['public'] = ( $this->isPublic() ) ? 1 : 0;
}
return $return;
}
@@ -3710,4 +3780,74 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
{
return static::$nodeTitle . '_sg';
}
/**
* @brief Field cache for getDataLayerProperties
*/
protected $_dataLayerProperties = array();
/**
* Get the properties that can be added to the datalayer for this key
*
* @return array
*/
public function getDataLayerProperties()
{
if ( empty( $this->_dataLayerProperties ) )
{
try
{
$url = (string) $this->url();
}
catch ( \BadMethodCallException $e )
{
$url = null;
}
/* Try to figure out the Node's app, its lang key will be the area */
$area = "System";
if ( static::$permApp )
{
$app = static::$permApp;
}
elseif ( !empty( static::$restrictions['app'] ) )
{
$app = static::$restrictions['app'];
}
else
{
$class = \get_called_class();
$matches = array();
preg_match( '/IPS\\\\([a-z]+)\\\\/', $class, $matches );
$app = $matches[1] ?? null;
}
/* If we got a valid app (directory) name, try its language key */
if ( $app )
{
$area = \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->addToStack( "__app_$app" );
}
/* Get the path */
$type = static::$nodeTitle ?? 'container';
$path = [
array( 'type' => $type, 'id' => $this->_id, 'name' => $this->_title )
];
foreach ( $this->parents() as $parent )
{
$path[] = array( 'type' => $parent::$nodeTitle ?? 'container', 'id' => $parent->_id, 'name' => $parent->_title );
}
$this->_dataLayerProperties = array(
'content_area' => $area,
'content_container_id' => $this->_id,
'content_container_name' => $this->_title,
'content_container_type' => $type,
'content_container_url' => $url,
'content_container_path' => array_reverse( $path ),
);
}
return $this->_dataLayerProperties;
}
}
+13 -5
View File
@@ -87,6 +87,15 @@ trait Statistics
if ( $reload )
{
$items = iterator_to_array( \IPS\Db::i()->select( 'map_item_id, map_member_id', 'core_item_member_map', array(array('map_class=?', $contentItemClass), array(\IPS\Db::i()->in( 'map_item_id', $inSet ) ) ) ) );
$itemIds = [];
if ( \count( $items ) )
{
foreach ( $items as $row )
{
$itemIds[] = $row['map_item_id'];
}
}
if ( $itemIds )
{
@@ -223,7 +232,6 @@ trait Statistics
$commentClass = $contentItemClass::$commentClass;
$commentItemField = $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['item'];
$authorColumn = $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['author'];
$dateColumn = $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['date'];
$where = array();
$deleteWhere = array( array( \IPS\Db::i()->in( 'map_item_id', $inSet ) ) );
@@ -258,9 +266,9 @@ trait Statistics
$itemWhere[] = array( \IPS\Db::i()->in( $hiddenColumn, array( 0, 2 ) ) ); # We want approved comments but also comments hidden because the item is hidden
}
$itemWhere[] = array( \IPS\Db::i()->in( $contentItemClass::$databaseColumnId, $inSet ) );
$itemWhere[] = array( \IPS\Db::i()->in( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnId, $inSet ) );
$insertFromSelect = \IPS\Db::i()->select( "'" . str_replace( '\\', '\\\\', $contentItemClass ) . "', `{$contentItemClass::$databaseColumnId}`, `{$itemAuthorColumn}`, `{$itemDateColumn}`" , $contentItemClass::$databaseTable, $itemWhere );
$insertFromSelect = \IPS\Db::i()->select( "'" . str_replace( '\\', '\\\\', $contentItemClass ) . "', `{$contentItemClass::$databasePrefix}{$contentItemClass::$databaseColumnId}`, `{$itemAuthorColumn}`, `{$itemDateColumn}`" , $contentItemClass::$databaseTable, $itemWhere );
\IPS\Db::i()->replace( 'core_item_member_map', $insertFromSelect, TRUE );
}
@@ -283,8 +291,8 @@ trait Statistics
$where[] = array(\IPS\Db::i()->in( $commentItemField, $inSet ));
$insertFromSelect = \IPS\Db::i()->select( "'" . str_replace( '\\', '\\\\', $contentItemClass ) . "', `{$commentItemField}`, `{$authorColumn}`, `{$dateColumn}`", $commentClass::$databaseTable, $where, $dateColumn . ' ASC' );
\IPS\Db::i()->replace( 'core_item_member_map', $insertFromSelect, TRUE );
$insertFromSelect = \IPS\Db::i()->select( "DISTINCT `{$authorColumn}`, `{$commentItemField}`, UNIX_TIMESTAMP(), '" . str_replace( '\\', '\\\\', $contentItemClass ) . "'", $commentClass::$databaseTable, $where );
\IPS\Db::i()->replace( 'core_item_member_map', [ 'map_member_id, map_item_id, map_latest_date, map_class', $insertFromSelect ], TRUE );
}
}