Version 5.0.13

This commit is contained in:
Neo committed 2025-12-19 19:04:57 -08:00
1 parent b89858b2ff
commit 5efe744c5f
1407 files changed
+69690 -30177

No files matched your search

+314 -32
View File
@@ -92,7 +92,7 @@ class Page extends Model implements Permissions
* @brief [ActiveRecord] Caches
* @note Defined cache keys will be cleared automatically as needed
*/
protected array $caches = array( 'frontNavigation' );
protected array $caches = array( 'frontNavigation', 'pageDefaults' );
/**
* @brief [ActiveRecord] Database Table
@@ -348,11 +348,11 @@ class Page extends Model implements Permissions
/* @var $class Model */
$folder = $class::load( $path, 'folder_path' );
static::$loadedPagesFromPath[ $path ] = static::getDefaultPage( $folder->id );
static::$loadedPagesFromPath[ $path ] = static::getDefaultForMember( $folder->id );
}
else
{
static::$loadedPagesFromPath[ $path ] = static::getDefaultPage();
static::$loadedPagesFromPath[ $path ] = static::getDefaultForMember();
}
}
catch ( OutOfRangeException $e )
@@ -474,6 +474,13 @@ class Page extends Model implements Permissions
/* Still here? Consistent with AR pattern */
throw new OutOfRangeException();
}
/**
* @brief Constants for default page settings
*/
const PAGE_NODEFAULT = 0;
const PAGE_DEFAULT = 1;
const PAGE_DEFAULT_OVERRIDE = 2;
/**
* Return the default page for this folder
@@ -488,7 +495,7 @@ class Page extends Model implements Permissions
/* Try the easiest method first */
try
{
static::$defaultPage[ $folderId ] = Page::load( Db::i()->select( 'page_id', static::$databaseTable, array( 'page_default=? AND page_folder_id=?', 1, $folderId ) )->first() );
static::$defaultPage[ $folderId ] = Page::load( Db::i()->select( 'page_id', static::$databaseTable, array( 'page_default=? AND page_folder_id=?', static::PAGE_DEFAULT, $folderId ) )->first() );
}
catch( Exception $ex )
{
@@ -510,6 +517,159 @@ class Page extends Model implements Permissions
return ( isset( static::$defaultPage[ $folderId ] ) ) ? static::$defaultPage[ $folderId ] : NULL;
}
/**
* Build a list of default pages for each group
*
* @return array
*/
public static function loadDefaultsPerGroup() : array
{
try
{
return Store::i()->pageDefaults;
}
catch( OutOfRangeException ){}
$folderRoots = iterator_to_array(
Db::i()->select( 'folder_id', 'cms_folders' )
);
array_unshift( $folderRoots, 0 );
$defaults = [];
foreach( $folderRoots as $folderId )
{
$defaults[ $folderId ] = [];
foreach( Db::i()->select( 'page_id,page_group_defaults', static::$databaseTable, [ 'page_default=? and page_folder_id=?', static::PAGE_DEFAULT_OVERRIDE, $folderId ] ) as $row )
{
foreach( explode( ",", $row['page_group_defaults'] ) as $groupId )
{
$defaults[ $folderId ][ $groupId ] = $row['page_id'];
}
}
try
{
$primaryDefault = static::getDefaultPage( $folderId );
foreach( Group::groups() as $group )
{
if( !isset( $defaults[ $folderId ][ $group->g_id ] ) )
{
$defaults[ $folderId ][ $group->g_id ] = $primaryDefault->_id;
}
}
}
catch( OutOfRangeException ){}
}
Store::i()->pageDefaults = $defaults;
return $defaults;
}
/**
* Load the default application for this member
*
* @param int $folderId
* @param Member|null $member
* @return static
*/
public static function getDefaultForMember( int $folderId=0, ?Member $member=null ) : static
{
/* We're only going to use the primary group here */
$member = $member ?? Member::loggedIn();
$pageId = static::loadDefaultsPerGroup()[ $folderId ][ $member->member_group_id ] ?? null;
if( $pageId )
{
try
{
return static::load( $pageId );
}
catch( OutOfRangeException ){}
}
return static::getDefaultPage( $folderId );
}
/**
* Set the page as a default for specific groups
*
* @param array $groups
* @return void
*/
public function setAsDefaultForGroups( array $groups ) : void
{
$currentDefaults = static::loadDefaultsPerGroup();
$newGroups = [];
$pagesToUpdate = [];
foreach( $groups as $group )
{
$groupId = ( $group instanceof Group ) ? $group->g_id : $group;
$currentPage = $currentDefaults[ $this->folder_id ][ $groupId ] ?? null;
if( $currentPage !== null and $currentPage != $this->id )
{
$pagesToUpdate[ $currentPage ] = [];
foreach( $currentDefaults[ $this->folder_id ] as $k => $v )
{
if( $v == $currentPage and $k != $groupId )
{
$pagesToUpdate[ $currentPage ][] = $k;
}
}
}
$newGroups[] = $groupId;
}
/* Did we make this the default for all the groups? */
if( count( $newGroups ) == count( Group::groups() ) )
{
$this->setAsDefault();
}
else
{
if( empty( $newGroups ) )
{
$this->default = static::PAGE_NODEFAULT;
$this->group_defaults = null;
}
else
{
$this->default = static::PAGE_DEFAULT_OVERRIDE;
$this->group_defaults = implode( ",", $newGroups );
}
$this->save();
}
/* Now update any pages that need to be changed */
foreach( $pagesToUpdate as $pageId => $_values )
{
try
{
$page = static::load( $pageId );
/* If it's set as default 1, we can leave that alone */
if( $page->default == static::PAGE_DEFAULT_OVERRIDE )
{
$groupsToUse = array_diff( $_values, $newGroups );
if( empty( $groupsToUse ) )
{
$page->default = static::PAGE_NODEFAULT;
$page->group_defaults = null;
}
else
{
$page->group_defaults = implode( ",", $groupsToUse );
}
$page->save();
}
}
catch( OutOfRangeException ){}
}
}
/**
* Delete compiled versions
@@ -756,7 +916,7 @@ class Page extends Model implements Permissions
if ( $pageType == 'html' )
{
$return['tab_content'] = array( 'content_page_form_tab__content', NULL, NULL, 'ipsForm_vertical' );
$return['tab_content'] = array( 'content_page_form_tab__content', NULL, NULL, 'ipsForm--vertical' );
$tagSource = Url::internal( "app=cms&module=pages&controller=ajax&do=loadTags" );
if ( $item )
@@ -809,7 +969,6 @@ class Page extends Model implements Permissions
{
$page = new self;
$page->type = $pageType;
$page->save();
$page->saveForm( $page->formatFormValues( $values ) );
@@ -1241,6 +1400,17 @@ class Page extends Model implements Permissions
public function getAreasFromDatabase( ?string $area=null ) : array
{
$areas = array();
if( $this->_version !== null )
{
foreach( $this->_version->data['areas'] as $id => $tree )
{
$areas[ $id ] = new Area( $tree, $id );
}
return $areas;
}
$where = [ array( 'area_page_id=?', $this->id ) ];
if ( $area !== null )
@@ -1267,10 +1437,23 @@ class Page extends Model implements Permissions
* Save an area to the database and link it to the page
*
* @param Area $area
* @param bool $backup Should we store a copy?
* @return void
*/
public function saveArea( Area $area ) : void
public function saveArea( Area $area, bool $backup=true ) : void
{
/* If we are viewing an old version, do nothing! These should be read-only */
if( $this->_version !== null )
{
return;
}
/* Create a backup before we do anything here */
if( $backup )
{
Revision::store( $this );
}
/* If we have no content, clear this out entirely */
if( !$area->hasWidgets() )
{
@@ -1429,22 +1612,33 @@ class Page extends Model implements Permissions
$return['edit'] = $buttons['edit'];
}
if ( Member::loggedIn()->hasAcpRestriction( 'cms', 'pages', 'page_edit' ) and !$this->default )
if ( Member::loggedIn()->hasAcpRestriction( 'cms', 'pages', 'page_edit' ) )
{
$return['default'] = array(
'icon' => $this->default ? 'star' : 'regular fa-star',
'title' => 'content_default_page',
'link' => $url->setQueryString( array( 'id' => $this->id, 'subnode' => 1, 'do' => 'setAsDefault' ) )->csrf()
);
}
$return['default'] = array(
'icon' => ( $this->default == static::PAGE_DEFAULT ) ? 'star' : 'regular fa-star',
'title' => 'content_default_page',
'link' => $url->setQueryString( array( 'id' => $this->id, 'subnode' => 1, 'do' => 'setAsDefault' ) ),
'data' => [ 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack( 'content_default_page' ) ]
);
if ( Member::loggedIn()->hasAcpRestriction( 'cms', 'pages', 'page_edit' ) and $this->type !== 'builder' )
{
$return['default_error'] = array(
'icon' => Settings::i()->cms_error_page == $this->id ? 'exclamation-circle' : 'exclamation',
'title' => Settings::i()->cms_error_page == $this->id ? 'content_remove_error_page' : 'content_default_error_page',
'link' => $url->setQueryString( array( 'id' => Settings::i()->cms_error_page ? 0 : $this->id, 'subnode' => 1, 'do' => 'toggleDefaultError' ) )->csrf()
);
if( $this->type !== 'builder' )
{
$return['default_error'] = array(
'icon' => Settings::i()->cms_error_page == $this->id ? 'exclamation-circle' : 'exclamation',
'title' => Settings::i()->cms_error_page == $this->id ? 'content_remove_error_page' : 'content_default_error_page',
'link' => $url->setQueryString( array( 'id' => Settings::i()->cms_error_page ? 0 : $this->id, 'subnode' => 1, 'do' => 'toggleDefaultError' ) )->csrf()
);
}
/* Do we have at least one stored version? */
if( $previous = Revision::previousVersion( $this ) )
{
$return['history'] = array(
'icon' => 'history',
'title' => 'content_page_history',
'link' => $url->setQueryString( array( 'id' => $this->id, 'subnode' => 1, 'do' => 'history' ) )
);
}
}
$return['view'] = array(
@@ -1587,6 +1781,12 @@ class Page extends Model implements Permissions
$this->save();
}
/* If this is not a new page, store the current page in the history before we make any changes */
if( !$isNew )
{
Revision::store( $this );
}
if( isset( $values['page_name'] ) )
{
$_copied = $values['page_name'];
@@ -1810,7 +2010,7 @@ class Page extends Model implements Permissions
{
if( $this->_url === NULL )
{
if ( Application::load('cms')->default AND $this->default AND ! $this->folder_id )
if ( Application::load('cms')->default AND $this->default == static::PAGE_DEFAULT AND ! $this->folder_id )
{
/* No - that's easy */
$this->_url = Url::internal( '', 'front' );
@@ -1841,6 +2041,33 @@ class Page extends Model implements Permissions
}
}
/**
* @var Revision|null
*/
protected ?Revision $_version = null;
/**
* View as a specific version
*
* @param int|null $version
* @return void
*/
public function setVersion( ?int $version=null ) : void
{
/* Clear it first */
$this->_version = null;
try
{
$this->_version = Revision::load( $version );
foreach( $this->_version->data['settings'] as $k => $v )
{
$this->_data[ $k ] = $v;
}
}
catch( OutOfRangeException ){}
}
/**
* Map this database to a specific page
*
@@ -1951,6 +2178,9 @@ class Page extends Model implements Permissions
/* Remove any widgets for this page */
Db::i()->delete( 'cms_page_widget_areas', array( 'area_page_id=?', $this->id ) );
/* Clear history */
Db::i()->delete( 'cms_page_revisions', array( 'revision_page_id=?', $this->id ) );
parent::delete();
@@ -2020,10 +2250,22 @@ class Page extends Model implements Permissions
*/
public function save(): void
{
/* If we are viewing an older version, don't allow any changes */
if( $this->_version !== null )
{
return;
}
if ( $this->id )
{
static::deleteCompiled( $this->id );
}
/* If this is not new and the folder changed, check defaults */
if( !$this->_new and isset( $this->changed['folder_id'] ) )
{
$this->checkDefaultPage();
}
parent::save();
@@ -2047,10 +2289,39 @@ class Page extends Model implements Permissions
*/
public function setAsDefault() : void
{
Db::i()->update( 'cms_pages', array( 'page_default' => 0 ), array( 'page_folder_id=?', $this->folder_id ) );
Db::i()->update( 'cms_pages', array( 'page_default' => 1 ), array( 'page_id=?', $this->id ) );
Db::i()->update( 'cms_pages', array( 'page_default' => static::PAGE_NODEFAULT, 'page_group_defaults' => null ), array( 'page_folder_id=? and page_default=?', $this->folder_id, static::PAGE_DEFAULT ) );
Db::i()->update( 'cms_pages', array( 'page_default' => static::PAGE_DEFAULT, 'page_group_defaults' => null ), array( 'page_id=?', $this->id ) );
static::buildPageUrlStore();
unset( Store::i()->pageDefaults );
}
/**
* Make sure that we only have one default page per folder.
* This is called only when the folder is changed.
*
* @return void
*/
protected function checkDefaultPage() : void
{
if( $this->default == static::PAGE_DEFAULT )
{
try
{
$currentDefault = Db::i()->select( 'page_id', 'cms_pages', [ 'page_default=? and page_folder_id=? and page_id <> ?', static::PAGE_DEFAULT, $this->folder_id, (int) $this->_id ] )->first();
/* If we found a different default page, remove the default from this one */
$this->default = static::PAGE_NODEFAULT;
}
catch( UnderflowException ){}
}
elseif( $this->default == static::PAGE_DEFAULT_OVERRIDE )
{
/* Group overrides are always removed when we move a page to another folder */
$this->default = static::PAGE_NODEFAULT;
$this->group_defaults = null;
}
}
/**
@@ -2121,7 +2392,7 @@ class Page extends Model implements Permissions
/* Make sure we initialize the main area */
Output::i()->sidebar['widgets']['col1'] = [];
Output::i()->output = Theme::i()->getTemplate( 'pages' )->globalWrap( $nav, $this->getPageContent(), $this );
Output::i()->output = Theme::i()->getTemplate( 'pages', 'cms' )->globalWrap( $nav, $this->getPageContent(), $this );
if ( isset( Settings::i()->cms_error_page ) and Settings::i()->cms_error_page and Settings::i()->cms_error_page == $this->id )
{
@@ -2138,7 +2409,7 @@ class Page extends Model implements Permissions
}
catch ( OutOfRangeException | OutOfBoundsException )
{
if ( !( Application::load( 'cms' )->default and !$this->folder_id and $this->default ) )
if ( !( Application::load( 'cms' )->default and !$this->folder_id and $this->default == static::PAGE_DEFAULT ) )
{
Output::i()->breadcrumb['module'] = array( $this->url(), $this->_title );
}
@@ -2156,10 +2427,6 @@ class Page extends Model implements Permissions
/* Send straight to the output engine */
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( Output::i()->title, Output::i()->output, array( 'app' => Dispatcher::i()->application->directory, 'module' => Dispatcher::i()->module->key, 'controller' => Dispatcher::i()->controller ) ), ( $httpStatusCode ?: 200 ), 'text/html', ( $httpHeaders ?: array() ) );
}
else
{
Output::i()->allowDefaultWidgets = FALSE;
}
}
else
{
@@ -2200,9 +2467,15 @@ class Page extends Model implements Permissions
*/
protected function getPageContent() : string
{
$content = '';
if( $this->_version !== null )
{
Output::i()->customHeader .= Theme::i()->getTemplate( 'pages', 'cms' )->revision( $this->_version );
}
if( $this->type == 'html' )
{
return $this->getHtmlContent();
return $content . $this->getHtmlContent();
}
$widgets = [];
@@ -2216,6 +2489,15 @@ class Page extends Model implements Permissions
}
$tree = $item['area_tree'] ? json_decode( $item['area_tree'], true ) : [];
if( $this->_version !== null )
{
if ( isset( $this->_version->data['areas'][ $item['area_area'] ] ) )
{
/* We have a previous version we want to show */
$tree = $this->_version->data['areas'][ $item['area_area'] ];
}
}
if ( empty( $tree ) )
{
/* If the tree is empty, generate one from the database rows */
@@ -2242,7 +2524,7 @@ class Page extends Model implements Permissions
$widgets['col1'] = new Area( [], 'col1' );
}
return Theme::i()->getTemplate( 'pages', 'cms' )->mainArea( $this, $widgets );
return $content . Theme::i()->getTemplate( 'pages', 'cms' )->mainArea( $this, $widgets );
}
/**