Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -12,90 +12,101 @@
|
||||
namespace IPS\cms\Blocks;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\Node;
|
||||
use IPS\Helpers\Form\Text;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Node\Model;
|
||||
use IPS\Request;
|
||||
use function defined;
|
||||
use function get_called_class;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Block Container Model
|
||||
*/
|
||||
class _Container extends \IPS\Node\Model
|
||||
class Container extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_containers';
|
||||
public static ?string $databaseTable = 'cms_containers';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'container_';
|
||||
public static string $databasePrefix = 'container_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array('container_key');
|
||||
protected static array $databaseIdFields = array('container_key');
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
protected static array $multitonMap = array();
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Database Column
|
||||
*/
|
||||
public static $databaseColumnParent = 'parent_id';
|
||||
public static ?string $databaseColumnParent = 'parent_id';
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Root Value
|
||||
* @note This normally doesn't need changing though some legacy areas use -1 to indicate a root node
|
||||
*/
|
||||
public static $databaseColumnParentRootValue = 0;
|
||||
public static int $databaseColumnParentRootValue = 0;
|
||||
|
||||
/**
|
||||
* @brief [Node] Order Database Column
|
||||
*/
|
||||
public static $databaseColumnOrder = 'order';
|
||||
public static ?string $databaseColumnOrder = 'order';
|
||||
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = '';
|
||||
public static string $nodeTitle = '';
|
||||
|
||||
/**
|
||||
* @brief [Node] Subnode class
|
||||
*/
|
||||
public static $subnodeClass = 'IPS\cms\Blocks\Block';
|
||||
public static ?string $subnodeClass = 'IPS\cms\Blocks\Block';
|
||||
|
||||
/**
|
||||
* @brief [Node] Show forms modally?
|
||||
*/
|
||||
public static $modalForms = TRUE;
|
||||
public static bool $modalForms = TRUE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Sortable?
|
||||
*/
|
||||
public static $nodeSortable = TRUE;
|
||||
public static bool $nodeSortable = TRUE;
|
||||
|
||||
/**
|
||||
* [Node] Get Title
|
||||
*
|
||||
* @return string|null
|
||||
* @return string
|
||||
*/
|
||||
protected function get__title()
|
||||
protected function get__title(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
@@ -104,12 +115,12 @@ class _Container extends \IPS\Node\Model
|
||||
* Fetch All Root Nodes
|
||||
*
|
||||
* @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 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(), $limit=NULL )
|
||||
public static function roots( ?string $permissionCheck='view', Member $member=NULL, mixed $where=array(), array $limit=NULL ): array
|
||||
{
|
||||
return parent::roots( $permissionCheck, $member, array( array( 'container_type=?', 'block' ) ), $limit );
|
||||
}
|
||||
@@ -117,11 +128,11 @@ class _Container extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Get buttons to display in tree
|
||||
*
|
||||
* @param string $url Base URL
|
||||
* @param bool $subnode Is this a subnode?
|
||||
* @return array
|
||||
* @param Url $url Base URL
|
||||
* @param bool $subnode Is this a subnode?
|
||||
* @return array
|
||||
*/
|
||||
public function getButtons( $url, $subnode=FALSE )
|
||||
public function getButtons( Url $url, bool $subnode=FALSE ): array
|
||||
{
|
||||
$buttons = parent::getButtons( $url, $subnode );
|
||||
$return = array();
|
||||
@@ -135,7 +146,7 @@ class _Container extends \IPS\Node\Model
|
||||
{
|
||||
$buttons['add']['icon'] = 'folder-open';
|
||||
$buttons['add']['title'] = 'content_block_cat_add';
|
||||
$buttons['add']['data'] = array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('content_block_cat_add') );
|
||||
$buttons['add']['data'] = array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('content_block_cat_add') );
|
||||
$buttons['add']['link'] = $url->setQueryString( array( 'subnode' => 0, 'do' => 'form', 'parent' => $this->_id ) );
|
||||
|
||||
$buttons['add_block'] = array(
|
||||
@@ -172,9 +183,9 @@ class _Container extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Does the currently logged in user have permission to delete this node?
|
||||
*
|
||||
* @return bool
|
||||
* @return bool
|
||||
*/
|
||||
public function canDelete()
|
||||
public function canDelete(): bool
|
||||
{
|
||||
if ( $this->key == 'block_custom' OR $this->key == 'block_plugins' )
|
||||
{
|
||||
@@ -187,17 +198,17 @@ class _Container extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Add/Edit Form
|
||||
*
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @param Form $form The form
|
||||
* @return void
|
||||
*/
|
||||
public function form( &$form )
|
||||
public function form( Form &$form ) : void
|
||||
{
|
||||
/* Build form */
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'container_name', $this->id ? $this->name : '', TRUE, array( 'maxLength' => 64 ) ) );
|
||||
$form->add( new Text( 'container_name', $this->id ? $this->name : '', TRUE, array( 'maxLength' => 64 ) ) );
|
||||
|
||||
$class = \get_called_class();
|
||||
$class = get_called_class();
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'container_parent_id', $this->parent_id ? $this->parent_id : 0, FALSE, array(
|
||||
$form->add( new Node( 'container_parent_id', $this->parent_id ? $this->parent_id : 0, FALSE, array(
|
||||
'class' => '\IPS\cms\Blocks\Container',
|
||||
'zeroVal' => 'node_no_parent',
|
||||
'permissionCheck' => function( $node ) use ( $class )
|
||||
@@ -207,7 +218,7 @@ class _Container extends \IPS\Node\Model
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return !isset( \IPS\Request::i()->id ) or ( $node->id != \IPS\Request::i()->id and !$node->isChildOf( $node::load( \IPS\Request::i()->id ) ) );
|
||||
return !isset( Request::i()->id ) or ( $node->id != Request::i()->id and !$node->isChildOf( $node::load( Request::i()->id ) ) );
|
||||
}
|
||||
) ) );
|
||||
}
|
||||
@@ -218,7 +229,7 @@ class _Container extends \IPS\Node\Model
|
||||
* @param array $values Values from the form
|
||||
* @return array
|
||||
*/
|
||||
public function formatFormValues( $values )
|
||||
public function formatFormValues( array $values ): array
|
||||
{
|
||||
if ( isset( $values['container_parent_id'] ) AND ( ! empty( $values['container_parent_id'] ) OR $values['container_parent_id'] === 0 ) )
|
||||
{
|
||||
@@ -239,7 +250,7 @@ class _Container extends \IPS\Node\Model
|
||||
* @param mixed $where Where clause
|
||||
* @return array
|
||||
*/
|
||||
public static function search( $column, $query, $order, $where=array() )
|
||||
public static function search( string $column, string $query, ?string $order=NULL, mixed $where=array() ): array
|
||||
{
|
||||
if ( $column === '_title' )
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -12,35 +12,41 @@
|
||||
namespace IPS\cms\Databases;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\Dispatcher\Controller as DispatcherController;
|
||||
use IPS\Http\Url;
|
||||
use function defined;
|
||||
use function get_called_class;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class that Controllers should extend
|
||||
*/
|
||||
abstract class _Controller extends \IPS\Dispatcher\Controller
|
||||
abstract class Controller extends DispatcherController
|
||||
{
|
||||
/**
|
||||
* @brief Base URL
|
||||
*/
|
||||
public $url;
|
||||
public mixed $url;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \IPS\Http\Url|NULL $url The base URL for this controller or NULL to calculate automatically
|
||||
* @param mixed|null $url The base URL for this controller or NULL to calculate automatically
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $url=NULL )
|
||||
public function __construct( mixed $url=NULL )
|
||||
{
|
||||
if ( $url === NULL )
|
||||
{
|
||||
$class = \get_called_class();
|
||||
$class = get_called_class();
|
||||
$exploded = explode( '\\', $class );
|
||||
$this->url = \IPS\Http\Url::internal( "app=cms&module=database", 'front' ); /* @todo fix URL */
|
||||
$this->url = Url::internal( "app=cms&module=database", 'front' ); /* @todo fix URL */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -12,75 +12,95 @@
|
||||
namespace IPS\cms\Databases;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\Application;
|
||||
use IPS\cms\Categories;
|
||||
use IPS\cms\Databases;
|
||||
use IPS\cms\Pages\Page;
|
||||
use IPS\cms\Records;
|
||||
use IPS\core\DataLayer;
|
||||
use IPS\Dispatcher as SystemDispatcher;
|
||||
use IPS\Dispatcher\Controller;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Output;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use OutOfRangeException;
|
||||
use UnderflowException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function intval;
|
||||
use function is_numeric;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database Dispatcher
|
||||
*/
|
||||
class _Dispatcher extends \IPS\Dispatcher
|
||||
class Dispatcher extends SystemDispatcher
|
||||
{
|
||||
/**
|
||||
* @brief Singleton Instance (So we don't re-use the regular dispatcher)
|
||||
*/
|
||||
protected static $instance = NULL;
|
||||
protected static mixed $instance = NULL;
|
||||
|
||||
/**
|
||||
* @brief Controller location
|
||||
*/
|
||||
public $controllerLocation = 'front';
|
||||
public string $controllerLocation = 'front';
|
||||
|
||||
/**
|
||||
* @brief Database Id
|
||||
*/
|
||||
public $databaseId = NULL;
|
||||
public ?int $databaseId = NULL;
|
||||
|
||||
/**
|
||||
* @brief Category Id
|
||||
*/
|
||||
public $categoryId = NULL;
|
||||
public ?int $categoryId = NULL;
|
||||
|
||||
/**
|
||||
* @brief Record Id
|
||||
*/
|
||||
public $recordId = NULL;
|
||||
public ?int $recordId = NULL;
|
||||
|
||||
/**
|
||||
* @brief Url
|
||||
*/
|
||||
public $url = NULL;
|
||||
public mixed $url = NULL;
|
||||
|
||||
/**
|
||||
* @brief Module
|
||||
*/
|
||||
public $module = NULL;
|
||||
public ?string $module = NULL;
|
||||
|
||||
/**
|
||||
* @brief Output to return
|
||||
*/
|
||||
public $output = "";
|
||||
public string $output = "";
|
||||
|
||||
/**
|
||||
* Set Database ID
|
||||
*
|
||||
* @param mixed $databaseId Database key or ID
|
||||
* @return \IPS\Dispatcher
|
||||
* @return Dispatcher
|
||||
*/
|
||||
public function setDatabase( $databaseId )
|
||||
public function setDatabase( mixed $databaseId ): Dispatcher
|
||||
{
|
||||
/* Other areas rely on $this->databaseId being numeric */
|
||||
if ( !\is_numeric( $databaseId ) )
|
||||
if ( !is_numeric( $databaseId ) )
|
||||
{
|
||||
$database = \IPS\cms\Databases::load( $databaseId, 'database_key' );
|
||||
$database = Databases::load( $databaseId, 'database_key' );
|
||||
$databaseId = $database->id;
|
||||
}
|
||||
|
||||
$this->databaseId = $databaseId;
|
||||
|
||||
$database = \IPS\cms\Databases::load( $databaseId );
|
||||
$database = Databases::load( $databaseId );
|
||||
if ( ! $database->use_categories )
|
||||
{
|
||||
$this->categoryId = $database->_default_category;
|
||||
@@ -93,9 +113,9 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
* Set Category ID
|
||||
*
|
||||
* @param mixed $categoryId Category ID
|
||||
* @return \IPS\Dispatcher
|
||||
* @return Dispatcher
|
||||
*/
|
||||
public function setCategory( $categoryId )
|
||||
public function setCategory( mixed $categoryId ): Dispatcher
|
||||
{
|
||||
$this->categoryId = $categoryId;
|
||||
return $this;
|
||||
@@ -106,11 +126,11 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
public function init() : void
|
||||
{
|
||||
if ( ( \IPS\cms\Pages\Page::$currentPage AND ! ( \IPS\Application::load('cms')->default AND ! \IPS\cms\Pages\Page::$currentPage->folder_id AND \IPS\cms\Pages\Page::$currentPage->default ) ) )
|
||||
if ( ( Page::$currentPage AND ! ( Application::load('cms')->default AND ! Page::$currentPage->folder_id AND Page::$currentPage->default ) ) )
|
||||
{
|
||||
\IPS\Output::i()->breadcrumb['module'] = array( \IPS\cms\Pages\Page::$currentPage->url(), \IPS\cms\Pages\Page::$currentPage->_title );
|
||||
Output::i()->breadcrumb['module'] = array( Page::$currentPage->url(), Page::$currentPage->_title );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,60 +139,62 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
public function run() : void
|
||||
{
|
||||
/* Coming from a widget? */
|
||||
if ( isset( \IPS\Request::i()->pageID ) and isset( \IPS\Request::i()->blockID ) )
|
||||
if ( isset( Request::i()->pageID ) and isset( Request::i()->blockID ) )
|
||||
{
|
||||
if ( \IPS\cms\Pages\Page::$currentPage === NULL )
|
||||
if ( Page::$currentPage === NULL )
|
||||
{
|
||||
/* make sure this is a valid widgetized page to stop tampering */
|
||||
try
|
||||
{
|
||||
foreach ( \IPS\Db::i()->select( '*', 'cms_page_widget_areas', array( 'area_page_id=?', \IPS\Request::i()->pageID ) ) as $item )
|
||||
$page = Page::load( Request::i()->pageID );
|
||||
foreach( $page->getAreasFromDatabase() as $area )
|
||||
{
|
||||
foreach( json_decode( $item['area_widgets'], TRUE ) as $block )
|
||||
foreach( $area->getAllWidgets() as $block )
|
||||
{
|
||||
if ( $block['key'] === 'Database' and isset( $block['configuration']['database'] ) and \intval( $block['configuration']['database'] ) === $this->databaseId )
|
||||
if ( $block['key'] === 'Database' and isset( $block['configuration']['database'] ) and intval( $block['configuration']['database'] ) === $this->databaseId )
|
||||
{
|
||||
\IPS\cms\Pages\Page::$currentPage = \IPS\cms\Pages\Page::load( \IPS\Request::i()->pageID );
|
||||
Page::$currentPage = $page;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e ) { }
|
||||
catch( UnderflowException $e ) { }
|
||||
}
|
||||
|
||||
/* Try again */
|
||||
if ( \IPS\cms\Pages\Page::$currentPage === NULL )
|
||||
if ( Page::$currentPage === NULL )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_doesnt_exist', '2T251/1', 404 );
|
||||
Output::i()->error( 'page_doesnt_exist', '2T251/1', 404 );
|
||||
}
|
||||
|
||||
/* Unset do query param otherwise it confuses the controller->execute(); */
|
||||
\IPS\Request::i()->do = NULL;
|
||||
Request::i()->do = NULL;
|
||||
}
|
||||
|
||||
$url = 'app=cms&module=pages&controller=page&path=' . \IPS\cms\Pages\Page::$currentPage->full_path;
|
||||
$url = 'app=cms&module=pages&controller=page&path=' . Page::$currentPage->full_path;
|
||||
|
||||
try
|
||||
{
|
||||
$database = \IPS\cms\Databases::load( $this->databaseId );
|
||||
$database = Databases::load( $this->databaseId );
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
catch( OutOfRangeException $ex )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_doesnt_exist', '2T251/2', 404 );
|
||||
Output::i()->error( 'page_doesnt_exist', '2T251/2', 404 );
|
||||
}
|
||||
|
||||
$path = trim( preg_replace( '#' . \IPS\cms\Pages\Page::$currentPage->full_path . '#', '', \IPS\Request::i()->path, 1 ), '/' );
|
||||
$path = trim( preg_replace( '#' . Page::$currentPage->full_path . '#', '', Request::i()->path, 1 ), '/' );
|
||||
|
||||
/* If we visited the default page in a folder, the full_path will be like folder/page but the request path will just be folder */
|
||||
if( \IPS\Request::i()->path . '/' . \IPS\cms\Pages\Page::$currentPage->seo_name == \IPS\cms\Pages\Page::$currentPage->full_path )
|
||||
if( Request::i()->path . '/' . Page::$currentPage->seo_name == Page::$currentPage->full_path )
|
||||
{
|
||||
$path = '';
|
||||
}
|
||||
|
||||
list( $path, $pageNumber ) = \IPS\cms\Pages\Page::getStrippedPagePath( $path );
|
||||
|
||||
list( $path, $pageNumber ) = Page::getStrippedPagePath( $path );
|
||||
|
||||
$this->databaseId = $database->id;
|
||||
|
||||
@@ -182,13 +204,13 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
}
|
||||
|
||||
/* Got a specific category ID? */
|
||||
if ( $this->categoryId !== NULL and ! $path and ( ( $database->use_categories and $database->cat_index_type !== 1 ) OR ( ! $database->use_categories and isset( \IPS\Request::i()->do ) ) ) )
|
||||
if ( $this->categoryId !== NULL and ! $path and ( ( $database->use_categories and $database->cat_index_type !== 1 ) OR ( ! $database->use_categories and isset( Request::i()->do ) ) ) )
|
||||
{
|
||||
$this->module = 'category';
|
||||
}
|
||||
else if ( isset( \IPS\Request::i()->c ) AND \is_numeric( \IPS\Request::i()->c ) )
|
||||
else if ( isset( Request::i()->c ) AND is_numeric( Request::i()->c ) )
|
||||
{
|
||||
$this->categoryId = \IPS\Request::i()->c;
|
||||
$this->categoryId = Request::i()->c;
|
||||
$this->module = 'category';
|
||||
}
|
||||
else if ( empty( $path ) )
|
||||
@@ -198,11 +220,14 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
else
|
||||
{
|
||||
$url .= '/' . $path;
|
||||
|
||||
/* @var Records $recordClass */
|
||||
$recordClass = '\IPS\cms\Records' . $database->id;
|
||||
$isLegacyCategoryUrl = FALSE;
|
||||
|
||||
if ( $database->use_categories )
|
||||
{
|
||||
/* @var Categories $catClass */
|
||||
$catClass = '\IPS\cms\Categories' . $database->id;
|
||||
$category = $catClass::loadFromPath( $path, $database->id );
|
||||
|
||||
@@ -231,7 +256,7 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
|
||||
$this->_redirectToCorrectUrl( $record->url() );
|
||||
}
|
||||
catch ( \OutOfRangeException $ex )
|
||||
catch ( OutOfRangeException $ex )
|
||||
{
|
||||
/* Check slug history */
|
||||
try
|
||||
@@ -240,9 +265,9 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
|
||||
$this->_redirectToCorrectUrl( $record->url() );
|
||||
}
|
||||
catch ( \OutOfRangeException $ex )
|
||||
catch ( OutOfRangeException $ex )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_doesnt_exist', '2T251/4', 404 );
|
||||
Output::i()->error( 'page_doesnt_exist', '2T251/4', 404 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,9 +289,9 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
$record = $recordClass::loadFromSlug( $whatsLeft, TRUE, $this->categoryId );
|
||||
|
||||
/* Make the Content controller all kinds of happy */
|
||||
\IPS\Request::i()->id = $this->recordId = $record->primary_id_field;
|
||||
Request::i()->id = $this->recordId = $record->primary_id_field;
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
catch( OutOfRangeException $ex )
|
||||
{
|
||||
/* Check slug history */
|
||||
try
|
||||
@@ -275,15 +300,15 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
|
||||
$this->_redirectToCorrectUrl( $record->url() );
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
catch( OutOfRangeException $ex )
|
||||
{
|
||||
/* We are absolutely certain this is not a record, but we have found a legacy category - redirect */
|
||||
if ( $isLegacyCategoryUrl === TRUE )
|
||||
{
|
||||
\IPS\Output::i()->redirect( $category->url() );
|
||||
Output::i()->redirect( $category->url() );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->error( 'page_doesnt_exist', '2T251/5', 404 );
|
||||
Output::i()->error( 'page_doesnt_exist', '2T251/5', 404 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,56 +327,57 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
}
|
||||
}
|
||||
|
||||
$this->url = \IPS\Http\Url::internal( $url, 'front', 'content_page_path' );
|
||||
$this->url = Url::internal( $url, 'front', 'content_page_path' );
|
||||
$className = '\\IPS\\cms\\modules\\front\\database\\' . $this->module;
|
||||
|
||||
/* Init class */
|
||||
if( !class_exists( $className ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_doesnt_exist', '2T251/6', 404 );
|
||||
Output::i()->error( 'page_doesnt_exist', '2T251/6', 404 );
|
||||
}
|
||||
$controller = new $className;
|
||||
if( !( $controller instanceof \IPS\Dispatcher\Controller ) )
|
||||
if( !( $controller instanceof Controller ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'page_not_found', '3T251/7', 500, '' );
|
||||
Output::i()->error( 'page_not_found', '3T251/7', 500, '' );
|
||||
}
|
||||
|
||||
\IPS\Dispatcher::i()->dispatcherController = $controller;
|
||||
Dispatcher::i()->dispatcherController = $controller;
|
||||
|
||||
/* Is this the default application? Default app should search all */
|
||||
if ( ! \IPS\Application::load('cms')->default )
|
||||
if ( ! Application::load('cms')->default )
|
||||
{
|
||||
/* If database doesn't allow search, default to all */
|
||||
if ( ! $database->search )
|
||||
{
|
||||
\IPS\Output::i()->defaultSearchOption = array( 'all', 'search_everything' );
|
||||
Output::i()->defaultSearchOption = array( 'all', 'search_everything' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->defaultSearchOption = array( "cms_records{$this->databaseId}", "cms_records{$this->databaseId}_pl" );
|
||||
Output::i()->defaultSearchOption = array( "cms_records{$this->databaseId}", "cms_records{$this->databaseId}_pl" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->defaultSearchOption = array( 'all', 'search_everything' );
|
||||
Output::i()->defaultSearchOption = array( 'all', 'search_everything' );
|
||||
}
|
||||
|
||||
/* Add database key to body classes for easier database specific themeing */
|
||||
\IPS\Output::i()->bodyClasses[] = 'cCmsDatabase_' . $database->key;
|
||||
Output::i()->bodyClasses[] = 'cCmsDatabase_' . $database->key;
|
||||
|
||||
/* Execute */
|
||||
$controller->execute();
|
||||
|
||||
/* Data Layer Context */
|
||||
if ( $this->databaseId AND \IPS\Settings::i()->core_datalayer_enabled )
|
||||
if ( $this->databaseId AND Settings::i()->core_datalayer_enabled )
|
||||
{
|
||||
try
|
||||
{
|
||||
$databaseOrCategory = \IPS\cms\Databases::load( $this->databaseId );
|
||||
$databaseOrCategory = Databases::load( $this->databaseId );
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
catch ( UnderflowException $e )
|
||||
{
|
||||
return $this->finish();
|
||||
$this->finish();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Use the category instead of the database if there is one */
|
||||
@@ -359,24 +385,25 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
{
|
||||
try
|
||||
{
|
||||
/* @var Categories $catClass */
|
||||
$catClass = '\IPS\cms\Categories' . $this->databaseId;
|
||||
$databaseOrCategory = $catClass::load( $this->categoryId );
|
||||
}
|
||||
catch ( \UnderflowException $e ) {}
|
||||
catch ( UnderflowException $e ) {}
|
||||
}
|
||||
|
||||
/* Add the database's/category's data layer properties to the page context */
|
||||
foreach ( $databaseOrCategory->getDataLayerProperties() as $key => $property )
|
||||
{
|
||||
\IPS\core\DataLayer::i()->addContextProperty( $key, $property, true );
|
||||
DataLayer::i()->addContextProperty( $key, $property );
|
||||
if ( $key === 'content_area' )
|
||||
{
|
||||
\IPS\core\DataLayer::i()->addContextProperty( 'community_area', $property, true );
|
||||
DataLayer::i()->addContextProperty( 'community_area', $property );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->finish();
|
||||
$this->finish();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,25 +413,25 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
* If the record is moved so the URL is cat-2, the embed needs to redirect while retaining
|
||||
* the /?do=embed
|
||||
*
|
||||
* @param \IPS\Http\Url $correctUrl The URL for the record
|
||||
* @param Url $correctUrl The URL for the record
|
||||
* @return void
|
||||
*/
|
||||
protected function _redirectToCorrectUrl( $correctUrl )
|
||||
protected function _redirectToCorrectUrl( Url $correctUrl ) : void
|
||||
{
|
||||
$paramsToSet = array();
|
||||
foreach ( \IPS\Request::i()->url()->queryString as $k => $v )
|
||||
foreach ( Request::i()->url()->queryString as $k => $v )
|
||||
{
|
||||
if ( !array_key_exists( $k, $correctUrl->queryString ) and !array_key_exists( $k, $correctUrl->hiddenQueryString ) )
|
||||
{
|
||||
$paramsToSet[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
if ( \count( $paramsToSet ) )
|
||||
if ( count( $paramsToSet ) )
|
||||
{
|
||||
$correctUrl = $correctUrl->setQueryString( $paramsToSet );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->redirect( $correctUrl, NULL, 301 );
|
||||
Output::i()->redirect( $correctUrl, NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,8 +439,9 @@ class _Dispatcher extends \IPS\Dispatcher
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function finish()
|
||||
public function finish() : void
|
||||
{
|
||||
return (string) $this->output ?: \IPS\Output::i()->output;
|
||||
Output::i()->output = $this->output ?: Output::i()->output;
|
||||
parent::finish();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -12,55 +12,64 @@
|
||||
namespace IPS\cms\Fields;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DomainException;
|
||||
use InvalidArgumentException;
|
||||
use IPS\cms\Fields;
|
||||
use IPS\Helpers\Form\Text;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Theme;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Soundcloud input class for Form Builder
|
||||
*/
|
||||
class _Soundcloud extends \IPS\Helpers\Form\Text
|
||||
class Soundcloud extends Text
|
||||
{
|
||||
/**
|
||||
* @brief Default Options
|
||||
*/
|
||||
public $childDefaultOptions = array(
|
||||
public array $childDefaultOptions = array(
|
||||
'parameters' => array()
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $name Name
|
||||
* @param mixed $defaultValue Default value
|
||||
* @param bool|NULL $required Required? (NULL for not required, but appears to be so)
|
||||
* @param array $options Type-specific options
|
||||
* @param callback $customValidationCode Custom validation code
|
||||
* @param string $prefix HTML to show before input field
|
||||
* @param string $suffix HTML to show after input field
|
||||
* @param string $id The ID to add to the row
|
||||
* @return void
|
||||
* @param string $name Name
|
||||
* @param mixed $defaultValue Default value
|
||||
* @param bool|null $required Required? (NULL for not required, but appears to be so)
|
||||
* @param array $options Type-specific options
|
||||
* @param callable|null $customValidationCode Custom validation code
|
||||
* @param string|null $prefix HTML to show before input field
|
||||
* @param string|null $suffix HTML to show after input field
|
||||
* @param string|null $id The ID to add to the row
|
||||
*/
|
||||
public function __construct( $name, $defaultValue=NULL, $required=FALSE, $options=array(), $customValidationCode=NULL, $prefix=NULL, $suffix=NULL, $id=NULL )
|
||||
public function __construct( string $name, mixed $defaultValue=NULL, bool $required=FALSE, array $options=array(), callable $customValidationCode=NULL, string $prefix=NULL, string $suffix=NULL, string $id=NULL )
|
||||
{
|
||||
$this->childDefaultOptions['placeholder'] = \IPS\Member::loggedIn()->language()->addToStack('field_placeholder_soundcloud');
|
||||
$this->childDefaultOptions['placeholder'] = Member::loggedIn()->language()->addToStack('field_placeholder_soundcloud');
|
||||
|
||||
/* Call parent constructor */
|
||||
parent::__construct( $name, $defaultValue, $required, $options, $customValidationCode, $prefix, $suffix, $id );
|
||||
|
||||
$this->formType = 'text';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the display value
|
||||
*
|
||||
* @param mixed $value Stored value from form
|
||||
* @param \IPS\cms\Field $customField Custom Field Object
|
||||
* @return string
|
||||
*
|
||||
* @param mixed $value Stored value from form
|
||||
* @param Fields $customField Custom Field Object
|
||||
* @return string|null
|
||||
*/
|
||||
public static function displayValue( $value, $customField )
|
||||
public static function displayValue( mixed $value, Fields $customField ): ?string
|
||||
{
|
||||
if( !$value )
|
||||
{
|
||||
@@ -79,34 +88,36 @@ class _Soundcloud extends \IPS\Helpers\Form\Text
|
||||
$params['height'] = 166;
|
||||
}
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'records', 'cms', 'global' )->soundcloud( urlencode( $value ), $params );
|
||||
return Theme::i()->getTemplate( 'records', 'cms', 'global' )->soundcloud( urlencode( $value ), $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \DomainException
|
||||
* @return TRUE
|
||||
*/
|
||||
public function validate()
|
||||
* @return bool
|
||||
* @throws DomainException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(): bool
|
||||
{
|
||||
parent::validate();
|
||||
|
||||
if ( $this->value )
|
||||
{
|
||||
/* Check the URL is valid */
|
||||
if ( !( $this->value instanceof \IPS\Http\Url ) )
|
||||
if ( !( $this->value instanceof Url ) )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_url_bad');
|
||||
throw new InvalidArgumentException('form_url_bad');
|
||||
}
|
||||
|
||||
/* Check its a valid Soundcloud URL */
|
||||
/* Check it's a valid Soundcloud URL */
|
||||
if ( ! mb_stristr( $this->value->data['host'], 'soundcloud.com' ) )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_url_bad');
|
||||
throw new InvalidArgumentException('form_url_bad');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,12 +125,12 @@ class _Soundcloud extends \IPS\Helpers\Form\Text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
public function getValue(): mixed
|
||||
{
|
||||
$val = parent::getValue();
|
||||
if ( $val and !mb_strpos( $val, '://' ) )
|
||||
{
|
||||
$val = "http://{$val}";
|
||||
$val = "https://{$val}";
|
||||
}
|
||||
|
||||
return $val;
|
||||
@@ -128,17 +139,17 @@ class _Soundcloud extends \IPS\Helpers\Form\Text
|
||||
/**
|
||||
* Format Value
|
||||
*
|
||||
* @return \IPS\Http\Url|string
|
||||
* @return Url|string
|
||||
*/
|
||||
public function formatValue()
|
||||
public function formatValue(): mixed
|
||||
{
|
||||
if ( $this->value and !( $this->value instanceof \IPS\Http\Url ) )
|
||||
if ( $this->value and !( $this->value instanceof Url ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
return new \IPS\Http\Url( $this->value );
|
||||
return new Url( $this->value );
|
||||
}
|
||||
catch ( \InvalidArgumentException $e )
|
||||
catch ( InvalidArgumentException $e )
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
@@ -12,40 +12,49 @@
|
||||
namespace IPS\cms\Fields;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DomainException;
|
||||
use InvalidArgumentException;
|
||||
use IPS\cms\Fields;
|
||||
use IPS\Helpers\Form\Text;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Theme;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spotify input class for Form Builder
|
||||
*/
|
||||
class _Spotify extends \IPS\Helpers\Form\Text
|
||||
class Spotify extends Text
|
||||
{
|
||||
/**
|
||||
* @brief Default Options
|
||||
*/
|
||||
public $childDefaultOptions = array(
|
||||
public array $childDefaultOptions = array(
|
||||
'parameters' => array()
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $name Name
|
||||
* @param mixed $defaultValue Default value
|
||||
* @param bool|NULL $required Required? (NULL for not required, but appears to be so)
|
||||
* @param array $options Type-specific options
|
||||
* @param callback $customValidationCode Custom validation code
|
||||
* @param string $prefix HTML to show before input field
|
||||
* @param string $suffix HTML to show after input field
|
||||
* @param string $id The ID to add to the row
|
||||
* @return void
|
||||
* @param string $name Name
|
||||
* @param mixed $defaultValue Default value
|
||||
* @param bool|NULL $required Required? (NULL for not required, but appears to be so)
|
||||
* @param array $options Type-specific options
|
||||
* @param callable|null $customValidationCode Custom validation code
|
||||
* @param string|null $prefix HTML to show before input field
|
||||
* @param string|null $suffix HTML to show after input field
|
||||
* @param string|null $id The ID to add to the row
|
||||
*/
|
||||
public function __construct( $name, $defaultValue=NULL, $required=FALSE, $options=array(), $customValidationCode=NULL, $prefix=NULL, $suffix=NULL, $id=NULL )
|
||||
public function __construct( string $name, mixed $defaultValue=null, bool $required=FALSE, array $options=array(), callable $customValidationCode=null, string $prefix=null, string $suffix=null, string $id=null )
|
||||
{
|
||||
$this->childDefaultOptions['placeholder'] = \IPS\Member::loggedIn()->language()->addToStack('field_placeholder_spotify');
|
||||
$this->childDefaultOptions['placeholder'] = Member::loggedIn()->language()->addToStack('field_placeholder_spotify');
|
||||
|
||||
/* Call parent constructor */
|
||||
parent::__construct( $name, $defaultValue, $required, $options, $customValidationCode, $prefix, $suffix, $id );
|
||||
@@ -57,10 +66,10 @@ class _Spotify extends \IPS\Helpers\Form\Text
|
||||
* Get the display value
|
||||
*
|
||||
* @param mixed $value Stored value from form
|
||||
* @param \IPS\cms\Field $customField Custom Field Object
|
||||
* @return string
|
||||
* @param Fields $customField Custom Field Object
|
||||
* @return string|null
|
||||
*/
|
||||
public static function displayValue( $value, $customField )
|
||||
public static function displayValue( mixed $value, Fields $customField ): ?string
|
||||
{
|
||||
if( !$value )
|
||||
{
|
||||
@@ -79,34 +88,36 @@ class _Spotify extends \IPS\Helpers\Form\Text
|
||||
$params['height'] = 80;
|
||||
}
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'records', 'cms', 'global' )->spotify( $value, $params );
|
||||
return Theme::i()->getTemplate( 'records', 'cms', 'global' )->spotify( $value, $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \DomainException
|
||||
* @return TRUE
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DomainException
|
||||
* @return bool
|
||||
*/
|
||||
public function validate()
|
||||
public function validate(): bool
|
||||
{
|
||||
parent::validate();
|
||||
|
||||
if ( $this->value )
|
||||
{
|
||||
/* Check the URL is valid */
|
||||
if ( !( $this->value instanceof \IPS\Http\Url ) )
|
||||
if ( !( $this->value instanceof Url ) )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_url_bad');
|
||||
throw new InvalidArgumentException('form_url_bad');
|
||||
}
|
||||
|
||||
/* Check its a valid Spotify URL */
|
||||
if ( ! mb_stristr( $this->value->data['host'], 'spotify.com' ) )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_url_bad');
|
||||
throw new InvalidArgumentException('form_url_bad');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,12 +125,12 @@ class _Spotify extends \IPS\Helpers\Form\Text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
public function getValue(): mixed
|
||||
{
|
||||
$val = parent::getValue();
|
||||
if ( $val and !mb_strpos( $val, '://' ) )
|
||||
{
|
||||
$val = "http://{$val}";
|
||||
$val = "https://{$val}";
|
||||
}
|
||||
|
||||
return $val;
|
||||
@@ -128,17 +139,17 @@ class _Spotify extends \IPS\Helpers\Form\Text
|
||||
/**
|
||||
* Format Value
|
||||
*
|
||||
* @return \IPS\Http\Url|string
|
||||
* @return Url|string
|
||||
*/
|
||||
public function formatValue()
|
||||
public function formatValue(): mixed
|
||||
{
|
||||
if ( $this->value and !( $this->value instanceof \IPS\Http\Url ) )
|
||||
if ( $this->value and !( $this->value instanceof Url ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
return new \IPS\Http\Url( $this->value );
|
||||
return new Url( $this->value );
|
||||
}
|
||||
catch ( \InvalidArgumentException $e )
|
||||
catch ( InvalidArgumentException $e )
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
@@ -12,40 +12,49 @@
|
||||
namespace IPS\cms\Fields;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DomainException;
|
||||
use InvalidArgumentException;
|
||||
use IPS\cms\Fields;
|
||||
use IPS\Helpers\Form\Text;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Theme;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Youtube input class for Form Builder
|
||||
* YouTube input class for Form Builder
|
||||
*/
|
||||
class _Youtube extends \IPS\Helpers\Form\Text
|
||||
class Youtube extends Text
|
||||
{
|
||||
/**
|
||||
* @brief Default Options
|
||||
*/
|
||||
public $childDefaultOptions = array(
|
||||
public array $childDefaultOptions = array(
|
||||
'parameters' => array()
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $name Name
|
||||
* @param mixed $defaultValue Default value
|
||||
* @param bool|NULL $required Required? (NULL for not required, but appears to be so)
|
||||
* @param array $options Type-specific options
|
||||
* @param callback $customValidationCode Custom validation code
|
||||
* @param string $prefix HTML to show before input field
|
||||
* @param string $suffix HTML to show after input field
|
||||
* @param string $id The ID to add to the row
|
||||
* @return void
|
||||
* @param string $name Name
|
||||
* @param mixed $defaultValue Default value
|
||||
* @param bool|null $required Required? (NULL for not required, but appears to be so)
|
||||
* @param array $options Type-specific options
|
||||
* @param callable|null $customValidationCode Custom validation code
|
||||
* @param string|null $prefix HTML to show before input field
|
||||
* @param string|null $suffix HTML to show after input field
|
||||
* @param string|null $id The ID to add to the row
|
||||
*/
|
||||
public function __construct( $name, $defaultValue=NULL, $required=FALSE, $options=array(), $customValidationCode=NULL, $prefix=NULL, $suffix=NULL, $id=NULL )
|
||||
public function __construct( string $name, mixed $defaultValue=NULL, bool $required=FALSE, array $options=array(), callable $customValidationCode=NULL, string $prefix=NULL, string $suffix=NULL, string $id=NULL )
|
||||
{
|
||||
$this->childDefaultOptions['placeholder'] = \IPS\Member::loggedIn()->language()->addToStack('field_placeholder_youtube');
|
||||
$this->childDefaultOptions['placeholder'] = Member::loggedIn()->language()->addToStack('field_placeholder_youtube');
|
||||
|
||||
/* Call parent constructor */
|
||||
parent::__construct( $name, $defaultValue, $required, $options, $customValidationCode, $prefix, $suffix, $id );
|
||||
@@ -57,17 +66,17 @@ class _Youtube extends \IPS\Helpers\Form\Text
|
||||
* Get the display value
|
||||
*
|
||||
* @param mixed $value Stored value from form
|
||||
* @param \IPS\cms\Field $customField Custom Field Object
|
||||
* @return string
|
||||
* @param Fields $customField Custom Field Object
|
||||
* @return string|null
|
||||
*/
|
||||
public static function displayValue( $value, $customField )
|
||||
public static function displayValue( mixed $value, Fields $customField ): ?string
|
||||
{
|
||||
if( !$value )
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$url = new \IPS\Http\Url( $value );
|
||||
$url = new Url( $value );
|
||||
|
||||
if ( isset( $url->queryString['v'] ) )
|
||||
{
|
||||
@@ -95,36 +104,38 @@ class _Youtube extends \IPS\Helpers\Form\Text
|
||||
$params['height'] = ( $params['width'] * ( 9 / 16 ) ) + 30;
|
||||
}
|
||||
|
||||
$url = \IPS\Http\Url::external( $url )->setQueryString( $params );
|
||||
$url = Url::external( $url )->setQueryString( $params );
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'records', 'cms', 'global' )->youtube( $url, array( 'width' => $params['width'], 'height' => $params['height'] ) );
|
||||
return Theme::i()->getTemplate( 'records', 'cms', 'global' )->youtube( $url, array( 'width' => $params['width'], 'height' => $params['height'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \DomainException
|
||||
* @return TRUE
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DomainException
|
||||
* @return bool
|
||||
*/
|
||||
public function validate()
|
||||
public function validate(): bool
|
||||
{
|
||||
parent::validate();
|
||||
|
||||
if ( $this->value )
|
||||
{
|
||||
/* Check the URL is valid */
|
||||
if ( !( $this->value instanceof \IPS\Http\Url ) )
|
||||
if ( !( $this->value instanceof Url ) )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_url_bad');
|
||||
throw new InvalidArgumentException('form_url_bad');
|
||||
}
|
||||
|
||||
/* Check its a valid Youtube URL */
|
||||
if ( ! mb_stristr( $this->value->data['host'], 'youtube.' ) and ! mb_stristr( $this->value->data['host'], 'youtu.be' ) )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_url_bad');
|
||||
throw new InvalidArgumentException('form_url_bad');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,12 +143,12 @@ class _Youtube extends \IPS\Helpers\Form\Text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue()
|
||||
public function getValue(): mixed
|
||||
{
|
||||
$val = parent::getValue();
|
||||
if ( $val and !mb_strpos( $val, '://' ) )
|
||||
{
|
||||
$val = "http://{$val}";
|
||||
$val = "https://{$val}";
|
||||
}
|
||||
|
||||
return $val;
|
||||
@@ -146,17 +157,17 @@ class _Youtube extends \IPS\Helpers\Form\Text
|
||||
/**
|
||||
* Format Value
|
||||
*
|
||||
* @return \IPS\Http\Url|string
|
||||
* @return Url|string
|
||||
*/
|
||||
public function formatValue()
|
||||
public function formatValue(): mixed
|
||||
{
|
||||
if ( $this->value and !( $this->value instanceof \IPS\Http\Url ) )
|
||||
if ( $this->value and !( $this->value instanceof Url ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
return new \IPS\Http\Url( $this->value );
|
||||
return new Url( $this->value );
|
||||
}
|
||||
catch ( \InvalidArgumentException $e )
|
||||
catch ( InvalidArgumentException $e )
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
@@ -12,26 +12,42 @@
|
||||
namespace IPS\cms\Media;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use InvalidArgumentException;
|
||||
use IPS\cms\Media;
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\Node;
|
||||
use IPS\Helpers\Form\Text;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Http\Url\Friendly;
|
||||
use IPS\Member;
|
||||
use IPS\Node\Model;
|
||||
use IPS\Request;
|
||||
use OutOfRangeException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function func_get_args;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Folder Model
|
||||
*/
|
||||
class _Folder extends \IPS\Node\Model
|
||||
class Folder extends Model
|
||||
{
|
||||
/**
|
||||
* Munge different record types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function munge()
|
||||
public static function munge() : array
|
||||
{
|
||||
$rows = array();
|
||||
$args = \func_get_args();
|
||||
$args = func_get_args();
|
||||
|
||||
foreach( $args as $arg )
|
||||
{
|
||||
@@ -49,73 +65,73 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_media_folders';
|
||||
public static ?string $databaseTable = 'cms_media_folders';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'media_folder_';
|
||||
public static string $databasePrefix = 'media_folder_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array('media_folder_path');
|
||||
protected static array $databaseIdFields = array('media_folder_path');
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
protected static array $multitonMap = array();
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Database Column
|
||||
*/
|
||||
public static $databaseColumnParent = 'parent';
|
||||
public static ?string $databaseColumnParent = 'parent';
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Root Value
|
||||
* @note This normally doesn't need changing though some legacy areas use -1 to indicate a root node
|
||||
*/
|
||||
public static $databaseColumnParentRootValue = 0;
|
||||
public static int $databaseColumnParentRootValue = 0;
|
||||
|
||||
/**
|
||||
* @brief [Node] Order Database Column
|
||||
*/
|
||||
public static $databaseColumnOrder = 'path';
|
||||
public static ?string $databaseColumnOrder = 'path';
|
||||
|
||||
/**
|
||||
* @brief [Node] Automatically set position for new nodes
|
||||
*/
|
||||
public static $automaticPositionDetermination = FALSE;
|
||||
public static bool $automaticPositionDetermination = FALSE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = 'folder';
|
||||
public static string $nodeTitle = 'folder';
|
||||
|
||||
/**
|
||||
* @brief [Node] Subnode class
|
||||
*/
|
||||
public static $subnodeClass = 'IPS\cms\Media';
|
||||
public static ?string $subnodeClass = 'IPS\cms\Media';
|
||||
|
||||
/**
|
||||
* @brief [Node] Show forms modally?
|
||||
*/
|
||||
public static $modalForms = TRUE;
|
||||
public static bool $modalForms = TRUE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Restrictions
|
||||
*/
|
||||
protected static $restrictions = array(
|
||||
protected static ?array $restrictions = array(
|
||||
'app' => 'cms',
|
||||
'module' => 'pages',
|
||||
'all' => 'media_manage',
|
||||
@@ -125,9 +141,9 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Get Title
|
||||
*
|
||||
* @return string|null
|
||||
* @return string
|
||||
*/
|
||||
protected function get__title()
|
||||
protected function get__title(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
@@ -137,7 +153,7 @@ class _Folder extends \IPS\Node\Model
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSortableName()
|
||||
public function getSortableName() : string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
@@ -145,11 +161,11 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Get buttons to display in tree
|
||||
*
|
||||
* @param string $url Base URL
|
||||
* @param bool $subnode Is this a subnode?
|
||||
* @return array
|
||||
* @param Url $url Base URL
|
||||
* @param bool $subnode Is this a subnode?
|
||||
* @return array
|
||||
*/
|
||||
public function getButtons( $url, $subnode=FALSE )
|
||||
public function getButtons( Url $url, bool $subnode=FALSE ): array
|
||||
{
|
||||
$buttons = parent::getButtons( $url, $subnode );
|
||||
$return = array();
|
||||
@@ -163,14 +179,14 @@ class _Folder extends \IPS\Node\Model
|
||||
{
|
||||
$buttons['add']['icon'] = 'folder-open';
|
||||
$buttons['add']['title'] = 'cms_add_media_folder';
|
||||
$buttons['add']['data'] = array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('cms_add_media_folder') );
|
||||
$buttons['add']['data'] = array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('cms_add_media_folder') );
|
||||
$buttons['add']['link'] = $url->setQueryString( array( 'subnode' => 0, 'do' => 'form', 'parent' => $this->_id ) );
|
||||
|
||||
$buttons['add_page'] = array(
|
||||
'icon' => 'plus-circle',
|
||||
'title' => 'cms_add_media',
|
||||
'link' => $url->setQueryString( array( 'subnode' => 1, 'do' => 'add', 'parent' => $this->_id ) ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('cms_add_media') )
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('cms_add_media') )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -201,42 +217,44 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Add/Edit Form
|
||||
*
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @param Form $form The form
|
||||
* @return void
|
||||
*/
|
||||
public function form( &$form )
|
||||
public function form( Form &$form ) : void
|
||||
{
|
||||
/* Build form */
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'media_folder_name', $this->id ? $this->name : '', TRUE, array( 'maxLength' => 64 ), function( $val )
|
||||
$form->add( new Text( 'media_folder_name', $this->id ? $this->name : '', TRUE, array( 'maxLength' => 64 ), function( $val )
|
||||
{
|
||||
try
|
||||
{
|
||||
$test = \IPS\cms\Media\Folder::load( \IPS\Http\Url\Friendly::seoTitle( $val ), 'media_folder_name' );
|
||||
$test = Folder::load( Friendly::seoTitle( $val ), 'media_folder_name' );
|
||||
|
||||
if ( ! empty( \IPS\Request::i()->id ) and $test->id != \IPS\Request::i()->id )
|
||||
if ( ! empty( Request::i()->id ) and $test->id != Request::i()->id )
|
||||
{
|
||||
throw new \InvalidArgumentException('content_folder_name_in_use');
|
||||
throw new InvalidArgumentException('content_folder_name_in_use');
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
}
|
||||
} ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'media_folder_parent', $this->parent ? $this->parent : 0, FALSE, array(
|
||||
$form->add( new Node( 'media_folder_parent', $this->parent ? $this->parent : 0, FALSE, array(
|
||||
'class' => '\IPS\cms\Media\Folder',
|
||||
'zeroVal' => 'node_no_parent',
|
||||
'permissionCheck' => function( $node )
|
||||
{
|
||||
if ( ! isset( \IPS\Request::i()->id ) )
|
||||
if ( ! isset( Request::i()->id ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! isset( \IPS\Request::i()->parent ) )
|
||||
if ( ! isset( Request::i()->parent ) )
|
||||
{
|
||||
return $node->id != \IPS\Request::i()->id;
|
||||
return $node->id != Request::i()->id;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
) ) );
|
||||
}
|
||||
@@ -244,12 +262,12 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* @brief Original parent ID
|
||||
*/
|
||||
protected $origParentId;
|
||||
protected int $origParentId;
|
||||
|
||||
/**
|
||||
* @brief Original parent Name
|
||||
*/
|
||||
protected $origName;
|
||||
protected string $origName;
|
||||
|
||||
/**
|
||||
* [Node] Format form values from add/edit form for save
|
||||
@@ -257,15 +275,15 @@ class _Folder extends \IPS\Node\Model
|
||||
* @param array $values Values from the form
|
||||
* @return array
|
||||
*/
|
||||
public function formatFormValues( $values )
|
||||
public function formatFormValues( array $values ): array
|
||||
{
|
||||
if ( ! $this->id )
|
||||
{
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$this->origParentId = $this->parent;
|
||||
$this->origName = $this->name;
|
||||
$this->origParentId = (int) $this->parent;
|
||||
$this->origName = (string) $this->name;
|
||||
|
||||
if ( isset( $values['media_folder_parent'] ) AND ( ! empty( $values['media_folder_parent'] ) OR $values['media_folder_parent'] === 0 ) )
|
||||
{
|
||||
@@ -275,7 +293,7 @@ class _Folder extends \IPS\Node\Model
|
||||
|
||||
if( isset( $values['media_folder_name'] ) )
|
||||
{
|
||||
$values['name'] = \IPS\Http\Url\Friendly::seoTitle( $values['media_folder_name'] );
|
||||
$values['name'] = Friendly::seoTitle( $values['media_folder_name'] );
|
||||
unset( $values['media_folder_name'] );
|
||||
}
|
||||
|
||||
@@ -288,7 +306,7 @@ class _Folder extends \IPS\Node\Model
|
||||
* @param array $values Values from the form
|
||||
* @return void
|
||||
*/
|
||||
public function postSaveForm( $values )
|
||||
public function postSaveForm( array $values ) : void
|
||||
{
|
||||
if ( $this->origParentId !== $values['parent'] OR $this->origName !== $values['name'] )
|
||||
{
|
||||
@@ -302,7 +320,7 @@ class _Folder extends \IPS\Node\Model
|
||||
* @param boolean $recursivelyCheck Recursively reset up and down the tree
|
||||
* @return void
|
||||
*/
|
||||
public function resetPath( $recursivelyCheck=true )
|
||||
public function resetPath( bool $recursivelyCheck=true ) : void
|
||||
{
|
||||
$path = array();
|
||||
|
||||
@@ -311,13 +329,13 @@ class _Folder extends \IPS\Node\Model
|
||||
$path[] = $obj->name;
|
||||
}
|
||||
|
||||
$this->path = ( \count( $path ) ) ? implode( '/', $path ) . '/' . $this->name : $this->name;
|
||||
$this->path = ( count( $path ) ) ? implode( '/', $path ) . '/' . $this->name : $this->name;
|
||||
|
||||
/* Save path update */
|
||||
parent::save();
|
||||
|
||||
/* Update media */
|
||||
\IPS\cms\Media::resetPath( $this->id );
|
||||
Media::resetPath( $this->id );
|
||||
|
||||
if ( $recursivelyCheck === true )
|
||||
{
|
||||
@@ -341,7 +359,7 @@ class _Folder extends \IPS\Node\Model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _recursivelyResetChildPaths()
|
||||
protected function _recursivelyResetChildPaths() : void
|
||||
{
|
||||
foreach( $this->children( NULL, NULL, FALSE ) as $child )
|
||||
{
|
||||
|
||||
@@ -12,76 +12,94 @@
|
||||
namespace IPS\cms;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use Exception;
|
||||
use IPS\cms\Media\Folder;
|
||||
use IPS\Db;
|
||||
use IPS\File;
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\Node;
|
||||
use IPS\Helpers\Form\Upload;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Node\Model;
|
||||
use OutOfRangeException;
|
||||
use RuntimeException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
use function intval;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Media Model
|
||||
*/
|
||||
class _Media extends \IPS\Node\Model
|
||||
class Media extends Model
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_media';
|
||||
public static ?string $databaseTable = 'cms_media';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'media_';
|
||||
public static string $databasePrefix = 'media_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array('media_full_path');
|
||||
protected static array $databaseIdFields = array('media_full_path');
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
protected static array $multitonMap = array();
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent Node ID Database Column
|
||||
*/
|
||||
public static $parentNodeColumnId = 'parent';
|
||||
public static string $parentNodeColumnId = 'parent';
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent Node Class
|
||||
*/
|
||||
public static $parentNodeClass = 'IPS\cms\Media\Folder';
|
||||
public static string $parentNodeClass = 'IPS\cms\Media\Folder';
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Database Column
|
||||
*/
|
||||
public static $databaseColumnOrder = 'filename';
|
||||
public static ?string $databaseColumnOrder = 'filename';
|
||||
|
||||
/**
|
||||
* @brief [Node] Automatically set position for new nodes
|
||||
*/
|
||||
public static $automaticPositionDetermination = FALSE;
|
||||
public static bool $automaticPositionDetermination = FALSE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Show forms modally?
|
||||
*/
|
||||
public static $modalForms = TRUE;
|
||||
public static bool $modalForms = TRUE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Title
|
||||
*/
|
||||
public static $nodeTitle = 'page';
|
||||
public static string $nodeTitle = 'page';
|
||||
|
||||
/**
|
||||
* @brief [Node] ACP Restrictions
|
||||
@@ -99,7 +117,7 @@ class _Media extends \IPS\Node\Model
|
||||
'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/delete"
|
||||
* @endcode
|
||||
*/
|
||||
protected static $restrictions = array(
|
||||
protected static ?array $restrictions = array(
|
||||
'app' => 'cms',
|
||||
'module' => 'pages',
|
||||
'prefix' => 'media_'
|
||||
@@ -110,11 +128,20 @@ class _Media extends \IPS\Node\Model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultValues()
|
||||
public function setDefaultValues() : void
|
||||
{
|
||||
$this->parent = 0;
|
||||
$this->full_path = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_file_icon() : string
|
||||
{
|
||||
$extension = strtolower( substr( $this->file_object, strrpos( $this->file_object, '.' ) + 1 ) );
|
||||
return File::$fileIconMap[ $extension ] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a media path
|
||||
@@ -122,15 +149,15 @@ class _Media extends \IPS\Node\Model
|
||||
* @param int $folderId Folder ID to reset
|
||||
* @return void
|
||||
*/
|
||||
public static function resetPath( $folderId )
|
||||
public static function resetPath( int $folderId ) : void
|
||||
{
|
||||
try
|
||||
{
|
||||
$path = \IPS\cms\Media\Folder::load( $folderId )->path;
|
||||
$path = Folder::load( $folderId )->path;
|
||||
}
|
||||
catch ( \OutOfRangeException $ex )
|
||||
catch ( OutOfRangeException $ex )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
throw new OutOfRangeException;
|
||||
}
|
||||
|
||||
$children = static::getChildren( $folderId );
|
||||
@@ -147,10 +174,10 @@ class _Media extends \IPS\Node\Model
|
||||
* @param INT $folderId Folder ID to fetch children from
|
||||
* @return array
|
||||
*/
|
||||
public static function getChildren( $folderId=0 )
|
||||
public static function getChildren( int $folderId=0 ) : array
|
||||
{
|
||||
$children = array();
|
||||
foreach( \IPS\Db::i()->select( '*', static::$databaseTable, array( 'media_parent=?', \intval( $folderId ) ), 'media_filename ASC' ) as $child )
|
||||
foreach( Db::i()->select( '*', static::$databaseTable, array( 'media_parent=?', intval( $folderId ) ), 'media_filename ASC' ) as $child )
|
||||
{
|
||||
$children[ $child[ static::$databasePrefix . static::$databaseColumnId ] ] = static::load( $child[ static::$databasePrefix . static::$databaseColumnId ] );
|
||||
}
|
||||
@@ -164,7 +191,7 @@ class _Media extends \IPS\Node\Model
|
||||
* @param array $ids Array of IDs to remove
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteByFileIds( $ids=array() )
|
||||
public static function deleteByFileIds( array $ids=array() ) : void
|
||||
{
|
||||
foreach( $ids as $id )
|
||||
{
|
||||
@@ -172,42 +199,35 @@ class _Media extends \IPS\Node\Model
|
||||
{
|
||||
static::load( $id )->delete();
|
||||
}
|
||||
catch( \Exception $ex ) { }
|
||||
catch( Exception $ex ) { }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL
|
||||
*
|
||||
* @return \IPS\Http\Url object
|
||||
* @return Url|string|null object
|
||||
*/
|
||||
public function url()
|
||||
function url(): Url|string|null
|
||||
{
|
||||
if ( \IPS\Theme::designersModeEnabled() )
|
||||
{
|
||||
return \IPS\Settings::i()->base_url . 'themes/cms/media/' . $this->full_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (string)\IPS\File::get( 'cms_Media', $this->file_object )->url;
|
||||
}
|
||||
return (string)File::get( 'cms_Media', $this->file_object )->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* [ActiveRecord] Delete Record
|
||||
*
|
||||
* @return void
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
public function delete(): void
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( $this->file_object )
|
||||
{
|
||||
\IPS\File::get( 'cms_Media', $this->file_object )->delete();
|
||||
File::get( 'cms_Media', $this->file_object )->delete();
|
||||
}
|
||||
}
|
||||
catch( \Exception $ex ) { }
|
||||
catch( Exception $ex ) { }
|
||||
|
||||
parent::delete();
|
||||
}
|
||||
@@ -215,11 +235,11 @@ class _Media extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Get buttons to display in tree
|
||||
*
|
||||
* @param string $url Base URL
|
||||
* @param bool $subnode Is this a subnode?
|
||||
* @return array
|
||||
* @param Url $url Base URL
|
||||
* @param bool $subnode Is this a subnode?
|
||||
* @return array
|
||||
*/
|
||||
public function getButtons( $url, $subnode=FALSE )
|
||||
public function getButtons( Url $url, bool $subnode=FALSE ): array
|
||||
{
|
||||
$buttons = parent::getButtons( $url, $subnode );
|
||||
$delete = NULL;
|
||||
@@ -241,10 +261,10 @@ class _Media extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
$buttons['key'] = array(
|
||||
'icon' => 'file-code-o',
|
||||
'icon' => 'file-code',
|
||||
'title' => 'cms_media_key',
|
||||
'link' => \IPS\Http\Url::internal( 'app=cms&module=pages&controller=media&do=key&id=' . $this->id ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('cms_media_key') )
|
||||
'link' => Url::internal( 'app=cms&module=pages&controller=media&do=key&id=' . $this->id ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('cms_media_key') )
|
||||
);
|
||||
|
||||
if ( $this->is_image )
|
||||
@@ -252,8 +272,8 @@ class _Media extends \IPS\Node\Model
|
||||
$buttons['preview'] = array(
|
||||
'icon' => 'search',
|
||||
'title' => 'cms_media_preview',
|
||||
'link' => \IPS\Http\Url::internal( 'app=cms&module=pages&controller=media&do=preview&id=' . $this->id ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('cms_media_preview') )
|
||||
'link' => Url::internal( 'app=cms&module=pages&controller=media&do=preview&id=' . $this->id ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('cms_media_preview') )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -269,15 +289,15 @@ class _Media extends \IPS\Node\Model
|
||||
* [Node] Add/Edit Form
|
||||
*
|
||||
* @note This is not used currently. See \IPS\cms\modules\admin\media.php upload()
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @param Form $form The form
|
||||
* @return void
|
||||
*/
|
||||
public function form( &$form )
|
||||
public function form( Form &$form ) : void
|
||||
{
|
||||
/* Build form */
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'media_filename', ( ( $this->filename ) ? \IPS\File::get( 'cms_Media', $this->file_object ) : NULL ), FALSE, array( 'obscure' => FALSE, 'maxFileSize' => 5, 'storageExtension' => 'cms_Media', 'storageContainer' => 'pages_media' ), NULL, NULL, NULL, 'media_filename' ) );
|
||||
$form->add( new Upload( 'media_filename', ( ( $this->filename ) ? File::get( 'cms_Media', $this->file_object ) : NULL ), FALSE, array( 'obscure' => FALSE, 'maxFileSize' => 5, 'storageExtension' => 'cms_Media', 'storageContainer' => 'pages_media' ), NULL, NULL, NULL, 'media_filename' ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'media_parent', $this->parent ? $this->parent : 0, FALSE, array(
|
||||
$form->add( new Node( 'media_parent', $this->parent ?: 0, FALSE, array(
|
||||
'class' => '\IPS\cms\Media\Folder',
|
||||
'zeroVal' => 'node_no_parent'
|
||||
) ) );
|
||||
@@ -290,7 +310,7 @@ class _Media extends \IPS\Node\Model
|
||||
* @param array $values Values from the form
|
||||
* @return array
|
||||
*/
|
||||
public function formatFormValues( $values )
|
||||
public function formatFormValues( array $values ): array
|
||||
{
|
||||
if ( isset( $values['media_parent'] ) AND ( ! empty( $values['media_parent'] ) OR $values['media_parent'] === 0 ) )
|
||||
{
|
||||
@@ -315,8 +335,7 @@ class _Media extends \IPS\Node\Model
|
||||
$values['filename'] = $filename;
|
||||
$values['filename_stored'] = $values['parent'] . '_' . $values['filename'];
|
||||
$values['is_image'] = $values['media_filename']->isImage();
|
||||
|
||||
$values['file_object'] = (string) $values['media_filename'];
|
||||
$values['file_object'] = (string) $values['media_filename'];
|
||||
|
||||
unset( $values['media_filename'] );
|
||||
}
|
||||
@@ -336,9 +355,9 @@ class _Media extends \IPS\Node\Model
|
||||
* @param array $values Values from the form
|
||||
* @return void
|
||||
*/
|
||||
public function postSaveForm( $values )
|
||||
public function postSaveForm( array $values ) : void
|
||||
{
|
||||
$this->setFullPath( ( $this->parent ? \IPS\cms\Media\Folder::load( $this->parent )->path : '' ) );
|
||||
$this->setFullPath( ( $this->parent ? Folder::load( $this->parent )->path : '' ) );
|
||||
$this->save();
|
||||
}
|
||||
|
||||
@@ -347,7 +366,7 @@ class _Media extends \IPS\Node\Model
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSortableName()
|
||||
public function getSortableName() : string
|
||||
{
|
||||
return $this->full_path;
|
||||
}
|
||||
@@ -358,63 +377,21 @@ class _Media extends \IPS\Node\Model
|
||||
* @param string $path Path to reset
|
||||
* @return void
|
||||
*/
|
||||
public function setFullPath( $path )
|
||||
public function setFullPath( string $path ) : void
|
||||
{
|
||||
$this->full_path = trim( $path . '/' . $this->filename, '/' );
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write media to disk for designer's mode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function exportDesignersModeMedia()
|
||||
{
|
||||
/* Make sure our media folder exists */
|
||||
if ( !is_dir( \IPS\ROOT_PATH . '/themes/cms/media' ) )
|
||||
{
|
||||
mkdir( \IPS\ROOT_PATH . '/themes/cms/media', \IPS\IPS_FOLDER_PERMISSION );
|
||||
chmod( \IPS\ROOT_PATH . '/themes/cms/media', \IPS\IPS_FOLDER_PERMISSION );
|
||||
}
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'cms_media' ) as $media )
|
||||
{
|
||||
/* We could use recursive mode but it wouldn't correctly chmod the intermediate dirs */
|
||||
$bits = explode( '/', "/themes/cms/media/" . $media['media_full_path'] );
|
||||
$dir = '';
|
||||
|
||||
$filename = array_pop( $bits );
|
||||
|
||||
foreach( $bits as $part )
|
||||
{
|
||||
$dir .= $part . '/';
|
||||
|
||||
if ( ! is_dir( \IPS\ROOT_PATH . '/' . trim( $dir, '/' ) ) )
|
||||
{
|
||||
mkdir( \IPS\ROOT_PATH . '/' . trim( $dir, '/' ), \IPS\IPS_FOLDER_PERMISSION );
|
||||
chmod( \IPS\ROOT_PATH . '/' . trim( $dir, '/' ), \IPS\IPS_FOLDER_PERMISSION );
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
\file_put_contents( \IPS\ROOT_PATH . '/' . trim( $dir, '/' ) . '/' . $filename, \IPS\File::get( 'cms_Media', $media['media_file_object'] )->contents() );
|
||||
@chmod( \IPS\ROOT_PATH . '/' . trim( $dir, '/' ) . '/' . $filename, \IPS\IPS_FILE_PERMISSION );
|
||||
}
|
||||
catch( \RuntimeException $e ) { }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes folders that are empty
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function removeEmptyFolders()
|
||||
public static function removeEmptyFolders() : void
|
||||
{
|
||||
$folders = iterator_to_array( \IPS\Db::i()->select( 'DISTINCT(media_parent)', 'cms_media', array( 'media_parent > 0' ) ) );
|
||||
$allFolders = iterator_to_array( \IPS\Db::i()->select( '*', 'cms_media_folders' )->setKeyField( 'media_folder_id' ) );
|
||||
$folders = iterator_to_array( Db::i()->select( 'DISTINCT(media_parent)', 'cms_media', array( 'media_parent > 0' ) ) );
|
||||
$allFolders = iterator_to_array( Db::i()->select( '*', 'cms_media_folders' )->setKeyField( 'media_folder_id' ) );
|
||||
|
||||
foreach( $folders as $id )
|
||||
{
|
||||
@@ -430,7 +407,7 @@ class _Media extends \IPS\Node\Model
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! \in_array( $currentParent, $folders ) )
|
||||
if ( ! in_array( $currentParent, $folders ) )
|
||||
{
|
||||
$folders[] = $currentParent;
|
||||
}
|
||||
@@ -440,59 +417,7 @@ class _Media extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Db::i()->delete( 'cms_media_folders', array( \IPS\Db::i()->in( 'media_folder_id', array_values( $folders ), TRUE ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Import media from disk for designer's mode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function importDesignersModeMedia()
|
||||
{
|
||||
$path = \IPS\ROOT_PATH . '/themes/cms/media';
|
||||
$seen = array();
|
||||
|
||||
if ( is_dir( $path ) )
|
||||
{
|
||||
static::importDesignersModeMediaRecurse( $seen, $path );
|
||||
}
|
||||
|
||||
\IPS\Db::i()->delete( 'cms_media', array( \IPS\Db::i()->in( 'media_id', $seen, TRUE ) ) );
|
||||
static::removeEmptyFolders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Import media from disk for designer's mode recursive method
|
||||
*
|
||||
* @param array $seen Files we've seen already
|
||||
* @param string $path Path to look in
|
||||
* @return void
|
||||
*/
|
||||
public static function importDesignersModeMediaRecurse( &$seen, $path )
|
||||
{
|
||||
if ( is_dir( $path ) )
|
||||
{
|
||||
foreach ( new \DirectoryIterator( $path ) as $dir )
|
||||
{
|
||||
if ( $dir->isDot() || mb_substr( $dir->getFilename(), 0, 1 ) === '.' )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $dir->isDir() )
|
||||
{
|
||||
static::importDesignersModeMediaRecurse( $seen, $path . '/' . $dir->getFilename() );
|
||||
}
|
||||
else
|
||||
{
|
||||
$contents = \file_get_contents( $dir->getRealPath() );
|
||||
|
||||
/* Create */
|
||||
$seen[] = static::createMedia( trim( str_replace( str_replace( '\\', '/', \IPS\ROOT_PATH ) . '/themes/cms/media', '', str_replace( '\\', '/', $dir->getRealPath() ) ), '/' ), $contents );
|
||||
}
|
||||
}
|
||||
}
|
||||
Db::i()->delete( 'cms_media_folders', array( Db::i()->in( 'media_folder_id', array_values( $folders ), TRUE ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,49 +425,49 @@ class _Media extends \IPS\Node\Model
|
||||
*
|
||||
* @param string $path File path (/folder/file.txt)
|
||||
* @param string $contents File contents
|
||||
* @return int ID of existing media or of new media
|
||||
* @return int|null ID of existing media or of new media
|
||||
*/
|
||||
public static function createMedia( $path, $contents )
|
||||
public static function createMedia( string $path, string $contents ): ?int
|
||||
{
|
||||
try
|
||||
{
|
||||
$test = static::load( $path, 'media_full_path' );
|
||||
|
||||
$test->file_object = \IPS\File::create( 'cms_Media', $test->filename_stored, $contents, 'pages_media', TRUE, NULL, FALSE );
|
||||
$test->file_object = File::create( 'cms_Media', $test->filename_stored, $contents, 'pages_media', TRUE, NULL, FALSE );
|
||||
$test->save();
|
||||
|
||||
return $test->id;
|
||||
}
|
||||
catch( \RuntimeException $ex )
|
||||
catch( RuntimeException $ex )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'cms_Media', $path );
|
||||
File::get( 'cms_Media', $path );
|
||||
}
|
||||
catch( \Exception $x )
|
||||
catch( Exception $x )
|
||||
{
|
||||
/* File doesn't exist already */
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
catch( OutOfRangeException $ex )
|
||||
{
|
||||
/* It doesn't exist */
|
||||
$exploded = explode( '/', $path );
|
||||
$filename = array_pop( $exploded );
|
||||
$folderId = 0;
|
||||
|
||||
if ( \count( $exploded ) )
|
||||
if ( count( $exploded ) )
|
||||
{
|
||||
$testDir = trim( implode( '/', $exploded ), '/' );
|
||||
try
|
||||
{
|
||||
$test = \IPS\cms\Media\Folder::load( $testDir, 'media_folder_path' );
|
||||
$test = Folder::load( $testDir, 'media_folder_path' );
|
||||
|
||||
/* Yep */
|
||||
$folderId = $test->id;
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
catch( OutOfRangeException $ex )
|
||||
{
|
||||
$testDir = '';
|
||||
foreach( $exploded as $dir )
|
||||
@@ -551,12 +476,12 @@ class _Media extends \IPS\Node\Model
|
||||
|
||||
try
|
||||
{
|
||||
$test = \IPS\cms\Media\Folder::load( $testDir, 'media_folder_path' );
|
||||
$test = Folder::load( $testDir, 'media_folder_path' );
|
||||
$folderId = $test->id;
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
catch( OutOfRangeException $ex )
|
||||
{
|
||||
$folder = new \IPS\cms\Media\Folder;
|
||||
$folder = new Folder;
|
||||
$folder->parent = $folderId;
|
||||
$folder->name = $dir;
|
||||
$folder->path = $testDir;
|
||||
@@ -567,17 +492,19 @@ class _Media extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
|
||||
$media = new \IPS\cms\Media;
|
||||
$media = new Media;
|
||||
$media->parent = $folderId;
|
||||
$media->filename = $filename;
|
||||
$media->added = time();
|
||||
$media->full_path = $path;
|
||||
$media->filename_stored = $folderId . '_' . $filename;
|
||||
$media->file_object = \IPS\File::create( 'cms_Media', $media->filename_stored, $contents, 'pages_media', TRUE, NULL, FALSE );
|
||||
$media->file_object = File::create( 'cms_Media', $media->filename_stored, $contents, 'pages_media', TRUE, NULL, FALSE );
|
||||
$media->is_image = $media->file_object->isImage();
|
||||
$media->save();
|
||||
|
||||
return $media->id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -12,26 +12,47 @@
|
||||
namespace IPS\cms\Pages;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use InvalidArgumentException;
|
||||
use IPS\Data\Store;
|
||||
use IPS\Db;
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\Node;
|
||||
use IPS\Helpers\Form\Text;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Http\Url\Friendly;
|
||||
use IPS\Member;
|
||||
use IPS\Node\Model;
|
||||
use IPS\Request;
|
||||
use IPS\Session;
|
||||
use OutOfRangeException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function func_get_args;
|
||||
use function get_called_class;
|
||||
use function get_class;
|
||||
use function intval;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Folder Model
|
||||
*/
|
||||
class _Folder extends \IPS\Node\Model
|
||||
class Folder extends Model
|
||||
{
|
||||
/**
|
||||
* Munge different record types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function munge()
|
||||
public static function munge() : array
|
||||
{
|
||||
$rows = array();
|
||||
$args = \func_get_args();
|
||||
$args = func_get_args();
|
||||
|
||||
foreach( $args as $arg )
|
||||
{
|
||||
@@ -49,73 +70,73 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_folders';
|
||||
public static ?string $databaseTable = 'cms_folders';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'folder_';
|
||||
public static string $databasePrefix = 'folder_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array('folder_name', 'folder_path');
|
||||
protected static array $databaseIdFields = array('folder_name', 'folder_path');
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
protected static array $multitonMap = array();
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Database Column
|
||||
*/
|
||||
public static $databaseColumnParent = 'parent_id';
|
||||
public static ?string $databaseColumnParent = 'parent_id';
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Root Value
|
||||
* @note This normally doesn't need changing though some legacy areas use -1 to indicate a root node
|
||||
*/
|
||||
public static $databaseColumnParentRootValue = 0;
|
||||
public static int $databaseColumnParentRootValue = 0;
|
||||
|
||||
/**
|
||||
* @brief [Node] Order Database Column
|
||||
*/
|
||||
public static $databaseColumnOrder = 'path';
|
||||
public static ?string $databaseColumnOrder = 'path';
|
||||
|
||||
/**
|
||||
* @brief [Node] Automatically set position for new nodes
|
||||
*/
|
||||
public static $automaticPositionDetermination = FALSE;
|
||||
public static bool $automaticPositionDetermination = FALSE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = 'folder';
|
||||
public static string $nodeTitle = 'folder';
|
||||
|
||||
/**
|
||||
* @brief [Node] Subnode class
|
||||
*/
|
||||
public static $subnodeClass = 'IPS\cms\Pages\Page';
|
||||
public static ?string $subnodeClass = 'IPS\cms\Pages\Page';
|
||||
|
||||
/**
|
||||
* @brief [Node] Show forms modally?
|
||||
*/
|
||||
public static $modalForms = TRUE;
|
||||
public static bool $modalForms = TRUE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Restrictions
|
||||
*/
|
||||
protected static $restrictions = array(
|
||||
protected static ?array $restrictions = array(
|
||||
'app' => 'cms',
|
||||
'module' => 'pages',
|
||||
'all' => 'page_manage',
|
||||
@@ -125,9 +146,9 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Get Title
|
||||
*
|
||||
* @return string|null
|
||||
* @return string
|
||||
*/
|
||||
protected function get__title()
|
||||
protected function get__title(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
@@ -137,7 +158,7 @@ class _Folder extends \IPS\Node\Model
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSortableName()
|
||||
public function getSortableName() : string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
@@ -145,11 +166,11 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Get buttons to display in tree
|
||||
*
|
||||
* @param string $url Base URL
|
||||
* @param bool $subnode Is this a subnode?
|
||||
* @return array
|
||||
* @param Url $url Base URL
|
||||
* @param bool $subnode Is this a subnode?
|
||||
* @return array
|
||||
*/
|
||||
public function getButtons( $url, $subnode=FALSE )
|
||||
public function getButtons( Url $url, bool $subnode=FALSE ): array
|
||||
{
|
||||
$buttons = parent::getButtons( $url, $subnode );
|
||||
$return = array();
|
||||
@@ -163,14 +184,13 @@ class _Folder extends \IPS\Node\Model
|
||||
{
|
||||
$buttons['add']['icon'] = 'folder-open';
|
||||
$buttons['add']['title'] = 'content_add_folder';
|
||||
$buttons['add']['data'] = array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('content_add_folder') );
|
||||
$buttons['add']['data'] = array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('content_add_folder') );
|
||||
$buttons['add']['link'] = $url->setQueryString( array( 'subnode' => 0, 'do' => 'form', 'parent' => $this->_id ) );
|
||||
|
||||
$buttons['add_page'] = array(
|
||||
'icon' => 'plus-circle',
|
||||
'title' => 'content_add_page',
|
||||
'link' => $url->setQueryString( array( 'subnode' => 1, 'do' => 'add', 'parent' => $this->_id ) ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('content_add_page') )
|
||||
'link' => $url->setQueryString( array( 'subnode' => 1, 'do' => 'form', 'parent' => $this->_id ) )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -195,10 +215,10 @@ class _Folder extends \IPS\Node\Model
|
||||
if ( $this->getItemCount() )
|
||||
{
|
||||
$return['delete'] = array(
|
||||
'icon' => 'trash-o',
|
||||
'icon' => 'trash',
|
||||
'title' => 'empty',
|
||||
'link' => $url->setQueryString( array( 'do' => 'delete', 'id' => $this->_id ) ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('empty') ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack('empty') ),
|
||||
'hotkey'=> 'd'
|
||||
);
|
||||
}
|
||||
@@ -214,52 +234,52 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* [Node] Add/Edit Form
|
||||
*
|
||||
* @param \IPS\Helpers\Form $form The form
|
||||
* @param Form $form The form
|
||||
* @return void
|
||||
*/
|
||||
public function form( &$form )
|
||||
public function form( Form &$form ) : void
|
||||
{
|
||||
/* Build form */
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'folder_name', $this->id ? $this->name : '', TRUE, array( 'maxLength' => 64 ), function( $val )
|
||||
$form->add( new Text( 'folder_name', $this->id ? $this->name : '', TRUE, array( 'maxLength' => 64 ), function( $val )
|
||||
{
|
||||
try
|
||||
{
|
||||
$testPage = \IPS\cms\Pages\Page::load( $val, 'page_seo_name' );
|
||||
$testPage = Page::load( $val, 'page_seo_name' );
|
||||
|
||||
/* Ok, we have a folder, but is it on the same tree as us ?*/
|
||||
if ( \intval( \IPS\Request::i()->folder_parent_id ) == $testPage->folder_id )
|
||||
if ( intval( Request::i()->folder_parent_id ) == $testPage->folder_id )
|
||||
{
|
||||
/* Yep, this will break designers' mode and may confuse the FURL engine so we cannot allow this */
|
||||
throw new \InvalidArgumentException('content_folder_name_furl_collision_folder');
|
||||
throw new InvalidArgumentException('content_folder_name_furl_collision_folder');
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
/* Nothing with the same name, so that's proper good that is */
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$test = \IPS\cms\Pages\Folder::load( ( $val === '.well-known' ) ? $val : \IPS\Http\Url\Friendly::seoTitle( $val ), 'folder_name' );
|
||||
$test = Folder::load( ( $val === '.well-known' ) ? $val : Friendly::seoTitle( $val ), 'folder_name' );
|
||||
|
||||
if ( empty( \IPS\Request::i()->id ) or $test->id != \IPS\Request::i()->id )
|
||||
if ( empty( Request::i()->id ) or $test->id != Request::i()->id )
|
||||
{
|
||||
throw new \InvalidArgumentException('content_folder_name_in_use');
|
||||
throw new InvalidArgumentException('content_folder_name_in_use');
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
/* If we hit here, we don't have an existing folder by that name so check for a collision */
|
||||
if ( \IPS\Request::i()->folder_parent_id == 0 AND \IPS\cms\Pages\Page::isFurlCollision( ( $val === '.well-known' ) ? $val : \IPS\Http\Url\Friendly::seoTitle( $val ) ) )
|
||||
if ( Request::i()->folder_parent_id == 0 AND Page::isFurlCollision( ( $val === '.well-known' ) ? $val : Friendly::seoTitle( $val ) ) )
|
||||
{
|
||||
throw new \InvalidArgumentException('content_folder_name_furl_collision');
|
||||
throw new InvalidArgumentException('content_folder_name_furl_collision');
|
||||
}
|
||||
}
|
||||
} ) );
|
||||
|
||||
$class = \get_called_class();
|
||||
$class = get_called_class();
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'folder_parent_id', $this->parent_id ? $this->parent_id : 0, FALSE, array(
|
||||
$form->add( new Node( 'folder_parent_id', $this->parent_id ? $this->parent_id : 0, FALSE, array(
|
||||
'class' => '\IPS\cms\Pages\Folder',
|
||||
'zeroVal' => 'node_no_parent',
|
||||
'permissionCheck' => function( $node ) use ( $class )
|
||||
@@ -269,7 +289,7 @@ class _Folder extends \IPS\Node\Model
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return !isset( \IPS\Request::i()->id ) or ( $node->id != \IPS\Request::i()->id and !$node->isChildOf( $node::load( \IPS\Request::i()->id ) ) );
|
||||
return !isset( Request::i()->id ) or ( $node->id != Request::i()->id and !$node->isChildOf( $node::load( Request::i()->id ) ) );
|
||||
}
|
||||
) ) );
|
||||
}
|
||||
@@ -280,7 +300,7 @@ class _Folder extends \IPS\Node\Model
|
||||
* @param array $values Values from the form
|
||||
* @return array
|
||||
*/
|
||||
public function formatFormValues( $values )
|
||||
public function formatFormValues( array $values ): array
|
||||
{
|
||||
$isNew = $this->_new;
|
||||
|
||||
@@ -307,7 +327,7 @@ class _Folder extends \IPS\Node\Model
|
||||
}
|
||||
else
|
||||
{
|
||||
$values['name'] = \IPS\Http\Url\Friendly::seoTitle( $values['folder_name'] );
|
||||
$values['name'] = Friendly::seoTitle( $values['folder_name'] );
|
||||
}
|
||||
unset( $values['folder_name'] );
|
||||
}
|
||||
@@ -323,12 +343,12 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* @brief Original parent ID
|
||||
*/
|
||||
protected $origParentId;
|
||||
protected ?int $origParentId = null;
|
||||
|
||||
/**
|
||||
* @brief Original Name
|
||||
*/
|
||||
protected $origName;
|
||||
protected ?string $origName = null;
|
||||
|
||||
/**
|
||||
* [Node] Perform actions after saving the form
|
||||
@@ -336,7 +356,7 @@ class _Folder extends \IPS\Node\Model
|
||||
* @param array $values Values from the form
|
||||
* @return void
|
||||
*/
|
||||
public function postSaveForm( $values )
|
||||
public function postSaveForm( array $values ) : void
|
||||
{
|
||||
if ( $this->origParentId !== $values['parent_id'] OR $this->origName !== $values['name'] )
|
||||
{
|
||||
@@ -349,9 +369,9 @@ class _Folder extends \IPS\Node\Model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function storeUrl()
|
||||
public function storeUrl() : void
|
||||
{
|
||||
\IPS\Db::i()->insert( 'cms_url_store', array(
|
||||
Db::i()->insert( 'cms_url_store', array(
|
||||
'store_path' => $this->path,
|
||||
'store_current_id' => $this->_id,
|
||||
'store_type' => 'folder'
|
||||
@@ -363,7 +383,7 @@ class _Folder extends \IPS\Node\Model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save()
|
||||
public function save(): void
|
||||
{
|
||||
$this->last_modified = time();
|
||||
|
||||
@@ -373,12 +393,12 @@ class _Folder extends \IPS\Node\Model
|
||||
/**
|
||||
* Retrieve item \count(if applicable) for a node.
|
||||
*
|
||||
* @return int|bool
|
||||
* @return int
|
||||
*/
|
||||
public function getItemCount()
|
||||
public function getItemCount() : int
|
||||
{
|
||||
$pages = (int) \IPS\Db::i()->select( 'COUNT(*)', 'cms_pages', array( 'page_folder_id=?', $this->id ) )->first();
|
||||
$subFolders = (int) \IPS\Db::i()->select( 'COUNT(*)', 'cms_folders', array( 'folder_parent_id=?', $this->id ) )->first();
|
||||
$pages = (int) Db::i()->select( 'COUNT(*)', 'cms_pages', array( 'page_folder_id=?', $this->id ) )->first();
|
||||
$subFolders = (int) Db::i()->select( 'COUNT(*)', 'cms_folders', array( 'folder_parent_id=?', $this->id ) )->first();
|
||||
|
||||
return $pages + $subFolders;
|
||||
}
|
||||
@@ -387,19 +407,19 @@ class _Folder extends \IPS\Node\Model
|
||||
* Form to delete or move content
|
||||
*
|
||||
* @param bool $showMoveToChildren If TRUE, will show "move to children" even if there are no children
|
||||
* @return \IPS\Helpers\Form
|
||||
* @return Form
|
||||
*/
|
||||
public function deleteOrMoveForm( $showMoveToChildren=FALSE )
|
||||
public function deleteOrMoveForm( bool $showMoveToChildren=FALSE ): Form
|
||||
{
|
||||
$hasContent = $this->getItemCount();
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'form', 'delete' );
|
||||
$form = new Form( 'form', 'delete' );
|
||||
|
||||
if ( $hasContent )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'cms_move_pages', 0, TRUE, array( 'class' => \get_class( $this ), 'disabled' => array( $this->_id ), 'disabledLang' => 'node_move_delete', 'zeroVal' => 'cms_move_to_root', 'subnodes' => FALSE, 'permissionCheck' => function( $node )
|
||||
$form->add( new Node( 'cms_move_pages', 0, TRUE, array( 'class' => get_class( $this ), 'disabled' => array( $this->_id ), 'disabledLang' => 'node_move_delete', 'zeroVal' => 'cms_move_to_root', 'subnodes' => FALSE, 'permissionCheck' => function( $node )
|
||||
{
|
||||
return \IPS\Request::i()->id != $node->id;
|
||||
return Request::i()->id != $node->id;
|
||||
} ) ) );
|
||||
}
|
||||
|
||||
@@ -412,7 +432,7 @@ class _Folder extends \IPS\Node\Model
|
||||
* @param array $values Values from form
|
||||
* @return void
|
||||
*/
|
||||
public function deleteOrMoveFormSubmit( $values )
|
||||
public function deleteOrMoveFormSubmit( array $values ) : void
|
||||
{
|
||||
if ( $values['cms_move_pages'] )
|
||||
{
|
||||
@@ -423,17 +443,17 @@ class _Folder extends \IPS\Node\Model
|
||||
$folderId = 0;
|
||||
}
|
||||
|
||||
\IPS\Db::i()->update( 'cms_pages', array( 'page_folder_id' => $folderId ), array( 'page_folder_id=?', $this->_id ) );
|
||||
\IPS\Db::i()->update( 'cms_folders', array( 'folder_parent_id' => $folderId ), array( 'folder_id=?', $this->_id ) );
|
||||
\IPS\Db::i()->update( 'cms_folders', array( 'folder_parent_id' => $folderId ), array( 'folder_parent_id=?', $this->_id ) );
|
||||
Db::i()->update( 'cms_pages', array( 'page_folder_id' => $folderId ), array( 'page_folder_id=?', $this->_id ) );
|
||||
Db::i()->update( 'cms_folders', array( 'folder_parent_id' => $folderId ), array( 'folder_id=?', $this->_id ) );
|
||||
Db::i()->update( 'cms_folders', array( 'folder_parent_id' => $folderId ), array( 'folder_parent_id=?', $this->_id ) );
|
||||
|
||||
unset( \IPS\Data\Store::i()->pages_page_urls );
|
||||
unset( Store::i()->pages_page_urls );
|
||||
|
||||
/* Update pages */
|
||||
\IPS\cms\Pages\Page::resetPath( $folderId );
|
||||
Page::resetPath( $folderId );
|
||||
|
||||
/* Delete it */
|
||||
\IPS\Session::i()->log( 'acplog__node_deleted', array( $this->title => TRUE, $this->titleForLog() => FALSE ) );
|
||||
Session::i()->log( 'acplog__node_deleted', array( $this->title => TRUE, $this->titleForLog() => FALSE ) );
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
@@ -443,7 +463,7 @@ class _Folder extends \IPS\Node\Model
|
||||
* @param boolean $recursivelyCheck Recursively reset up and down the tree
|
||||
* @return void
|
||||
*/
|
||||
public function resetPath( $recursivelyCheck=true )
|
||||
public function resetPath( bool $recursivelyCheck=true ) : void
|
||||
{
|
||||
$path = array();
|
||||
|
||||
@@ -452,13 +472,13 @@ class _Folder extends \IPS\Node\Model
|
||||
$path[] = $obj->name;
|
||||
}
|
||||
|
||||
$this->path = ( \count( $path ) ) ? implode( '/', $path ) . '/' . $this->name : $this->name;
|
||||
$this->path = ( count( $path ) ) ? implode( '/', $path ) . '/' . $this->name : $this->name;
|
||||
|
||||
/* Save path update */
|
||||
parent::save();
|
||||
|
||||
/* Update pages */
|
||||
\IPS\cms\Pages\Page::resetPath( $this->id );
|
||||
Page::resetPath( $this->id );
|
||||
|
||||
if ( $recursivelyCheck === true )
|
||||
{
|
||||
@@ -482,7 +502,7 @@ class _Folder extends \IPS\Node\Model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _recursivelyResetChildPaths()
|
||||
protected function _recursivelyResetChildPaths() : void
|
||||
{
|
||||
foreach( $this->children( NULL, NULL, FALSE ) as $child )
|
||||
{
|
||||
|
||||
+688
-1113
File diff suppressed because it is too large.
Load diff
@@ -12,272 +12,107 @@
|
||||
namespace IPS\cms\Pages;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\Content\Item;
|
||||
use IPS\Content\ViewUpdates;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Node\Model;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package Item Model
|
||||
*/
|
||||
class _PageItem extends \IPS\Content\Item implements \IPS\Content\Searchable
|
||||
class PageItem extends Item
|
||||
{
|
||||
use \IPS\Content\ViewUpdates;
|
||||
|
||||
use ViewUpdates;
|
||||
|
||||
/**
|
||||
* @brief Application
|
||||
*/
|
||||
public static $application = 'cms';
|
||||
public static string $application = 'cms';
|
||||
|
||||
/**
|
||||
* @brief Module
|
||||
*/
|
||||
public static $module = 'pages';
|
||||
public static string $module = 'pages';
|
||||
|
||||
/**
|
||||
* @brief Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_pages';
|
||||
public static ?string $databaseTable = 'cms_pages';
|
||||
|
||||
/**
|
||||
* @brief Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'page_';
|
||||
public static string $databasePrefix = 'page_';
|
||||
|
||||
/**
|
||||
* @brief Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief Database Column Map
|
||||
*/
|
||||
public static $databaseColumnMap = array(
|
||||
public static array $databaseColumnMap = array(
|
||||
'views' => 'views'
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Title
|
||||
*/
|
||||
public static $title = 'cms_page';
|
||||
public static string $title = 'cms_page';
|
||||
|
||||
/**
|
||||
* @brief Icon
|
||||
*/
|
||||
public static $icon = 'files-o';
|
||||
public static string $icon = 'file';
|
||||
|
||||
/**
|
||||
* @brief Include In Sitemap
|
||||
*/
|
||||
public static $includeInSitemap = FALSE;
|
||||
public static bool $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;
|
||||
public static bool $canBeModeratedFromFrontend = FALSE;
|
||||
|
||||
/**
|
||||
* Columns needed to query for search result / stream view
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function basicDataColumns()
|
||||
public static function basicDataColumns(): array
|
||||
{
|
||||
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()
|
||||
* @param string|NULL $action Action
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public static function urlFromIndexData( $indexData, $itemData, $action = NULL )
|
||||
{
|
||||
if ( \IPS\Application::load('cms')->default AND $itemData['page_default'] AND !$itemData['page_folder_id'] )
|
||||
{
|
||||
/* 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, 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
|
||||
{
|
||||
/* Remove {tags="foo"} */
|
||||
$this->content = preg_replace( '#\{([a-z]+?=([\'"]).+?\\2 ?+)}#', '', $this->content );
|
||||
|
||||
/* Convert {{}} logic into html tags */
|
||||
$this->content = preg_replace( '#{{(if|foreach)([^{]+?)}}#', '<ips$1 data="$2">', $this->content );
|
||||
$this->content = preg_replace( '#{{end(if|foreach)}}#', '</ips$1>', $this->content );
|
||||
|
||||
/* Remove custom PHP {{$foo = $this->test();}} */
|
||||
$this->content = preg_replace( '#{{(.+?)}}#', '', $this->content );
|
||||
|
||||
$source = new \IPS\Xml\DOMDocument( '1.0', 'UTF-8' );
|
||||
$source->loadHTML( \IPS\Xml\DOMDocument::wrapHtml( $this->content ) );
|
||||
|
||||
/* And then remove the HTML logic */
|
||||
$domElemsToRemove = array();
|
||||
foreach ( $source->getElementsByTagname('ipsif') as $domElement )
|
||||
{
|
||||
$domElemsToRemove[] = $domElement;
|
||||
}
|
||||
|
||||
foreach ( $source->getElementsByTagname('ipsforeach') as $domElement )
|
||||
{
|
||||
$domElemsToRemove[] = $domElement;
|
||||
}
|
||||
|
||||
foreach( $domElemsToRemove as $domElement )
|
||||
{
|
||||
$domElement->parentNode->removeChild($domElement);
|
||||
}
|
||||
|
||||
$content = \IPS\Text\DOMParser::getDocumentBodyContents( $source );
|
||||
|
||||
return $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 '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should posting this increment the poster's post count?
|
||||
*
|
||||
* @param \IPS\Node\Model|NULL $container Container
|
||||
* @return void
|
||||
* @param Model|NULL $container Container
|
||||
* @return bool
|
||||
*/
|
||||
public static function incrementPostCount( \IPS\Node\Model $container = NULL )
|
||||
public static function incrementPostCount( Model $container = NULL ): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Title for Analytics
|
||||
* Get URL
|
||||
*
|
||||
* @return string
|
||||
* @param string|null $action Action
|
||||
* @return Url
|
||||
*/
|
||||
public function titleForAnalytics(): string
|
||||
public function url( ?string $action=NULL ): Url
|
||||
{
|
||||
try
|
||||
{
|
||||
return \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( \IPS\cms\Pages\Page::$titleLangPrefix . $this->id );
|
||||
}
|
||||
catch( \OutOfRangeException | \UnderflowException )
|
||||
{
|
||||
return \IPS\cms\Pages\Page::$titleLangPrefix . $this->id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get node
|
||||
*
|
||||
* @return \IPS\cms\Pages\Page
|
||||
*/
|
||||
public function node()
|
||||
{
|
||||
$data = array();
|
||||
foreach ( $this->_data as $k => $v )
|
||||
{
|
||||
$data[ 'page_' . $k ] = $v;
|
||||
}
|
||||
|
||||
return \IPS\cms\Pages\Page::constructFromData( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* URL
|
||||
*
|
||||
* @param string|NULL $action Action
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function url( $action = null )
|
||||
{
|
||||
return $this->node()->url();
|
||||
return Page::constructFromData( $this->_data )->url();
|
||||
}
|
||||
}
|
||||
@@ -12,37 +12,48 @@
|
||||
namespace IPS\cms\Pages;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use IPS\cms\Categories;
|
||||
use IPS\cms\Records;
|
||||
use IPS\Db;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Patterns\ActiveRecord;
|
||||
use OutOfRangeException;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Page Model
|
||||
*/
|
||||
class _Router extends \IPS\Patterns\ActiveRecord
|
||||
class Router extends ActiveRecord
|
||||
{
|
||||
/**
|
||||
* Load Pages Thing based on a URL.
|
||||
* The URL is sometimes complex to figure out, so this will help
|
||||
*
|
||||
* @param \IPS\Http\Url $url URL to load from
|
||||
* @return \IPS\cms\Pages\Page
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \OutOfRangeException
|
||||
* @param Url $url URL to load from
|
||||
* @return mixed
|
||||
* @throws InvalidArgumentException
|
||||
* @throws OutOfRangeException
|
||||
*/
|
||||
public static function loadFromUrl( \IPS\Http\Url $url )
|
||||
public static function loadFromUrl( Url $url ): mixed
|
||||
{
|
||||
if ( ! isset( $url->queryString['path'] ) )
|
||||
{
|
||||
throw new \OutOfRangeException();
|
||||
throw new OutOfRangeException();
|
||||
}
|
||||
|
||||
$path = $url->queryString['path'];
|
||||
|
||||
/* First, we need a page */
|
||||
$page = \IPS\cms\Pages\Page::loadFromPath( $path );
|
||||
$page = Page::loadFromPath( $path );
|
||||
|
||||
/* What do we have left? */
|
||||
$whatsLeft = trim( preg_replace( '#' . $page->full_path . '#', '', $path, 1 ), '/' );
|
||||
@@ -50,23 +61,23 @@ class _Router extends \IPS\Patterns\ActiveRecord
|
||||
if ( $whatsLeft )
|
||||
{
|
||||
/* Check databases */
|
||||
$databases = iterator_to_array( \IPS\Db::i()->select( '*', 'cms_databases', array( 'database_page_id > 0' ) ) );
|
||||
$databases = iterator_to_array( Db::i()->select( '*', 'cms_databases', array( 'database_page_id > 0' ) ) );
|
||||
foreach( $databases as $db )
|
||||
{
|
||||
$classToTry = 'IPS\cms\Records' . $db['database_id'];
|
||||
/* @var $classToTry Records */
|
||||
try
|
||||
{
|
||||
$record = $classToTry::loadFromSlug( $whatsLeft, FALSE, FALSE );
|
||||
|
||||
return $record;
|
||||
return $classToTry::loadFromSlug( $whatsLeft, FALSE, FALSE );
|
||||
}
|
||||
catch( \Exception $ex ) { }
|
||||
catch( Exception $ex ) { }
|
||||
}
|
||||
|
||||
/* Check categories */
|
||||
foreach( $databases as $db )
|
||||
{
|
||||
$classToTry = 'IPS\cms\Categories' . $db['database_id'];
|
||||
/* @var $classToTry Categories */
|
||||
try
|
||||
{
|
||||
$category = $classToTry::loadFromPath( $whatsLeft );
|
||||
@@ -76,7 +87,7 @@ class _Router extends \IPS\Patterns\ActiveRecord
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
catch( \Exception $ex ) { }
|
||||
catch( Exception $ex ) { }
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -86,6 +97,6 @@ class _Router extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
/* No idea, sorry */
|
||||
throw new \InvalidArgumentException;
|
||||
throw new InvalidArgumentException;
|
||||
}
|
||||
}
|
||||
@@ -12,58 +12,86 @@
|
||||
namespace IPS\cms\Records;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\cms\Databases;
|
||||
use IPS\cms\Records;
|
||||
use IPS\Content\Anonymous;
|
||||
use IPS\Content\Comment as ContentComment;
|
||||
use IPS\Content\EditHistory;
|
||||
use IPS\Content\Embeddable;
|
||||
use IPS\Content\Filter;
|
||||
use IPS\Content\Hideable;
|
||||
use IPS\Content\Featurable;
|
||||
use IPS\Content\Reactable;
|
||||
use IPS\Content\Reportable;
|
||||
use IPS\Content\Shareable;
|
||||
use IPS\Db;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Patterns\ActiveRecord;
|
||||
use IPS\Theme;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post Model
|
||||
*/
|
||||
class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \IPS\Content\Hideable, \IPS\Content\Shareable, \IPS\Content\Searchable, \IPS\Content\Embeddable, \IPS\Content\Anonymous
|
||||
class Comment extends ContentComment implements Embeddable,
|
||||
Filter
|
||||
{
|
||||
use \IPS\Content\Reactable, \IPS\Content\Reportable;
|
||||
use Reactable,
|
||||
Reportable,
|
||||
Anonymous,
|
||||
Shareable,
|
||||
EditHistory,
|
||||
Hideable,
|
||||
Featurable;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [Content\Comment] Item Class
|
||||
*/
|
||||
public static $itemClass = NULL;
|
||||
public static ?string $itemClass = NULL;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_database_comments';
|
||||
public static ?string $databaseTable = 'cms_database_comments';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'comment_';
|
||||
public static string $databasePrefix = 'comment_';
|
||||
|
||||
/**
|
||||
* @brief Application
|
||||
*/
|
||||
public static $application = 'cms';
|
||||
public static string $application = 'cms';
|
||||
|
||||
/**
|
||||
* @brief Title
|
||||
*/
|
||||
public static $title = 'content_comment';
|
||||
public static string $title = 'content_comment';
|
||||
|
||||
/**
|
||||
* @brief Database Column Map
|
||||
*/
|
||||
public static $databaseColumnMap = array(
|
||||
public static array $databaseColumnMap = array(
|
||||
'item' => 'record_id',
|
||||
'author' => 'user',
|
||||
'author_name' => 'author',
|
||||
@@ -83,35 +111,28 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
/**
|
||||
* @brief Icon
|
||||
*/
|
||||
public static $icon = 'comment';
|
||||
public static string $icon = 'comments';
|
||||
|
||||
/**
|
||||
* @brief [Content\Comment] Comment Template
|
||||
*/
|
||||
public static $commentTemplate = array( array( 'display', 'cms', 'database' ), 'commentContainer' );
|
||||
public static array $commentTemplate = array( array( 'display', 'cms', 'database' ), 'commentContainer' );
|
||||
|
||||
/**
|
||||
* @brief Database ID
|
||||
*/
|
||||
public static $customDatabaseId = NULL;
|
||||
|
||||
/**
|
||||
* @brief Store generated search index titles for efficiency
|
||||
*/
|
||||
protected static $searchIndexTitles = array();
|
||||
public static ?int $customDatabaseId = NULL;
|
||||
|
||||
/**
|
||||
* Load Record
|
||||
*
|
||||
* @see \IPS\Db::build
|
||||
* @param int|string $id ID
|
||||
* @param string $idField The database column that the $id parameter pertains to (NULL will use static::$databaseColumnId)
|
||||
* @param mixed $extraWhereClause Additional where clause(s) (see \IPS\Db::build for details) - if used will cause multiton store to be skipped and a query always ran
|
||||
* @return static
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \OutOfRangeException
|
||||
* @param int|string|null $id ID
|
||||
* @param string|null $idField The database column that the $id parameter pertains to (NULL will use static::$databaseColumnId)
|
||||
* @param mixed $extraWhereClause Additional where clause(s) (see \IPS\Db::build for details) - if used will cause multiton store to be skipped and a query always ran
|
||||
* @return ActiveRecord|Comment
|
||||
* @see Db::build
|
||||
*/
|
||||
public static function load( $id, $idField=NULL, $extraWhereClause=NULL )
|
||||
public static function load( int|string|null $id, string $idField=NULL, mixed $extraWhereClause=NULL ): ActiveRecord|static
|
||||
{
|
||||
if ( static::commentWhere() !== NULL )
|
||||
{
|
||||
@@ -131,10 +152,12 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
/**
|
||||
* Post count for member
|
||||
*
|
||||
* @param \IPS\Member $member The memner
|
||||
* @return int
|
||||
* @param Member $member The memner
|
||||
* @param bool $includeNonPostCountIncreasing
|
||||
* @param bool $includeHiddenAndPendingApproval
|
||||
* @return int
|
||||
*/
|
||||
public static function memberPostCount( \IPS\Member $member, bool $includeNonPostCountIncreasing = FALSE, bool $includeHiddenAndPendingApproval = TRUE )
|
||||
public static function memberPostCount( Member $member, bool $includeNonPostCountIncreasing = FALSE, bool $includeHiddenAndPendingApproval = TRUE ): int
|
||||
{
|
||||
$where = [];
|
||||
$where[] = [ 'comment_database_id=?', static::$customDatabaseId ];
|
||||
@@ -145,7 +168,7 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
$where[] = [ 'comment_approved=1' ];
|
||||
}
|
||||
|
||||
return \IPS\Db::i()->select( 'COUNT(*)', static::$databaseTable, $where )->first();
|
||||
return Db::i()->select( 'COUNT(*)', static::$databaseTable, $where )->first();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +177,7 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
* @param int $id Content item to delete from
|
||||
* @return array
|
||||
*/
|
||||
public static function deleteWhereSql( $id )
|
||||
public static function deleteWhereSql( int $id ) : array
|
||||
{
|
||||
return array( array( static::$databasePrefix . static::$databaseColumnMap['item'] . '=?', $id ), array( static::$databasePrefix . 'database_id=?', static::$customDatabaseId ) );
|
||||
}
|
||||
@@ -164,73 +187,36 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function findByIPWhere()
|
||||
public static function findByIPWhere() : string
|
||||
{
|
||||
return " AND comment_database_id=" . static::$customDatabaseId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get items with permisison check
|
||||
*
|
||||
* @param array $where Where clause
|
||||
* @param string $order MySQL ORDER BY clause (NULL to order by date)
|
||||
* @param int|array $limit Limit clause
|
||||
* @param string $permissionKey A key which has a value in the permission map (either of the container or of this class) matching a column ID in core_permission_index
|
||||
* @param mixed $includeHiddenItems Include hidden comments? NULL to detect if currently logged in member has permission, -1 to return public content only, TRUE to return unapproved content and FALSE to only return unapproved content the viewing member submitted
|
||||
* @param int $queryFlags Select bitwise flags
|
||||
* @param \IPS\Member $member The member (NULL to use currently logged in member)
|
||||
* @param bool $joinContainer If true, will join container data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $joinComments If true, will join comment data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $joinReviews If true, will join review data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $countOnly If true will return the count
|
||||
* @param array|null $joins Additional arbitrary joins for the query
|
||||
* @return array|NULL|\IPS\Content\Comment If $limit is 1, will return \IPS\Content\Comment or NULL for no results. For any other number, will return an array.
|
||||
* @param array $where Where clause
|
||||
* @param string|null $order MySQL ORDER BY clause (NULL to order by date)
|
||||
* @param int|array|null $limit Limit clause
|
||||
* @param string|null $permissionKey A key which has a value in the permission map (either of the container or of this class) matching a column ID in core_permission_index
|
||||
* @param mixed $includeHiddenComments
|
||||
* @param int $queryFlags Select bitwise flags
|
||||
* @param Member|null $member The member (NULL to use currently logged in member)
|
||||
* @param bool $joinContainer If true, will join container data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $joinComments If true, will join comment data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $joinReviews If true, will join review data (set to TRUE if your $where clause depends on this data)
|
||||
* @param bool $countOnly If true will return the count
|
||||
* @param array|null $joins Additional arbitrary joins for the query
|
||||
* @return array|NULL|Comment If $limit is 1, will return \IPS\Content\Comment or NULL for no results. For any other number, will return an array.
|
||||
*/
|
||||
public static function getItemsWithPermission( $where=array(), $order=NULL, $limit=10, $permissionKey='read', $includeHiddenItems=\IPS\Content\Hideable::FILTER_AUTOMATIC, $queryFlags=0, \IPS\Member $member=NULL, $joinContainer=FALSE, $joinComments=FALSE, $joinReviews=FALSE, $countOnly=FALSE, $joins=NULL )
|
||||
public static function getItemsWithPermission( array $where=array(), string $order= null, int|array|null $limit=10, string|null $permissionKey='read', mixed $includeHiddenComments= Filter::FILTER_AUTOMATIC, int $queryFlags=0, Member|null $member= null, bool $joinContainer=FALSE, bool $joinComments=FALSE, bool $joinReviews=FALSE, bool $countOnly=FALSE, array|null $joins= null ): mixed
|
||||
{
|
||||
$class = '\IPS\cms\Records' . static::$customDatabaseId;
|
||||
|
||||
/* @var $class Records */
|
||||
$where = $class::getItemsWithPermissionWhere( $where, $permissionKey, $member, $joinContainer );
|
||||
|
||||
return parent::getItemsWithPermission( $where, $order, $limit, $permissionKey, $includeHiddenItems, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, array $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments=FALSE, $template=NULL, $reactions=array() )
|
||||
{
|
||||
/* Ensure that the comment title is formatted correctly */
|
||||
try
|
||||
{
|
||||
$databases = \IPS\cms\Databases::databases();
|
||||
|
||||
if ( ! isset( static::$searchIndexTitles[ $itemData['primary_id_field'] ] ) )
|
||||
{
|
||||
$fields = '\IPS\cms\Fields' . static::$customDatabaseId;
|
||||
$titleField = $databases[ static::$customDatabaseId ]->field_title;
|
||||
|
||||
static::$searchIndexTitles[ $itemData['primary_id_field'] ] = $fields::load( $databases[ static::$customDatabaseId ]->field_title )->displayValue( $itemData['field_' . $databases[ static::$customDatabaseId ]->field_title ] );
|
||||
}
|
||||
|
||||
$itemData['field_' . $databases[ static::$customDatabaseId ]->field_title ] = static::$searchIndexTitles[ $itemData['primary_id_field'] ];
|
||||
}
|
||||
catch ( \Exception $ex ) { }
|
||||
|
||||
return parent::searchResult( $indexData, $authorData, $itemData, $containerData, $reputationData, $reviewRating, $iPostedIn, $view, $asItem, $canIgnoreComments, $template, $reactions );
|
||||
return parent::getItemsWithPermission( $where, $order, $limit, $permissionKey, $includeHiddenComments, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,13 +224,14 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function postCreate()
|
||||
public function postCreate(): void
|
||||
{
|
||||
$this->database_id = static::$customDatabaseId;
|
||||
$this->save();
|
||||
|
||||
$item = $this->item();
|
||||
if ( $item->hidden() OR ( \IPS\cms\Databases::load( static::$customDatabaseId )->_comment_bump & \IPS\cms\Databases::BUMP_ON_COMMENT ) )
|
||||
/* @var $item Records */
|
||||
if ( $item->hidden() OR ( Databases::load( static::$customDatabaseId )->_comment_bump & Databases::BUMP_ON_COMMENT ) )
|
||||
{
|
||||
parent::postCreate();
|
||||
}
|
||||
@@ -270,7 +257,7 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function html()
|
||||
public function html(): string
|
||||
{
|
||||
$template = static::$commentTemplate[1];
|
||||
static::$commentTemplate[0][0] = $this->item()->database()->template_display;
|
||||
@@ -282,9 +269,9 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
* Get URL for doing stuff
|
||||
*
|
||||
* @param string|NULL $action Action
|
||||
* @return \IPS\Http\Url
|
||||
* @return Url
|
||||
*/
|
||||
public function url( $action='find' )
|
||||
public function url( ?string $action='find' ): Url
|
||||
{
|
||||
$url = parent::url( $action );
|
||||
|
||||
@@ -303,7 +290,7 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attachmentIds()
|
||||
public function attachmentIds(): array
|
||||
{
|
||||
$item = $this->item();
|
||||
$idColumn = $item::$databaseColumnId;
|
||||
@@ -316,27 +303,17 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
*
|
||||
* @return array|NULL
|
||||
*/
|
||||
public static function commentWhere()
|
||||
public static function commentWhere(): ?array
|
||||
{
|
||||
return array( 'comment_database_id=?', static::$customDatabaseId );
|
||||
}
|
||||
|
||||
/**
|
||||
* WHERE clause for getting items for ACP overview statistics
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function overviewStatisticsWhere()
|
||||
{
|
||||
return [ static::commentWhere() ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reaction Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function reactionType()
|
||||
public static function reactionType(): string
|
||||
{
|
||||
$databaseId = static::$customDatabaseId;
|
||||
return "comment_id_{$databaseId}";
|
||||
@@ -348,9 +325,9 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
|
||||
* @param array $params Additional parameters to add to URL
|
||||
* @return string
|
||||
*/
|
||||
public function embedContent( $params )
|
||||
public function embedContent( array $params ): string
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'cms', 'front' ) );
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'cms' )->embedRecordComment( $this, $this->item(), $this->url()->setQueryString( $params ) );
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'embed.css', 'cms', 'front' ) );
|
||||
return Theme::i()->getTemplate( 'global', 'cms' )->embedRecordComment( $this, $this->item(), $this->url()->setQueryString( $params ) );
|
||||
}
|
||||
}
|
||||
@@ -12,41 +12,53 @@
|
||||
namespace IPS\cms\Records;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\cms\Records;
|
||||
use IPS\cms\Theme;
|
||||
use IPS\Content\Item;
|
||||
use IPS\core\Reports\Report;
|
||||
use IPS\forums\Topic\Post;
|
||||
use IPS\Member;
|
||||
use IPS\Request;
|
||||
use OutOfRangeException;
|
||||
use UnexpectedValueException;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Records Model
|
||||
*/
|
||||
class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
class CommentTopicSync extends Comment
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'pid';
|
||||
public static string $databaseColumnId = 'pid';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'forums_posts';
|
||||
public static ?string $databaseTable = 'forums_posts';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = '';
|
||||
public static string $databasePrefix = '';
|
||||
|
||||
/**
|
||||
* @brief Database Column Map
|
||||
*/
|
||||
public static $databaseColumnMap = array(
|
||||
public static array $databaseColumnMap = array(
|
||||
'item' => 'topic_id',
|
||||
'author' => 'author_id',
|
||||
'author_name' => 'author_name',
|
||||
@@ -64,27 +76,27 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
/**
|
||||
* @brief Application
|
||||
*/
|
||||
public static $application = 'forums';
|
||||
public static string $application = 'forums';
|
||||
|
||||
/**
|
||||
* @brief Icon
|
||||
*/
|
||||
public static $icon = 'comment';
|
||||
public static string $icon = 'comment';
|
||||
|
||||
/**
|
||||
* @brief [Content] Key for hide reasons
|
||||
*/
|
||||
public static $hideLogKey = 'post';
|
||||
public static ?string $hideLogKey = 'post';
|
||||
|
||||
/**
|
||||
* @brief [Content\Comment] Comment Template
|
||||
*/
|
||||
public static $commentTemplate = array( array( 'display', 'cms', 'database' ), 'commentContainer' );
|
||||
public static array $commentTemplate = array( array( 'display', 'cms', 'database' ), 'commentContainer' );
|
||||
|
||||
/**
|
||||
* @brief Bitwise values for post_bwoptions field
|
||||
*/
|
||||
public static $bitOptions = array(
|
||||
public static array $bitOptions = array(
|
||||
'post_bwoptions' => array(
|
||||
'post_bwoptions' => array(
|
||||
'best_answer' => 1
|
||||
@@ -95,11 +107,12 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
/**
|
||||
* Get containing item
|
||||
*
|
||||
* @return \IPS\cms\Record
|
||||
* @return Item
|
||||
*/
|
||||
public function item()
|
||||
public function item(): Item
|
||||
{
|
||||
$itemClass = static::$itemClass;
|
||||
/* @var $itemClass Records */
|
||||
$lookFor = 'IPS\cms\Records\RecordsTopicSync';
|
||||
|
||||
if ( mb_substr( $itemClass, 0, mb_strlen( $lookFor ) ) === $lookFor )
|
||||
@@ -108,7 +121,7 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
}
|
||||
else
|
||||
{
|
||||
$id = \IPS\Request::i()->id;
|
||||
$id = Request::i()->id;
|
||||
}
|
||||
|
||||
return $itemClass::load( $id );
|
||||
@@ -117,24 +130,24 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
/**
|
||||
* Delete Comment
|
||||
*
|
||||
* @return void
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
public function delete(): void
|
||||
{
|
||||
/* We need the classname to be \IPS\forums\Topic\Post so topic/forum sync occur and to ensure post is removed from search index */
|
||||
$comment = \IPS\forums\Topic\Post::load( $this->pid );
|
||||
$comment = Post::load( $this->pid );
|
||||
$comment->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit Comment Contents - Note: does not add edit log
|
||||
*
|
||||
* @param string $newContent New content
|
||||
* @return string|NULL
|
||||
* @param string $newContent New content
|
||||
* @return void
|
||||
*/
|
||||
public function editContents( $newContent )
|
||||
public function editContents( string $newContent ): void
|
||||
{
|
||||
$comment = \IPS\forums\Topic\Post::load( $this->pid );
|
||||
$comment = Post::load( $this->pid );
|
||||
$comment->editContents( $newContent );
|
||||
}
|
||||
|
||||
@@ -143,19 +156,19 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function html()
|
||||
public function html(): string
|
||||
{
|
||||
if ( ! empty( $this->pid ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$comment = \IPS\forums\Topic\Post::load( $this->pid );
|
||||
$comment = Post::load( $this->pid );
|
||||
$template = static::$commentTemplate[1];
|
||||
static::$commentTemplate[0][0] = $this->item()->database()->template_display;
|
||||
|
||||
return \IPS\cms\Theme::i()->getTemplate( static::$commentTemplate[0][0], static::$commentTemplate[0][1], static::$commentTemplate[0][2] )->$template( $comment->item(), $comment );
|
||||
return Theme::i()->getTemplate( static::$commentTemplate[0][0], static::$commentTemplate[0][1], static::$commentTemplate[0][2] )->$template( $comment->item(), $comment );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
catch( OutOfRangeException $e )
|
||||
{
|
||||
return parent::html();
|
||||
}
|
||||
@@ -165,20 +178,19 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
return parent::html();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Report
|
||||
*
|
||||
* @param string $reportContent Report content message from member
|
||||
* @param int $reportType Report type (see constants in \IPS\core\Reports\Report)
|
||||
* @param \IPS\Member|NULL $member Member making the report (or NULL for loggedIn())
|
||||
* @param array $guestDetails Details for a guest report
|
||||
* @return \IPS\core\Reports\Report
|
||||
* @throws \UnexpectedValueException If there is a permission error - you should only call this method after checking canReport
|
||||
* @param string $reportContent Report content message from member
|
||||
* @param int $reportType Report type (see constants in \IPS\core\Reports\Report
|
||||
* @param Member|null $member Member making the report (or NULL for loggedIn())
|
||||
* @return Report
|
||||
* @throws UnexpectedValueException If there is a permission error - you should only call this method after checking canReport
|
||||
*/
|
||||
public function report( $reportContent, $reportType=1, $member=NULL, $guestDetails=array() )
|
||||
public function report( string $reportContent, int $reportType=1, Member|null $member=null ): Report
|
||||
{
|
||||
$comment = \IPS\forums\Topic\Post::load( $this->pid );
|
||||
$comment = Post::load( $this->pid );
|
||||
return $comment->report( $reportContent, $reportType, $member );
|
||||
}
|
||||
|
||||
@@ -187,7 +199,7 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
*
|
||||
* @return array|NULL
|
||||
*/
|
||||
public static function commentWhere()
|
||||
public static function commentWhere(): ?array
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -198,7 +210,7 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
* @param int $id Content item to delete from
|
||||
* @return array
|
||||
*/
|
||||
public static function deleteWhereSql( $id )
|
||||
public static function deleteWhereSql( int $id ): array
|
||||
{
|
||||
return array( array( static::$databasePrefix . static::$databaseColumnMap['item'] . '=?', $id ) );
|
||||
}
|
||||
@@ -206,12 +218,12 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
/**
|
||||
* Post count for member
|
||||
*
|
||||
* @param \IPS\Member $member The member
|
||||
* @param Member $member The member
|
||||
* @param bool $includeNonPostCountIncreasing If FALSE, will skip any posts which would not cause the user's post count to increase
|
||||
* @param bool $includeHiddenAndPendingApproval If FALSE, will skip any hidden posts, or posts pending approval
|
||||
* @return int
|
||||
*/
|
||||
public static function memberPostCount( \IPS\Member $member, bool $includeNonPostCountIncreasing = FALSE, bool $includeHiddenAndPendingApproval = TRUE )
|
||||
public static function memberPostCount( Member $member, bool $includeNonPostCountIncreasing = FALSE, bool $includeHiddenAndPendingApproval = TRUE ): int
|
||||
{
|
||||
return 0; // We explicitely return 0 because any posts will be counted via the forums application inherently already
|
||||
}
|
||||
@@ -221,7 +233,7 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function reactionType()
|
||||
public static function reactionType(): string
|
||||
{
|
||||
return 'pid';
|
||||
}
|
||||
@@ -231,7 +243,7 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function reactionClass()
|
||||
public static function reactionClass() : string
|
||||
{
|
||||
return 'IPS\forums\Topic\Post';
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -12,58 +12,79 @@
|
||||
namespace IPS\cms\Records;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use BadMethodCallException;
|
||||
use IPS\Content\EditHistory;
|
||||
use IPS\Content\Embeddable;
|
||||
use IPS\Content\Filter;
|
||||
use IPS\Content\Hideable;
|
||||
use IPS\Content\Item;
|
||||
use IPS\Content\Reactable;
|
||||
use IPS\Content\Reportable;
|
||||
use IPS\Content\Review as ContentReview;
|
||||
use IPS\Content\Shareable;
|
||||
use IPS\DateTime;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Http\Url\Exception;
|
||||
use IPS\Member;
|
||||
use IPS\Output;
|
||||
use IPS\Patterns\ActiveRecord;
|
||||
use IPS\Theme;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post Model
|
||||
*/
|
||||
class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \IPS\Content\Hideable, \IPS\Content\Shareable, \IPS\Content\Searchable, \IPS\Content\Embeddable
|
||||
class Review extends ContentReview implements Embeddable,
|
||||
Filter
|
||||
{
|
||||
use \IPS\Content\Reactable, \IPS\Content\Reportable;
|
||||
use Reactable, Reportable, Shareable, EditHistory, Hideable;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [Content\Comment] Item Class
|
||||
*/
|
||||
public static $itemClass = NULL;
|
||||
public static ?string $itemClass = NULL;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_database_reviews';
|
||||
public static ?string $databaseTable = 'cms_database_reviews';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'review_';
|
||||
public static string $databasePrefix = 'review_';
|
||||
|
||||
/**
|
||||
* @brief Application
|
||||
*/
|
||||
public static $application = 'cms';
|
||||
public static string $application = 'cms';
|
||||
|
||||
/**
|
||||
* @brief Title
|
||||
*/
|
||||
public static $title = 'content_review';
|
||||
public static string $title = 'content_review';
|
||||
|
||||
/**
|
||||
* @brief Database Column Map
|
||||
*/
|
||||
public static $databaseColumnMap = array(
|
||||
public static array $databaseColumnMap = array(
|
||||
'item' => 'item',
|
||||
'author' => 'author',
|
||||
'author_name' => 'author_name',
|
||||
@@ -84,30 +105,28 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
|
||||
/**
|
||||
* @brief Icon
|
||||
*/
|
||||
public static $icon = 'comment';
|
||||
public static string $icon = 'star';
|
||||
|
||||
/**
|
||||
* @brief [Content\Comment] Comment Template
|
||||
*/
|
||||
public static $commentTemplate = array( array( 'display', 'cms', 'database' ), 'reviewContainer' );
|
||||
public static array $commentTemplate = array( array( 'display', 'cms', 'database' ), 'reviewContainer' );
|
||||
|
||||
/**
|
||||
* @brief Database ID
|
||||
*/
|
||||
public static $customDatabaseId = NULL;
|
||||
public static ?int $customDatabaseId = NULL;
|
||||
|
||||
/**
|
||||
* Load Record
|
||||
*
|
||||
* @see \IPS\Db::build
|
||||
* @param int|string $id ID
|
||||
* @param string $idField The database column that the $id parameter pertains to (NULL will use static::$databaseColumnId)
|
||||
* @param mixed $extraWhereClause Additional where clause(s) (see \IPS\Db::build for details) - if used will cause multiton store to be skipped and a query always ran
|
||||
* @return static
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \OutOfRangeException
|
||||
* @param int|string|null $id ID
|
||||
* @param string|null $idField The database column that the $id parameter pertains to (NULL will use static::$databaseColumnId)
|
||||
* @param mixed $extraWhereClause Additional where clause(s) (see \IPS\Db::build for details) - if used will cause multiton store to be skipped and a query always ran
|
||||
* @return ActiveRecord|Review
|
||||
* @see \IPS\Db::build
|
||||
*/
|
||||
public static function load( $id, $idField=NULL, $extraWhereClause=NULL )
|
||||
public static function load( int|string|null $id, string $idField=NULL, mixed $extraWhereClause=NULL ): ActiveRecord|static
|
||||
{
|
||||
if( $extraWhereClause === NULL )
|
||||
{
|
||||
@@ -124,52 +143,42 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
|
||||
/**
|
||||
* Create first comment (created with content item)
|
||||
*
|
||||
* @param \IPS\Content\Item $item The content item just created
|
||||
* @param string $comment The comment
|
||||
* @param bool $first Is the first comment?
|
||||
* @param int $rating The rating (1-5)
|
||||
* @param string $guestName If author is a guest, the name to use
|
||||
* @param \IPS\Member|NULL $member The author of this comment. If NULL, uses currently logged in member.
|
||||
* @param \IPS\DateTime|NULL $time The time
|
||||
* @param string|NULL $ipAddress The IP address or NULL to detect automatically
|
||||
* @param int|NULL $hiddenStatus NULL to set automatically or override: 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
|
||||
* @param int|NULL $anonymous NULL for no value, 0 or 1 for a value (0=no, 1=yes)
|
||||
* @return static
|
||||
* @throws \InvalidArgumentException
|
||||
* @param Item $item The content item just created
|
||||
* @param string $comment The comment
|
||||
* @param bool $first Is the first comment?
|
||||
* @param string|null $guestName If author is a guest, the name to use
|
||||
* @param bool|null $incrementPostCount
|
||||
* @param Member|null $member The author of this comment. If NULL, uses currently logged in member.
|
||||
* @param DateTime|null $time The time
|
||||
* @param string|null $ipAddress The IP address or NULL to detect automatically
|
||||
* @param int|null $hiddenStatus NULL to set automatically or override: 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
|
||||
* @param int|null $anonymous NULL for no value, 0 or 1 for a value (0=no, 1=yes)
|
||||
* @return Review|null
|
||||
*/
|
||||
public static function create( $item, $comment, $first=FALSE, $rating=NULL, $guestName=NULL, $member=NULL, \IPS\DateTime $time=NULL, $ipAddress=NULL, $hiddenStatus=NULL, $anonymous=NULL )
|
||||
public static function create( Item $item, string $comment, bool $first=false, string|null $guestName=null, bool|null $incrementPostCount= null, Member|null $member= null, DateTime|null $time= null, string|null $ipAddress= null, int|null $hiddenStatus= null, int|null $anonymous= null ): static|null
|
||||
{
|
||||
$review = parent::create( $item, $comment, $first, $rating, $guestName, $member, $time, $ipAddress, $hiddenStatus, $anonymous );
|
||||
$review = parent::create( $item, $comment, $first, $guestName, $member, $time, $ipAddress, $hiddenStatus, $anonymous );
|
||||
|
||||
$review->database_id = static::$customDatabaseId;
|
||||
$review->save();
|
||||
|
||||
/* Have to do these AFTER database id is set */
|
||||
/* @var array $databaseColumnMap */
|
||||
$ratingField = $item::$databaseColumnMap['rating'];
|
||||
|
||||
$review->item()->$ratingField = $review->item()->averageReviewRating() ?: 0;
|
||||
$review->item()->$ratingField = (int) $review->item()->averageReviewRating() ?: 0;
|
||||
$review->item()->save();
|
||||
|
||||
return $review;
|
||||
}
|
||||
|
||||
/**
|
||||
* WHERE clause for getting items for ACP overview statistics
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function overviewStatisticsWhere()
|
||||
{
|
||||
return [ static::commentWhere() ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return custom where for SQL delete
|
||||
*
|
||||
* @param int $id Content item to delete from
|
||||
* @return array
|
||||
*/
|
||||
public static function deleteWhereSql( $id )
|
||||
public static function deleteWhereSql( int $id ) : array
|
||||
{
|
||||
return array( array( static::$databasePrefix . static::$databaseColumnMap['item'] . '=?', $id ), array( static::$databasePrefix . 'database_id=?', static::$customDatabaseId ) );
|
||||
}
|
||||
@@ -179,7 +188,7 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function html()
|
||||
public function html(): string
|
||||
{
|
||||
$template = static::$commentTemplate[1];
|
||||
static::$commentTemplate[0][0] = $this->item()->database()->template_display;
|
||||
@@ -191,11 +200,11 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
|
||||
* Get URL for doing stuff
|
||||
*
|
||||
* @param string|NULL $action Action
|
||||
* @return \IPS\Http\Url
|
||||
* @throws \BadMethodCallException
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
* @return Url
|
||||
* @throws BadMethodCallException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function url( $action='find' )
|
||||
public function url( ?string $action='find' ): Url
|
||||
{
|
||||
$url = parent::url( $action );
|
||||
|
||||
@@ -212,7 +221,7 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attachmentIds()
|
||||
public function attachmentIds(): array
|
||||
{
|
||||
$item = $this->item();
|
||||
$idColumn = $item::$databaseColumnId;
|
||||
@@ -225,7 +234,7 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
|
||||
*
|
||||
* @return array|NULL
|
||||
*/
|
||||
public static function commentWhere()
|
||||
public static function commentWhere(): ?array
|
||||
{
|
||||
return array( 'review_database_id=?', static::$customDatabaseId );
|
||||
}
|
||||
@@ -236,7 +245,7 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
|
||||
* @return string
|
||||
* @todo This was implemented improperly before - look into upgrade routine to fix
|
||||
*/
|
||||
public static function reactionType()
|
||||
public static function reactionType(): string
|
||||
{
|
||||
$databaseId = static::$customDatabaseId;
|
||||
return "review_id_{$databaseId}";
|
||||
@@ -248,9 +257,9 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
|
||||
* @param array $params Additional parameters to add to URL
|
||||
* @return string
|
||||
*/
|
||||
public function embedContent( $params )
|
||||
public function embedContent( array $params ): string
|
||||
{
|
||||
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'cms', 'front' ) );
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'cms' )->embedRecordReview( $this, $this->item(), $this->url()->setQueryString( $params ) );
|
||||
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'embed.css', 'cms', 'front' ) );
|
||||
return Theme::i()->getTemplate( 'global', 'cms' )->embedRecordReview( $this, $this->item(), $this->url()->setQueryString( $params ) );
|
||||
}
|
||||
}
|
||||
@@ -12,41 +12,50 @@
|
||||
namespace IPS\cms\Records;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\cms\Fields;
|
||||
use IPS\cms\Records;
|
||||
use IPS\Login;
|
||||
use IPS\Member;
|
||||
use IPS\Patterns\ActiveRecord;
|
||||
use function defined;
|
||||
use function is_array;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Records Model
|
||||
*/
|
||||
class _Revisions extends \IPS\Patterns\ActiveRecord
|
||||
class Revisions extends ActiveRecord
|
||||
{
|
||||
/**
|
||||
* @brief Multiton Store
|
||||
*/
|
||||
protected static $multitons = array();
|
||||
protected static array $multitons = array();
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_database_revisions';
|
||||
public static ?string $databaseTable = 'cms_database_revisions';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'revision_';
|
||||
public static string $databasePrefix = 'revision_';
|
||||
|
||||
/**
|
||||
* @brief Unpacked data
|
||||
*/
|
||||
protected $_dataJson = NULL;
|
||||
protected ?array $_dataJson = NULL;
|
||||
|
||||
/**
|
||||
* Constructor - Create a blank object with default values
|
||||
@@ -59,7 +68,7 @@ class _Revisions extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
if ( $this->_new )
|
||||
{
|
||||
$this->member_id = \IPS\Member::loggedIn()->member_id;
|
||||
$this->member_id = Member::loggedIn()->member_id;
|
||||
$this->date = time();
|
||||
}
|
||||
}
|
||||
@@ -70,7 +79,7 @@ class _Revisions extends \IPS\Patterns\ActiveRecord
|
||||
* @param string $key Key of value to return
|
||||
* @return mixed
|
||||
*/
|
||||
public function get( $key )
|
||||
public function get( string $key ) : mixed
|
||||
{
|
||||
if ( $this->_dataJson === NULL )
|
||||
{
|
||||
@@ -88,14 +97,15 @@ class _Revisions extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* Compute differences
|
||||
*
|
||||
* @param int $databaseId Database ID
|
||||
* @param \IPS\cms\Records $record Record
|
||||
* @param boolean $justChanged Get changed only
|
||||
* @param int $databaseId Database ID
|
||||
* @param Records $record Record
|
||||
* @param boolean $justChanged Get changed only
|
||||
* @return array
|
||||
*/
|
||||
public function getDiffHtmlTables( $databaseId, $record, $justChanged=FALSE )
|
||||
public function getDiffHtmlTables( int $databaseId, Records $record, bool $justChanged=FALSE ): array
|
||||
{
|
||||
$fieldsClass = 'IPS\cms\Fields' . $databaseId;
|
||||
/* @var $fieldsClass Fields */
|
||||
$customFields = $fieldsClass::data( 'view' );
|
||||
$conflicts = array();
|
||||
|
||||
@@ -104,7 +114,7 @@ class _Revisions extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
$key = 'field_' . $field->id;
|
||||
|
||||
if( $justChanged === FALSE OR !\IPS\Login::compareHashes( md5( $record->$key ), md5( $this->get( $key ) ) ) )
|
||||
if( $justChanged === FALSE OR !Login::compareHashes( md5( $record->$key ), md5( $this->get( $key ) ) ) )
|
||||
{
|
||||
$conflicts[] = array( 'original' => $this->get( $key ), 'current' => $record->$key, 'field' => $field );
|
||||
}
|
||||
@@ -119,9 +129,9 @@ class _Revisions extends \IPS\Patterns\ActiveRecord
|
||||
* @param string|array $value
|
||||
* @return void
|
||||
*/
|
||||
public function set_data( $value )
|
||||
public function set_data( string|array $value ) : void
|
||||
{
|
||||
$this->_data['data'] = ( \is_array( $value ) ? json_encode( $value ) : $value );
|
||||
$this->_data['data'] = ( is_array( $value ) ? json_encode( $value ) : $value );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +139,7 @@ class _Revisions extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_data()
|
||||
public function get_data() : array
|
||||
{
|
||||
return json_decode( $this->_data['data'], TRUE );
|
||||
}
|
||||
|
||||
@@ -12,116 +12,125 @@
|
||||
namespace IPS\cms\Selector;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\cms\Databases;
|
||||
use IPS\cms\Pages\Page;
|
||||
use IPS\Member;
|
||||
use IPS\Node\Model;
|
||||
use IPS\Node\Permissions;
|
||||
use OutOfRangeException;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Categories Model
|
||||
*/
|
||||
class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
class Categories extends Model implements Permissions
|
||||
{
|
||||
/**
|
||||
* @brief Multiton Store
|
||||
*/
|
||||
protected static $multitons = array();
|
||||
protected static array $multitons = array();
|
||||
|
||||
/**
|
||||
* @brief [Records] Custom Database Id
|
||||
*/
|
||||
public static $customDatabaseId = NULL;
|
||||
public static ?int $customDatabaseId = NULL;
|
||||
|
||||
/**
|
||||
* @brief [Records] Content item class
|
||||
*/
|
||||
public static $contentItemClass = NULL;
|
||||
public static ?string $contentItemClass = NULL;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_database_categories';
|
||||
public static ?string $databaseTable = 'cms_database_categories';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'category_';
|
||||
public static string $databasePrefix = 'category_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array('category_furl_name', 'category_full_path');
|
||||
protected static array $databaseIdFields = array('category_furl_name', 'category_full_path');
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
protected static array $multitonMap = array();
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Database Column
|
||||
*/
|
||||
public static $databaseColumnParent = 'parent_id';
|
||||
public static ?string $databaseColumnParent = 'parent_id';
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Database Column
|
||||
*/
|
||||
public static $databaseColumnOrder = 'position';
|
||||
public static ?string $databaseColumnOrder = 'position';
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent Node ID Database Column
|
||||
*/
|
||||
public static $parentNodeColumnId = 'database_id';
|
||||
public static string $parentNodeColumnId = 'database_id';
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent Node Class
|
||||
*/
|
||||
public static $parentNodeClass = 'IPS\cms\Selector\Databases';
|
||||
public static string $parentNodeClass = 'IPS\cms\Selector\Databases';
|
||||
|
||||
/**
|
||||
* @brief [Node] Show forms modally?
|
||||
*/
|
||||
public static $modalForms = FALSE;
|
||||
public static bool $modalForms = FALSE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Sortable?
|
||||
*/
|
||||
public static $nodeSortable = TRUE;
|
||||
public static bool $nodeSortable = TRUE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = 'r__categories';
|
||||
public static string $nodeTitle = 'r__categories';
|
||||
|
||||
/**
|
||||
* @brief [Node] Title prefix. If specified, will look for a language key with "{$key}_title" as the key
|
||||
*/
|
||||
public static $titleLangPrefix = 'content_cat_name_';
|
||||
public static ?string $titleLangPrefix = 'content_cat_name_';
|
||||
|
||||
/**
|
||||
* @brief [Node] Description suffix. If specified, will look for a language key with "{$titleLangPrefix}_{$id}_{$descriptionLangSuffix}" as the key
|
||||
*/
|
||||
public static $descriptionLangSuffix = '_desc';
|
||||
public static ?string $descriptionLangSuffix = '_desc';
|
||||
|
||||
/**
|
||||
* @brief [Node] App for permission index
|
||||
*/
|
||||
public static $permApp = 'cms';
|
||||
public static ?string $permApp = 'cms';
|
||||
|
||||
/**
|
||||
* @brief [Node] Type for permission index
|
||||
*/
|
||||
public static $permType = 'categories';
|
||||
public static ?string $permType = 'categories';
|
||||
|
||||
/**
|
||||
* @brief The map of permission columns
|
||||
*/
|
||||
public static $permissionMap = array(
|
||||
public static array $permissionMap = array(
|
||||
'view' => 'view',
|
||||
'read' => 2,
|
||||
'add' => 3,
|
||||
@@ -134,31 +143,31 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
/**
|
||||
* @brief [Node] Moderator Permission
|
||||
*/
|
||||
public static $modPerm = 'cms';
|
||||
public static string $modPerm = 'cms';
|
||||
|
||||
/**
|
||||
* @brief [Node] Prefix string that is automatically prepended to permission matrix language strings
|
||||
*/
|
||||
public static $permissionLangPrefix = 'perm_content_';
|
||||
public static string $permissionLangPrefix = 'perm_content_';
|
||||
|
||||
/**
|
||||
* Get title of category
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get__title()
|
||||
protected function get__title(): string
|
||||
{
|
||||
/* If the DB is in a page, and we're not using categories, then return the page title, not the category title for continuity */
|
||||
if ( ! \IPS\cms\Databases::load( $this->database_id )->use_categories )
|
||||
if ( ! Databases::load( $this->database_id )->use_categories )
|
||||
{
|
||||
if ( ! $this->_catTitle )
|
||||
{
|
||||
try
|
||||
{
|
||||
$page = \IPS\cms\Pages\Page::loadByDatabaseId( $this->database_id );
|
||||
$page = Page::loadByDatabaseId( $this->database_id );
|
||||
$this->_catTitle = $page->_title;
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
catch( OutOfRangeException $e )
|
||||
{
|
||||
$this->_catTitle = parent::get__title();
|
||||
}
|
||||
@@ -172,19 +181,39 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief [Records] Database objects
|
||||
*/
|
||||
protected static array $database = array();
|
||||
|
||||
/**
|
||||
* Returns the database parent
|
||||
*
|
||||
* @return Databases
|
||||
*/
|
||||
public static function database(): Databases
|
||||
{
|
||||
if ( !isset( static::$database[ static::$customDatabaseId ] ) )
|
||||
{
|
||||
static::$database[ static::$customDatabaseId ] = Databases::load( static::$customDatabaseId );
|
||||
}
|
||||
|
||||
return static::$database[ static::$customDatabaseId ];
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Get Description
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function get__description()
|
||||
protected function get__description(): ?string
|
||||
{
|
||||
if ( ! static::database()->use_categories )
|
||||
{
|
||||
return static::database()->_description;
|
||||
}
|
||||
|
||||
return ( \IPS\Member::loggedIn()->language()->addToStack('content_cat_name_' . $this->id . '_desc') === 'content_cat_name_' . $this->id . '_desc' ) ? $this->description : \IPS\Member::loggedIn()->language()->addToStack('content_cat_name_' . $this->id . '_desc');
|
||||
return ( Member::loggedIn()->language()->addToStack('content_cat_name_' . $this->id . '_desc') === 'content_cat_name_' . $this->id . '_desc' ) ? $this->description : Member::loggedIn()->language()->addToStack('content_cat_name_' . $this->id . '_desc');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,71 +12,76 @@
|
||||
namespace IPS\cms\Selector;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\Node\Model;
|
||||
use IPS\Node\Permissions;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Databases Model
|
||||
*/
|
||||
class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
class Databases extends Model implements Permissions
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons = array();
|
||||
protected static array $multitons = array();
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'database_';
|
||||
public static string $databasePrefix = 'database_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_databases';
|
||||
public static ?string $databaseTable = 'cms_databases';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array( 'database_key', 'database_page_id' );
|
||||
protected static array $databaseIdFields = array( 'database_key', 'database_page_id' );
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
protected static array $multitonMap = array();
|
||||
|
||||
/**
|
||||
* @brief [Node] Parent ID Database Column
|
||||
*/
|
||||
public static $databaseColumnOrder = 'id';
|
||||
public static ?string $databaseColumnOrder = 'id';
|
||||
|
||||
/**
|
||||
* @brief [Node] Sortable?
|
||||
*/
|
||||
public static $nodeSortable = FALSE;
|
||||
public static bool $nodeSortable = FALSE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Node Title
|
||||
*/
|
||||
public static $nodeTitle = '';
|
||||
public static string $nodeTitle = '';
|
||||
|
||||
/**
|
||||
* @brief [Node] Subnode class
|
||||
*/
|
||||
public static $subnodeClass = 'IPS\cms\Selector\Categories';
|
||||
public static ?string $subnodeClass = 'IPS\cms\Selector\Categories';
|
||||
|
||||
/**
|
||||
* @brief The map of permission columns
|
||||
*/
|
||||
public static $permissionMap = array(
|
||||
public static array $permissionMap = array(
|
||||
'view' => 'view',
|
||||
'read' => 2,
|
||||
'add' => 3,
|
||||
@@ -89,25 +94,25 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
/**
|
||||
* @brief [Node] App for permission index
|
||||
*/
|
||||
public static $permApp = 'cms';
|
||||
public static ?string $permApp = 'cms';
|
||||
|
||||
/**
|
||||
* @brief [Node] Type for permission index
|
||||
*/
|
||||
public static $permType = 'databases';
|
||||
public static ?string $permType = 'databases';
|
||||
|
||||
/**
|
||||
* @brief [Node] Prefix string that is automatically prepended to permission matrix language strings
|
||||
*/
|
||||
public static $permissionLangPrefix = 'perm_content_';
|
||||
public static string $permissionLangPrefix = 'perm_content_';
|
||||
|
||||
/**
|
||||
* @brief [Node] Title prefix. If specified, will look for a language key with "{$titleLangPrefix}_{$id}" as the key
|
||||
*/
|
||||
public static $titleLangPrefix = 'content_db_';
|
||||
public static ?string $titleLangPrefix = 'content_db_';
|
||||
|
||||
/**
|
||||
* @brief [Node] Description suffix. If specified, will look for a language key with "{$titleLangPrefix}_{$id}_{$descriptionLangSuffix}" as the key
|
||||
*/
|
||||
public static $descriptionLangSuffix = 'desc';
|
||||
public static ?string $descriptionLangSuffix = 'desc';
|
||||
}
|
||||
@@ -12,62 +12,68 @@
|
||||
namespace IPS\cms\Templates;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\Db;
|
||||
use IPS\Http\Url\Friendly;
|
||||
use IPS\Patterns\ActiveRecord;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Template Model
|
||||
*/
|
||||
class _Container extends \IPS\Patterns\ActiveRecord
|
||||
class Container extends ActiveRecord
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons = array();
|
||||
protected static array $multitons = array();
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'container_';
|
||||
public static string $databasePrefix = 'container_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_containers';
|
||||
public static ?string $databaseTable = 'cms_containers';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array( 'container_key' );
|
||||
protected static array $databaseIdFields = array( 'container_key' );
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
protected static array $multitonMap = array();
|
||||
|
||||
/**
|
||||
* @brief Have fetched all?
|
||||
*/
|
||||
protected static $gotAll = FALSE;
|
||||
protected static bool $gotAll = FALSE;
|
||||
|
||||
/**
|
||||
* Return all containers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function containers()
|
||||
public static function containers() : array
|
||||
{
|
||||
if ( ! static::$gotAll )
|
||||
{
|
||||
foreach( \IPS\Db::i()->select( '*', static::$databaseTable ) as $container )
|
||||
foreach( Db::i()->select( '*', static::$databaseTable ) as $container )
|
||||
{
|
||||
static::$multitons[ $container['container_id'] ] = static::constructFromData( $container );
|
||||
}
|
||||
@@ -84,7 +90,7 @@ class _Container extends \IPS\Patterns\ActiveRecord
|
||||
* @param string $type Type of container (template_block, page, etc)
|
||||
* @return array of Container objects
|
||||
*/
|
||||
public static function getByType( $type )
|
||||
public static function getByType( string $type ) : array
|
||||
{
|
||||
$return = array();
|
||||
static::containers();
|
||||
@@ -109,18 +115,20 @@ class _Container extends \IPS\Patterns\ActiveRecord
|
||||
* Add a new container
|
||||
*
|
||||
* @param array $container Template Data
|
||||
* @return object \IPS\cms\Templates
|
||||
* @return static
|
||||
*/
|
||||
public static function add( $container )
|
||||
public static function add( array $container ) : static
|
||||
{
|
||||
$newContainer = new static;
|
||||
$newContainer->_new = TRUE;
|
||||
$newContainer->name = $container['name'];
|
||||
$newContainer->type = $container['type'];
|
||||
$newContainer->save();
|
||||
|
||||
/* Create a unique key */
|
||||
if ( empty( $newContainer->key ) )
|
||||
{
|
||||
$newContainer->key = 'template__' . \IPS\Http\Url\Friendly::seoTitle( $newContainer->name ) . '.' . $newContainer->id;
|
||||
$newContainer->key = 'template__' . Friendly::seoTitle( $newContainer->name ) . '.' . $newContainer->id;
|
||||
$newContainer->save();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,39 @@ namespace IPS\cms;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use BadMethodCallException;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use IPS\cms\Pages\Page;
|
||||
use IPS\Data\Store;
|
||||
use IPS\Db;
|
||||
use IPS\Db\Select;
|
||||
use IPS\File;
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\CheckboxSet;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Http\Url\Friendly;
|
||||
use IPS\Http\Url\Internal;
|
||||
use IPS\Member;
|
||||
use IPS\Patterns\ActiveRecord;
|
||||
use IPS\Theme;
|
||||
use OutOfRangeException;
|
||||
use SimpleXMLElement;
|
||||
use UnderflowException;
|
||||
use UnexpectedValueException;
|
||||
use XMLReader;
|
||||
use XMLWriter;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_numeric;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
|
||||
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' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -32,47 +60,47 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*
|
||||
* Updating templates: Update where user_edited is FALSE and user_created is FALSE OR user_created is TRUE and original_group is in master groups
|
||||
*/
|
||||
class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
class Templates extends ActiveRecord
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Prefix
|
||||
*/
|
||||
public static $databasePrefix = 'template_';
|
||||
public static string $databasePrefix = 'template_';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Table
|
||||
*/
|
||||
public static $databaseTable = 'cms_templates';
|
||||
public static ?string $databaseTable = 'cms_templates';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'id';
|
||||
public static string $databaseColumnId = 'id';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database ID Fields
|
||||
*/
|
||||
protected static $databaseIdFields = array( 'template_key', 'template_id' );
|
||||
protected static array $databaseIdFields = array( 'template_key', 'template_id' );
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Map
|
||||
*/
|
||||
protected static $multitonMap = array();
|
||||
protected static array $multitonMap = array();
|
||||
|
||||
/**
|
||||
* @brief Master groups
|
||||
*/
|
||||
protected static $masterGroups = NULL;
|
||||
protected static ?array $masterGroups = NULL;
|
||||
|
||||
/**
|
||||
* @brief Master templates
|
||||
*/
|
||||
protected static $masterTemplates = NULL;
|
||||
protected static ?array $masterTemplates = NULL;
|
||||
|
||||
/**
|
||||
* @brief Retusn all types
|
||||
@@ -110,7 +138,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
const RETURN_ONLY_TEMPLATE = 64;
|
||||
|
||||
/**
|
||||
* @brief Return just contents of cms_templates ignoring IN_DEV and DESIGNERS' MODE
|
||||
* @brief Return just contents of cms_templates ignoring IN_DEV
|
||||
*/
|
||||
const RETURN_DATABASE_ONLY = 128;
|
||||
|
||||
@@ -122,8 +150,8 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* @brief Default database template group names
|
||||
*/
|
||||
public static $databaseDefaults = array(
|
||||
'featured' => 'category_articles',
|
||||
public static array $databaseDefaults = array(
|
||||
'index' => 'category_articles',
|
||||
'form' => 'form',
|
||||
'display' => 'display',
|
||||
'listing' => 'listing',
|
||||
@@ -136,9 +164,9 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
* @param string $group Group to load templates from
|
||||
* @return void
|
||||
*/
|
||||
public static function fixTemplateTags( $group )
|
||||
public static function fixTemplateTags( string $group ) : void
|
||||
{
|
||||
$templates = iterator_to_array( \IPS\Db::i()->select( '*', 'cms_templates', array( array( 'template_group=?', $group ) ) )->setKeyField('template_title') );
|
||||
$templates = iterator_to_array( Db::i()->select( '*', 'cms_templates', array( array( 'template_group=?', $group ) ) )->setKeyField('template_title') );
|
||||
|
||||
foreach( $templates as $template )
|
||||
{
|
||||
@@ -167,18 +195,18 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
if ( isset( $options['app'] ) and $options['app'] == 'cms' and isset( $options['location'] ) and $options['location'] == 'database' and isset( $options['group'] ) and $options['group'] != $template['template_original_group'] )
|
||||
{
|
||||
if ( \in_array( $value, array_keys( $templates ) ) )
|
||||
if ( in_array( $value, array_keys( $templates ) ) )
|
||||
{
|
||||
$options['group'] = $group;
|
||||
|
||||
$replace = '{template="' . $value . '" app="' . $options['app'] . '" location="' . $options['location'] . '" group="' . $options['group'] . '" params="' . ( isset($options['params']) ? $options['params'] : NULL ) . '"}';
|
||||
$replace = '{template="' . $value . '" app="' . $options['app'] . '" location="' . $options['location'] . '" group="' . $options['group'] . '" params="' . ( $options['params'] ?? NULL ) . '"}';
|
||||
$save['template_content'] = str_replace( $matches[$index][0], $replace, $template['template_content'] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $save ) )
|
||||
if ( count( $save ) )
|
||||
{
|
||||
\IPS\Db::i()->update( 'cms_templates', $save, array( 'template_id=?', $template['template_id'] ) );
|
||||
Db::i()->update( 'cms_templates', $save, array( 'template_id=?', $template['template_id'] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,24 +219,24 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
* Overloaded so we can force loading by key by default but still retain the template_id field as the primary key so
|
||||
* save still updates the primary ID.
|
||||
*
|
||||
* @see \IPS\Db::build
|
||||
* @param int|string $id ID
|
||||
* @param string $idField The database column that the $id parameter pertains to (NULL will use static::$databaseColumnId)
|
||||
* @see Db::build
|
||||
* @param int|string|null $id ID
|
||||
* @param string|null $idField The database column that the $id parameter pertains to (NULL will use static::$databaseColumnId)
|
||||
* @param mixed $extraWhereClause Additional where clause(s) (see \IPS\Db::build for details)
|
||||
* @return static
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \OutOfRangeException
|
||||
* @return ActiveRecord|static
|
||||
* @throws InvalidArgumentException
|
||||
* @throws OutOfRangeException
|
||||
*/
|
||||
public static function load( $id, $idField=NULL, $extraWhereClause=NULL )
|
||||
public static function load( int|string|null $id, string $idField=NULL, mixed $extraWhereClause=NULL ): ActiveRecord|static
|
||||
{
|
||||
if ( !\is_numeric( $id ) and $idField === NULL )
|
||||
if ( !is_numeric( $id ) and $idField === NULL )
|
||||
{
|
||||
$idField = 'template_key';
|
||||
}
|
||||
|
||||
if ( !\is_numeric( $id ) and ( \IPS\IN_DEV or \IPS\Theme::designersModeEnabled() ) )
|
||||
if ( !is_numeric( $id ) and \IPS\IN_DEV )
|
||||
{
|
||||
$templates = \IPS\cms\Theme::i()->getRawTemplates( NULL, NULL, NULL, \IPS\cms\Theme::RETURN_AS_OBJECT );
|
||||
$templates = Theme::i()->getAllTemplates( '', '', '', \IPS\cms\Theme::RETURN_AS_OBJECT );
|
||||
|
||||
if ( isset( $templates[ $id ] ) )
|
||||
{
|
||||
@@ -220,7 +248,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
return parent::load( $id, $idField, $extraWhereClause );
|
||||
}
|
||||
catch( \OutOfRangeException $ex )
|
||||
catch( OutOfRangeException $ex )
|
||||
{
|
||||
throw $ex;
|
||||
}
|
||||
@@ -232,7 +260,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
* @param string $name Group name from the database
|
||||
* @return string
|
||||
*/
|
||||
public static function readableGroupName( $name )
|
||||
public static function readableGroupName( string $name ) : string
|
||||
{
|
||||
if ( $name === 'js' )
|
||||
{
|
||||
@@ -256,7 +284,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
if ( static::$masterTemplates === NULL )
|
||||
{
|
||||
static::$masterTemplates = iterator_to_array( \IPS\Db::i()->select( '*, MD5( CONCAT( template_location, \'.\', template_group, \'.\', template_title ) ) as bit_key', 'cms_templates', array( 'template_master=1' ) )->setKeyField( 'bit_key' ) );
|
||||
static::$masterTemplates = iterator_to_array( Db::i()->select( '*, MD5( CONCAT( template_location, \'.\', template_group, \'.\', template_title ) ) as bit_key', 'cms_templates', array( 'template_master=1' ) )->setKeyField( 'bit_key' ) );
|
||||
}
|
||||
|
||||
return static::$masterTemplates;
|
||||
@@ -265,9 +293,9 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* Get the master template version of this template
|
||||
*
|
||||
* @return \IPS\cms\Templates|NULL
|
||||
* @return Templates|null
|
||||
*/
|
||||
public function getMasterOfThis()
|
||||
public function getMasterOfThis(): ?Templates
|
||||
{
|
||||
if ( $this->master )
|
||||
{
|
||||
@@ -275,12 +303,13 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
$key = md5( $this->location . '.'. $this->original_group . '.' . $this->title );
|
||||
if ( \in_array( $key, array_keys( static::getMasterTemplates() ) ) )
|
||||
if ( in_array( $key, array_keys( static::getMasterTemplates() ) ) )
|
||||
{
|
||||
/* @var static Templates */
|
||||
return static::constructFromData( static::$masterTemplates[ $key ] );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,7 +323,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
if ( static::$masterGroups === NULL )
|
||||
{
|
||||
static::$masterGroups = array();
|
||||
foreach( \IPS\Db::i()->select( 'template_group', static::$databaseTable, array( 'template_master=1 and template_original_group=template_group' ), 'template_group ASC', NULL, 'template_group' ) as $template )
|
||||
foreach( Db::i()->select( 'template_group', static::$databaseTable, array( 'template_master=1 and template_original_group=template_group' ), 'template_group ASC', NULL, 'template_group' ) as $template )
|
||||
{
|
||||
static::$masterGroups[ $template ] = $template;
|
||||
}
|
||||
@@ -306,32 +335,28 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* Get all template group
|
||||
*
|
||||
* @param int|constant $returnType Determines the content returned
|
||||
* @param string $returnType Determines the content returned
|
||||
* @return array
|
||||
*/
|
||||
public static function getGroups( $returnType=1 )
|
||||
public static function getGroups( string $returnType ): array
|
||||
{
|
||||
$where = array();
|
||||
$return = array();
|
||||
$locations = NULL;
|
||||
|
||||
if ( \is_string( $returnType ) )
|
||||
|
||||
switch( $returnType )
|
||||
{
|
||||
switch( $returnType )
|
||||
{
|
||||
case 'all':
|
||||
$returnType = self::RETURN_ALL;
|
||||
case 'all':
|
||||
$returnType = self::RETURN_ALL;
|
||||
break;
|
||||
case 'block':
|
||||
$returnType = self::RETURN_BLOCK;
|
||||
case 'block':
|
||||
$returnType = self::RETURN_BLOCK;
|
||||
break;
|
||||
case 'page':
|
||||
$returnType = self::RETURN_PAGE;
|
||||
case 'page':
|
||||
$returnType = self::RETURN_PAGE;
|
||||
break;
|
||||
case 'database':
|
||||
$returnType = self::RETURN_DATABASE;
|
||||
case 'database':
|
||||
$returnType = self::RETURN_DATABASE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $returnType & self::RETURN_ALL )
|
||||
@@ -357,15 +382,15 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
$locations[] = 'database';
|
||||
}
|
||||
|
||||
if ( ! \count( $locations ) )
|
||||
if ( ! count( $locations ) )
|
||||
{
|
||||
throw new \UnexpectedValueException();
|
||||
throw new UnexpectedValueException();
|
||||
}
|
||||
|
||||
$where[] = array( "template_location IN ('" . implode( "','", $locations ) . "')" );
|
||||
}
|
||||
|
||||
foreach( \IPS\Db::i()->select( 'template_group', static::$databaseTable, $where, 'template_group ASC', NULL, 'template_group' ) as $template )
|
||||
foreach( Db::i()->select( 'template_group', static::$databaseTable, $where, 'template_group ASC', NULL, 'template_group' ) as $template )
|
||||
{
|
||||
$return[ $template ] = $template;
|
||||
}
|
||||
@@ -376,16 +401,15 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* Get all templates
|
||||
*
|
||||
* @param int|constant $returnType Determines the content returned
|
||||
* @param int $returnType Determines the content returned
|
||||
* @return array
|
||||
*/
|
||||
public static function getTemplates( $returnType=1 )
|
||||
public static function getTemplates( int $returnType=1 ): array
|
||||
{
|
||||
$where = array();
|
||||
$return = array();
|
||||
$locations = NULL;
|
||||
|
||||
if ( ( \IPS\IN_DEV or \IPS\Theme::designersModeEnabled() ) AND ( $returnType & self::RETURN_DATABASE_AND_IN_DEV ) AND ! ( $returnType & self::RETURN_DATABASE_ONLY ) )
|
||||
if ( ( \IPS\IN_DEV ) AND ( $returnType & self::RETURN_DATABASE_AND_IN_DEV ) AND ! ( $returnType & self::RETURN_DATABASE_ONLY ) )
|
||||
{
|
||||
$flags = \IPS\cms\Theme::RETURN_AS_OBJECT;
|
||||
|
||||
@@ -419,10 +443,10 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
$return = \IPS\cms\Theme::i()->getRawTemplates( 'cms', NULL, NULL, $flags );
|
||||
$return = Theme::i()->getAllTemplates( 'cms', '', '', $flags );
|
||||
}
|
||||
|
||||
if ( ! ( \IPS\IN_DEV or \IPS\Theme::designersModeEnabled() ) OR ( $returnType & self::RETURN_DATABASE_AND_IN_DEV ) OR ( $returnType & self::RETURN_DATABASE_ONLY ) )
|
||||
if ( ! ( \IPS\IN_DEV ) OR ( $returnType & self::RETURN_DATABASE_AND_IN_DEV ) OR ( $returnType & self::RETURN_DATABASE_ONLY ) )
|
||||
{
|
||||
if ( $returnType & self::RETURN_ALL )
|
||||
{
|
||||
@@ -459,15 +483,15 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
$locations[] = 'database';
|
||||
}
|
||||
|
||||
if ( !\count( $locations ) )
|
||||
if ( !count( $locations ) )
|
||||
{
|
||||
throw new \UnexpectedValueException();
|
||||
throw new UnexpectedValueException();
|
||||
}
|
||||
|
||||
$where[] = array( "template_location IN ('" . implode( "','", $locations ) . "')" );
|
||||
}
|
||||
|
||||
foreach ( \IPS\Db::i()->select( '*', static::$databaseTable, $where, 'template_user_edited DESC' ) as $template )
|
||||
foreach ( Db::i()->select( '*', static::$databaseTable, $where, 'template_user_edited DESC' ) as $template )
|
||||
{
|
||||
/* user_edited version is returned first, so only add to the array if the key isn't already in $return */
|
||||
if ( !isset( $return[ $template['template_key'] ] ) )
|
||||
@@ -487,14 +511,14 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
* @param int|string $id ID
|
||||
* @param string $idField The database column that the $id parameter pertains to
|
||||
* @param mixed $extraWhereClause Additional where clause(s)
|
||||
* @return \IPS\Db\Select
|
||||
* @return Select
|
||||
*/
|
||||
protected static function constructLoadQuery( $id, $idField, $extraWhereClause )
|
||||
protected static function constructLoadQuery( int|string $id, string $idField, mixed $extraWhereClause ): Select
|
||||
{
|
||||
$where = array( array( $idField . '=?', $id ) );
|
||||
if( $extraWhereClause !== NULL )
|
||||
{
|
||||
if ( !\is_array( $extraWhereClause ) or !\is_array( $extraWhereClause[0] ) )
|
||||
if ( !is_array( $extraWhereClause ) or !is_array( $extraWhereClause[0] ) )
|
||||
{
|
||||
$extraWhereClause = array( $extraWhereClause );
|
||||
}
|
||||
@@ -510,13 +534,13 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
* @param array $templates Template data from the database
|
||||
* @return array
|
||||
*/
|
||||
public static function buildTree( $templates )
|
||||
public static function buildTree( array $templates ) : array
|
||||
{
|
||||
$return = array();
|
||||
|
||||
foreach( $templates as $id => $template )
|
||||
{
|
||||
if ( ! ( $template->location === 'database' and ! \IPS\Member::loggedIn()->hasAcpRestriction( 'cms', 'databases', 'databases_use' ) ) )
|
||||
if ( ! ( $template->location === 'database' and ! Member::loggedIn()->hasAcpRestriction( 'cms', 'databases', 'databases_use' ) ) )
|
||||
{
|
||||
$return[$template->location][$template->group][$template->key] = $template;
|
||||
}
|
||||
@@ -529,9 +553,9 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
* Add a new template
|
||||
*
|
||||
* @param array $template Template Data
|
||||
* @return object \IPS\cms\Templates
|
||||
* @return static
|
||||
*/
|
||||
public static function add( $template )
|
||||
public static function add( array $template ) : static
|
||||
{
|
||||
$newTemplate = new static;
|
||||
|
||||
@@ -548,7 +572,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
$newTemplate->save();
|
||||
|
||||
/* Create a unique key */
|
||||
$newTemplate->key = $newTemplate->location . '_' . \IPS\Http\Url\Friendly::seoTitle( $newTemplate->title ) . '_' . $newTemplate->id;
|
||||
$newTemplate->key = $newTemplate->location . '_' . Friendly::seoTitle( $newTemplate->title ) . '_' . $newTemplate->id;
|
||||
|
||||
/* Make sure there's no double __ in there */
|
||||
foreach( array( 'group', 'title', 'key' ) as $field )
|
||||
@@ -569,18 +593,18 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteCompiledFiles()
|
||||
public static function deleteCompiledFiles() : void
|
||||
{
|
||||
foreach( \IPS\Db::i()->select( '*', 'cms_templates', array( 'template_file_object IS NOT NULL' ) ) as $template )
|
||||
foreach( Db::i()->select( '*', 'cms_templates', array( 'template_file_object IS NOT NULL' ) ) as $template )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'core_Theme', $template['template_file_object'] )->delete();
|
||||
File::get( 'core_Theme', $template['template_file_object'] )->delete();
|
||||
}
|
||||
catch( \Exception $ex ) { }
|
||||
catch( Exception $ex ) { }
|
||||
}
|
||||
|
||||
\IPS\Db::i()->update( 'cms_templates', array( 'template_file_object' => NULL ) );
|
||||
Db::i()->update( 'cms_templates', array( 'template_file_object' => NULL ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -588,15 +612,15 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @param bool $appPluginBuild TRUE to customise form for app/plugin builds
|
||||
* @param array $preSelected Array of default values for checkboxSet
|
||||
* @return \IPS\Helpers\Form
|
||||
* @return Form
|
||||
*/
|
||||
public static function exportForm( bool $appPluginBuild=FALSE, array $preSelected=array() ): \IPS\Helpers\Form
|
||||
public static function exportForm( bool $appPluginBuild=FALSE, array $preSelected=array() ): Form
|
||||
{
|
||||
$form = new \IPS\Helpers\Form( 'form', $appPluginBuild ? 'save' : 'next' );
|
||||
$form = new Form( 'form', $appPluginBuild ? 'save' : 'next' );
|
||||
if( !$appPluginBuild )
|
||||
{
|
||||
$form->addMessage( 'cms_templates_export_description', 'ipsMessage ipsMessage_information' );
|
||||
$form->addButton( 'cms_templates_export_return', 'link', \IPS\Http\Url::internal( "app=cms&module=pages&controller=templates" ) );
|
||||
$form->addButton( 'cms_templates_export_return', 'link', Url::internal( "app=cms&module=pages&controller=templates" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -605,9 +629,9 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
$templates = [];
|
||||
|
||||
foreach( \IPS\cms\Templates::getTemplates( \IPS\cms\Templates::RETURN_DATABASE_ONLY + \IPS\cms\Templates::RETURN_ALL ) as $template )
|
||||
foreach(Templates::getTemplates( Templates::RETURN_DATABASE_ONLY + Templates::RETURN_ALL ) as $template )
|
||||
{
|
||||
$title = \IPS\cms\Templates::readableGroupName( $template->group );
|
||||
$title = Templates::readableGroupName( $template->group );
|
||||
|
||||
if ( $template->location === 'database' )
|
||||
{
|
||||
@@ -626,7 +650,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
foreach( $templates as $location => $data )
|
||||
{
|
||||
$defaultValue = $preSelected[ 'templates_' . $location ] ?? FALSE;
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'templates_' . $location, $defaultValue, FALSE, array( 'options' => $data ) ) );
|
||||
$form->add( new CheckboxSet( 'templates_' . $location, $defaultValue, FALSE, array( 'options' => $data ) ) );
|
||||
}
|
||||
|
||||
return $form;
|
||||
@@ -636,23 +660,23 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
* Export requested templates as XML
|
||||
*
|
||||
* @param array $values Checkbox set values
|
||||
* @return \XMLWriter|null
|
||||
* @return XMLWriter|null
|
||||
*/
|
||||
public static function exportAsXml( array $values ):? \XMLWriter
|
||||
public static function exportAsXml( array $values ):? XMLWriter
|
||||
{
|
||||
$templates = array();
|
||||
foreach( \IPS\cms\Templates::getTemplates( \IPS\cms\Templates::RETURN_DATABASE_ONLY + \IPS\cms\Templates::RETURN_ALL ) as $template )
|
||||
foreach(Templates::getTemplates( Templates::RETURN_DATABASE_ONLY + Templates::RETURN_ALL ) as $template )
|
||||
{
|
||||
$templates[ $template->location ][ $template->group ] = $template->title;
|
||||
}
|
||||
$exportTemplates = array();
|
||||
foreach ( $templates as $location => $data )
|
||||
foreach ( $templates as $location => $outerData )
|
||||
{
|
||||
if ( isset( $values[ 'templates_' . $location ] ) and \count( $values[ 'templates_' . $location ] ) )
|
||||
if ( isset( $values[ 'templates_' . $location ] ) and count( $values[ 'templates_' . $location ] ) )
|
||||
{
|
||||
if ( $location === 'database' )
|
||||
{
|
||||
$tmp = \IPS\cms\Theme::i()->getRawTemplates( 'cms', array( 'database' ), array_values( $values[ 'templates_' . $location ] ), \IPS\cms\Theme::RETURN_DATABASE_ONLY + \IPS\cms\Theme::RETURN_ALL );
|
||||
$tmp = \IPS\cms\Theme::i()->getAllTemplates( 'cms', array( 'database' ), array_values( $values[ 'templates_' . $location ] ), \IPS\cms\Theme::RETURN_DATABASE_ONLY + Theme::RETURN_ALL );
|
||||
|
||||
foreach ( $tmp['cms'] as $loc => $data )
|
||||
{
|
||||
@@ -670,7 +694,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
else
|
||||
{
|
||||
$tmp = \IPS\cms\Theme::i()->getRawTemplates( 'cms', array( $location ), NULL, \IPS\cms\Theme::RETURN_DATABASE_ONLY + \IPS\cms\Theme::RETURN_ALL );
|
||||
$tmp = \IPS\cms\Theme::i()->getAllTemplates( 'cms', array( $location ), [], \IPS\cms\Theme::RETURN_DATABASE_ONLY + Theme::RETURN_ALL );
|
||||
|
||||
foreach ( $tmp['cms'] as $loc => $data )
|
||||
{
|
||||
@@ -680,7 +704,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
foreach ( $tdata as $tkey => $tkdata )
|
||||
{
|
||||
if ( \in_array( $tkey, $values[ 'templates_' . $location ] ) )
|
||||
if ( in_array( $tkey, $values[ 'templates_' . $location ] ) )
|
||||
{
|
||||
$exportTemplates['cms'][ $location ][ $key ][ $tkey ] = $tkdata;
|
||||
}
|
||||
@@ -692,10 +716,10 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $exportTemplates ) )
|
||||
if ( count( $exportTemplates ) )
|
||||
{
|
||||
/* Init */
|
||||
$xml = new \XMLWriter;
|
||||
$xml = new XMLWriter;
|
||||
$xml->openMemory();
|
||||
$xml->setIndent( TRUE );
|
||||
$xml->startDocument( '1.0', 'UTF-8' );
|
||||
@@ -716,7 +740,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
foreach ( $templateData as $k => $v )
|
||||
{
|
||||
if ( !\in_array( \substr( $k, 9 ), array( 'content', 'params' ) ) )
|
||||
if ( !in_array( substr( $k, 9 ), array( 'content', 'params' ) ) )
|
||||
{
|
||||
$xml->startAttribute( $k );
|
||||
$xml->text( $v );
|
||||
@@ -730,7 +754,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
if ( isset( $templateData[ $field ] ) )
|
||||
{
|
||||
$xml->startElement( $field );
|
||||
if ( preg_match( '/<|>|&/', $templateData[ $field ] ) )
|
||||
if ( preg_match( '/[<>&]/', $templateData[ $field ] ) )
|
||||
{
|
||||
$xml->writeCData( str_replace( ']]>', ']]]]><![CDATA[>', $templateData[ $field ] ) );
|
||||
}
|
||||
@@ -763,7 +787,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSuitableForCustomWrapper()
|
||||
public function isSuitableForCustomWrapper(): bool
|
||||
{
|
||||
if ( $this->location == 'page' and preg_match( '#<html([^>]+?)?>#', $this->content ) )
|
||||
{
|
||||
@@ -781,7 +805,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSuitableForBuilderWrapper()
|
||||
public function isSuitableForBuilderWrapper() : bool
|
||||
{
|
||||
if ( $this->location == 'page' and mb_stristr( $this->content, '{template="widgetContainer"' ) )
|
||||
{
|
||||
@@ -812,13 +836,11 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* Import User Template XML
|
||||
*
|
||||
* @param string $filePath Path to XML file
|
||||
* @param string $fileContents XML Contents as string
|
||||
* @throws \UnexpectedValueException
|
||||
* @throws \BadMethodCallException
|
||||
* @return \IPS\Http\Url\Internal|null
|
||||
* @param string|null $filePath Path to XML file
|
||||
* @param string|null $fileContents XML Contents as string
|
||||
* @return Internal|null
|
||||
*/
|
||||
public static function importUserTemplateXml( string $filePath=NULL, string $fileContents=NULL ):? \IPS\Http\Url\Internal
|
||||
public static function importUserTemplateXml( string $filePath=NULL, string $fileContents=NULL ):? Internal
|
||||
{
|
||||
/* Open XML file */
|
||||
if( $filePath )
|
||||
@@ -831,28 +853,28 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
if( !$xml->xml( $fileContents, NULL, LIBXML_NONET ) )
|
||||
{
|
||||
throw \UnexpectedValueException('bad_xml_string');
|
||||
throw new UnexpectedValueException('bad_xml_string');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \BadMethodCallException('path_or_data_missing');
|
||||
throw new BadMethodCallException('path_or_data_missing');
|
||||
}
|
||||
|
||||
$conflictKey = md5( microtime() );
|
||||
|
||||
$templates = \IPS\cms\Theme::i()->getRawTemplates( 'cms', NULL, NULL, \IPS\cms\Theme::RETURN_DATABASE_ONLY + \IPS\cms\Theme::RETURN_ALL );
|
||||
$templates = Theme::i()->getAllTemplates( 'cms', NULL, NULL, \IPS\cms\Theme::RETURN_DATABASE_ONLY + Theme::RETURN_ALL );
|
||||
|
||||
if ( ! @$xml->read() )
|
||||
{
|
||||
throw new \UnexpectedValueException('xml_file_bad_format');
|
||||
throw new UnexpectedValueException('xml_file_bad_format');
|
||||
}
|
||||
$xml->read();
|
||||
while ( $xml->read() )
|
||||
{
|
||||
if ( $xml->name === 'template' )
|
||||
{
|
||||
$node = new \SimpleXMLElement( $xml->readOuterXML() );
|
||||
$node = new SimpleXMLElement( $xml->readOuterXML() );
|
||||
$attrs = array();
|
||||
|
||||
foreach( $node->attributes() as $k => $v )
|
||||
@@ -864,7 +886,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
foreach( $node->children() as $k => $v )
|
||||
{
|
||||
$tryJson = json_decode( $v, TRUE );
|
||||
$attrs[ $k ] = ( $tryJson ) ? $tryJson : (string) $v;
|
||||
$attrs[ $k ] = ( $tryJson ) ?: (string) $v;
|
||||
}
|
||||
|
||||
|
||||
@@ -889,7 +911,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
if ( $exists['template_master'] )
|
||||
{
|
||||
/* This is a master template bit, so it's totes fine to create a new one, as it will 'overload' this one */
|
||||
$obj = new \IPS\cms\Templates;
|
||||
$obj = new Templates;
|
||||
$obj->location = $attrs['template_location'];
|
||||
$obj->group = $attrs['template_group'];
|
||||
$obj->title = $attrs['template_title'];
|
||||
@@ -909,7 +931,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
if ( $exists['template_user_edited'] )
|
||||
{
|
||||
/* User has edited this, so we should just log a conflict... */
|
||||
\IPS\Db::i()->insert( 'cms_template_conflicts', array(
|
||||
Db::i()->insert( 'cms_template_conflicts', array(
|
||||
'conflict_key' => $conflictKey,
|
||||
'conflict_item_id' => $exists['template_id'],
|
||||
'conflict_type' => $attrs['template_type'],
|
||||
@@ -927,18 +949,18 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
/* Not edited, so just update */
|
||||
try
|
||||
{
|
||||
$template = \IPS\cms\Templates::load( $exists['template_key'], 'template_key' );
|
||||
$template = Templates::load( $exists['template_key'], 'template_key' );
|
||||
$template->params = $attrs['template_params'];
|
||||
$template->content = $attrs['template_content'];
|
||||
$template->save();
|
||||
}
|
||||
catch( \Exception $e ) { }
|
||||
catch( Exception $e ) { }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is an overloaded master template, so just update */
|
||||
$template = \IPS\cms\Templates::load( $exists['template_key'], 'template_key' );
|
||||
$template = Templates::load( $exists['template_key'], 'template_key' );
|
||||
$template->user_edited = 1;
|
||||
$template->params = $attrs['template_params'];
|
||||
$template->content = $attrs['template_content'];
|
||||
@@ -949,7 +971,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
if ( $exists === NULL )
|
||||
{
|
||||
$obj = new \IPS\cms\Templates;
|
||||
$obj = new Templates;
|
||||
$obj->location = $attrs['template_location'];
|
||||
$obj->group = $attrs['template_group'];
|
||||
$obj->title = $attrs['template_title'];
|
||||
@@ -968,9 +990,9 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
try
|
||||
{
|
||||
$check = \IPS\Db::i()->select( 'template_id', 'cms_templates', array( 'template_key=?', $attrs['template_key'] ) )->first();
|
||||
$check = Db::i()->select( 'template_id', 'cms_templates', array( 'template_key=?', $attrs['template_key'] ) )->first();
|
||||
|
||||
if ( $check['template_master'] == 1 and \in_array( $attrs['template_group'], \IPS\cms\Templates::getMasterGroups() ) )
|
||||
if ( $check['template_master'] == 1 and in_array( $attrs['template_group'], Templates::getMasterGroups() ) )
|
||||
{
|
||||
/* it's OK to overload this with a user edited version */
|
||||
$obj->user_created = 0;
|
||||
@@ -979,14 +1001,14 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
$key = $attrs['template_key'];
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e )
|
||||
catch( UnderflowException $e )
|
||||
{
|
||||
/* It doesn't exist! */
|
||||
$key = $attrs['template_key'];
|
||||
}
|
||||
}
|
||||
|
||||
$obj->key = ( $key ) ? $key : $obj->location . \IPS\Http\Url\Friendly::seoTitle( $obj->group ) . '_' . \IPS\Http\Url\Friendly::seoTitle( $obj->title ) . '_' . $obj->id;
|
||||
$obj->key = ( $key ) ?: $obj->location . Friendly::seoTitle( $obj->group ) . '_' . Friendly::seoTitle( $obj->title ) . '_' . $obj->id;
|
||||
$obj->save();
|
||||
}
|
||||
}
|
||||
@@ -996,10 +1018,10 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
/* Check for conflicts */
|
||||
$conflicts = \IPS\Db::i()->select( 'count(*)', 'cms_template_conflicts', array( 'conflict_key=?', $conflictKey ) )->first();
|
||||
$conflicts = Db::i()->select( 'count(*)', 'cms_template_conflicts', array( 'conflict_key=?', $conflictKey ) )->first();
|
||||
if( $conflicts )
|
||||
{
|
||||
return \IPS\Http\Url::internal( 'app=cms&module=pages&controller=templates&do=conflicts&key=' . $conflictKey );
|
||||
return Url::internal( 'app=cms&module=pages&controller=templates&do=conflicts&key=' . $conflictKey );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -1014,7 +1036,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
* @param boolean $update If updating, files written as master templates
|
||||
* @return bool Rows imported (true) or none imported (false)
|
||||
*/
|
||||
public static function importXml( $file, $offset=NULL, $limit=NULL, $update=TRUE )
|
||||
public static function importXml( string $file, ?int $offset=NULL, ?int $limit=NULL, bool $update=TRUE ) : bool
|
||||
{
|
||||
$i = 0;
|
||||
$worked = false;
|
||||
@@ -1026,8 +1048,8 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
if ( $update === TRUE )
|
||||
{
|
||||
\IPS\Db::i()->delete( 'cms_templates', array( 'template_master=1' ) );
|
||||
\IPS\cms\Theme::deleteCompiledTemplate( 'cms' );
|
||||
Db::i()->delete( 'cms_templates', array( 'template_master=1' ) );
|
||||
Theme::deleteCompiledTemplate( 'cms' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1037,7 +1059,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
while( $xml->read() )
|
||||
{
|
||||
if( $xml->nodeType != \XMLReader::ELEMENT )
|
||||
if( $xml->nodeType != XMLReader::ELEMENT )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1067,7 +1089,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
$save[ 'template_' . $field ] = $xml->getAttribute( 'template_' . $field );
|
||||
}
|
||||
|
||||
\IPS\Db::i()->insert( 'cms_templates', $save );
|
||||
Db::i()->insert( 'cms_templates', $save );
|
||||
$worked = true;
|
||||
}
|
||||
|
||||
@@ -1091,10 +1113,10 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateAllInheritedMasterTemplates()
|
||||
public static function updateAllInheritedMasterTemplates() : void
|
||||
{
|
||||
/* Update existing template bits */
|
||||
foreach( \IPS\Db::i()->select( '*', 'cms_templates', array( 'template_master=0 and template_user_created=1 and template_user_edited=0' ) ) as $key => $template )
|
||||
foreach( Db::i()->select( '*', 'cms_templates', array( 'template_master=0 and template_user_created=1 and template_user_edited=0' ) ) as $key => $template )
|
||||
{
|
||||
$obj = static::constructFromData( $template );
|
||||
|
||||
@@ -1114,14 +1136,14 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
$masterByGroup[ $template['template_group'] ][ $template['template_title'] ] = $template;
|
||||
}
|
||||
|
||||
$customGroups = iterator_to_array( \IPS\Db::i()->select( 'template_group, MIN(template_original_group) as template_original_group', static::$databaseTable, array( 'template_master=0 and template_original_group != template_group' ), 'template_group ASC', NULL, 'template_group' )->setKeyField('template_group')->setValueField('template_original_group') );
|
||||
$customGroups = iterator_to_array( Db::i()->select( 'template_group, MIN(template_original_group) as template_original_group', static::$databaseTable, array( 'template_master=0 and template_original_group != template_group' ), 'template_group ASC', NULL, 'template_group' )->setKeyField('template_group')->setValueField('template_original_group') );
|
||||
|
||||
foreach( $customGroups as $customGroup => $masterGroup )
|
||||
{
|
||||
if ( $masterGroup )
|
||||
{
|
||||
$groupByGroup = array();
|
||||
foreach ( \IPS\Db::i()->select( '*', static::$databaseTable, array('template_group=?', $customGroup) ) as $bit )
|
||||
foreach ( Db::i()->select( '*', static::$databaseTable, array('template_group=?', $customGroup) ) as $bit )
|
||||
{
|
||||
$groupByGroup[$bit['template_title']] = $bit;
|
||||
}
|
||||
@@ -1129,7 +1151,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
/* Now, see if we're missing any */
|
||||
foreach( $masterByGroup[ $masterGroup ] as $title => $template )
|
||||
{
|
||||
if ( ! \in_array( $title, array_keys( $groupByGroup ) ) )
|
||||
if ( ! in_array( $title, array_keys( $groupByGroup ) ) )
|
||||
{
|
||||
$copy = $template;
|
||||
foreach( array( 'template_id', 'template_file_object', 'bit_key' ) as $field )
|
||||
@@ -1142,7 +1164,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
$copy['template_group'] = $customGroup;
|
||||
$copy['template_key'] = $copy['template_location'] . '_' . $customGroup . '_' . $title;
|
||||
|
||||
\IPS\Db::i()->insert( 'cms_templates', $copy );
|
||||
Db::i()->insert( 'cms_templates', $copy );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1155,13 +1177,13 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
public function delete(): void
|
||||
{
|
||||
\IPS\cms\Theme::deleteCompiledTemplate( 'cms', $this->location, $this->group );
|
||||
Theme::deleteCompiledTemplate( 'cms', $this->location, $this->group );
|
||||
|
||||
if ( $this->user_created )
|
||||
{
|
||||
\IPS\Db::i()->delete( 'cms_templates', array( 'template_key=? AND template_user_created=?', $this->key, 1 ) );
|
||||
Db::i()->delete( 'cms_templates', array( 'template_key=? AND template_user_created=?', $this->key, 1 ) );
|
||||
|
||||
if ( isset( static::$multitons[ $this->id ] ) )
|
||||
{
|
||||
@@ -1182,7 +1204,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
if ( $this->user_edited )
|
||||
{
|
||||
\IPS\Db::i()->delete( 'cms_templates', array( 'template_key=? AND template_user_edited=?', $this->key, 1 ) );
|
||||
Db::i()->delete( 'cms_templates', array( 'template_key=? AND template_user_edited=?', $this->key, 1 ) );
|
||||
|
||||
if ( isset( static::$multitons[ $this->id ] ) )
|
||||
{
|
||||
@@ -1201,7 +1223,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \OutOfRangeException('CANNOT_DELETE');
|
||||
throw new OutOfRangeException('CANNOT_DELETE');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1211,9 +1233,9 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get__inherited()
|
||||
public function get__inherited() : string
|
||||
{
|
||||
if ( $this->user_created and ! \in_array( $this->original_group, static::getMasterGroups() ) )
|
||||
if ( $this->user_created and ! in_array( $this->original_group, static::getMasterGroups() ) )
|
||||
{
|
||||
return 'custom';
|
||||
}
|
||||
@@ -1234,7 +1256,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get__file_object()
|
||||
public function get__file_object(): string
|
||||
{
|
||||
if ( ! $this->file_object )
|
||||
{
|
||||
@@ -1244,12 +1266,12 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
if ( $this->type != 'js' and ( mb_stristr( $this->content, "{block=" ) or mb_stristr( $this->content, "{{if" ) or mb_stristr( $this->content, "{media=" ) ) )
|
||||
{
|
||||
$functionName = 'css_' . mt_rand();
|
||||
\IPS\Theme::makeProcessFunction( str_replace( '\\', '\\\\', $content ), $functionName );
|
||||
Theme::makeProcessFunction( str_replace( '\\', '\\\\', $content ), $functionName );
|
||||
$functionName = "IPS\Theme\\{$functionName}";
|
||||
$content = $functionName();
|
||||
}
|
||||
|
||||
$this->file_object = (string) \IPS\File::create( 'cms_Pages', $this->title, $content ?: ' ', 'page_objects', TRUE );
|
||||
$this->file_object = (string) File::create( 'cms_Pages', $this->title, $content ?: ' ', 'page_objects', TRUE );
|
||||
parent::save(); # Go to parent save to prevent $this->save() from wiping file objects
|
||||
}
|
||||
|
||||
@@ -1261,22 +1283,22 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save()
|
||||
public function save(): void
|
||||
{
|
||||
/* Trash file object if appropriate */
|
||||
if ( $this->file_object )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'cms_Pages', $this->file_object )->delete();
|
||||
File::get( 'cms_Pages', $this->file_object )->delete();
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
catch ( Exception $e )
|
||||
{
|
||||
/* Just to be sure nothing is throw, we don't care too much if it's not deleted */
|
||||
}
|
||||
|
||||
/* Trash all cached page file objects too */
|
||||
\IPS\cms\Pages\Page::deleteCachedIncludes( $this->file_object );
|
||||
Page::deleteCachedIncludes( $this->file_object );
|
||||
|
||||
$this->file_object = NULL;
|
||||
}
|
||||
@@ -1284,7 +1306,7 @@ class _Templates extends \IPS\Patterns\ActiveRecord
|
||||
parent::save();
|
||||
|
||||
/* Clear store */
|
||||
$key = \strtolower( 'template_cms_' . \IPS\cms\Theme::makeBuiltTemplateLookupHash( 'cms', $this->location, $this->group ) . '_' . $this->group );
|
||||
unset( \IPS\Data\Store::i()->$key );
|
||||
$key = strtolower( 'template_cms_' . Theme::makeBuiltTemplateLookupHash( 'cms', $this->location, $this->group ) . '_' . $this->group );
|
||||
unset( Store::i()->$key );
|
||||
}
|
||||
}
|
||||
@@ -12,23 +12,34 @@
|
||||
namespace IPS\cms\Theme\Advanced;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DirectoryIterator;
|
||||
use IPS\cms\Theme as SystemTheme;
|
||||
use IPS\Data\Store;
|
||||
use IPS\Db;
|
||||
use IPS\Theme\Dev\Theme as DevTheme;
|
||||
use IPS\Theme\Setup\Theme as SetupTheme;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function strtolower;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Designer's Mode Theme
|
||||
*/
|
||||
class _Theme extends \IPS\cms\Theme
|
||||
class Theme extends SystemTheme
|
||||
{
|
||||
/**
|
||||
* Get currently logged in member's theme
|
||||
*
|
||||
* @return static
|
||||
* @return static|SetupTheme|DevTheme
|
||||
*/
|
||||
public static function i()
|
||||
public static function i() : static|SetupTheme|DevTheme
|
||||
{
|
||||
return new self;
|
||||
}
|
||||
@@ -38,9 +49,9 @@ class _Theme extends \IPS\cms\Theme
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function export()
|
||||
public static function export() : void
|
||||
{
|
||||
foreach( \IPS\Db::i()->select(
|
||||
foreach( Db::i()->select(
|
||||
"*, MD5( CONCAT(template_location, ',', template_group, ',', template_title) ) as bit_key",
|
||||
'cms_templates',
|
||||
NULL,
|
||||
@@ -56,20 +67,20 @@ class _Theme extends \IPS\cms\Theme
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function import()
|
||||
public static function import() : void
|
||||
{
|
||||
$seen = array();
|
||||
|
||||
foreach( array( 'block', 'page', 'database', 'js', 'css' ) as $location )
|
||||
{
|
||||
$templates = iterator_to_array( \IPS\Db::i()->select(
|
||||
$templates = iterator_to_array( Db::i()->select(
|
||||
"*, MD5( CONCAT(template_location, ',', template_group, ',', template_title) ) as bit_key",
|
||||
'cms_templates',
|
||||
array( 'template_location=?', $location ),
|
||||
'template_user_edited ASC' /* Ensure we get edited versions, not the master version if one exists */
|
||||
)->setKeyField('bit_key') );
|
||||
|
||||
$master = iterator_to_array( \IPS\Db::i()->select(
|
||||
$master = iterator_to_array( Db::i()->select(
|
||||
"*, MD5( CONCAT(template_location, ',', template_group, ',', template_title) ) as bit_key",
|
||||
'cms_templates',
|
||||
array( 'template_master=1 and template_location=?', $location )
|
||||
@@ -79,7 +90,7 @@ class _Theme extends \IPS\cms\Theme
|
||||
|
||||
if ( is_dir( $path ) )
|
||||
{
|
||||
foreach( new \DirectoryIterator( $path ) as $group )
|
||||
foreach( new DirectoryIterator( $path ) as $group )
|
||||
{
|
||||
if ( $group->isDot() || mb_substr( $group->getFilename(), 0, 1 ) === '.' )
|
||||
{
|
||||
@@ -90,7 +101,7 @@ class _Theme extends \IPS\cms\Theme
|
||||
{
|
||||
$seen = array();
|
||||
|
||||
foreach( new \DirectoryIterator( $path . '/' . $group->getFilename() ) as $file )
|
||||
foreach( new DirectoryIterator( $path . '/' . $group->getFilename() ) as $file )
|
||||
{
|
||||
if ( $file->isDot() || ( mb_substr( $file->getFilename(), -6 ) !== '.phtml' and mb_substr( $file->getFilename(), -3 ) !== '.js' and mb_substr( $file->getFilename(), -4 ) !== '.css' ) )
|
||||
{
|
||||
@@ -136,7 +147,7 @@ class _Theme extends \IPS\cms\Theme
|
||||
if ( ! $templates[ $key ]['template_master'] )
|
||||
{
|
||||
$added = TRUE;
|
||||
\IPS\Db::i()->update( 'cms_templates', array(
|
||||
Db::i()->update( 'cms_templates', array(
|
||||
'template_content' => $html,
|
||||
'template_params' => ( isset($params[1]) ) ? $params[1] : '',
|
||||
'template_file_object' => NULL
|
||||
@@ -158,7 +169,7 @@ class _Theme extends \IPS\cms\Theme
|
||||
}
|
||||
|
||||
/* New template */
|
||||
\IPS\Db::i()->insert( 'cms_templates', array(
|
||||
Db::i()->insert( 'cms_templates', array(
|
||||
'template_key' => $location . '_' . $group->getFilename() . '_' . $title,
|
||||
'template_title' => $title,
|
||||
'template_desc' => '',
|
||||
@@ -176,19 +187,19 @@ class _Theme extends \IPS\cms\Theme
|
||||
}
|
||||
|
||||
/* remove compiled version */
|
||||
$key = \strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( 'cms', $location, $group->getFilename() ) . '_' . static::cleanGroupName( $group->getFilename() ) );
|
||||
$key = strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( 'cms', $location, $group->getFilename() ) . '_' . static::cleanGroupName( $group->getFilename() ) );
|
||||
|
||||
if ( isset( \IPS\Data\Store::i()->$key ) )
|
||||
if ( isset( Store::i()->$key ) )
|
||||
{
|
||||
unset(\IPS\Data\Store::i()->$key);
|
||||
unset(Store::i()->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove any templates we've not imported for this location/group */
|
||||
if ( \count( $seen ) )
|
||||
if ( count( $seen ) )
|
||||
{
|
||||
\IPS\Db::i()->delete( 'cms_templates', array('template_master=0 and template_location=? and template_group=? and ' . \IPS\Db::i()->in( 'template_title', $seen, TRUE ), $location, $group->getFilename() ) );
|
||||
Db::i()->delete( 'cms_templates', array('template_master=0 and template_location=? and template_group=? and ' . Db::i()->in( 'template_title', $seen, TRUE ), $location, $group->getFilename() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,7 +213,7 @@ class _Theme extends \IPS\cms\Theme
|
||||
* @param string|null $path Path or Filename
|
||||
* @return string
|
||||
*/
|
||||
protected static function _getHtmlPath( $app, $location=null, $path=null )
|
||||
protected static function _getHtmlPath( string $app, ?string $location=null, ?string $path=null ) : string
|
||||
{
|
||||
return rtrim( \IPS\ROOT_PATH . "/themes/cms/{$location}/{$path}", '/' ) . '/';
|
||||
}
|
||||
|
||||
@@ -12,31 +12,40 @@
|
||||
namespace IPS\cms\Theme;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use BadMethodCallException;
|
||||
use IPS\cms\Theme;
|
||||
use IPS\Db;
|
||||
use IPS\Theme\Template as ThemeTemplate;
|
||||
use UnderflowException;
|
||||
use function defined;
|
||||
use function function_exists;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Template Class for IN_DEV mode
|
||||
*/
|
||||
class _Template extends \IPS\Theme\Template
|
||||
class Template extends ThemeTemplate
|
||||
{
|
||||
/**
|
||||
* @brief Source Folder
|
||||
*/
|
||||
public $sourceFolder = NULL;
|
||||
|
||||
public ?string $sourceFolder = NULL;
|
||||
|
||||
/**
|
||||
* Extract the data object from the param tag
|
||||
* @note We always assume the tag is in the format of <ips:template parameters="" data="" />
|
||||
*
|
||||
* @param string $tag <ips:template> tag
|
||||
* @param string $key Data key to fetch
|
||||
* @return null|array
|
||||
* @param string $tag <ips:template> tag
|
||||
* @param string $key Data key to fetch
|
||||
* @return string|null
|
||||
*/
|
||||
public static function extractDataFromTag( $tag, $key="data" )
|
||||
public static function extractDataFromTag( string $tag, string $key="data" ): ?string
|
||||
{
|
||||
if ( ! preg_match( '/^<ips:template parameters="(.+?)?"([^>]+?)>(\r\n?|\n)/', $tag, $matches ) )
|
||||
{
|
||||
@@ -55,10 +64,10 @@ class _Template extends \IPS\Theme\Template
|
||||
* Extract the data object from the param tag
|
||||
* @note We always assume the tag is in the format of <ips:template parameters="" data="" />
|
||||
*
|
||||
* @param string $tag <ips:template> tag
|
||||
* @return null|array
|
||||
* @param string $tag <ips:template> tag
|
||||
* @return null|string
|
||||
*/
|
||||
public static function extractParamsFromTag( $tag )
|
||||
public static function extractParamsFromTag( string $tag ): ?string
|
||||
{
|
||||
if ( ! preg_match( '/^<ips:template parameters="(.+?)?"([^>]+?)>(\r\n?|\n)/', $tag, $matches ) )
|
||||
{
|
||||
@@ -76,18 +85,14 @@ class _Template extends \IPS\Theme\Template
|
||||
* @param string $templateName Template Name
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $app, $templateLocation, $templateName )
|
||||
public function __construct( string $app, string $templateLocation, string $templateName )
|
||||
{
|
||||
parent::__construct( $app, $templateLocation, $templateName );
|
||||
$this->app = $app;
|
||||
$this->templateLocation = $templateLocation;
|
||||
$this->templateName = $templateName;
|
||||
|
||||
if ( \IPS\Theme::designersModeEnabled() )
|
||||
{
|
||||
$this->sourceFolder = \IPS\ROOT_PATH . "/themes/cms/{$templateLocation}/{$templateName}/";
|
||||
}
|
||||
else if ( \IPS\IN_DEV )
|
||||
if ( \IPS\IN_DEV )
|
||||
{
|
||||
$this->sourceFolder = \IPS\ROOT_PATH . "/applications/{$app}/dev/html/{$templateLocation}/{$templateName}/";
|
||||
}
|
||||
@@ -96,36 +101,35 @@ class _Template extends \IPS\Theme\Template
|
||||
/**
|
||||
* Magic Method: Call Template Bit
|
||||
*
|
||||
* @param string $bit Template Bit Name
|
||||
* @param array $params Parameters
|
||||
* @return string
|
||||
* @param string $bit Template Bit Name
|
||||
* @param array $params Parameters
|
||||
*/
|
||||
public function __call( $bit, $params )
|
||||
public function __call( string $bit, array $params )
|
||||
{
|
||||
if ( \IPS\IN_DEV or \IPS\Theme::designersModeEnabled() )
|
||||
if ( \IPS\IN_DEV )
|
||||
{
|
||||
/* What are we calling this? */
|
||||
$functionName = "theme_{$this->app}_{$this->templateLocation}_{$this->templateName}_{$bit}";
|
||||
|
||||
/* If it doesn't exist, build it */
|
||||
if( !\function_exists( 'IPS\\Theme\\'.$functionName ) )
|
||||
if( !function_exists( 'IPS\\Theme\\'.$functionName ) )
|
||||
{
|
||||
/* Find the file */
|
||||
$file = $this->sourceFolder . $bit . '.phtml';
|
||||
|
||||
/* Get the content */
|
||||
if ( $file === NULL or !file_exists( $file ) )
|
||||
if ( !file_exists( $file ) )
|
||||
{
|
||||
/* Try the database */
|
||||
try
|
||||
{
|
||||
$template = \IPS\Db::i()->select( '*', 'cms_templates', array( 'template_location=? and LOWER(template_group)=? and template_title=?', $this->templateLocation, $this->templateName, $bit ) )->first();
|
||||
$template = Db::i()->select( '*', 'cms_templates', array( 'template_location=? and LOWER(template_group)=? and template_title=?', $this->templateLocation, $this->templateName, $bit ) )->first();
|
||||
|
||||
\IPS\cms\Theme::makeProcessFunction( $template['template_content'], $functionName, $template['template_params'], TRUE );
|
||||
Theme::makeProcessFunction( $template['template_content'], $functionName, $template['template_params'], TRUE );
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
catch ( UnderflowException $e )
|
||||
{
|
||||
throw new \BadMethodCallException( 'NO_TEMPLATE_FILE - ' . $file );
|
||||
throw new BadMethodCallException( 'NO_TEMPLATE_FILE - ' . $file );
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -136,14 +140,14 @@ class _Template extends \IPS\Theme\Template
|
||||
/* Parse the header tag */
|
||||
if ( !preg_match( '/^<ips:template parameters="(.+?)?"([^>]+?)>(\r\n?|\n)/', $output, $matches ) )
|
||||
{
|
||||
throw new \BadMethodCallException( 'NO_HEADER - ' . $file );
|
||||
throw new BadMethodCallException( 'NO_HEADER - ' . $file );
|
||||
}
|
||||
|
||||
/* Strip it */
|
||||
$output = preg_replace( '/^<ips:template parameters="(.+?)?"([^>]+?)>(\r\n?|\n)/', '', $output );
|
||||
|
||||
/* Make it into a lovely function */
|
||||
\IPS\cms\Theme::makeProcessFunction( $output, $functionName, ( isset( $matches[1] ) ? $matches[1] : '' ), TRUE );
|
||||
Theme::makeProcessFunction( $output, $functionName, ( $matches[1] ?? '' ), TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,29 +12,59 @@
|
||||
namespace IPS\cms;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DirectoryIterator;
|
||||
use DomainException;
|
||||
use ErrorException;
|
||||
use IPS\Data\Store;
|
||||
use IPS\Db;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Http\Url\Friendly;
|
||||
use IPS\Log;
|
||||
use IPS\Login;
|
||||
use IPS\Member;
|
||||
use IPS\Theme as SystemTheme;
|
||||
use IPS\Theme\Dev\Template;
|
||||
use IPS\Theme\Dev\Theme as DevTheme;
|
||||
use IPS\Theme\Setup\Theme as SetupTheme;
|
||||
use ParseError;
|
||||
use RuntimeException;
|
||||
use UnexpectedValueException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function file_put_contents;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function strtolower;
|
||||
use const IPS\IPS_FILE_PERMISSION;
|
||||
use const IPS\IPS_FOLDER_PERMISSION;
|
||||
use const IPS\IN_DEV;
|
||||
use const IPS\ROOT_PATH;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* IN_DEV Skin set
|
||||
*/
|
||||
class _Theme extends \IPS\Theme
|
||||
class Theme extends SystemTheme
|
||||
{
|
||||
/**
|
||||
* @brief Template Classes
|
||||
*/
|
||||
protected $templates;
|
||||
protected ?array $templates = null;
|
||||
|
||||
/**
|
||||
* @brief [SkinSets] Templates already loaded and evald via getTemplate()
|
||||
*/
|
||||
public static $calledTemplates = array();
|
||||
public static array $calledTemplates = array();
|
||||
|
||||
/**
|
||||
* @brief Return type for getRawTemplates/getRawCss: Uses DB if not IN_DEV, otherwise uses disk .phtml look up
|
||||
* @brief Return type for getAllTemplates/getRawCss: Uses DB if not IN_DEV, otherwise uses disk .phtml look up
|
||||
*/
|
||||
const RETURN_AS_OBJECT = 32;
|
||||
|
||||
@@ -69,71 +99,43 @@ class _Theme extends \IPS\Theme
|
||||
const RETURN_BLOCK = 2048;
|
||||
|
||||
/**
|
||||
* @brief Return just contents of cms_templates ignoring IN_DEV and DESIGNERS' MODE
|
||||
* @brief Return just contents of cms_templates ignoring IN_DEV
|
||||
*/
|
||||
const RETURN_DATABASE_ONLY = 4096;
|
||||
|
||||
/**
|
||||
* Get currently logged in member's theme
|
||||
*
|
||||
* @return \IPS\Theme
|
||||
* @return static|DevTheme|SystemTheme
|
||||
*/
|
||||
public static function i()
|
||||
public static function i(): static|SetupTheme|DevTheme
|
||||
{
|
||||
if ( \IPS\Theme::designersModeEnabled() )
|
||||
{
|
||||
return new \IPS\cms\Theme\Advanced\Theme;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new self;
|
||||
}
|
||||
return new self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports templates from the /dev directories.
|
||||
*
|
||||
* @param string $location Location (database, block)
|
||||
* @param string $location Location (database, block)
|
||||
* @return void
|
||||
*/
|
||||
public static function importInDev( $location )
|
||||
public static function importFromFiles( string $location ): void
|
||||
{
|
||||
/* Clear out existing template bits */
|
||||
\IPS\Db::i()->delete( 'cms_templates', array( 'template_master=1 and template_user_created=0 and template_user_edited=0 AND template_location=?', $location ) );
|
||||
Db::i()->delete( 'cms_templates', array( 'template_master=1 and template_user_created=0 and template_user_edited=0 AND template_location=?', $location ) );
|
||||
|
||||
static::importLocation( $location );
|
||||
}
|
||||
|
||||
/**
|
||||
* Write IN_DEV files
|
||||
*
|
||||
* @param boolean $force TRUE to rewrite templates, FALSE to check if exists first
|
||||
* @return void
|
||||
*/
|
||||
public static function writeInDev( $force=FALSE )
|
||||
{
|
||||
foreach( \IPS\Db::i()->select( '*', 'cms_templates', array( 'template_master=?', 1 ) ) as $template )
|
||||
{
|
||||
try
|
||||
{
|
||||
static::writeTemplate( $template, $force );
|
||||
}
|
||||
catch( \RuntimeException $ex )
|
||||
{
|
||||
throw new \RuntimeException( $ex->getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports templates from the /dev directories.
|
||||
*
|
||||
* @param string $location Location (database, block)
|
||||
* @param string $location Location (database, block)
|
||||
* @return array
|
||||
*/
|
||||
public static function importLocation( $location )
|
||||
public static function importLocation( string $location ): array
|
||||
{
|
||||
$master = iterator_to_array( \IPS\Db::i()->select(
|
||||
$master = iterator_to_array( Db::i()->select(
|
||||
"*, MD5( CONCAT(template_location, ',', template_group, ',', template_title) ) as bit_key",
|
||||
'cms_templates',
|
||||
array( 'template_master=1 and template_user_created=0 and template_user_edited=0 AND template_location=?', $location )
|
||||
@@ -144,7 +146,7 @@ class _Theme extends \IPS\Theme
|
||||
|
||||
if ( is_dir( $path ) )
|
||||
{
|
||||
foreach( new \DirectoryIterator( $path ) as $group )
|
||||
foreach( new DirectoryIterator( $path ) as $group )
|
||||
{
|
||||
if ( $group->isDot() || mb_substr( $group->getFilename(), 0, 1 ) === '.' || $group->getFilename() == 'index.html' )
|
||||
{
|
||||
@@ -153,7 +155,7 @@ class _Theme extends \IPS\Theme
|
||||
|
||||
if ( $group->isDir() )
|
||||
{
|
||||
foreach( new \DirectoryIterator( $path . '/' . $group->getFilename() ) as $file )
|
||||
foreach( new DirectoryIterator( $path . '/' . $group->getFilename() ) as $file )
|
||||
{
|
||||
if ( $file->isDot() || mb_substr( $file->getFilename(), -6 ) !== '.phtml')
|
||||
{
|
||||
@@ -175,7 +177,7 @@ class _Theme extends \IPS\Theme
|
||||
|
||||
if ( isset( $master[ $key ] ) )
|
||||
{
|
||||
if( \IPS\Login::compareHashes( md5( trim( $master[ $key ]['template_content'] ) ), md5( trim( $html ) ) ) )
|
||||
if( Login::compareHashes( md5( trim( $master[ $key ]['template_content'] ) ), md5( trim( $html ) ) ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -184,14 +186,14 @@ class _Theme extends \IPS\Theme
|
||||
$seen[ $group->getFilename() ] = $title;
|
||||
|
||||
/* remove compiled version */
|
||||
$key = \strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( 'cms', $location, $group->getFilename() ) . '_' . static::cleanGroupName( $group->getFilename() ) );
|
||||
$key = strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( 'cms', $location, $group->getFilename() ) . '_' . static::cleanGroupName( $group->getFilename() ) );
|
||||
|
||||
if ( isset( \IPS\Data\Store::i()->$key ) )
|
||||
if ( isset( Store::i()->$key ) )
|
||||
{
|
||||
unset(\IPS\Data\Store::i()->$key);
|
||||
unset(Store::i()->$key);
|
||||
}
|
||||
|
||||
\IPS\Db::i()->insert( 'cms_templates', array(
|
||||
Db::i()->insert( 'cms_templates', array(
|
||||
'template_key' => $location . '_' . $group->getFilename() . '_' . $title,
|
||||
'template_title' => $title,
|
||||
'template_desc' => '',
|
||||
@@ -217,30 +219,30 @@ class _Theme extends \IPS\Theme
|
||||
* @param array $template Template to write
|
||||
* @param boolean $force Force overwrite
|
||||
* @return void
|
||||
* @throws \RuntimeException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function writeTemplate( $template, $force=FALSE )
|
||||
public static function writeTemplate( array $template, bool $force=FALSE ) : void
|
||||
{
|
||||
$path = static::_getHtmlPath('cms');
|
||||
|
||||
if ( ! is_dir( $path ) )
|
||||
{
|
||||
if ( ! mkdir( $path, \IPS\IPS_FOLDER_PERMISSION, TRUE ) )
|
||||
if ( ! mkdir( $path, IPS_FOLDER_PERMISSION, TRUE ) )
|
||||
{
|
||||
throw new \DomainException();
|
||||
throw new DomainException();
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! is_dir( $path . '/' . $template['template_location'] ) )
|
||||
{
|
||||
mkdir( $path . '/' . $template['template_location'] );
|
||||
@chmod( $path . '/' . $template['template_location'], \IPS\IPS_FOLDER_PERMISSION );
|
||||
@chmod( $path . '/' . $template['template_location'], IPS_FOLDER_PERMISSION );
|
||||
}
|
||||
|
||||
if ( ! is_dir( $path . '/' . $template['template_location'] . '/' . $template['template_group'] ) )
|
||||
{
|
||||
mkdir( $path . '/' . $template['template_location'] . '/' . $template['template_group'] );
|
||||
@chmod( $path . '/' . $template['template_location'] . '/' . $template['template_group'], \IPS\IPS_FOLDER_PERMISSION );
|
||||
@chmod( $path . '/' . $template['template_location'] . '/' . $template['template_group'], IPS_FOLDER_PERMISSION );
|
||||
}
|
||||
|
||||
$fileName = ( $template['template_type'] === 'template' ) ? $template['template_title'] . '.phtml' : $template['template_title'];
|
||||
@@ -256,13 +258,13 @@ class _Theme extends \IPS\Theme
|
||||
|
||||
$write .= $template['template_content'];
|
||||
|
||||
if ( @\file_put_contents( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName, $write ) === FALSE )
|
||||
if ( @file_put_contents( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName, $write ) === FALSE )
|
||||
{
|
||||
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack( 'content_theme_dev_cannot_write_template', FALSE, array( 'sprintf' => array( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName ) ) ) );
|
||||
throw new RuntimeException( Member::loggedIn()->language()->addToStack( 'content_theme_dev_cannot_write_template', FALSE, array( 'sprintf' => array( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName ) ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@chmod( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName, \IPS\IPS_FILE_PERMISSION );
|
||||
@chmod( $path . '/' . $template['template_location'] . '/' . $template['template_group'] . '/' . $fileName, IPS_FILE_PERMISSION );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,31 +272,31 @@ class _Theme extends \IPS\Theme
|
||||
/**
|
||||
* Get raw templates. Raw means HTML logic and variables are still in {{format}}
|
||||
*
|
||||
* @param string|array $app Template app (e.g. core, forum)
|
||||
* @param string|array $location Template location (e.g. admin,global,front)
|
||||
* @param string|array $group Template group (e.g. login, share)
|
||||
* @param int|constant $returnType Determines the content returned
|
||||
* @param boolean $returnThisSetOnly Returns rows unique to this set only
|
||||
* @param array|string $app Template app (e.g. core, forum)
|
||||
* @param array|string $location Template location (e.g. admin,global,front)
|
||||
* @param array|string $group Template group (e.g. login, share)
|
||||
* @param int|null $returnType Determines the content returned
|
||||
* @param boolean $returnThisSetOnly Returns rows unique to this set only
|
||||
* @return array
|
||||
*/
|
||||
public function getRawTemplates( $app=array(), $location=array(), $group=array(), $returnType=null, $returnThisSetOnly=false )
|
||||
public function getAllTemplates( array|string $app=array(), array|string $location=array(), array|string $group=array(), int $returnType=null, bool $returnThisSetOnly=false ): array
|
||||
{
|
||||
$returnType = ( $returnType === null ) ? self::RETURN_ALL : $returnType;
|
||||
$app = ( \is_string( $app ) AND ! empty( $app ) ) ? array( $app ) : $app;
|
||||
$location = ( \is_string( $location ) AND ! empty( $location ) ) ? array( $location ) : $location;
|
||||
$group = ( \is_string( $group ) AND ! empty( $group ) ) ? array( $group ) : $group;
|
||||
$app = ( is_string( $app ) AND ! empty( $app ) ) ? array( $app ) : $app;
|
||||
$location = ( is_string( $location ) AND ! empty( $location ) ) ? array( $location ) : $location;
|
||||
$group = ( is_string( $group ) AND ! empty( $group ) ) ? array( $group ) : $group;
|
||||
$where = array();
|
||||
$templates = array();
|
||||
|
||||
if ( ( \IPS\IN_DEV or \IPS\Theme::designersModeEnabled() ) AND ! ( $returnType & static::RETURN_DATABASE_ONLY ) )
|
||||
if ( ( IN_DEV ) AND ! ( $returnType & static::RETURN_DATABASE_ONLY ) )
|
||||
{
|
||||
$fixedLocations = array( 'admin', 'front', 'global' );
|
||||
$results = array();
|
||||
$seenKeys = array();
|
||||
|
||||
foreach( new \DirectoryIterator( static::_getHtmlPath('cms') ) as $location )
|
||||
foreach( new DirectoryIterator( static::_getHtmlPath('cms') ) as $location )
|
||||
{
|
||||
if ( ! \in_array( $location->getFilename(), $fixedLocations ) AND $location->isDir() AND mb_substr( $location->getFilename(), 0, 1 ) !== '.' )
|
||||
if ( ! in_array( $location->getFilename(), $fixedLocations ) AND $location->isDir() AND mb_substr( $location->getFilename(), 0, 1 ) !== '.' )
|
||||
{
|
||||
$allowedLocations = array();
|
||||
if ( $returnType & static::RETURN_ONLY_TEMPLATE )
|
||||
@@ -329,31 +331,31 @@ class _Theme extends \IPS\Theme
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $allowedLocations ) and ! \in_array( $location->getFilename(), $allowedLocations ) )
|
||||
if ( count( $allowedLocations ) and ! in_array( $location->getFilename(), $allowedLocations ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach( new \DirectoryIterator( static::_getHtmlPath( 'cms', $location->getFilename() ) ) as $file )
|
||||
foreach( new DirectoryIterator( static::_getHtmlPath( 'cms', $location->getFilename() ) ) as $file )
|
||||
{
|
||||
if ( $file->isDir() AND mb_substr( $file->getFilename(), 0, 1 ) !== '.' )
|
||||
{
|
||||
if ( $group === NULL or ! \count( $group ) or ( \in_array( $file->getFilename(), $group ) ) )
|
||||
if ( empty( $group ) or ! count( $group ) or ( in_array( $file->getFilename(), $group ) ) )
|
||||
{
|
||||
foreach( new \DirectoryIterator( static::_getHtmlPath( 'cms', $location->getFilename(), $file->getFilename() ) ) as $template )
|
||||
foreach( new DirectoryIterator( static::_getHtmlPath( 'cms', $location->getFilename(), $file->getFilename() ) ) as $template )
|
||||
{
|
||||
if ( ! $template->isDir() AND ( mb_substr( $template->getFilename(), -6 ) === '.phtml' or mb_substr( $template->getFilename(), -4 ) === '.css' or mb_substr( $template->getFilename(), -3 ) === '.js' ) )
|
||||
{
|
||||
$title = str_replace( ".phtml", "", $template->getFilename() );
|
||||
|
||||
$contents = file_get_contents( static::_getHtmlPath( 'cms', $location->getFilename(), $file->getFilename() ) . '/' . $template->getFilename() );
|
||||
$key = \IPS\cms\Theme\Template::extractDataFromTag( $contents, 'key' );
|
||||
$key = $key ? $key : \IPS\Http\Url\Friendly::seoTitle( $file->getFilename() . '_' . $title );
|
||||
$ogroup = \IPS\cms\Theme\Template::extractDataFromTag( $contents, 'original_group' );
|
||||
$params = \IPS\cms\Theme\Template::extractParamsFromTag( $contents );
|
||||
$key = Theme\Template::extractDataFromTag( $contents, 'key' );
|
||||
$key = $key ?: Friendly::seoTitle( $file->getFilename() . '_' . $title );
|
||||
$ogroup = Theme\Template::extractDataFromTag( $contents, 'original_group' );
|
||||
$params = Theme\Template::extractParamsFromTag( $contents );
|
||||
$container = NULL;
|
||||
|
||||
if ( \in_array( $key, $seenKeys ) )
|
||||
if ( in_array( $key, $seenKeys ) )
|
||||
{
|
||||
$key .= filemtime( static::_getHtmlPath( 'cms', $location->getFilename(), $file->getFilename() ) . '/' . $template->getFilename() ) . mt_rand();
|
||||
}
|
||||
@@ -364,7 +366,7 @@ class _Theme extends \IPS\Theme
|
||||
|
||||
if ( $returnType & static::RETURN_AS_OBJECT )
|
||||
{
|
||||
$object = new \IPS\cms\Templates;
|
||||
$object = new Templates;
|
||||
$object->key = $key;
|
||||
$object->title = $title;
|
||||
$object->desc = NULL;
|
||||
@@ -372,7 +374,7 @@ class _Theme extends \IPS\Theme
|
||||
$object->content = $contents;
|
||||
$object->location = $location->getFilename();
|
||||
$object->group = $file->getFilename();
|
||||
$object->original_group = isset( $ogroup ) ? $ogroup : $object->group;
|
||||
$object->original_group = $ogroup ?? $object->group;
|
||||
$object->user_created = 0;
|
||||
$object->user_edited = 0;
|
||||
$object->params = $params;
|
||||
@@ -389,7 +391,7 @@ class _Theme extends \IPS\Theme
|
||||
'template_content' => $contents,
|
||||
'template_location' => $location->getFilename(),
|
||||
'template_group' => $file->getFilename(),
|
||||
'template_original_group' => isset( $ogroup ) ? $ogroup : $file->getFilename(),
|
||||
'template_original_group' => $ogroup ?? $file->getFilename(),
|
||||
'template_user_created' => 0,
|
||||
'template_user_edited' => 0,
|
||||
'template_params' => $params,
|
||||
@@ -416,12 +418,12 @@ class _Theme extends \IPS\Theme
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( \is_array( $location ) AND \count( $location ) )
|
||||
if ( is_array( $location ) AND count( $location ) )
|
||||
{
|
||||
$where[] = "template_location IN ('" . implode( "','", $location ) . "')";
|
||||
}
|
||||
|
||||
if ( \is_array( $group ) AND \count( $group ) )
|
||||
if ( is_array( $group ) AND count( $group ) )
|
||||
{
|
||||
$where[] = "template_group IN ('" . implode( "','", $group ) . "')";
|
||||
}
|
||||
@@ -443,7 +445,7 @@ class _Theme extends \IPS\Theme
|
||||
$originalTemplateNames = array();
|
||||
$groupMap = array();
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'cms_templates', implode( " AND ", $where ), 'template_location, template_group, template_key, template_user_edited ASC' ) as $row )
|
||||
foreach( Db::i()->select( '*', 'cms_templates', implode( " AND ", $where ), 'template_location, template_group, template_key, template_user_edited ASC' ) as $row )
|
||||
{
|
||||
$rawTemplates[] = $row;
|
||||
$templateNames[ $row['template_original_group'] ][] = $row['template_title'];
|
||||
@@ -455,9 +457,9 @@ class _Theme extends \IPS\Theme
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $originalGroups ) )
|
||||
if ( count( $originalGroups ) )
|
||||
{
|
||||
foreach( \IPS\Db::i()->select( '*', 'cms_templates', array( array( 'template_original_group = template_group '), array( \IPS\Db::i()->in( 'template_original_group', $originalGroups ) ) ) ) as $row )
|
||||
foreach( Db::i()->select( '*', 'cms_templates', array( array( 'template_original_group = template_group '), array( Db::i()->in( 'template_original_group', $originalGroups ) ) ) ) as $row )
|
||||
{
|
||||
$originalTemplates[ $row['template_group'] . '_' . $row['template_title'] ] = $row;
|
||||
$originalTemplateNames[ $row['template_group'] ][] = $row['template_title'];
|
||||
@@ -471,7 +473,7 @@ class _Theme extends \IPS\Theme
|
||||
{
|
||||
foreach( $names as $name )
|
||||
{
|
||||
if ( ! \in_array( $name, $templateNames[ $group ] ) )
|
||||
if ( ! in_array( $name, $templateNames[ $group ] ) )
|
||||
{
|
||||
$rawTemplates[] = array_merge( $originalTemplates[ $group . '_' . $name ], array( 'template_group' => $groupMap[ $group ]) );
|
||||
}
|
||||
@@ -538,37 +540,37 @@ class _Theme extends \IPS\Theme
|
||||
/**
|
||||
* Get a template
|
||||
*
|
||||
* @param string $group Template Group
|
||||
* @param string $app Application key (NULL for current application)
|
||||
* @param string $location Template Location (NULL for current template location)
|
||||
* @return \IPS\Theme\Template
|
||||
* @param string $group Template Group
|
||||
* @param string|null $app Application key (NULL for current application)
|
||||
* @param string|null $location Template Location (NULL for current template location)
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTemplate( $group, $app=NULL, $location=NULL )
|
||||
public function getTemplate( string $group, string $app=NULL, string $location=NULL ): mixed
|
||||
{
|
||||
/* Do we have an application? */
|
||||
if( $app === NULL )
|
||||
{
|
||||
$app = \IPS\Dispatcher::i()->application->directory;
|
||||
$app = Dispatcher::i()->application->directory;
|
||||
}
|
||||
|
||||
/* How about a template location? */
|
||||
if( $location === NULL )
|
||||
{
|
||||
$location = \IPS\Dispatcher::i()->controllerLocation;
|
||||
$location = Dispatcher::i()->controllerLocation;
|
||||
}
|
||||
|
||||
/* Get template */
|
||||
if ( \IPS\IN_DEV or \IPS\Theme::designersModeEnabled() )
|
||||
if ( IN_DEV )
|
||||
{
|
||||
if ( ! isset( $this->templates[ $app ][ $location ][ $group ] ) )
|
||||
{
|
||||
if ( $app === 'cms' AND ! \in_array( $location, array( 'admin', 'front', 'global' ) ) )
|
||||
if ( $app === 'cms' AND ! in_array( $location, array( 'admin', 'front', 'global' ) ) )
|
||||
{
|
||||
$this->templates[ $app ][ $location ][ $group ] = new \IPS\cms\Theme\Template( $app, $location, $group );
|
||||
$this->templates[ $app ][ $location ][ $group ] = new Template( $app, $location, $group );
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->templates[ $app ][ $location ][ $group ] = \IPS\Theme::i()->getTemplate( $group, $app, $location );
|
||||
$this->templates[ $app ][ $location ][ $group ] = Theme::i()->getTemplate( $group, $app, $location );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,42 +581,42 @@ class _Theme extends \IPS\Theme
|
||||
/* Group is saved clean */
|
||||
$group = static::cleanGroupName( $group );
|
||||
|
||||
if ( ( $app !== 'cms' ) OR ( $app === 'cms' AND \in_array( $location, array( 'admin', 'front', 'global' ) ) ) )
|
||||
if ( ( $app !== 'cms' ) OR ( in_array( $location, array( 'admin', 'front', 'global' ) ) ) )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( $group, $app, $location );
|
||||
return SystemTheme::i()->getTemplate( $group, $app, $location );
|
||||
}
|
||||
|
||||
$key = \strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
|
||||
$key = strtolower( 'template_cms_' .static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
|
||||
|
||||
/* Still here */
|
||||
if ( !\in_array( $key, array_keys( static::$calledTemplates ) ) )
|
||||
if ( !in_array( $key, array_keys( static::$calledTemplates ) ) )
|
||||
{
|
||||
/* If we don't have a compiled template, do that now */
|
||||
if ( !isset( \IPS\Data\Store::i()->$key ) )
|
||||
if ( !isset( Store::i()->$key ) )
|
||||
{
|
||||
$this->compileTemplates( $app, $location, $group );
|
||||
}
|
||||
|
||||
/* Still no key? */
|
||||
if ( ! isset( \IPS\Data\Store::i()->$key ) )
|
||||
if ( ! isset( Store::i()->$key ) )
|
||||
{
|
||||
\IPS\Log::log( "Template store key: {$key} missing ({$app}, {$location}, {$group})", "template_store_missing" );
|
||||
Log::log( "Template store key: {$key} missing ({$app}, {$location}, {$group})", "template_store_missing" );
|
||||
|
||||
throw new \ErrorException('template_store_missing ' . $key);
|
||||
throw new ErrorException('template_store_missing ' . $key);
|
||||
}
|
||||
|
||||
/* Load compiled template */
|
||||
$compiledGroup = \IPS\Data\Store::i()->$key;
|
||||
$compiledGroup = Store::i()->$key;
|
||||
try
|
||||
{
|
||||
if ( @eval( $compiledGroup ) === FALSE )
|
||||
{
|
||||
throw new \UnexpectedValueException;
|
||||
throw new UnexpectedValueException;
|
||||
}
|
||||
}
|
||||
catch ( \ParseError $e )
|
||||
catch ( ParseError $e )
|
||||
{
|
||||
throw new \UnexpectedValueException;
|
||||
throw new UnexpectedValueException;
|
||||
}
|
||||
|
||||
/* Hooks */
|
||||
@@ -633,14 +635,14 @@ class _Theme extends \IPS\Theme
|
||||
* Build Templates ready for non IN_DEV use
|
||||
* This fetches all templates in a group, converts HTML logic into ready to eval PHP and stores as a single PHP class per template group
|
||||
*
|
||||
* @param string|array $app Templates app (e.g. core, forum)
|
||||
* @param string|array $location Templates location (e.g. admin,global,front)
|
||||
* @param string|array $group Templates group (e.g. forms, members)
|
||||
* @return void
|
||||
* @param array|string|null $app Templates app (e.g. core, forum)
|
||||
* @param array|string|null $location Templates location (e.g. admin,global,front)
|
||||
* @param array|string|null $group Templates group (e.g. forms, members)
|
||||
* @return bool
|
||||
*/
|
||||
public function compileTemplates( $app=null, $location=null, $group=null )
|
||||
public function compileTemplates( array|string $app=null, array|string $location=null, array|string $group=null ): bool
|
||||
{
|
||||
$templates = $this->getRawTemplates( $app, $location, static::cleanGroupName( $group ) );
|
||||
$templates = $this->getAllTemplates( $app, $location, static::cleanGroupName( $group ) );
|
||||
|
||||
foreach( $templates as $templateApp => $v )
|
||||
{
|
||||
@@ -648,37 +650,10 @@ class _Theme extends \IPS\Theme
|
||||
{
|
||||
foreach( $templates[ $templateApp ][ $location ] as $group => $bits )
|
||||
{
|
||||
|
||||
/* Any template hooks? */
|
||||
$templateHooks = array();
|
||||
if( isset( \IPS\IPS::$hooks[ "\IPS\Theme\class_{$app}_{$location}_{$group}" ] ) AND \IPS\RECOVERY_MODE === FALSE )
|
||||
{
|
||||
foreach ( \IPS\IPS::$hooks[ "\IPS\Theme\class_{$app}_{$location}_{$group}" ] as $k => $data )
|
||||
{
|
||||
if ( !class_exists( "IPS\Theme\hook{$k}", FALSE ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( @eval( "namespace IPS\Theme;\n\n" . str_replace( ' extends _HOOK_CLASS_', '', file_get_contents( \IPS\ROOT_PATH . '/' . $data['file'] ) ) ) !== FALSE )
|
||||
{
|
||||
$class = "IPS\Theme\hook{$data['class']}";
|
||||
$templateHooks = array_merge( $templateHooks, $class::hookData() );
|
||||
}
|
||||
}
|
||||
catch ( \ParseError $e ) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Build all the functions */
|
||||
$functions = array();
|
||||
foreach( $templates[ $templateApp ][ $location ][ $group ] as $name => $data )
|
||||
{
|
||||
if ( isset( $templateHooks[ $name ] ) )
|
||||
{
|
||||
$data['template_content'] = static::themeHooks( $data['template_content'], $templateHooks[ $name ] );
|
||||
}
|
||||
|
||||
$functions[ $name ] = static::compileTemplate( $data['template_content'], $data['template_title'], $data['template_params'], true, false, $app, $location, $group );
|
||||
}
|
||||
|
||||
@@ -696,27 +671,29 @@ EOF;
|
||||
EOF;
|
||||
|
||||
/* Store it */
|
||||
$key = \strtolower( 'template_cms_' . static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
|
||||
$key = strtolower( 'template_cms_' . static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
|
||||
|
||||
\IPS\Data\Store::i()->$key = $template;
|
||||
Store::i()->$key = $template;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete compiled templates
|
||||
* Removes compiled templates bits for all themes that match the arguments
|
||||
*
|
||||
* @param string $app Application Directory (core, forums, etc)
|
||||
* @param string|null $location Template location (front, admin, global, etc)
|
||||
* @param string|null $group Template group (forms, messaging, etc)
|
||||
* @param int|null $themeId Limit to a specific theme (and children)
|
||||
* @param string|null $app Application Directory (core, forums, etc)
|
||||
* @param array|string|null $location Template location (front, admin, global, etc)
|
||||
* @param array|string|null $group Template group (forms, messaging, etc)
|
||||
* @param int|null $themeId Limit to a specific theme (and children)
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteCompiledTemplate( $app=null, $location=null, $group=null, $themeId=null )
|
||||
public static function deleteCompiledTemplate( string $app=null, array|string|null $location=null, array|string|null $group=null, int $themeId=null ): void
|
||||
{
|
||||
$templates = \IPS\cms\Theme::i()->getRawTemplates( $app, $location, $group );
|
||||
$templates = Theme::i()->getAllTemplates( $app, $location, $group );
|
||||
|
||||
foreach( $templates as $templateApp => $v )
|
||||
{
|
||||
@@ -724,11 +701,11 @@ EOF;
|
||||
{
|
||||
foreach( $templates[ $templateApp ][ $location ] as $group => $bits )
|
||||
{
|
||||
$key = \strtolower( 'template_cms_' . static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
|
||||
$key = strtolower( 'template_cms_' . static::makeBuiltTemplateLookupHash( $app, $location, $group ) . '_' . static::cleanGroupName( $group ) );
|
||||
|
||||
if ( isset( \IPS\Data\Store::i()->$key ) )
|
||||
if ( isset( Store::i()->$key ) )
|
||||
{
|
||||
unset( \IPS\Data\Store::i()->$key );
|
||||
unset( Store::i()->$key );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -744,9 +721,9 @@ EOF;
|
||||
* @param string|null $path Path or Filename
|
||||
* @return string
|
||||
*/
|
||||
protected static function _getHtmlPath( $app, $location=null, $path=null )
|
||||
protected static function _getHtmlPath( string $app, ?string $location=null, ?string $path=null ) : string
|
||||
{
|
||||
return rtrim( \IPS\ROOT_PATH . "/applications/{$app}/dev/html/{$location}/{$path}", '/' ) . '/';
|
||||
return rtrim( ROOT_PATH . "/applications/{$app}/dev/html/{$location}/{$path}", '/' ) . '/';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,59 +12,37 @@
|
||||
namespace IPS\cms;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\Data\Store;
|
||||
use IPS\Db;
|
||||
use IPS\Widget as SystemWidget;
|
||||
use IPS\Widget\Area;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function json_decode;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* CMS Widgets
|
||||
*/
|
||||
class _Widget extends \IPS\Widget
|
||||
class Widget extends SystemWidget
|
||||
{
|
||||
/**
|
||||
* Fetch the configuration for this unqiue ID. Looks in active tables and trash. When a widget is moved, saveOrder is called twice,
|
||||
* once to remove the widget from column A and again to add it to column B. We store the widget removed from column A into the trash
|
||||
* table.
|
||||
*
|
||||
* @param string $uniqueId Widget's unique ID
|
||||
* @return array
|
||||
*/
|
||||
public static function getConfiguration( $uniqueId )
|
||||
{
|
||||
foreach( \IPS\Db::i()->select( '*', 'cms_page_widget_areas' ) as $item )
|
||||
{
|
||||
$widgets = json_decode( $item['area_widgets'], TRUE );
|
||||
|
||||
if( \is_array( $widgets ) )
|
||||
{
|
||||
foreach( $widgets as $widget )
|
||||
{
|
||||
if ( $widget['unique'] == $uniqueId )
|
||||
{
|
||||
if ( isset( $widget['configuration'] ) )
|
||||
{
|
||||
return $widget['configuration'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Still here? rummage in the trash */
|
||||
return parent::getConfiguration( $uniqueId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete caches. We need a different name from the parent class otherwise the Pages app hook will get stuck in infinite recursion
|
||||
*
|
||||
* @param String $key Widget key
|
||||
* @param String $app Parent application
|
||||
* @param String $plugin Parent plugin
|
||||
* @param String|null $key Widget key
|
||||
* @param String|null $app Parent application
|
||||
* @param String|null $plugin Parent plugin
|
||||
* @return void
|
||||
*/
|
||||
static public function deleteCachesForBlocks( $key=NULL, $app=NULL, $plugin=NULL )
|
||||
static public function deleteCachesForBlocks( string $key=NULL, string $app=NULL, string $plugin=NULL ) : void
|
||||
{
|
||||
/* Delete any custom block caches relevant to this plug in */
|
||||
if ( $key OR $app )
|
||||
@@ -82,23 +60,36 @@ class _Widget extends \IPS\Widget
|
||||
}
|
||||
|
||||
$blocks = array();
|
||||
foreach( \IPS\Db::i()->select( '*', 'cms_blocks', $where ) as $row )
|
||||
foreach( Db::i()->select( '*', 'cms_blocks', $where ) as $row )
|
||||
{
|
||||
$blocks[ $row['block_key'] ] = $row;
|
||||
}
|
||||
|
||||
if ( \count( $blocks ) )
|
||||
if ( count( $blocks ) )
|
||||
{
|
||||
$uniqueIds = array();
|
||||
foreach( \IPS\Db::i()->select( '*', 'cms_page_widget_areas' ) as $item )
|
||||
foreach( Db::i()->select( '*', 'cms_page_widget_areas' ) as $item )
|
||||
{
|
||||
$widgets = json_decode( $item['area_widgets'], TRUE );
|
||||
if( $item['area_widgets'] )
|
||||
{
|
||||
$widgets = json_decode( $item['area_widgets'], TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$area = new Area( json_decode( $item['area_tree'], true ), $item['area_area'] );
|
||||
$widgets = $area->getAllWidgets();
|
||||
}
|
||||
|
||||
if( !is_array( $widgets ) or !count( $widgets ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach( $widgets as $widget )
|
||||
{
|
||||
if ( ( isset( $widget['app'] ) and $widget['app'] === 'cms' ) and $widget['key'] === 'Blocks' and isset( $widget['unique'] ) and isset( $widget['configuration'] ) and isset( $widget['configuration']['cms_widget_custom_block'] ) )
|
||||
{
|
||||
if ( \in_array( $widget['configuration']['cms_widget_custom_block'], array_keys( $blocks ) ) )
|
||||
if ( in_array( $widget['configuration']['cms_widget_custom_block'], array_keys( $blocks ) ) )
|
||||
{
|
||||
$uniqueIds[] = $widget['unique'];
|
||||
}
|
||||
@@ -106,15 +97,28 @@ class _Widget extends \IPS\Widget
|
||||
}
|
||||
}
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_widget_areas' ) as $item )
|
||||
foreach( Db::i()->select( '*', 'core_widget_areas' ) as $item )
|
||||
{
|
||||
$widgets = json_decode( $item['widgets'], TRUE );
|
||||
if( $item['widgets'] )
|
||||
{
|
||||
$widgets = json_decode( $item['widgets'], TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
$area = new Area( $item['area'], json_decode( $item['tree'], true ) );
|
||||
$widgets = $area->getAllWidgets();
|
||||
}
|
||||
|
||||
if( !is_array( $widgets ) or !count( $widgets ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach( $widgets as $widget )
|
||||
{
|
||||
if ( ( isset( $widget['app'] ) and $widget['app'] === 'cms' ) and $widget['key'] === 'Blocks' and isset( $widget['unique'] ) and isset( $widget['configuration'] ) and isset( $widget['configuration']['cms_widget_custom_block'] ) )
|
||||
{
|
||||
if ( \in_array( $widget['configuration']['cms_widget_custom_block'], array_keys( $blocks ) ) )
|
||||
if ( in_array( $widget['configuration']['cms_widget_custom_block'], array_keys( $blocks ) ) )
|
||||
{
|
||||
$uniqueIds[] = $widget['unique'];
|
||||
}
|
||||
@@ -122,15 +126,15 @@ class _Widget extends \IPS\Widget
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $uniqueIds ) )
|
||||
if ( count( $uniqueIds ) )
|
||||
{
|
||||
$widgetRow = \IPS\Db::i()->select( '*', 'core_widgets', array( '`key`=? and app=?', 'Blocks', 'cms' ) )->first();
|
||||
$widgetRow = Db::i()->select( '*', 'core_widgets', array( '`key`=? and app=?', 'Blocks', 'cms' ) )->first();
|
||||
|
||||
if ( ! empty( $widgetRow['caches'] ) )
|
||||
{
|
||||
$caches = json_decode( $widgetRow['caches'], TRUE );
|
||||
|
||||
if ( \is_array( $caches ) )
|
||||
if ( is_array( $caches ) )
|
||||
{
|
||||
$save = $caches;
|
||||
foreach( $caches as $key => $time )
|
||||
@@ -139,9 +143,9 @@ class _Widget extends \IPS\Widget
|
||||
{
|
||||
if ( mb_stristr( $key, 'widget_Blocks_' . $id ) )
|
||||
{
|
||||
if ( isset( \IPS\Data\Store::i()->$key ) )
|
||||
if ( isset( Store::i()->$key ) )
|
||||
{
|
||||
unset( \IPS\Data\Store::i()->$key );
|
||||
unset( Store::i()->$key );
|
||||
}
|
||||
|
||||
unset( $save[ $key ] );
|
||||
@@ -149,10 +153,10 @@ class _Widget extends \IPS\Widget
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $save ) !== \count( $caches ) )
|
||||
if ( count( $save ) !== count( $caches ) )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_widgets', array( 'caches' => ( \count( $save ) ? json_encode( $save ) : NULL ) ), array( 'id=?', $widgetRow['id'] ) );
|
||||
unset( \IPS\Data\Store::i()->widgets );
|
||||
Db::i()->update( 'core_widgets', array( 'caches' => ( count( $save ) ? json_encode( $save ) : NULL ) ), array( 'id=?', $widgetRow['id'] ) );
|
||||
unset( Store::i()->widgets );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,25 +170,40 @@ class _Widget extends \IPS\Widget
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getUniqueIds()
|
||||
public static function getUniqueIds(): array
|
||||
{
|
||||
$uniqueIds = parent::getUniqueIds();
|
||||
foreach ( \IPS\Db::i()->select( '*', 'cms_page_widget_areas' ) as $row )
|
||||
foreach ( Db::i()->select( '*', 'cms_page_widget_areas' ) as $row )
|
||||
{
|
||||
$data = json_decode( $row['area_widgets'], TRUE );
|
||||
|
||||
if ( is_countable( $data ) AND \count( $data ) )
|
||||
if( $row['area_widgets'] )
|
||||
{
|
||||
foreach( $data as $widget )
|
||||
$data = json_decode( $row['area_widgets'], TRUE );
|
||||
|
||||
if ( is_countable( $data ) AND count( $data ) )
|
||||
{
|
||||
if ( isset( $widget['unique'] ) )
|
||||
{
|
||||
foreach( $data as $widget )
|
||||
{
|
||||
if ( isset( $widget['unique'] ) )
|
||||
{
|
||||
$uniqueIds[] = $widget['unique'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( $row['area_tree'] )
|
||||
{
|
||||
$area = new Area( json_decode( $row['area_tree'], true ), $row['area_area'] );
|
||||
foreach( $area->getAllWidgets() as $widget )
|
||||
{
|
||||
if( isset( $widget['unique'] ) )
|
||||
{
|
||||
$uniqueIds[] = $widget['unique'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $uniqueIds;
|
||||
return array_unique( $uniqueIds );
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user