Invision Power Services, Inc. * @copyright (c) Invision Power Services, Inc. * @license https://www.invisioncommunity.com/legal/standards/ * @package Invision Community * @subpackage Content * @since 31 March 2014 */ namespace IPS\cms\modules\admin\databases; /* To prevent PHP errors (extending class does not exist) revealing path */ use Cassandra\Set; use Exception; use InvalidArgumentException; use IPS\Application; use IPS\cms\Categories; use IPS\cms\Databases as CmsDatabases; use IPS\cms\Pages\Page; use IPS\cms\Templates; use IPS\Data\Store; use IPS\Db; use IPS\Dispatcher; use IPS\Helpers\Form; use IPS\Helpers\Form\Node; use IPS\Helpers\Form\Number; use IPS\Helpers\Form\Radio; use IPS\Helpers\Form\Select; use IPS\Helpers\Form\Text; use IPS\Helpers\Form\Translatable; use IPS\Helpers\Form\Upload; use IPS\Helpers\Form\WidthHeight; use IPS\Helpers\Form\YesNo; use IPS\Helpers\MultipleRedirect; use IPS\Helpers\Table\Db as TableDb; use IPS\Http\Url; use IPS\Http\Url\Friendly; use IPS\IPS; use IPS\Lang; use IPS\Member; use IPS\Node\Controller; use IPS\Output; use IPS\Patterns\Bitwise; use IPS\Platform\Bridge; use IPS\Request; use IPS\Session; use IPS\Settings; use IPS\Task; use IPS\Theme; use IPS\Widget\Area; use LogicException; use OutOfRangeException; use SimpleXMLElement; use UnderflowException; use XMLReader; use XMLWriter; use function count; use function defined; use function file_get_contents; use function in_array; use function intval; use function is_array; use function ucfirst; use const IPS\TEMP_DIRECTORY; if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) ) { header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' ); exit; } /** * databases */ class databases extends Controller { /** * @brief Has been CSRF-protected */ public static bool $csrfProtected = TRUE; /** * Node Class */ protected string $nodeClass = '\IPS\cms\Databases'; /** * Execute * * @return void */ public function execute() : void { Dispatcher::i()->checkAcpPermission( 'databases_use' ); parent::execute(); } /** * Manage * * @return void */ protected function manage() : void { /* Create the table */ $table = new TableDb( 'cms_databases', Url::internal( 'app=cms&module=databases&controller=databases' ) ); $table->langPrefix = 'content_'; /* Columns */ $table->joins = array( array( 'select' => 'w.word_custom', 'from' => array( 'core_sys_lang_words', 'w' ), 'where' => "w.word_key=CONCAT( 'content_db_', cms_databases.database_id ) AND w.lang_id=" . Member::loggedIn()->language()->id ) ); $table->include = array( 'word_custom', 'database_record_count', 'database_category_count' ); $table->widths = array( 'word_custom' => '50' ); $table->mainColumn = 'word_custom'; $table->quickSearch = 'word_custom'; $table->sortBy = $table->sortBy ?: 'word_custom'; $table->sortDirection = $table->sortDirection ?: 'asc'; /* Parsers */ $table->parsers = array( 'word_custom' => function( $val, $row ) { $page = NULL; $database = NULL; try { $database = CmsDatabases::load( $row['database_id'] ); if ( $database->page_id > 0 ) { try { $page = Page::load( $database->page_id ); } catch ( OutOfRangeException $ex ) { $database->page_id = 0; $database->save(); } } } catch ( OutOfRangeException $ex ) { } return Theme::i()->getTemplate( 'databases' )->manageDatabaseName( $database, $row, $page ); }, 'database_category_count' => function( $val, $row ) { try { $database = CmsDatabases::load( $row['database_id'] ); if ( ! $database->use_categories ) { return Member::loggedIn()->language()->addToStack('cms_db_cats_disabled'); } } catch ( OutOfRangeException ) { } /* This sucks but adding a COUNT into a join breaks the query and sub selects not easy with DB driver */ return Db::i()->select( 'COUNT(*)', 'cms_database_categories', array( 'category_database_id=?', $row['database_id'] ) )->first(); }, 'database_record_count' => function( $val, $row ) { return Db::i()->select( 'SUM(category_records)', 'cms_database_categories', array( 'category_database_id=?', $row['database_id'] ) )->first(); } ); if ( Member::loggedIn()->hasAcpRestriction( 'cms', 'databases', 'databases_add' ) ) { /* Buttons */ Output::i()->sidebar['actions']['add'] = array( 'primary' => true, 'title' => 'cms_database_add_new', 'icon' => 'plus', 'link' => Url::internal( 'app=cms&module=databases&controller=databases&do=form' ) ); Output::i()->sidebar['actions']['import'] = array( 'primary' => true, 'title' => 'cms_database_add_upload', 'icon' => 'upload', 'link' => Url::internal( "app=cms&module=databases&controller=databases&do=add" ), 'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack( 'cms_database_add_upload' ) ) ); } $table->rowButtons = function( $row ) { $return = array(); if ( Member::loggedIn()->hasAcpRestriction( 'cms', 'databases', 'databases_edit' ) ) { $return['edit'] = array( 'title' => 'edit', 'icon' => 'pencil', 'link' => Url::internal( 'app=cms&module=databases&controller=databases&do=form&id=' . $row['database_id'] ), ); } $return['records'] = array( 'title' => 'content_database_manage_records', 'icon' => 'file-text', 'link' => Url::internal( 'app=cms&module=databases&controller=records&database_id=' . $row['database_id'] ), ); $return['permissions'] = array( 'title' => 'content_database_manage_permissions', 'icon' => 'lock', 'link' => Url::internal( 'app=cms&module=databases&controller=databases&do=permissions&id=' . $row['database_id'] ), ); if ( Member::loggedIn()->hasAcpRestriction( 'cms', 'categories', 'categories_manage' ) and $row['database_use_categories'] ) { $return['categories'] = array( 'title' => 'content_database_manage_categories', 'icon' => 'folder', 'link' => Url::internal( 'app=cms&module=databases&controller=categories&database_id=' . $row['database_id'] ), ); } if ( Member::loggedIn()->hasAcpRestriction( 'cms', 'fields', 'cms_fields_manage' ) ) { $return['fields'] = array( 'title' => 'content_database_manage_fields', 'icon' => 'tasks', 'link' => Url::internal( 'app=cms&module=databases&controller=fields&database_id=' . $row['database_id'] ), ); } if ( Member::loggedIn()->hasAcpRestriction( 'cms', 'databases', 'databases_edit' ) ) { $return['download'] = array( 'title' => 'download', 'icon' => 'download', 'link' => Url::internal( 'app=cms&module=databases&controller=databases&do=download&id=' . $row['database_id'] ), 'data' => array( 'controller' => 'cms.admin.databases.download', 'downloadURL' => Url::internal( "app=cms&module=databases&controller=databases&do=download&id=" . $row['database_id'] . '&go=true' ) ) ); } if ( Member::loggedIn()->hasAcpRestriction( 'cms', 'databases', 'databases_delete' ) ) { $return['delete'] = array( 'title' => 'delete', 'icon' => 'times-circle', 'link' => Url::internal( 'app=cms&module=databases&controller=databases&do=delete&id=' . $row['database_id'] ), 'data' => array( 'delete' => '' ) ); if ( CmsDatabases::isUsedAsReciprocalField( $row['database_id'] ) ) { unset( $return['delete']['data'] ); } } return $return; }; /* Javascript */ Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'admin_databases.js', 'cms', 'admin' ) ); /* Display */ Output::i()->output = (string) $table; Output::i()->title = Member::loggedIn()->language()->addToStack('menu__cms_databases_databases'); } /** * Add a database dialog * * @return void */ public function add() : void { Dispatcher::i()->checkAcpPermission( 'databases_add' ); $form = new Form( 'form', 'continue' ); $form->addMessage( 'cms_database_import_info', 'ipsMessage ipsMessage--warning' ); $form->add( new Upload( 'cms_database_import', NULL, FALSE, array( 'allowedFileTypes' => array( 'xml' ), 'temporary' => TRUE ), NULL, NULL, NULL, 'cms_database_import' ) ); if ( $values = $form->values() ) { /* Move it to a temporary location */ $tempFile = tempnam( TEMP_DIRECTORY, 'IPS' ); move_uploaded_file( $values['cms_database_import'], $tempFile ); /* Initate a redirector */ Output::i()->redirect( Url::internal( 'app=cms&module=databases&controller=databases&do=import' )->setQueryString( array( 'file' => $tempFile, 'key' => md5_file( $tempFile ) ) )->csrf() ); } Output::i()->output = Theme::i()->getTemplate( 'global', 'core', 'admin' )->block( 'cms_database_add_upload', $form, FALSE ); } /** * Import from upload * * @return void */ public function import() : void { Dispatcher::i()->checkAcpPermission( 'databases_add' ); Session::i()->csrfCheck(); if ( !file_exists( Request::i()->file ) or md5_file( Request::i()->file ) !== Request::i()->key ) { Output::i()->error( 'generic_error', '3T259/3', 403, '' ); } Output::i()->output = new MultipleRedirect( Url::internal( 'app=cms&module=databases&controller=databases&do=import' )->setQueryString( array( 'file' => Request::i()->file, 'key' => Request::i()->key ) )->csrf(), function( $data ) { /* Open XML file */ $xml = \IPS\Xml\XMLReader::safeOpen( Request::i()->file ); if ( ! @$xml->read() ) { @unlink( Request::i()->file ); Output::i()->error( 'xml_upload_invalid', '2T259/5', 403, '' ); } /* Is this the first batch? */ $i = 0; if ( !is_array( $data ) ) { $database = new CmsDatabases; $database->key = 'import_' . mt_rand(); $database->save(); /* Set default perms, these will be editable post DB import */ Db::i()->replace( 'core_permission_index', array( 'app' => 'cms', 'perm_type' => $database::$permType, 'perm_type_id' => $database->id, 'perm_view' => '*', 'perm_2' => '*', #read 'perm_3' => Settings::i()->admin_group, #add 'perm_4' => Settings::i()->admin_group, #edit 'perm_5' => Settings::i()->admin_group, #reply 'perm_6' => Settings::i()->admin_group, #rate 'perm_7' => Settings::i()->admin_group, #review ) ); $json = json_decode( @file_get_contents( \IPS\ROOT_PATH . "/applications/cms/data/databaseschema.json" ), true ); $table = $json['cms_custom_database_1']; $table['name'] = 'cms_custom_database_' . $database->id; foreach( $table['columns'] as $name => $data ) { if ( mb_substr( $name, 0, 6 ) === 'field_' ) { unset( $table['columns'][ $name ] ); } } foreach( $table['indexes'] as $name => $data ) { if ( mb_substr( $name, 0, 6 ) === 'field_' ) { unset( $table['indexes'][ $name ] ); } } try { if ( ! Db::i()->checkForTable( $table['name'] ) ) { Db::i()->createTable( $table ); } } catch(Db\Exception $ex ) { throw new LogicException( $ex ); } /* What version are we importing from? */ $data['sourceVersion'] = $xml->getAttribute( 'version' ); $langs = array(); foreach( array( 'content_db_lang_sl', 'content_db_lang_pl', 'content_db_lang_su', 'content_db_lang_pu', 'content_db_lang_ia' ) as $lang ) { if ( $xml->getAttribute( $lang ) ) { $langs[ $lang ] = $xml->getAttribute( $lang ); Lang::saveCustom( 'cms', $lang . "_" . $database->id, $xml->getAttribute( $lang ) ); if ( $lang === 'content_db_lang_pu' ) { Lang::saveCustom( 'cms', $lang . "_" . $database->id . '_pl', $xml->getAttribute( $lang ) ); } } } /* Other data */ while ( $xml->read() ) { $name = NULL; $desc = NULL; $displaySettings = [ 'index' => [ 'type' => 'categories', 'layout' => 'table' ] ]; if ( $xml->name == 'data' ) { /* Life is too short otherwise */ $node = new SimpleXMLElement( $xml->readOuterXML() ); foreach( $node->attributes() as $k => $v ) { switch( $k ) { case 'template_listing': $displaySettings['listing'] = ( $v == 'listing' ) ? [ 'layout' => 'table', 'template' => null ] : [ 'layout' => 'custom', 'template' => (string) $v ]; break; case 'template_display': $displaySettings['display'] = [ 'layout' => 'custom', 'template' => (string) $v ]; break; case 'template_form': $displaySettings['form'] = [ 'layout' => 'custom', 'template' => (string) $v ]; break; case 'template_categories': $displaySettings['categories'] = ( $v == 'category_index' ) ? [ 'layout' => 'table', 'template' => null ] : [ 'layout' => 'custom', 'template' => (string) $v ]; break; case 'template_featured': break; case 'cat_index_type': if( $v ) { $displaySettings['index'] = [ 'type' => 'all', 'layout' => 'grid' ]; } break; default: $database->$k = (string) $v; break; } } /* Any kids of your own? */ foreach( $node->children() as $k => $v ) { switch( $k ) { case 'name': $name = $v; break; case 'description': $desc = $v; break; case 'featured_settings': $json = json_decode( $v, true ); if( isset( $json['featured'] ) and $json['featured'] ) { $displaySettings['index']['type'] = 'featured'; } break; default: $tryJson = json_decode( $v, TRUE ); $database->$k = ( $tryJson ) ?: (string) $v; break; } } $database->display_settings = $displaySettings; Lang::saveCustom( 'cms', "content_db_" . $database->id, $name ); Lang::saveCustom( 'cms', "content_db_" . $database->id . '_desc', $desc ); Lang::saveCustom( 'cms', "digest_area_cms_records" . $database->id, $langs['content_db_lang_pu'] ); Lang::saveCustom( 'cms', "digest_area_cms_categories" . $database->id, $langs['content_db_lang_pu'] ); $menu = array(); /* Notification, search/followed/new content langs */ Lang::saveCustom( 'cms', "cms_records" . $database->id . '_pl', $langs['content_db_lang_su'] ); Lang::saveCustom( 'cms', "module__cms_records" . $database->id, $name ); break; } } $database->save(); /* set up some stores (and make sure they are clean from previous possibly broken imports */ Store::i()->db_import_cat_map = array(); Store::i()->db_import_cat_parent_map = array(); Store::i()->db_import_field_map = array(); Store::i()->db_import_db_id = $database->id; /* Start impoprting */ $data = array( 'next' => 'field', 'done' => 0 ); return array( $data, Member::loggedIn()->language()->addToStack('processing') ); } $database = CmsDatabases::load( Store::i()->db_import_db_id ); $xml->read(); $next = NULL; $areas = array( 'field', 'category' ); /* Only allow templates that were created after 5.0 */ if( isset( $data['sourceVersion'] ) and $data['sourceVersion'] >= 500000 ) { $areas[] = 'template'; } if ( $data['next'] ) { while ( $xml->read() ) { if( ! in_array( $xml->name, $areas ) OR $xml->nodeType != XMLReader::ELEMENT ) { continue; } $i++; if ( $data['done'] ) { if ( $i - 1 < $data['done'] ) { $xml->next(); continue; } } $doneSomething = false; if ( $xml->name == $data['next'] ) { $data['done']++; $areaName = $xml->name; $node = new SimpleXMLElement( $xml->readOuterXML() ); /* Import */ switch ( $areaName ) { case 'field': $attrs = array(); $perms = array(); foreach( $node->attributes() as $k => $v ) { if ( mb_substr( $k, 0, 5 ) === 'perm_' or in_array( $k, array( 'app', 'owner_only', 'friend_only', 'authorized_users' ) ) ) { $perms[ $k ] = (string) $v; } else { $attrs[ $k ] = (string) $v; } } /* Any kids of your own? */ $displayJson = NULL; foreach( $node->children() as $k => $v ) { if ( $k == 'field_name' ) { $k = 'field_title'; } $tryJson = json_decode( $v, TRUE ); if ( $k === 'field_display_json' AND is_array( $tryJson ) ) { $displayJson = $tryJson; continue; } $attrs[ $k ] = ( $tryJson ) ?: (string) $v; } /* Check to ensure we have the same field type, if not default to text so data is retained (buildHelper also does this */ if ( ! class_exists( '\IPS\cms\Fields\\' . IPS::mb_ucfirst( $attrs['field_type'] ) ) and ! class_exists( '\IPS\Helpers\Form\\' . IPS::mb_ucfirst( $attrs['field_type'] ) ) ) { $attrs['field_type'] = 'Text'; } /* Upload field has a special "is multiple" option we need to set */ if( IPS::mb_ucfirst( $attrs['field_type'] ) == 'Upload' ) { $attrs['field_upload_is_multiple'] = $attrs['field_is_multiple']; } $attrs['_skip_formatting'] = TRUE; $originalFieldId = $attrs['field_id']; unset( $attrs['field_id'] ); $fieldsClass = 'IPS\cms\Fields' . $database->id; $obj = new $fieldsClass; $values = $obj->formatFormValues( $attrs ); if ( isset( $attrs['field_extra'] ) ) { /* The export format is always correct */ $values['extra'] = $attrs['field_extra']; } $obj->saveForm( $values ); if ( $displayJson ) { $obj->display_json = $displayJson; $obj->save(); } if ( $originalFieldId == $database->field_title ) { $database->field_title = $obj->id; $database->save(); } else if ( $originalFieldId == $database->field_content ) { $database->field_content = $obj->id; $database->save(); } $map = Store::i()->db_import_field_map; $map[ $originalFieldId ] = $obj->id; Store::i()->db_import_field_map = $map; $existingPerms = $obj->permissions(); $newPerms = array_merge( $perms, array( 'perm_id' => $existingPerms['perm_id'], 'perm_type_id' => $obj->id ) ); /* Set default permissions if not defined in export */ if( !isset( $perms['perm_view'] ) ) { $newPerms['perm_view'] = '*'; } if( !isset( $perms['perm_2'] ) ) { $newPerms['perm_2'] = Settings::i()->admin_group; } if( !isset( $perms['perm_3'] ) ) { $newPerms['perm_3'] = Settings::i()->admin_group; } Db::i()->update( 'core_permission_index', $newPerms, array( 'perm_id=?', $existingPerms['perm_id'] ) ); Lang::saveCustom( 'cms', "content_field_" . $obj->id, $attrs['field_title'] ); if ( isset($attrs['field_description']) ) { Lang::saveCustom( 'cms', "content_field_" . $obj->id . '_desc', $attrs['field_description'] ); } if ( isset($attrs['field_validator_error']) ) { Lang::saveCustom( 'cms', "content_field_" . $obj->id . '_validation_error', $attrs['field_validator_error'] ); } $next = 'category'; $doneSomething = true; break; case 'category': $attrs = array(); $perms = array(); foreach( $node->attributes() as $k => $v ) { if ( mb_substr( $k, 0, 5 ) === 'perm_' or in_array( $k, array( 'app', 'owner_only', 'friend_only', 'authorized_users' ) ) ) { $perms[ $k ] = (string) $v; } else { switch( $k ) { case 'category_parent_id': $attrs[ $k ] = (int) $v; break; case 'category_club_id': // always null because the club itself is not imported here $attrs[ $k ] = null; break; default: $attrs[ $k ] = (string) $v; break; } } } /* Any kids of your own? */ foreach( $node->children() as $k => $v ) { $tryJson = json_decode( $v, TRUE ); $attrs[ $k ] = ( $tryJson ) ?: (string) $v; } $originalCategoryId = $attrs['category_id']; $originalParentId = $attrs['category_parent_id']; unset( $attrs['category_id'] ); $attrs['category_parent_id'] = 0; /* Create a category */ $category = new Categories; $category->database_id = $database->id; $category::$permType = 'categories_' . $database->id; $category->saveForm( $category->formatFormValues( $attrs ) ); if ( $category->fields AND $category->fields !== '*' ) { if ( is_array( $category->fields ) ) { $field_map = Store::i()->db_import_field_map; $newMap = array(); foreach( $category->fields as $fid ) { if ( isset( $field_map[ $fid ] ) ) { $newMap[] = $field_map[ $fid ]; } } if ( count( $newMap ) ) { $category->fields = json_encode( $newMap ); $category->save(); } } } /* If the database does not have a default category yet, assign one */ if( !$database->use_categories and !$database->default_category ) { $database->default_category = $category->id; $database->save(); } $cat_map = Store::i()->db_import_cat_map; $parent_map = Store::i()->db_import_cat_parent_map; $cat_map[ $originalCategoryId ] = $category->id; $parent_map[ $category->id ] = $originalParentId; Store::i()->db_import_cat_map = $cat_map; Store::i()->db_import_cat_parent_map = $parent_map; /* Perms */ $existingPerms = $category->permissions(); /* Make sure these are at least visible */ if( $category->has_perms ) { if( !isset( $perms['perm_view'] ) or empty( $perms['perm_view'] ) ) { $perms['perm_view'] = '*'; } if( !isset( $perms['perm_2'] ) or empty( $perms['perm_2'] ) ) { $perms['perm_2'] = Settings::i()->admin_group; } if( !isset( $perms['perm_3'] ) or empty( $perms['perm_3'] ) ) { $perms['perm_3'] = Settings::i()->admin_group; } Db::i()->update( 'core_permission_index', array_merge( $perms, array( 'perm_id' => $existingPerms['perm_id'], 'perm_type_id' => $category->id ) ), array( 'perm_id=?', $existingPerms['perm_id'] ) ); } $next = 'template'; $doneSomething = true; break; case 'template': $templates = \IPS\cms\Theme::i()->getAllTemplates( 'cms', 'database', '', Theme::RETURN_ALL ); $attrs = array(); foreach( $node->attributes() as $k => $v ) { $attrs[ $k ] = (string) $v; } /* Any kids of your own? */ foreach( $node->children() as $k => $v ) { $tryJson = json_decode( $v, TRUE ); $attrs[ $k ] = ( $tryJson ) ?: (string) $v; } $obj = new Templates; $obj->location = $attrs['template_location']; $obj->group = $attrs['template_group']; $obj->title = $attrs['template_title']; $obj->params = $attrs['template_params']; $obj->content = $attrs['template_content']; $obj->original_group = $attrs['template_original_group']; $obj->user_created = 1; $obj->user_edited = 1; $obj->desc = ''; if ( $attrs['template_group'] !== 'template_form' ) { $obj->group .= '_' . $database->id; foreach( array( 'template_listing', 'template_display', 'template_categories', 'template_form', 'template_featured' ) as $name ) { if ( $database->$name == $attrs['template_group'] ) { $database->$name = $obj->group; $database->save(); } } } else { $obj->title .= '_' . $database->_id; $database->template_form = $obj->title; $database->save(); } $obj->save(); $obj->key = 'database_' . Friendly::seoTitle( $obj->group ) . '_' . Friendly::seoTitle( $obj->title ) . '_' . $obj->id; $obj->save(); $doneSomething = true; $next = NULL; break; } if ( $i % 10 === 0 ) { return array( $data, Member::loggedIn()->language()->addToStack('cms_db_import_progress', FALSE, array( 'sprintf' => array( ucfirst( $data['next'] ) ) ) ) ); } } else { /* Did we do anything? if not, skip to the next section */ if ( ! $doneSomething ) { switch ( $data['next'] ) { case 'field': $data['next'] = 'category'; break; case 'category': $data['next'] = 'template'; break; case 'template': $data['next'] = null; break; } if ( $data['next'] ) { return array( $data, Member::loggedIn()->language()->addToStack('cms_db_import_progress', FALSE, array( 'sprintf' => array( ucfirst( $data['next'] ) ) ) ) ); } } } $xml->next(); } /* Done */ $data['next'] = $next; return array( $data, Member::loggedIn()->language()->addToStack('cms_db_import_progress', FALSE, array( 'sprintf' => array( ucfirst( $data['next'] ?? '' ) ) ) ) ); } else { return NULL; } }, function() { /* Remap categories */ if ( isset( Store::i()->db_import_cat_map ) and isset( Store::i()->db_import_cat_parent_map ) ) { foreach( Store::i()->db_import_cat_parent_map as $new => $oldParent ) { if ( $oldParent > 0 ) { if ( isset( Store::i()->db_import_cat_map[ $oldParent ] ) ) { $cat = Categories::load( $new ); $cat->parent_id = Store::i()->db_import_cat_map[ $oldParent ]; $cat->save(); } } } } $databaseId = Store::i()->db_import_db_id; $database = CmsDatabases::load( $databaseId ); /* SORT IT OUT! */ if ( isset( Store::i()->db_import_field_map ) ) { $map = Store::i()->db_import_field_map; $sortId = intval( mb_substr( $database->field_sort, 6 ) ); if ( isset( $map[ $sortId ] ) ) { $database->field_sort = 'field_' . $map[ $sortId ]; $database->save(); } } foreach( array( 'template_listing', 'template_display', 'template_categories', 'template_form', 'template_featured' ) as $name ) { Templates::fixTemplateTags( $database->$name ); } unset( Store::i()->db_import_db_id ); unset( Store::i()->db_import_cat_map ); unset( Store::i()->db_import_cat_parent_map ); unset( Store::i()->db_import_field_map ); @unlink( Request::i()->file ); Session::i()->log( 'acplogs__cms_imported_database', array( 'content_db_' . $database->id => TRUE ) ); /* Done */ Output::i()->redirect( Url::internal( 'app=cms&module=databases&controller=databases&do=permissions&id=' . $databaseId ) ); } ); } /** * Download * * @return void */ protected function download() : void { Dispatcher::i()->checkAcpPermission( 'databases_edit' ); try { $database = CmsDatabases::load( Request::i()->id ); } catch( OutofRangeException $ex ) { Output::i()->error( 'cms_database_not_exist', '3T259/4', 403, '' ); } if( empty( Request::i()->go ) ) { Output::i()->output = Theme::i()->getTemplate( 'databases' )->downloadDialog( $database ); } else { /* We need to know the database schema to prevent non-standard columns from add-ons being added to the XML (which then causes the import to fail) */ $schema = json_decode( file_get_contents( \IPS\ROOT_PATH . '/applications/cms/data/schema.json' ), TRUE ); /* Init */ $xml = new XMLWriter; $xml->openMemory(); $xml->setIndent( TRUE ); $xml->startDocument( '1.0', 'UTF-8' ); /* Root tag */ $xml->startElement('database'); /* Add the current version */ $xml->startAttribute( 'version' ); $xml->text( Application::load( 'cms' )->long_version ); $xml->endAttribute(); foreach( array( 'content_db_lang_sl', 'content_db_lang_pl', 'content_db_lang_su', 'content_db_lang_pu', 'content_db_lang_ia' ) as $lang ) { try { $xml->startAttribute( $lang ); $xml->text( Member::loggedIn()->language()->get( $lang . '_' . $database->id ) ); $xml->endAttribute(); } catch( UnderflowException $ex ) { } } /* Initiate the tag */ $xml->startElement('data'); $arrays = array(); foreach( array( 'use_categories', 'all_editable', 'revisions', 'field_title', 'field_content', 'field_sort', 'field_direction', 'field_perpage', 'comment_approve', 'record_approve', 'rss', 'comment_bump', 'forum_record', 'forum_comments', 'forum_delete', 'forum_forum', 'forum_prefix', 'forum_suffix', 'search', 'fixed_field_perms', 'fixed_field_settings', 'options', 'display_settings' ) as $field ) { if ( is_array( $database->$field ) ) { $arrays[ $field ] = $database->$field; } else { $store = $database->$field; if ( $database->$field instanceof Bitwise ) { $bwfield = $database->$field; $store = intval( $bwfield->values['options'] ); } $xml->startAttribute( $field ); $xml->text( $store ); $xml->endAttribute(); } } if ( count( $arrays ) ) { foreach( $arrays as $field => $v ) { $xml->startElement( $field ); $xml->writeCData( json_encode( $v ) ); $xml->endElement(); } } $xml->startElement('name'); $xml->writeCData( $database->_title ); $xml->endElement(); $xml->startElement('description'); $xml->writeCData( $database->_description ); $xml->endElement(); $xml->endElement(); /* Custom fields */ $textFields = array( 'field_extra', 'field_default_value', 'field_name', 'field_description', 'field_display_json', 'field_validator_error' ); $removeFields = array( 'field_database_id', 'perm_id', 'perm_type', 'perm_type_id' ); $fieldSchema = $schema['cms_database_fields']; foreach ( Db::i()->select( 'f.*, w.word_custom as field_name, w2.word_custom as field_description, w3.word_custom as field_validator_error, p.*', array('cms_database_fields', 'f'), array( 'field_database_id=?', $database->id ) ) ->join( array( 'core_sys_lang_words', 'w' ), "w.word_key=CONCAT( 'content_field_', f.field_id ) and w.lang_id=" . Lang::defaultLanguage() ) ->join( array( 'core_sys_lang_words', 'w2' ), "w2.word_key=CONCAT( 'content_field_', f.field_id, '_desc' ) and w2.lang_id=" . Lang::defaultLanguage() ) ->join( array( 'core_sys_lang_words', 'w3' ), "w3.word_key=CONCAT( 'content_field_', f.field_id, '_validation_error' ) and w3.lang_id=" . Lang::defaultLanguage() ) ->join( array( 'core_permission_index', 'p' ), "p.app='cms' and p.perm_type='fields' and p.perm_type_id=f.field_id" ) as $row ) { /* Initiate the tag */ $xml->startElement('field'); foreach( $row as $k => $v ) { if ( !in_array( $k, $removeFields ) AND !in_array( $k, $textFields ) and ( mb_substr( $k, 0, 5 ) == 'perm_' or array_key_exists( $k, $fieldSchema['columns'] ) ) ) { $xml->startAttribute( $k ); $xml->text( $v ?? '' ); $xml->endAttribute(); } } /* Write (potential) HTML fields */ foreach( $textFields as $field ) { if ( isset( $row[ $field ] ) ) { $xml->startElement( $field ); if ( preg_match( '/[<>&"]/', $row[ $field ] ) ) { $xml->writeCData( str_replace( ']]>', ']]]]>', $row[ $field ] ) ); } else { $xml->text( $row[ $field ] ); } $xml->endElement(); } } /* Close the tag */ $xml->endElement(); } /* Categories */ $textFields = array( 'category_name', 'category_description', 'category_meta_keywords', 'category_meta_description', 'category_page_title' ); $removeFields = array( 'category_database_id', 'category_last_record_id', 'category_last_record_date', 'category_last_record_member', 'category_last_record_name', 'category_last_record_seo_name', 'category_records', 'category_record_comments', 'category_record_comments_queued', 'category_rss_cache', 'category_rss_cached', 'category_rss_exclude', 'category_forum_override', 'category_forum_record', 'category_forum_comments', 'category_forum_delete', 'category_forum_suffix', 'category_forum_prefix', 'category_forum_forum', 'category_full_path', 'category_last_title', 'category_last_seo_title', 'perm_id', 'perm_type_id', 'perm_type' ); $categorySchema = $schema['cms_database_categories']; foreach ( Db::i()->select( 'c.*, w.word_custom as category_name, w2.word_custom as category_description, p.*', array('cms_database_categories', 'c'), array( 'category_database_id=?', $database->id ) ) ->join( array( 'core_sys_lang_words', 'w' ), "w.word_key=CONCAT( 'content_cat_name_', c.category_id ) and w.lang_id=" . Lang::defaultLanguage() ) ->join( array( 'core_sys_lang_words', 'w2' ), "w2.word_key=CONCAT( 'content_cat_name_', c.category_id, '_desc' ) and w2.lang_id=" . Lang::defaultLanguage() ) ->join( array( 'core_permission_index', 'p' ), "p.app='cms' and p.perm_type='categories' and p.perm_type_id=c.category_id" ) as $row ) { /* Initiate the tag */ $xml->startElement('category'); foreach( $row as $k => $v ) { if ( !in_array( $k, $removeFields ) AND !in_array( $k, $textFields ) and ( mb_substr( $k, 0, 5 ) == 'perm_' or array_key_exists( $k, $categorySchema['columns'] ) ) ) { $xml->startAttribute( $k ); $xml->text( $v ?? '' ); $xml->endAttribute(); } } /* Write (potential) HTML fields */ foreach( $textFields as $field ) { if ( isset( $row[ $field ] ) ) { $xml->startElement( $field ); if ( preg_match( '/[<>&]/', $row[ $field ] ) ) { $xml->writeCData( str_replace( ']]>', ']]]]>', $row[ $field ] ) ); } else { $xml->text( $row[ $field ] ); } $xml->endElement(); } } /* Close the tag */ $xml->endElement(); } /* Templates @todo see if blocks are used in template and export too */ $templates = \IPS\cms\Theme::i()->getAllTemplates( 'cms', 'database', '', Theme::RETURN_ALL ); $toSave = array(); foreach( array('template_listing', 'template_display', 'template_categories', 'template_featured') as $area ) { if ( isset( $templates['cms']['database'][ $database->$area ] ) and is_array( $templates['cms']['database'][ $database->$area ] ) ) { /* Only fetch edited/added templates, no point in fetching default theme templates */ foreach( $templates['cms']['database'][ $database->$area ] as $key => $item ) { if ( $item['template_user_created'] or $item['template_user_edited'] ) { $toSave[ $database->$area ][ $key ] = $item; } } } } /* Form template */ if ( isset( $templates['cms']['database']['form'][ $database->template_form ] ) ) { $item = $templates['cms']['database']['form'][ $database->template_form ]; if ( $item['template_user_created'] or $item['template_user_edited'] ) { $toSave['form'][ $database->template_form ] = $item; } } if ( count( $toSave ) ) { foreach( $toSave as $group => $items ) { foreach( $items as $key => $item ) { /* Initiate the