Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+101 -60
View File
@@ -142,21 +142,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
{
$member = $member ?: \IPS\Member::loggedIn();
}
/* Have we got a cached result we can use? */
if ( $usingPermssions )
{
$cacheKey = md5( get_called_class() . $permissionCheck . $member->member_id . json_encode( $where ) );
}
else
{
$cacheKey = md5( get_called_class() . $permissionCheck . json_encode( $where ) );
}
if( isset( static::$rootsResult[ $cacheKey ] ) )
{
return static::$rootsResult[ $cacheKey ];
}
/* Specify that we only want the ones without a parent */
if( static::$databaseColumnParent !== NULL )
{
@@ -168,7 +154,22 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
{
$where[] = array( static::$databasePrefix . static::clubIdColumn() . ' IS NULL' );
}
/* Have we got a cached result we can use? */
if ( $usingPermssions )
{
$cacheKey = md5( get_called_class() . $permissionCheck . $member->member_id . json_encode( $where ) );
}
else
{
$cacheKey = md5( get_called_class() . $permissionCheck . json_encode( $where ) );
}
if( isset( static::$rootsResult[ $cacheKey ] ) )
{
return static::$rootsResult[ $cacheKey ];
}
/* Fetch */
$nodes = static::nodesWithPermission( $usingPermssions ? $permissionCheck : NULL, $member, $where );
@@ -185,13 +186,15 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
* @param string|NULL $permissionCheck The permission key to check for or NULl to not check permissions
* @param \IPS\Member|NULL $member The member to check permissions for or NULL for the currently logged in member
* @param mixed $where Additional WHERE clause
* @param string $order ORDER BY clause
* @return array
*/
protected static function nodesWithPermission( $permissionCheck, $member, $where=array() )
protected static function nodesWithPermission( $permissionCheck, $member, $where=array(), $order=NULL )
{
/* Permission check? */
if ( $permissionCheck )
{
$member = $member ?: \IPS\Member::loggedIn();
$where[] = array( '(' . \IPS\Db::i()->findInSet( 'core_permission_index.perm_' . static::$permissionMap[ $permissionCheck ], $member->permissionArray() ) . ' OR ' . 'core_permission_index.perm_' . static::$permissionMap[ $permissionCheck ] . '=? )', '*' );
if ( static::$databaseColumnEnabledDisabled )
{
@@ -200,12 +203,11 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
}
/* Specify the order */
$order = NULL;
if( static::$databaseColumnOrder !== NULL )
if( $order == NULL and static::$databaseColumnOrder !== NULL )
{
$order = static::$databasePrefix . static::$databaseColumnOrder;
}
/* Select */
$select = \IPS\Db::i()->select( '*', static::$databaseTable, $where, $order );
if ( $permissionCheck )
@@ -224,7 +226,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
}
catch ( \Exception $e ) { }
}
/* Return */
return $nodes;
}
@@ -480,6 +482,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
* Get URL
*
* @return \IPS\Http\Url
* @throws BadMethodCallException
*/
public function url()
{
@@ -508,7 +511,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
/* Using colorize trait? We can't check the object but we can check the added class variable */
if ( isset( static::$featureColumnName ) )
{
$return[] = static::$featureColumnName;
$return[] = static::$databasePrefix . static::$featureColumnName;
}
return $return;
@@ -528,7 +531,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
}
/**
* Get URL from index data
* Get title from index data
*
* @param array $indexData Data from the search index
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
@@ -635,17 +638,36 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
/**
* [Node] Get Title
*
* @return string|null
* @return string
*/
protected function get__title()
{
if ( static::$titleLangPrefix )
{
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $this->_id );
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $this->_id, NULL, array( 'escape' => TRUE ) );
}
return '';
}
/**
* Get the title for a node using the specified language object
* This is commonly used where we cannot use the logged in member's language, such as sending emails
*
* @param \IPS\Lang $language Language object to fetch the title with
* @param array $options What options to use for language parsing
* @return string
*/
public function getTitleForLanguage( $language, $options=array() )
{
if ( static::$titleLangPrefix )
{
return $language->addToStack( static::$titleLangPrefix . $this->_id, NULL, $options );
}
return '';
}
/**
* [Node] Get Title language key, not added to a language stack
*
@@ -662,16 +684,13 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
/**
* Node titles can contain HTML. Apparently.
* @deprecated in 4.3, will be removed in 4.4.
*
* @return string
*/
public function get__stripTagsTitle()
{
if ( static::$titleLangPrefix )
{
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $this->_id, NULL, array( 'striptags' => true ) );
}
return '';
return $this->_title;
}
/**
@@ -729,7 +748,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
*/
public function metaTitle()
{
return $this->_stripTagsTitle;
return $this->_title;
}
/**
@@ -1807,10 +1826,11 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
*
* @param mixed $permission A key which has a value in static::$permissionMap['view'] matching a column ID in core_permission_index
* @param \IPS\Member|\IPS\Member\Group|NULL $member The member or group to check (NULL for currently logged in member)
* @param array $where Additional WHERE clause
* @return bool
* @throws \OutOfBoundsException If $permission does not exist in static::$permissionMap
*/
public static function canOnAny( $permission, $member=NULL )
public static function canOnAny( $permission, $member=NULL, $where = array() )
{
/* If this is not permission-dependant, return TRUE */
if ( !in_array( 'IPS\Node\Permissions', class_implements( get_called_class() ) ) )
@@ -1821,7 +1841,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
/* Check it exists */
if ( !isset( static::$permissionMap[ $permission ] ) )
{
throw \OutOfBoundsException;
throw new \OutOfBoundsException;
}
/* Load member */
@@ -1836,28 +1856,25 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
return FALSE;
}
$_key = md5( get_called_class() );
$_key = md5( get_called_class() . json_encode( $where ) );
/* Have we already cached the check? */
if( isset( static::$_canOnAny[ $_key ] ) )
if( isset( static::$_canOnAny[ $_key ][ $permission ] ) )
{
return static::$_canOnAny[ $_key ];
return static::$_canOnAny[ $_key ][ $permission ];
}
/* Return */
$where = array( );
$where[] = array( \IPS\Db::i()->findInSet( 'core_permission_index.perm_' . static::$permissionMap[ $permission ], $member->groups ) . ' OR core_permission_index.perm_' . static::$permissionMap[ $permission ] . "='*'" );
if ( static::$databaseColumnEnabledDisabled )
{
static::$_canOnAny[ $_key ] = (bool) \IPS\Db::i()->select( 'COUNT(*)', static::$databaseTable, array( static::$databasePrefix . static::$databaseColumnEnabledDisabled . '=1 AND (' . \IPS\Db::i()->findInSet( 'core_permission_index.perm_' . static::$permissionMap[ $permission ], $member->groups ) . ' OR core_permission_index.perm_' . static::$permissionMap[ $permission ] . "='*' )" ) )
->join( 'core_permission_index', array( "core_permission_index.app=? AND core_permission_index.perm_type=? AND core_permission_index.perm_type_id=" . static::$databaseTable . "." . static::$databasePrefix . static::$databaseColumnId, static::$permApp, static::$permType ) )
->first();
}
else
{
static::$_canOnAny[ $_key ] = (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_permission_index', array( 'core_permission_index.app=? AND core_permission_index.perm_type=? AND (' . \IPS\Db::i()->findInSet( 'core_permission_index.perm_' . static::$permissionMap[ $permission ], $member->groups ) . ' OR core_permission_index.perm_' . static::$permissionMap[ $permission ] . "='*' )", static::$permApp, static::$permType ) )->first();
$where[] = array( static::$databasePrefix . static::$databaseColumnEnabledDisabled . '=1' );
}
static::$_canOnAny[ $_key ][ $permission ] = (bool) \IPS\Db::i()->select( 'COUNT(*)', static::$databaseTable, $where )
->join( 'core_permission_index', array( "core_permission_index.app=? AND core_permission_index.perm_type=? AND core_permission_index.perm_type_id=" . static::$databaseTable . "." . static::$databasePrefix . static::$databaseColumnId, static::$permApp, static::$permType ) )
->first();
return static::$_canOnAny[ $_key ];
return static::$_canOnAny[ $_key ][ $permission ];
}
/**
@@ -1865,6 +1882,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
* Allow node classes to define permissions that are unselectable in the permission matrix
*
* @return array array( {group_id} => array( 'read', 'view', 'perm_7' );
* @throws UnderflowException (if guest group ID is invalid)
*/
public function disabledPermissions()
{
@@ -1987,6 +2005,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
* @return array
* @throws
* @li BadMethodCallException
* @li UnderflowException (if guest group ID is invalid)
*/
public static function populatePermissionMatrix( &$rows, $node, $group, $current, $level=0 )
{
@@ -2100,6 +2119,17 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
/* If we are checking view permissions, make sure we can view parent too */
if( $permission == 'view' )
{
/* If this is a club node, make sure we can access the clubs module */
if ( \IPS\IPS::classUsesTrait( get_called_class(), 'IPS\Content\ClubContainer' ) AND $this->club() )
{
if ( !$member->canAccessModule( \IPS\Application\Module::get( 'core', 'clubs', 'front' ) ) )
{
return FALSE;
}
/* Let the rest of the method bubble up as necessary */
}
try
{
foreach( $this->parents() as $parent )
@@ -2131,11 +2161,11 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
if( $member instanceof \IPS\Member\Group )
{
return ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] === '*' or ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] and in_array( $member->g_id, explode( ',', $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] ) ) ) );
return ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] === '*' or ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] and in_array( $member->g_id, array_filter( explode( ',', $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] ) ) ) ) );
}
else
{
return ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] === '*' or ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] and array_intersect( explode( ',', $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] ), $member->permissionArray() ) ) );
return ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] === '*' or ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] and array_intersect( array_filter( explode( ',', $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] ) ), $member->permissionArray() ) ) );
}
}
@@ -2144,13 +2174,14 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
*
* @param string $type 'edit', 'hide', 'unhide', 'delete', etc.
* @param \IPS\Member|NULL $member The member to check for or NULL for the currently logged in member
* @param string|NULL $itemClass The item class to check against, or NULL to check against static::$contentItemClass
* @return bool
*/
public function modPermission( $type, \IPS\Member $member )
public function modPermission( $type, \IPS\Member $member, $itemClass=NULL )
{
if ( isset( static::$modPerm ) )
{
$itemClass = static::$contentItemClass;
$itemClass = $itemClass ?: static::$contentItemClass;
$title = $itemClass::$title;
if ( $member->modPermission( "can_{$type}_{$title}" ) )
{
@@ -2378,7 +2409,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
}
if ( $this->_items !== NULL )
{
$this->_items = \IPS\Db::i()->select( 'COUNT(*)', $itemClass::$databaseTable, $containerWhere )->first();
$this->_items = \IPS\Db::i()->select( 'COUNT(*)', $itemClass::$databaseTable, $containerWhere, NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
}
if ( $this->_comments !== NULL AND $commentClass !== NULL )
{
@@ -2410,8 +2441,8 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
$this->_comments = \IPS\Db::i()->select( 'COUNT(*)', array(
array( $commentClass::$databaseTable, $commentClass::$databaseTable ),
array( $itemClass::$databaseTable, $itemClass::$databaseTable )
), $commentWhere )->first();
array( $itemClass::$databaseTable, $itemClass::$databaseTable ),
), $commentWhere, NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
}
if ( $this->_reviews !== NULL AND $reviewClass !== NULL )
@@ -2433,7 +2464,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
$this->_reviews = \IPS\Db::i()->select( 'COUNT(*)', array(
array( $reviewClass::$databaseTable, $reviewClass::$databaseTable ),
array( $itemClass::$databaseTable, $itemClass::$databaseTable )
), $reviewWhere )->first();
), $reviewWhere, NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
}
if ( in_array( 'IPS\Content\Hideable', class_implements( $itemClass ) ) )
@@ -2451,7 +2482,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
$hiddenContainerWhere[] = array( $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['hidden'] . '=?', 1 );
}
$this->_unapprovedItems = \IPS\Db::i()->select( 'COUNT(*)', $itemClass::$databaseTable, $hiddenContainerWhere )->first();
$this->_unapprovedItems = \IPS\Db::i()->select( 'COUNT(*)', $itemClass::$databaseTable, $hiddenContainerWhere, NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
}
if( $commentClass !== NULL AND in_array( 'IPS\Content\Hideable', class_implements( $commentClass ) ) )
@@ -2481,7 +2512,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
$this->_unapprovedComments = \IPS\Db::i()->select( 'COUNT(*)', array(
array( $commentClass::$databaseTable, $commentClass::$databaseTable ),
array( $itemClass::$databaseTable, $itemClass::$databaseTable )
), $commentWhere )->first();
), $commentWhere, NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
}
}
@@ -2490,7 +2521,6 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
$reviewWhere = array( array( $reviewClass::$databaseTable . '.' . $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['item'] . ' = ' . $itemClass::$databaseTable . '.' . $itemClass::$databasePrefix . $itemIdColumn ) );
if ( $this->_unapprovedReviews !== NULL )
{
$reviewWhere = array_merge( $reviewWhere, $anyContainerWhere );
if ( isset( $reviewClass::$databaseColumnMap['approved'] ) )
@@ -2505,7 +2535,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
$this->_unapprovedReviews = \IPS\Db::i()->select( 'COUNT(*)', array(
array( $reviewClass::$databaseTable, $reviewClass::$databaseTable ),
array( $itemClass::$databaseTable, $itemClass::$databaseTable )
), $reviewWhere )->first();
), $reviewWhere, NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
}
}
}
@@ -3056,7 +3086,17 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
if ( $showMoveToChildren or $this->hasChildren( NULL, NULL, TRUE ) )
{
\IPS\Member::loggedIn()->language()->words['node_move_children'] = sprintf( \IPS\Member::loggedIn()->language()->get( 'node_move_children', FALSE ), \IPS\Member::loggedIn()->language()->addToStack( static::$nodeTitle, FALSE, array( 'strtolower' => TRUE) ) );
$form->add( new \IPS\Helpers\Form\Node( 'node_move_children', 0, TRUE, array( 'class' => get_class( $this ), 'disabled' => array( $this->_id ), 'disabledLang' => 'node_move_delete', 'zeroVal' => 'node_delete_children', 'subnodes' => FALSE ) ) );
$form->add( new \IPS\Helpers\Form\Node( 'node_move_children', 0, TRUE, array(
'class' => get_class( $this ),
'disabled' => array( $this->_id ),
'disabledLang' => 'node_move_delete',
'zeroVal' => 'node_delete_children',
'subnodes' => FALSE,
'permissionCheck' => function( $node )
{
return $node->canAdd();
}
) ) );
}
if ( $hasContent )
{
@@ -3089,7 +3129,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
{
if ( $values['node_move_children'] )
{
$parentColumn = static::$databaseColumnParent;
$parentColumn = ( isset( static::$subnodeClass ) AND $child instanceof static::$subnodeClass ) ? $child::$parentNodeColumnId : $child::$databaseColumnParent;
$child->$parentColumn = \IPS\Request::i()->node_move_children;
$child->setLastComment();
$child->setLastReview();
@@ -3330,12 +3370,13 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @return array
* @apiresponse int id ID number
* @apiresponse string name Name
* @apiresponse string url URL
*/
public function apiOutput()
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
return array(
'id' => $this->id,