Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

@@ -12,7 +12,7 @@
namespace IPS\cms;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -128,7 +128,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
/**
* @brief [Node] Type for permission index
*/
public static $permType = 'categories';
public static $permType = NULL;
/**
* @brief The map of permission columns
@@ -235,7 +235,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
* Test to see if this is a valid container ID
*
* @param int $id Container ID
* @reply boolean
* @return boolean
*/
public static function isValidContainerId( $id )
{
@@ -244,7 +244,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
static::$containerIds = iterator_to_array( \IPS\Db::i()->select( 'category_id', static::$databaseTable, array( array( 'category_database_id=?', static::$customDatabaseId ) ) ) );
}
return in_array( $id, static::$containerIds );
return \in_array( $id, static::$containerIds );
}
/**
@@ -258,16 +258,17 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
* @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 array|NULL $limit Limit/offset to use, or NULL for no limit (default)
* @return array
*/
public static function roots( $permissionCheck='view', $member=NULL, $where=array() )
public static function roots( $permissionCheck='view', $member=NULL, $where=array(), $limit=NULL )
{
if ( static::$customDatabaseId !== NULL )
{
$where[] = array('category_database_id=?', static::$customDatabaseId );
$where[] = array( 'category_database_id=?', static::$customDatabaseId );
}
return parent::roots( $permissionCheck, $member, $where );
return parent::roots( $permissionCheck, $member, $where, $limit );
}
/**
@@ -320,7 +321,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
$root = $bits[0];
/* _ is here due to IP.Board 3.x using that to denote the articles database (eg. articles.html/_/category/record-r1/), which we need to redirect from for records and articles. */
if ( in_array( $root, array( 'submit', '_' ) ) )
if ( \in_array( $root, array( 'submit', '_' ) ) )
{
return TRUE;
}
@@ -336,7 +337,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function set_fields( $value )
{
$this->_data['fields'] = ( is_array( $value ) ? json_encode( $value ) : $value );
$this->_data['fields'] = ( \is_array( $value ) ? json_encode( $value ) : $value );
}
/**
@@ -465,7 +466,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
'key' => ( $this->id ? "content_cat_name_" . $this->id . "_desc" : NULL )
) ) );
$class = get_called_class();
$class = \get_called_class();
$form->add( new \IPS\Helpers\Form\Node( 'category_parent_id', ( ! $this->id ) ? ( isset( \IPS\Request::i()->parent ) ? \IPS\Request::i()->parent : 0 ) : $this->parent_id, FALSE, array(
'class' => '\IPS\cms\Categories' . \IPS\Request::i()->database_id,
@@ -576,7 +577,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
}
}
if ( count( $options ) )
if ( \count( $options ) )
{
$form->add( new \IPS\Helpers\Form\Select( 'category_fields', $cats, FALSE, array(
'multiple' => true,
@@ -607,6 +608,9 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
$values['category_database_id'] = $this->database_id;
}
/* Need this for later */
$_new = $this->_new;
if ( ! $this->id )
{
@@ -614,7 +618,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
$this->save();
}
if ( isset( $values['category_name'] ) AND is_array( $values['category_name'] ) )
if ( isset( $values['category_name'] ) AND \is_array( $values['category_name'] ) )
{
$name = $values['category_name'][ \IPS\Lang::defaultLanguage() ];
}
@@ -679,6 +683,12 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
else if( isset( $values['category_furl_name'] ) )
{
$values['furl_name'] = \IPS\Http\Url\Friendly::seoTitle( $values['category_furl_name'] );
/* We cannot have numeric furl_names 'cos you could do page/2/ and it will confuse SEO pagination. This is not possible with other areas as it is always /id-furl/ */
if ( \is_numeric( $values['furl_name'] ) )
{
$values['furl_name'] = 'n' . $values['furl_name'];
}
}
if( array_key_exists( 'category_furl_name_choice', $values ) )
@@ -719,6 +729,17 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
unset( $values[ 'database_' . $field ] );
}
}
/* Are we changing where comments go? */
if ( !$_new AND ( (int) $this->forum_record != (int) $values['forum_record'] OR (int) $this->forum_comments != (int) $values['forum_comments'] ) )
{
\IPS\Task::queue( 'cms', 'MoveComments', array(
'databaseId' => $this->database()->id,
'categoryId' => $this->_id,
'to' => ( $values['forum_comments'] AND $values['forum_record'] ) ? 'forums' : 'pages',
'deleteTopics' => (bool) ( !$values['forum_record'] )
), 1, array( 'databaseId', 'to', 'categoryId' ) );
}
if ( isset( $values['database_forum_forum'] ) )
{
@@ -768,7 +789,13 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
$catPerms = $this->permissions(); /* Called to ensure it has a perm row */
$dbPerms = \IPS\cms\Databases::load( $this->database_id )->permissions();
$this->_permissions = array_merge( $dbPerms, array( 'perm_id' => $catPerms['perm_id'], 'perm_type_id' => $this->_id, 'perm_type' => 'categories' ) );
$this->_permissions = array_merge( $dbPerms, array( 'perm_id' => $catPerms['perm_id'], 'perm_type_id' => $this->_id, 'perm_type' => 'categories_' . $this->database_id ) );
if( $this->_permissions['perm_view'] === NULL )
{
$this->_permissions['perm_view'] = '';
}
\IPS\Db::i()->update( 'core_permission_index', $this->_permissions, array( 'perm_id=?', $catPerms['perm_id'] ) );
/* Update tags permission cache */
@@ -821,15 +848,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
\IPS\Db::i()->delete( 'core_tags_perms', array( 'tag_perm_aap_lookup=?', $aap ) );
/* Delete category follows */
\IPS\Db::i()->delete( 'core_follow', array( "follow_app=? AND follow_area=? AND follow_rel_id=?", static::$permApp, static::$permType . $this->database()->id, $this->_id ) );
if( $this->hasChildren() )
{
foreach ( $this->children( NULL, NULL, TRUE ) as $child )
{
$child->delete();
}
}
\IPS\Db::i()->delete( 'core_follow', array( "follow_app=? AND follow_area=? AND follow_rel_id=?", 'cms', 'categories' . $this->database()->id, $this->_id ) );
parent::delete();
}
@@ -885,7 +904,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
foreach ( \IPS\Member\Group::groups() as $group )
{
if ( ! in_array( $group->g_id, $db ) )
if ( ! \in_array( $group->g_id, $db ) )
{
$disabled[ $group->g_id ][] = $perm;
}
@@ -1064,7 +1083,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
public static function urlFromIndexData( $indexData, $itemData, $containerData )
{
$recordClass = $indexData['index_class'];
if ( in_array( 'IPS\Content\Comment', class_parents( $recordClass ) ) )
if ( \in_array( 'IPS\Content\Comment', class_parents( $recordClass ) ) )
{
$recordClass = $recordClass::$itemClass;
}
@@ -1088,29 +1107,30 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
*
* @param array $indexData Data from the search index
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
* @param array|NULL $containerData Basic data about the author. Only includes columns returned by container::basicDataColumns()
* @param array|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
* @param bool $escape If the title should be escaped for HTML output
* @return \IPS\Http\Url
*/
public static function titleFromIndexData( $indexData, $itemData, $containerData )
public static function titleFromIndexData( $indexData, $itemData, $containerData, $escape = TRUE )
{
$recordClass = $indexData['index_class'];
if ( in_array( 'IPS\Content\Comment', class_parents( $recordClass ) ) )
if ( \in_array( 'IPS\Content\Comment', class_parents( $recordClass ) ) )
{
$recordClass = $recordClass::$itemClass;
}
if ( $recordClass::database()->use_categories )
{
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $indexData['index_container_id'] );
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $indexData['index_container_id'], NULL, $escape ? array( 'escape' => $escape ) : array() );
}
else
{
return $recordClass::database()->_title;
return $escape ? $recordClass::database()->_title : $recordClass::database()->getTitleForLanguage( \IPS\Member::loggedIn()->language() );
}
}
/**
* Get Page Title for use in <title> tag
* Get Page Title for use in `<title>` tag
*
* @return string
*/
@@ -1154,6 +1174,24 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
return parent::get__title();
}
}
/**
* 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 ( ! \IPS\cms\Databases::load( $this->database_id )->use_categories )
{
return $language->addToStack( 'content_db_' . $this->database_id, NULL, $options );
}
return parent::getTitleForLanguage( $language, $options );
}
/**
* [Node] Get Title language key, not added to a language stack
@@ -1417,7 +1455,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
'*',
'cms_custom_database_' . $this->database_id,
array( 'category_id=? AND record_approved=1 AND record_future_date=0', $this->id ),
'record_last_comment DESC',
'record_last_comment DESC, primary_id_field DESC', /* Just in case RSS imports the exact same time */
array( 0, 1 ),
NULL,
NULL,
@@ -1471,7 +1509,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
if ( $comment !== NULL and ( $database->_comment_bump & \IPS\cms\Databases::BUMP_ON_COMMENT ) )
{
$this->last_record_date = $comment->mapped('date');
$this->last_record_member = intval( $comment->author()->member_id );
$this->last_record_member = \intval( $comment->author()->member_id );
$this->last_record_name = $comment->author()->member_id ? $comment->author()->name : NULL;
$this->last_record_seo_name = \IPS\Http\Url\Friendly::seoTitle( $this->last_poster_name );
$this->last_title = mb_substr( $comment->item()->mapped('title'), 0, 255 );
@@ -1656,7 +1694,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
foreach ( \IPS\Db::i()->select( '*', 'core_moderators' ) as $moderator )
{
if ( $moderator['perms'] === '*' or in_array( 'can_content_view_others_records', explode( ',', $moderator['perms'] ) ) )
if ( $moderator['perms'] === '*' or \in_array( 'can_content_view_others_records', explode( ',', $moderator['perms'] ) ) )
{
if( $moderator['type'] === 'g' )
{
@@ -1671,7 +1709,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
$return = ( $normal == '*' ) ? array_unique( $return ) : array_intersect( explode( ',', $normal ), array_unique( $return ) );
if( count( $members ) )
if( \count( $members ) )
{
$return = array_merge( $return, $members );
}
@@ -1709,7 +1747,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$saved = json_decode( \IPS\Request::i()->cookie['cms_filters'], TRUE );
if ( array_key_exists( $this->id, $saved ) and count( $saved[ $this->id ] ) )
if ( array_key_exists( $this->id, $saved ) and \count( $saved[ $this->id ] ) )
{
return $saved[ $this->id ];
}
@@ -1741,7 +1779,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
$toSave = array();
foreach( $values AS $key => $data )
{
if ( count( $data ) )
if ( \is_numeric( $key ) or $key == 'cms_record_i_started' )
{
$toSave[$key] = $data;
}
@@ -1776,12 +1814,13 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
/**
* Check permissions
*
* @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 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 bool $considerPostBeforeRegistering If TRUE, and $member is a guest, will return TRUE if "Post Before Registering" feature is enabled
* @return bool
* @throws \OutOfBoundsException If $permission does not exist in static::$permissionMap
*/
public function can( $permission, $member=NULL )
public function can( $permission, $member=NULL, $considerPostBeforeRegistering = TRUE )
{
$_member = $member ?: \IPS\Member::loggedIn();
@@ -1790,6 +1829,24 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
return FALSE;
}
return parent::can( $permission, $member );
return parent::can( $permission, $member, $considerPostBeforeRegistering );
}
/**
* Search
*
* @param string $column Column to search
* @param string $query Search query
* @param string|null $order Column to order by
* @param mixed $where Where clause
* @return array
*/
public static function search( $column, $query, $order, $where=array() )
{
$results = parent::search( $column, $query, $order, $where );
return array_filter( $results, function( $node ){
return $node->database_id == static::$customDatabaseId;
} );
}
}