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

-10
View File
@@ -130,16 +130,6 @@ class _Folder extends \IPS\Node\Model
return $this->name;
}
/**
* Node titles can contain HTML. Apparently.
*
* @return string
*/
public function get__stripTagsTitle()
{
return $this->name;
}
/**
* Get sortable name
*
+58 -20
View File
@@ -245,14 +245,7 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public static function resetPath( $folderId )
{
try
{
$path = \IPS\cms\Pages\Folder::load( $folderId )->path;
}
catch ( \OutOfRangeException $ex )
{
throw new \OutOfRangeException;
}
$path = $folderId ? \IPS\cms\Pages\Folder::load( $folderId )->path : '';
$children = static::getChildren( $folderId );
@@ -502,10 +495,13 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
/**
* Removes all include objects from all pages
*
* @param boolean $url The URL to find and remove
* @param boolean $url The URL to find and remove
* @param NULL|int $storageConfiguration Delete the cached includes from an alternate storage configuration
* @note This method is called by \IPS\cms\extensions\core\FileStorage\Pages.php during a move, and in that process we want to remove resources
from the old storage configuration, not the new one (which is what happens when the configuration id is not passed in to \IPS\File and a move is in progress)
* @return void
*/
static public function deleteCachedIncludes( $url=NULL )
static public function deleteCachedIncludes( $url=NULL, $storageConfiguration=NULL )
{
/* Remove them all */
if ( $url === NULL )
@@ -516,7 +512,7 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
\IPS\Db::i()->update( 'cms_pages', array( 'page_js_css_objects' => NULL ) );
}
/* Remove from file system */
\IPS\File::getClass( 'cms_Pages')->deleteContainer('page_objects');
\IPS\File::getClass( $storageConfiguration ?: 'cms_Pages' )->deleteContainer('page_objects');
}
else
{
@@ -708,8 +704,9 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$jsincludes[ $obj->key ] = \IPS\cms\Templates::readableGroupName( $obj->group ) . '/' . \IPS\cms\Templates::readableGroupName( $obj->title );
}
ksort( $jsincludes );
$return['page_includes_js'] = new \IPS\Helpers\Form\Select( 'page_includes_js', $item ? $item->js_includes : FALSE, FALSE, array( 'options' => $jsincludes, 'multiple' => true ), NULL, NULL, NULL, 'page_includes_js' );
$return['page_includes_js'] = new \IPS\Helpers\Form\CheckboxSet( 'page_includes_js', $item ? $item->js_includes : FALSE, FALSE, array( 'options' => $jsincludes, 'multiple' => true ), NULL, NULL, NULL, 'page_includes_js' );
}
if ( count( $css ) )
@@ -719,8 +716,9 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$cssincludes[ $obj->key ] = \IPS\cms\Templates::readableGroupName( $obj->group ) . '/' . \IPS\cms\Templates::readableGroupName( $obj->title );
}
$return['page_includes_css'] = new \IPS\Helpers\Form\Select( 'page_includes_css', $item ? $item->css_includes : FALSE, FALSE, array( 'options' => $cssincludes, 'multiple' => true ), NULL, NULL, NULL, 'page_includes_css' );
ksort( $cssincludes );
$return['page_includes_css'] = new \IPS\Helpers\Form\CheckboxSet( 'page_includes_css', $item ? $item->css_includes : FALSE, FALSE, array( 'options' => $cssincludes, 'multiple' => true ), NULL, NULL, NULL, 'page_includes_css' );
}
}
@@ -754,7 +752,7 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
$tags['cms_tag_pages']['{pageurl="' . $page['page_full_path'] . '"}' ] = \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $page['page_id'] );
}
$return['page_content'] = new \IPS\Helpers\Form\Codemirror( 'page_content', $item ? htmlentities( $item->content, \IPS\HTMLENTITIES, 'UTF-8', TRUE ) : NULL, FALSE, array( 'tags' => $tags, 'height' => 600 ), function( $val )
$return['page_content'] = new \IPS\Helpers\Form\Codemirror( 'page_content', $item ? htmlentities( $item->content, ENT_DISALLOWED, 'UTF-8', TRUE ) : NULL, FALSE, array( 'tags' => $tags, 'height' => 600 ), function( $val )
{
/* Test */
try
@@ -1063,6 +1061,14 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
/* Empty? Lets take a look and see if we need to compile anything */
if ( empty( $this->_data['js_css_objects'] ) )
{
/* Lock it up to prevent a race condition */
if ( \IPS\Theme::checkLock( "page_object_build" . $this->id ) )
{
return NULL;
}
\IPS\Theme::lock( "page_object_build" . $this->id );
if ( count( $this->js_includes ) )
{
/* Build a file object for each JS */
@@ -1107,6 +1113,8 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
$this->js_css_objects = json_encode( $return );
$this->save();
}
\IPS\Theme::unlock( "page_object_build" . $this->id );
}
else
{
@@ -1187,7 +1195,15 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$contents = @file_get_contents( \IPS\ROOT_PATH . '/themes/cms/pages/' . $this->full_path );
$contents = preg_replace( '#^<ips:pages.+?/>(\r\n?|\n)#', '', $contents );
\IPS\Theme::runProcessFunction( \IPS\Theme::compileTemplate( $contents, $functionName, null, true ), $functionName );
try
{
\IPS\Theme::runProcessFunction( \IPS\Theme::compileTemplate( $contents, $functionName, null, true ), $functionName );
}
catch( \Exception $e )
{
\IPS\Log::log( $e, 'pages_error' );
return '';
}
}
else
{
@@ -1634,6 +1650,8 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('content_err_page_save_exception', FALSE, array( 'sprintf' => \IPS\Member::loggedIn()->language()->addToStack( $ex->getMessage() ) ) ) );
}
\IPS\Content\Search\Index::i()->index( $this->item() );
}
/**
@@ -1760,7 +1778,7 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
preg_match_all( '/(.+?)='.$array[2].'(.+?)'.$array[2].'\s?/', $array[1], $submatches );
$plugin = array_shift( $submatches[1] );
$pluginClass = 'IPS\\Output\\Plugin\\' . ucfirst( $plugin );
$pluginClass = 'IPS\\Output\\Plugin\\' . mb_ucfirst( $plugin );
$value = array_shift( $submatches[2] );
$options = array();
@@ -1777,9 +1795,9 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
{
foreach ( \IPS\Application::applications() as $app )
{
if ( file_exists( \IPS\ROOT_PATH . "/applications/{$app->directory}/extensions/core/OutputPlugins/" . ucfirst( $plugin ) . ".php" ) )
if ( file_exists( \IPS\ROOT_PATH . "/applications/{$app->directory}/extensions/core/OutputPlugins/" . mb_ucfirst( $plugin ) . ".php" ) )
{
$pluginClass = 'IPS\\' . $app->directory . '\\extensions\\core\\OutputPlugins\\' . ucfirst( $plugin );
$pluginClass = 'IPS\\' . $app->directory . '\\extensions\\core\\OutputPlugins\\' . mb_ucfirst( $plugin );
}
}
}
@@ -2009,6 +2027,8 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
{
static::cleanUpDesignersModeFiles();
}
\IPS\Content\Search\Index::i()->removeFromSearchIndex( $this->item() );
}
/**
@@ -2063,6 +2083,8 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
}
}
catch( \Exception $e ) { }
\IPS\Content\Search\Index::i()->index( $this->item() );
}
/**
@@ -2084,7 +2106,7 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
if ( $this->full_path and \IPS\Theme::designersModeEnabled() )
{
static::exportDesignersMode( $this->id );
}
}
}
/**
@@ -2493,4 +2515,20 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
}
}
}
/**
* Get item
*
* @return \IPS\nexus\Package\Item
*/
public function item()
{
$data = array();
foreach ( $this->_data as $k => $v )
{
$data[ 'page_' . $k ] = $v;
}
return \IPS\cms\Pages\PageItem::constructFromData( $data );
}
}
+202
View File
@@ -0,0 +1,202 @@
<?php
/**
* @brief Pages Page Item Model
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Nexus
* @since 15 Dec 2017
*/
namespace IPS\cms\Pages;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Package Item Model
*/
class _PageItem extends \IPS\Content\Item implements \IPS\Content\Searchable
{
/**
* @brief Application
*/
public static $application = 'cms';
/**
* @brief Module
*/
public static $module = 'pages';
/**
* @brief Database Table
*/
public static $databaseTable = 'cms_pages';
/**
* @brief Database Prefix
*/
public static $databasePrefix = 'page_';
/**
* @brief Multiton Store
*/
protected static $multitons;
/**
* @brief Database Column Map
*/
public static $databaseColumnMap = array(
);
/**
* @brief Title
*/
public static $title = 'cms_page';
/**
* @brief Icon
*/
public static $icon = 'files-o';
/**
* @brief Include In Sitemap
*/
public static $includeInSitemap = FALSE;
/**
* @brief Can this content be moderated normally from the front-end (will be FALSE for things like Pages and Commerce Products)
*/
public static $canBeModeratedFromFrontend = FALSE;
/**
* Columns needed to query for search result / stream view
*
* @return array
*/
public static function basicDataColumns()
{
return array( 'page_id', 'page_folder_id', 'page_full_path', 'page_default' );
}
/**
* Get URL 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()
* @return \IPS\Http\Url
*/
public static function urlFromIndexData( $indexData, $itemData )
{
if ( ( \IPS\Application::load('cms')->default OR \IPS\Settings::i()->cms_use_different_gateway ) AND $itemData['page_default'] AND !$itemData['page_folder_id'] )
{
/* Are we using the gateway file? */
if ( \IPS\Settings::i()->cms_use_different_gateway )
{
/* Yes, work out the proper URL. */
return \IPS\Http\Url::createFromString( \IPS\Settings::i()->cms_root_page_url, TRUE );
}
else
{
/* No - that's easy */
return \IPS\Http\Url::internal( '', 'front' );
}
}
else
{
return \IPS\Http\Url::internal( 'app=cms&module=pages&controller=page&path=' . $itemData['page_full_path'], 'front', 'content_page_path', array( $itemData['page_full_path'] ) );
}
}
/**
* Get HTML for search result display
*
* @param array $indexData Data from the search index
* @param array $authorData Basic data about the author. Only includes columns returned by \IPS\Member::columnsForPhoto()
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
* @param array|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
* @param array $reputationData Array of people who have given reputation and the reputation they gave
* @param int|NULL $reviewRating If this is a review, the rating
* @param bool $iPostedIn If the user has posted in the item
* @param string $view 'expanded' or 'condensed'
* @param bool $asItem Displaying results as items?
* @param bool $canIgnoreComments Can ignore comments in the result stream? Activity stream can, but search results cannot.
* @param array $template Optional custom template
* @param array $reactions Reaction Data
* @return string
*/
public static function searchResult( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments=FALSE, $template=NULL, $reactions=array() )
{
$indexData['index_title'] = \IPS\Member::loggedIn()->language()->addToStack( 'cms_page_' . $indexData['index_item_id'] );
return parent::searchResult( $indexData, $authorData, $itemData, $containerData, $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments, $template, $reactions );
}
/**
* Title for search index
*
* @return string
*/
public function searchIndexTitle()
{
$titles = array();
foreach ( \IPS\Lang::languages() as $lang )
{
$titles[] = $lang->get("cms_page_{$this->id}");
}
return implode( ' ', $titles );
}
/**
* Content for search index
*
* @return string
*/
public function searchIndexContent()
{
if ( $this->type == 'builder' )
{
$content = array();
foreach( \IPS\Db::i()->select( '*', 'cms_page_widget_areas', array( 'area_page_id=?', $this->id ) ) as $widgetArea )
{
foreach ( json_decode( $widgetArea['area_widgets'], TRUE ) as $widget )
{
if ( $widget['app'] == 'cms' and $widget['key'] == 'Wysiwyg' )
{
$content[] = trim( $widget['configuration']['content'] );
}
}
}
return implode( ' ', $content );
}
else
{
return $this->content;
}
}
/**
* Search Index Permissions
*
* @return string Comma-delimited values or '*'
* @li Number indicates a group
* @li Number prepended by "m" indicates a member
* @li Number prepended by "s" indicates a social group
*/
public function searchIndexPermissions()
{
try
{
return \IPS\Db::i()->select( 'perm_view', 'core_permission_index', array( "app='cms' AND perm_type='pages' AND perm_type_id=?", $this->id ) )->first();
}
catch ( \UnderflowException $e )
{
return '';
}
}
}