Files
IPB/applications/cms/Application.php
T
2025-12-19 04:57:54 -08:00

540 lines
16 KiB
PHP

<?php
/**
* @brief Content Application Class
* @author <a href=''>Invision Power Services, Inc.</a>
* @copyright (c) 2014 Invision Power Services, Inc.
* @package IPS Social Suite
* @subpackage Content
* @since 13 Jan 2014
* @version
*/
namespace IPS\cms;
spl_autoload_register( function( $class )
{
if ( mb_substr( $class, 0, 15 ) === 'IPS\cms\Records' and is_numeric( mb_substr( $class, 15, 1 ) ) )
{
$databaseId = mb_substr( $class, 15 );
$titleField = \IPS\cms\Databases::load( $databaseId )->field_title;
$contentField = \IPS\cms\Databases::load( $databaseId )->field_content;
$titleLang = 'content_db_lang_sl_' . $databaseId;
$data = <<<EOF
namespace IPS\cms;
class Records{$databaseId} extends Records
{
protected static \$multitons = array();
public static \$customDatabaseId = $databaseId;
public static \$commentClass = 'IPS\cms\Records\Comment{$databaseId}';
public static \$reviewClass = 'IPS\cms\Records\Review{$databaseId}';
public static \$containerNodeClass = 'IPS\cms\Categories{$databaseId}';
public static \$databaseTable = 'cms_custom_database_{$databaseId}';
public static \$title = '{$titleLang}';
public static \$module = 'records{$databaseId}';
public static \$reputationType = 'record_id_{$databaseId}';
public static \$databaseColumnMap = array(
'author' => 'member_id',
'container' => 'category_id',
'date' => 'record_publish_date',
'is_future_entry' => 'record_future_date',
'future_date' => 'record_publish_date',
'title' => 'field_{$titleField}',
'content' => 'field_{$contentField}',
'num_comments' => 'record_comments',
'unapproved_comments' => 'record_comments_queued',
'hidden_comments' => 'record_comments_hidden',
'last_comment' => 'record_last_comment',
'last_comment_by' => 'record_last_comment_by',
'last_comment_name' => 'record_last_comment_name',
'last_comment_name_seo' => 'record_last_comment_seo',
'views' => 'record_views',
'approved' => 'record_approved',
'pinned' => 'record_pinned',
'locked' => 'record_locked',
'featured' => 'record_featured',
'rating_hits' => 'rating_hits',
'rating' => 'record_rating',
'rating_total' => 'rating_value',
'num_reviews' => 'record_reviews',
'last_review' => 'record_last_review',
'last_review_by' => 'record_last_review_by',
'last_review_name' => 'record_last_review_name',
'updated' => 'record_last_comment'
);
}
EOF;
eval( $data );
}
if ( mb_substr( $class, 0, 23 ) === 'IPS\cms\Records\Comment' and is_numeric( mb_substr( $class, 23, 1 ) ) )
{
$databaseId = mb_substr( $class, 23 );
$data = <<<EOF
namespace IPS\cms\Records;
class Comment{$databaseId} extends Comment
{
public static \$customDatabaseId = $databaseId;
public static \$itemClass = 'IPS\cms\Records{$databaseId}';
public static \$title = 'content_record_comments_title_{$databaseId}';
}
EOF;
eval( $data );
}
if ( mb_substr( $class, 0, 22 ) === 'IPS\cms\Records\Review' and is_numeric( mb_substr( $class, 22, 1 ) ) )
{
$databaseId = mb_substr( $class, 22 );
$data = <<<EOF
namespace IPS\cms\Records;
class Review{$databaseId} extends Review
{
public static \$customDatabaseId = $databaseId;
public static \$itemClass = 'IPS\cms\Records{$databaseId}';
public static \$title = 'content_record_reviews_title_{$databaseId}';
}
EOF;
eval( $data );
}
if ( mb_substr( $class, 0, 18 ) === 'IPS\cms\Categories' and is_numeric( mb_substr( $class, 18, 1 ) ) )
{
$databaseId = mb_substr( $class, 18 );
$data = <<<EOF
namespace IPS\cms;
class Categories{$databaseId} extends Categories
{
public static \$customDatabaseId = $databaseId;
public static \$contentItemClass = 'IPS\cms\Records{$databaseId}';
}
EOF;
eval( $data );
}
if ( mb_substr( $class, 0, 32 ) === 'IPS\cms\Records\RecordsTopicSync' )
{
$databaseId = mb_substr( $class, 32 );
$data = <<<EOF
namespace IPS\cms\Records;
class RecordsTopicSync{$databaseId} extends \IPS\cms\Records{$databaseId}
{
public static \$customDatabaseId = $databaseId;
public static \$databaseTable = 'cms_custom_database_{$databaseId}';
public static \$databaseColumnId = 'record_topicid';
public static \$commentClass = 'IPS\cms\Records\CommentTopicSync{$databaseId}';
public function useForumComments()
{
return false;
}
}
EOF;
eval( $data );
}
if ( mb_substr( $class, 0, 32 ) === 'IPS\cms\Records\CommentTopicSync' )
{
$databaseId = mb_substr( $class, 32 );
$data = <<<EOF
namespace IPS\cms\Records;
class CommentTopicSync{$databaseId} extends CommentTopicSync
{
public static \$customDatabaseId = $databaseId;
public static \$itemClass = 'IPS\cms\Records\RecordsTopicSync{$databaseId}';
public static \$title = 'content_record_comments_title_{$databaseId}';
}
EOF;
eval( $data );
}
if ( mb_substr( $class, 0, 14 ) === 'IPS\cms\Fields' and is_numeric( mb_substr( $class, 14, 1 ) ) )
{
$databaseId = mb_substr( $class, 14 );
eval( "namespace IPS\\cms; class Fields{$databaseId} extends Fields { public static \$customDatabaseId = $databaseId; }" );
}
if ( mb_substr( $class, 0, 47 ) === 'IPS\cms\extensions\core\EditorLocations\Records' and is_numeric( mb_substr( $class, 47, 1 ) ) )
{
$databaseId = mb_substr( $class, 47 );
eval( "namespace IPS\\cms\\extensions\\core\\EditorLocations; class Records{$databaseId} extends \\IPS\\cms\\extensions\\core\\EditorLocations\\Records { public static \$customDatabaseId = $databaseId; }" );
}
} );
/**
* Content Application Class
*/
class _Application extends \IPS\Application
{
/**
* Returns the ACP Menu JSON for this application.
*
* @return array
*/
public function acpMenu()
{
$menu = parent::acpMenu();
if ( ! \IPS\Db::i()->checkForTable('cms_databases') )
{
return $menu;
}
/* Now add in the databases... */
foreach( \IPS\cms\Databases::acpMenu() as $database )
{
$menu['databases'][ 'records_' . $database['id'] ] = array(
'tab' => 'cms',
'controller' => 'records',
'do' => 'manage&database_id=' . $database['id'],
'restriction' => 'records_manage'
);
if ( $database['use_categories'] )
{
$menu['databases'][ 'categories_' . $database['id'] ] = array(
'tab' => 'cms',
'controller' => 'categories',
'do' => 'manage&database_id=' . $database['id'],
'restriction' => 'categories_manage'
);
}
$menu['databases'][ 'fields_' . $database['id'] ] = array(
'tab' => 'cms',
'controller' => 'fields',
'do' => 'manage&database_id=' . $database['id'],
'restriction' => 'fields_manage'
);
\IPS\Member::loggedIn()->language()->words[ 'menu__cms_databases_records_' . $database['id'] ] = \IPS\Member::loggedIn()->language()->addToStack('content_db_' . $database['id']);
\IPS\Member::loggedIn()->language()->words[ 'menu__cms_databases_categories_' . $database['id'] ] = '&nbsp;&nbsp;' . \IPS\Member::loggedIn()->language()->addToStack('menu__cms_categories');
\IPS\Member::loggedIn()->language()->words[ 'menu__cms_databases_fields_' . $database['id'] ] = '&nbsp;&nbsp;' . \IPS\Member::loggedIn()->language()->addToStack('menu__cms_fields');
}
/* Check for, and build IN_DEV templates if required */
$this->_writeInDevTemplates();
return $menu;
}
/**
* Get Extensions
*
* @param \IPS\Application|string $app The app key of the application which owns the extension
* @param string $extension Extension Type
* @param bool $construct Should an object be returned? (If false, just the classname will be returned)
* @param \IPS\Member|bool $checkAccess Check access permission for extension against supplied member (or logged in member, if TRUE)
* @return array
*/
public function extensions( $app, $extension, $construct=TRUE, $checkAccess=FALSE )
{
$classes = parent::extensions( $app, $extension, $construct );
if ( $extension === 'EditorLocations' )
{
foreach( \IPS\cms\Databases::databases() as $obj )
{
$classname = '\\IPS\\cms\\extensions\\core\\EditorLocations\\Records' . $obj->_id;
if ( method_exists( $classname, 'generate' ) )
{
$classes = array_merge( $classes, $classname::generate() );
}
elseif ( !$construct )
{
$classes[ 'Records' . $obj->_id ] = $classname;
}
else
{
try
{
$classes[ 'Records' . $obj->_id ] = new $classname;
}
catch( \RuntimeException $e ){}
}
}
}
else if ( $checkAccess and $extension === 'ContentRouter' )
{
if ( isset( $classes['Records'] ) )
{
foreach ( $classes['Records']->classes as $id => $name )
{
if ( ! $name::database()->can( 'view' ) )
{
unset( $classes['Records']->classes[ $id ] );
continue;
}
if ( isset( $name::$reviewClass ) and ! $name::database()->options['reviews'] )
{
$_class = $classes['Records']->classes[ $id ];
if ( class_exists( $_class ) )
{
$_class::$reviewClass = NULL;
}
}
}
}
}
return $classes;
}
/**
* Developer sync items
*
* @param int $lastSync Last time syncd
* @return boolean Updated (true), nothing updated(false)
*/
public function developerSync( $lastSync )
{
$updated = false;
if ( $lastSync < filemtime( \IPS\ROOT_PATH . "/applications/{$this->directory}/data/databaseschema.json" ) )
{
foreach( \IPS\cms\Databases::databases() as $key => $db )
{
\IPS\cms\Databases::checkandFixDatabaseSchema( $db->_id );
$updated = TRUE;
}
}
return $updated;
}
/**
* Install 'other' items.
*
* @return void
*/
public function installOther()
{
/* Install default database and page */
$database = new \IPS\cms\Databases;
$database->key = 'articles';
$database->save();
/* Add in permissions */
$groups = array();
foreach( \IPS\Db::i()->select( 'g_id', 'core_groups', array( 'g_access_cp=1' ) ) as $row )
{
$groups[] = $row;
}
$default = implode( ',', $groups );
\IPS\Db::i()->insert( 'core_permission_index', array(
'app' => 'cms',
'perm_type' => 'databases',
'perm_type_id' => $database->id,
'perm_view' => '*', # view
'perm_2' => '*', # read
'perm_3' => $default, # add
'perm_4' => $default, # edit
'perm_5' => $default, # reply
'perm_6' => $default # rate
) );
/* Needs to be added before createDatabase is called */
\IPS\Lang::saveCustom( 'cms', "content_db_" . $database->id, "Articles" );
\IPS\Lang::saveCustom( 'cms', "module__cms_records" . $database->id, "Articles" );
\IPS\Lang::saveCustom( 'cms', "content_db_" . $database->id . '_desc', "Our website articles" );
\IPS\Lang::saveCustom( 'cms', "content_db_lang_sl_" . $database->id, 'article' );
\IPS\Lang::saveCustom( 'cms', "content_db_lang_pl_" . $database->id, 'articles' );
\IPS\Lang::saveCustom( 'cms', "content_db_lang_su_" . $database->id, 'Article' );
\IPS\Lang::saveCustom( 'cms', "content_db_lang_pu_" . $database->id, 'Articles' );
\IPS\Lang::saveCustom( 'cms', "content_db_lang_ia_" . $database->id, 'an article' );
\IPS\Lang::saveCustom( 'cms', "content_db_lang_sl_" . $database->id . '_pl', 'Articles' );
\IPS\Lang::saveCustom( 'cms', "cms_create_menu_records_" . $database->id, 'Article in Articles' );
\IPS\Lang::saveCustom( 'cms', "digest_area_records" . $database->id, "Articles" );
\IPS\Lang::saveCustom( 'cms', "cms_records" . $database->id . '_pl', 'Article' );
try
{
\IPS\cms\Databases::createDatabase( $database );
}
catch ( \Exception $ex )
{
$database->delete();
\IPS\Log::i( LOG_ERR )->write( $ex, 'pages_create_db_error' );
throw new \LogicException( $ex->getMessage() );
}
$database->all_editable = 0;
$database->revisions = 1;
$database->search = 1;
$database->comment_bump = 1; # Just new comments bump record
$database->rss = 1;
$database->options['comments'] = 1;
$database->field_sort = 'primary_id_field';
$database->field_direction = 'asc';
$database->field_perpage = 25;
$database->save();
/* Create the page */
$pageValues = array(
'page_name' => "Articles",
'page_title' => "Articles",
'page_seo_name' => "articles.html",
'page_folder_id' => 0,
'page_ipb_wrapper' => 1,
'page_show_sidebar' => 1,
'page_content' => '{database="' . $database->id . '"}',
);
$page = \IPS\cms\Pages\Page::createFromForm( $pageValues, 'html' );
$page->setAsDefault();
/* Add block container (custom)*/
$container = new \IPS\cms\Blocks\Container;
$container->parent_id = 0;
$container->name = "Custom";
$container->type = 'block';
$container->key = 'block_custom';
$container->save();
/* Add block container (plugins) */
$container = new \IPS\cms\Blocks\Container;
$container->parent_id = 0;
$container->name = "Plugins";
$container->type = 'block';
$container->key = 'block_plugins';
$container->save();
\IPS\cms\Templates::importXml( \IPS\ROOT_PATH . "/applications/cms/data/cms_theme.xml", NULL, NULL, FALSE );
}
/**
* Install the application's templates
* Theme resources should be raw binary data everywhere (filesystem and DB) except in the theme XML download where they are base64 encoded.
*
* @param bool $update If set to true, do not overwrite current theme setting values
* @param int|null $offset Offset to begin import from
* @param int|null $limit Number of rows to import
* @return int Rows inserted
*/
public function installTemplates( $update=FALSE, $offset=null, $limit=null )
{
$inserted = parent::installTemplates( $update, $offset, $limit );
if ( ( ! $inserted or ( $inserted < $limit ) ) AND $update )
{
\IPS\cms\Templates::importXml( \IPS\ROOT_PATH . "/applications/cms/data/cms_theme.xml", NULL, NULL, $update );
}
}
/**
* Build skin templates for an app
*
* @return void
* @throws \RuntimeException
*/
public function buildThemeTemplates()
{
parent::buildThemeTemplates();
foreach( array( 'database', 'block', 'page' ) as $location )
{
\IPS\cms\Theme::importInDev( $location );
}
/* Build XML and write to app directory */
$xml = new \XMLWriter;
$xml->openMemory();
$xml->setIndent( TRUE );
$xml->startDocument( '1.0', 'UTF-8' );
/* Root tag */
$xml->startElement('theme');
$xml->startAttribute('name');
$xml->text( "Default" );
$xml->endAttribute();
$xml->startAttribute('author_name');
$xml->text( "Invision Power Services, Inc" );
$xml->endAttribute();
$xml->startAttribute('author_url');
$xml->text( "http://www.invisionpower.com" );
$xml->endAttribute();
/* Templates */
foreach ( \IPS\Db::i()->select( '*', 'cms_templates', array( 'template_master=1 and template_user_created=0 and template_user_edited=0' ), 'template_group, template_title' ) as $template )
{
/* Initiate the <template> tag */
$xml->startElement('template');
foreach( $template as $k => $v )
{
if ( in_array( \substr( $k, 9 ), array('key', 'title', 'desc', 'location', 'group', 'params', 'app', 'type' ) ) )
{
$xml->startAttribute( $k );
$xml->text( $v );
$xml->endAttribute();
}
}
/* Write value */
if ( preg_match( '/<|>|&/', $template['template_content'] ) )
{
$xml->writeCData( str_replace( ']]>', ']]]]><![CDATA[>', $template['template_content'] ) );
}
else
{
$xml->text( $template['template_content'] );
}
/* Close the <template> tag */
$xml->endElement();
}
/* Finish */
$xml->endDocument();
/* Write it */
if ( is_writable( \IPS\ROOT_PATH . '/applications/' . $this->directory . '/data' ) )
{
\file_put_contents( \IPS\ROOT_PATH . '/applications/' . $this->directory . '/data/cms_theme.xml', $xml->outputMemory() );
}
else
{
throw new \RuntimeException( \IPS\Member::loggedIn()->language()->addToStack('dev_could_not_write_data') );
}
}
/**
* [Node] Get Icon for tree
*
* @note Return the class for the icon (e.g. 'globe')
* @return string|null
*/
protected function get__icon()
{
return 'folder-open';
}
/**
* Write the IN_DEV templates to disk
*
* @return void
*/
public function _writeInDevTemplates()
{
if ( \IPS\IN_DEV )
{
//\IPS\cms\Theme::writeInDev();
}
}
}