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

+174 -45
View File
@@ -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;
@@ -27,6 +27,12 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
* @brief [ActiveRecord] Multiton Store
*/
protected static $multitons = array();
/**
* @brief [ActiveRecord] Caches
* @note Defined cache keys will be cleared automatically as needed
*/
protected $caches = array( 'database_reciprocal_links', 'cms_databases' );
/**
* @brief [ActiveRecord] Database Prefix
@@ -155,7 +161,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
/**
* @brief Page title
*/
protected static $pageTitle = NULL;
protected $pageTitle = NULL;
/**
* Return all databases
@@ -169,7 +175,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
/* Avoid using SHOW TABLES LIKE / checkForTable() */
try
{
foreach( static::getDatabaseDataFromStore() as $db )
foreach( static::getStore() as $db )
{
$id = $db[ static::$databasePrefix . static::$databaseColumnId ];
static::$multitons[ $id ] = static::constructFromData( $db );
@@ -188,7 +194,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
*
* @return array
*/
public static function getDatabaseDataFromStore()
public static function getStore()
{
if ( ! isset( \IPS\Data\Store::i()->cms_databases ) )
{
@@ -231,6 +237,41 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
return $obj;
}
/**
* Can this database accept RSS imports?
*
* @return boolean
*/
public function canImportRss(): bool
{
if ( ! $this->page_id )
{
return FALSE;
}
$fieldsClass = '\IPS\cms\Fields' . $this->id;
try
{
if ( ! \in_array( mb_ucfirst( $fieldsClass::load( $this->field_title )->type ), array( 'Text', 'TextArea', 'Editor' ) ) )
{
return FALSE;
}
if ( ! \in_array( mb_ucfirst( $fieldsClass::load( $this->field_content )->type ), array( 'TextArea', 'Editor' ) ) )
{
return FALSE;
}
}
catch( \Exception $e )
{
return FALSE;
}
return TRUE;
}
/**
* Return data for the ACP Menu
@@ -242,14 +283,15 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
$menu = array();
foreach(
\IPS\Db::i()->select( '*, core_sys_lang_words.word_custom as database_name', 'cms_databases', NULL, 'core_sys_lang_words.word_custom' )->join(
'core_sys_lang_words', "core_sys_lang_words.word_key=CONCAT( 'content_db_', cms_databases.database_id ) AND core_sys_lang_words.lang_id=" . \IPS\Member::loggedIn()->language()->id
)
\IPS\Db::i()->select( '*, core_sys_lang_words.word_custom as database_name, core_sys_lang_words2.word_custom as record_name', 'cms_databases', NULL, 'core_sys_lang_words.word_custom' )
->join( 'core_sys_lang_words', "core_sys_lang_words.word_key=CONCAT( 'content_db_', cms_databases.database_id ) AND core_sys_lang_words.lang_id=" . \IPS\Member::loggedIn()->language()->id )
->join( array( 'core_sys_lang_words', 'core_sys_lang_words2' ), "core_sys_lang_words2.word_key=CONCAT( 'content_db_lang_pu_', cms_databases.database_id ) AND core_sys_lang_words2.lang_id=" . \IPS\Member::loggedIn()->language()->id )
as $row )
{
$menu[] = array(
'id' => $row['database_id'],
'title' => $row['database_name'],
'record_name' => $row['record_name'],
'use_categories' => $row['database_use_categories']
);
}
@@ -308,7 +350,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
}
else
{
$changes[] = "DROP " . \IPS\Db::i()->escape_string( $key );
$changes[] = "DROP KEY `" . \IPS\Db::i()->escape_string( $key ) . "`";
}
$changes[] = \IPS\Db::i()->buildIndex( $tableName, $data );
@@ -317,14 +359,14 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
}
/* We collect all the changes so we can run one database query instead of, potentially, dozens */
if( count( $changes ) )
if( \count( $changes ) )
{
\IPS\Db::i()->query( "ALTER TABLE " . \IPS\Db::i()->prefix . $tableName . " " . implode( ', ', $changes ) );
}
return $fixes;
}
/**
* Create a new database
*
@@ -439,7 +481,8 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
), array( 'perm_id=?', $perms['perm_id']) );
/* Create a category */
$category = new \IPS\cms\Categories;
$categoryClass = '\IPS\cms\Categories' . $database->id;
$category = new $categoryClass;
$category->database_id = $database->id;
$category->saveForm( $category->formatFormValues( array(
@@ -543,10 +586,10 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$roots = $categoryClass::roots( NULL );
if ( ! count( $roots ) )
if ( ! \count( $roots ) )
{
/* Create a category */
$category = new \IPS\cms\Categories;
$category = new $categoryClass;
$category->database_id = $this->id;
$catTitle = array();
@@ -596,12 +639,12 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function get_fixed_field_perms()
{
if ( ! is_array( $this->_data['fixed_field_perms'] ) )
if ( ! \is_array( $this->_data['fixed_field_perms'] ) )
{
$this->_data['fixed_field_perms'] = json_decode( $this->_data['fixed_field_perms'], true );
}
if ( is_array( $this->_data['fixed_field_perms'] ) )
if ( \is_array( $this->_data['fixed_field_perms'] ) )
{
return $this->_data['fixed_field_perms'];
}
@@ -617,7 +660,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function set_fixed_field_perms( $value )
{
$this->_data['fixed_field_perms'] = ( is_array( $value ) ? json_encode( $value ) : $value );
$this->_data['fixed_field_perms'] = ( \is_array( $value ) ? json_encode( $value ) : $value );
}
/**
@@ -627,12 +670,12 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function get_fixed_field_settings()
{
if ( ! is_array( $this->_data['fixed_field_settings'] ) )
if ( ! \is_array( $this->_data['fixed_field_settings'] ) )
{
$this->_data['fixed_field_settings'] = json_decode( $this->_data['fixed_field_settings'], true );
}
if ( is_array( $this->_data['fixed_field_settings'] ) )
if ( \is_array( $this->_data['fixed_field_settings'] ) )
{
return $this->_data['fixed_field_settings'];
}
@@ -648,7 +691,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function set_fixed_field_settings( $value )
{
$this->_data['fixed_field_settings'] = ( is_array( $value ) ? json_encode( $value ) : $value );
$this->_data['fixed_field_settings'] = ( \is_array( $value ) ? json_encode( $value ) : $value );
}
/**
@@ -658,12 +701,12 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function get_featured_settings()
{
if ( ! is_array( $this->_data['featured_settings'] ) )
if ( ! \is_array( $this->_data['featured_settings'] ) )
{
$this->_data['featured_settings'] = json_decode( $this->_data['featured_settings'], true );
}
if ( is_array( $this->_data['featured_settings'] ) )
if ( \is_array( $this->_data['featured_settings'] ) )
{
return $this->_data['featured_settings'];
}
@@ -679,7 +722,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function set_featured_settings( $value )
{
$this->_data['featured_settings'] = ( is_array( $value ) ? json_encode( $value ) : $value );
$this->_data['featured_settings'] = ( \is_array( $value ) ? json_encode( $value ) : $value );
}
/**
@@ -689,49 +732,83 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function pageTitle()
{
if ( static::$pageTitle === NULL )
if ( $this->pageTitle === NULL )
{
if ( $this->use_as_page_title )
{
static::$pageTitle = $this->_title;
$this->pageTitle = $this->_title;
}
else
{
try
{
static::$pageTitle = \IPS\cms\Pages\Page::load( $this->page_id )->getHtmlTitle();
$this->pageTitle = \IPS\cms\Pages\Page::load( $this->page_id )->getHtmlTitle();
}
catch( \Exception $e ) { }
}
}
return static::$pageTitle;
return $this->pageTitle;
}
/**
* 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 )
{
/* If we're looking from the front, make sure the database page also passes */
if ( $permission === 'view' and \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation === 'front' and $this->page_id )
{
try
{
return parent::can( 'view', $member ) AND \IPS\cms\Pages\Page::load( $this->page_id )->can( 'view', $member );
return parent::can( 'view', $member, $considerPostBeforeRegistering ) AND \IPS\cms\Pages\Page::load( $this->page_id )->can( 'view', $member, $considerPostBeforeRegistering );
}
catch( \OutOfRangeException $ex )
{
return parent::can( 'view', $member );
return parent::can( 'view', $member, $considerPostBeforeRegistering );
}
}
return parent::can( $permission, $member );
return parent::can( $permission, $member, $considerPostBeforeRegistering );
}
/**
* Disabled permissions
* 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()
{
$disabled = array();
try
{
$permissions = \IPS\cms\Pages\Page::load( $this->page_id )->permissions();
if( $permissions['perm_view'] != '*' )
{
$pageViewPermissions = explode( ',', $permissions['perm_view'] );
foreach ( \IPS\Member\Group::groups() as $group )
{
if ( ! \in_array( $group->g_id, $pageViewPermissions ) )
{
$disabled[ $group->g_id ] = array( 'view', 2, 3, 4, 5, 6, 7 );
}
}
}
}
catch( \OutOfRangeException $e ){}
return $disabled;
}
/**
@@ -764,7 +841,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
\IPS\Member::loggedIn()->language()->words['can_' . $lang . '_content_db_lang_sl_' . $this->id ] = \IPS\Member::loggedIn()->language()->addToStack( 'can_' . $lang . '_record', FALSE, array( 'sprintf' => array( $this->recordWord( 1 ) ) ) );
\IPS\Member::loggedIn()->language()->words['can_' . $lang . '_content_db_lang_su_' . $this->id ] = \IPS\Member::loggedIn()->language()->addToStack( 'can_' . $lang . '_record', FALSE, array( 'sprintf' => array( $this->recordWord( 1, TRUE ) ) ) );
if ( in_array( $lang, array( 'edit', 'hide', 'unhide', 'view_hidden', 'delete' ) ) )
if ( \in_array( $lang, array( 'edit', 'hide', 'unhide', 'view_hidden', 'delete' ) ) )
{
\IPS\Member::loggedIn()->language()->words['can_' . $lang . '_content_record_comments_title_' . $this->id ] = \IPS\Member::loggedIn()->language()->addToStack( 'can_' . $lang . '_rcomment', FALSE, array( 'sprintf' => array( $this->recordWord( 1 ) ) ) );
\IPS\Member::loggedIn()->language()->words['can_' . $lang . '_content_record_reviews_title_' . $this->id ] = \IPS\Member::loggedIn()->language()->addToStack( 'can_' . $lang . '_rreview', FALSE, array( 'sprintf' => array( $this->recordWord( 1 ) ) ) );
@@ -811,9 +888,6 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
parent::save();
/* Clear cache */
unset( \IPS\Data\Store::i()->cms_databases );
/* If this database isn't searchable, make sure its content is not in the search index */
if( $removeSearchIndex )
{
@@ -876,7 +950,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
/* Delete notifications */
$memberIds = array();
foreach( \IPS\Db::i()->select( 'member', 'core_notifications', array( 'item_class=? ', 'IPS\cms\Records' . $this->id ) ) as $member )
foreach( \IPS\Db::i()->select( '`member`', 'core_notifications', array( 'item_class=? ', 'IPS\cms\Records' . $this->id ) ) as $member )
{
$memberIds[ $member ] = $member;
}
@@ -927,9 +1001,6 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
/* Delete the database record */
parent::delete();
/* Best remove the stored data */
unset( \IPS\Data\Store::i()->database_reciprocal_links, \IPS\Data\Store::i()->cms_databases );
}
/**
@@ -947,7 +1018,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
$resaveBlock = NULL;
foreach( $pageBlocks as $id => $pageBlock )
{
if( $pageBlock['app'] == 'cms' AND in_array( $pageBlock['key'], $databaseWidgets ) AND ! empty( $pageBlock['configuration']['database'] ) )
if( $pageBlock['app'] == 'cms' AND \in_array( $pageBlock['key'], $databaseWidgets ) AND ! empty( $pageBlock['configuration']['database'] ) )
{
if ( $pageBlock['configuration']['database'] == $this->id )
{
@@ -968,12 +1039,11 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
* Set the permission index permissions
*
* @param array $insert Permission data to insert
* @param \IPS\Helpers\Form\Matrix $matrix \IPS\Helpers\Form\Matrix
* @return void
*/
public function setPermissions( $insert, \IPS\Helpers\Form\Matrix $matrix )
public function setPermissions( $insert )
{
parent::setPermissions( $insert, $matrix );
parent::setPermissions( $insert );
/* Clear cache */
unset( \IPS\Data\Store::i()->cms_databases );
@@ -1060,7 +1130,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
}
}
if ( is_array( $values ) )
if ( \is_array( $values ) )
{
foreach( $values as $id => $fields )
{
@@ -1102,4 +1172,63 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
}
}
}
/**
* Returns database data from the store
*
* @return array
* @deprecated
*/
public static function getDatabaseDataFromStore()
{
return static::getStore();
}
/**
* 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 bool useCategories If this database uses categories
* @clientapiresponse [\IPS\cms\Fields] fields The fields
* @apiresponse string url URL
* @clientapiresponse object|null permissions Node permissions
*/
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
$return = array(
'id' => $this->id,
'name' => $this->_title,
'useCategories' => (bool) $this->use_categories,
);
if ( $authorizedMember === NULL )
{
$return['fields'] = array();
$fieldsClass = '\IPS\cms\Fields' . $this->id;
foreach ( $fieldsClass::roots() as $field )
{
$return['fields'][] = $field->apiOutput( $authorizedMember );
}
}
try
{
$pagePath = \IPS\cms\Pages\Page::loadByDatabaseId( $this->id )->full_path;
$return['url'] = (string) \IPS\Http\Url::internal( "app=cms&module=pages&controller=page&path=" . $pagePath, 'front', 'content_page_path' );
}
catch( \OutOfRangeException $ex )
{
$return['url'] = NULL;
}
if( $authorizedMember === NULL )
{
$return['permissions'] = \in_array( 'IPS\Node\Permissions', class_implements( \get_class( $this ) ) ) ? $this->permissions() : NULL;
}
return $return;
}
}