1058 lines
32 KiB
PHP
1058 lines
32 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @brief Record View
|
|
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
|
* @copyright (c) Invision Power Services, Inc.
|
|
* @license https://www.invisioncommunity.com/legal/standards/
|
|
* @package Invision Community
|
|
* @subpackage Content
|
|
* @since 17 Apr 2014
|
|
*/
|
|
|
|
namespace IPS\cms\modules\front\database;
|
|
|
|
/* To prevent PHP errors (extending class does not exist) revealing path */
|
|
|
|
use BadMethodCallException;
|
|
use DateTimeInterface;
|
|
use DomainException;
|
|
use Exception;
|
|
use IPS\cms\Categories;
|
|
use IPS\cms\Databases;
|
|
use IPS\cms\Databases\Dispatcher;
|
|
use IPS\cms\Fields;
|
|
use IPS\cms\Pages\Page;
|
|
use IPS\cms\Records;
|
|
use IPS\cms\Records\Revisions;
|
|
use IPS\Content\Comment;
|
|
use IPS\Content\Controller;
|
|
use IPS\Content\Item;
|
|
use IPS\DateTime;
|
|
use IPS\File;
|
|
use IPS\GeoLocation;
|
|
use IPS\Helpers\Form;
|
|
use IPS\Helpers\Form\Checkbox;
|
|
use IPS\Helpers\Form\Radio;
|
|
use IPS\Helpers\Table\Db;
|
|
use IPS\Http\Url;
|
|
use IPS\Member;
|
|
use IPS\Output;
|
|
use IPS\Output\UI\UiExtension;
|
|
use IPS\Request;
|
|
use IPS\Session;
|
|
use IPS\Settings;
|
|
use IPS\Theme;
|
|
use LogicException;
|
|
use OutOfRangeException;
|
|
use RuntimeException;
|
|
use function count;
|
|
use function defined;
|
|
use function get_class;
|
|
use const IPS\THUMBNAIL_SIZE;
|
|
|
|
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
|
{
|
|
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Record View
|
|
*/
|
|
class record extends Controller
|
|
{
|
|
/**
|
|
* [Content\Controller] Class
|
|
*/
|
|
protected static string $contentModel = '';
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param Url|null $url The base URL for this controller or NULL to calculate automatically
|
|
* @return void
|
|
*/
|
|
public function __construct( Url $url=NULL )
|
|
{
|
|
static::$contentModel = 'IPS\cms\Records' . Dispatcher::i()->databaseId;
|
|
|
|
parent::__construct( Dispatcher::i()->url );
|
|
}
|
|
|
|
/**
|
|
* View Record
|
|
*
|
|
* @return mixed
|
|
*/
|
|
protected function manage() : mixed
|
|
{
|
|
/* Load record */
|
|
/** @var Records $record */
|
|
try
|
|
{
|
|
$record = parent::manage();
|
|
}
|
|
catch( Exception $e )
|
|
{
|
|
Output::i()->error( 'node_error', '2T252/1', 403, '' );
|
|
}
|
|
|
|
if ( $record === NULL )
|
|
{
|
|
Output::i()->error( 'node_error', '2T252/2', 404, '' );
|
|
}
|
|
|
|
if ( Request::i()->view )
|
|
{
|
|
$this->_doViewCheck();
|
|
}
|
|
|
|
/* Sort out comments and reviews */
|
|
$tabs = $record->commentReviewTabs();
|
|
$_tabs = array_keys( $tabs );
|
|
$tab = isset( Request::i()->tab ) ? Request::i()->tab : array_shift( $_tabs );
|
|
$activeTabContents = $record->commentReviews( $tab );
|
|
$comments = count( $tabs ) ? \IPS\cms\Theme::i()->getTemplate( $record->container()->_template_display, 'cms', 'database' )->commentsAndReviewsTabs( Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $tab, $activeTabContents, $record->url(), 'tab', FALSE, FALSE ), md5( $record->url() ) ) : NULL;
|
|
|
|
if ( Request::i()->isAjax() and !isset( Request::i()->rating_submitted ) and ( isset( Request::i()->page ) OR isset( Request::i()->tab ) ) and $activeTabContents )
|
|
{
|
|
Output::i()->sendOutput( $activeTabContents );
|
|
}
|
|
|
|
/* @var Categories $class */
|
|
$class = '\IPS\cms\Categories' . $record::$customDatabaseId;
|
|
$category = $class::load( $record->category_id );
|
|
|
|
$fieldsClass = '\\IPS\\cms\\Fields' . $record::$customDatabaseId;
|
|
/* @var Fields $fieldsClass */
|
|
$updateFields = $fieldsClass::fields( $record->fieldValues(), 'edit', $record->container(), $fieldsClass::FIELD_DISPLAY_COMMENTFORM, $record );
|
|
$form = null;
|
|
|
|
/* We need edit permission to change the record */
|
|
if ( count( $updateFields ) and $record->canEdit() )
|
|
{
|
|
$form = new Form( 'update_record', 'update', $record->url()->setQueryString( array( 'd' => $record::$customDatabaseId ) ) );
|
|
$form->class = 'ipsForm--vertical ipsForm--manage-record';
|
|
|
|
$hasAdditionalFields = false;
|
|
foreach( $updateFields as $id => $field )
|
|
{
|
|
$form->add( $field );
|
|
if( $id != $record::database()->field_title AND $id != $record::database()->field_content )
|
|
{
|
|
$hasAdditionalFields = true;
|
|
}
|
|
}
|
|
|
|
/* The comment is only added for fields that are NOT the title/content. So don't show this checkbox if the only field available is one of those. */
|
|
if( $hasAdditionalFields AND $record->canComment() )
|
|
{
|
|
$form->add( new Checkbox( 'record_display_field_change', TRUE, FALSE ) );
|
|
}
|
|
|
|
if ( $values = $form->values() )
|
|
{
|
|
$record->processBeforeEdit( $values );
|
|
|
|
/* Custom fields */
|
|
$customValues = array();
|
|
$fieldsClass = 'IPS\cms\Fields' . $record::$customDatabaseId;
|
|
|
|
foreach( $values as $k => $v )
|
|
{
|
|
if ( mb_substr( $k, 0, 14 ) === 'content_field_' )
|
|
{
|
|
$customValues[ mb_substr( $k, 8 ) ] = $v;
|
|
}
|
|
}
|
|
|
|
if ( count( $customValues ) )
|
|
{
|
|
/* Store a revision before we change any values */
|
|
if ( $record::database()->revisions )
|
|
{
|
|
$revision = new Revisions;
|
|
$revision->database_id = $record::$customDatabaseId;
|
|
$revision->record_id = $record->_id;
|
|
$revision->data = $record->fieldValues( TRUE );
|
|
$revision->save();
|
|
}
|
|
|
|
if ( isset( $values['record_display_field_change'] ) AND $values['record_display_field_change'] )
|
|
{
|
|
$record->addCommentWhenFiltersChanged( $values );
|
|
}
|
|
else
|
|
{
|
|
/* Set excludes for custom field updates. We'll send the notifications later, once we know the new content of the fields. */
|
|
$record->setFieldQuoteAndMentionExcludes();
|
|
}
|
|
|
|
foreach( $fieldsClass::fields( $customValues, 'edit', $record->category_id ? $category : NULL, $fieldsClass::FIELD_DISPLAY_COMMENTFORM ) as $key => $field )
|
|
{
|
|
$key = 'field_' . $key;
|
|
$record->$key = $field::stringValue($values[$field->name] ?? NULL);
|
|
}
|
|
|
|
/* Send custom field update notifications */
|
|
if ( !isset( $values['record_display_field_change'] ) OR !$values['record_display_field_change'] )
|
|
{
|
|
$record->sendFieldQuoteAndMentionNotifications();
|
|
}
|
|
|
|
$record->save();
|
|
$record->processAfterEdit( $values );
|
|
|
|
$fieldObjects = $fieldsClass::data( NULL, $record->category_id ? $category : NULL );
|
|
|
|
foreach( $fieldObjects as $id => $row )
|
|
{
|
|
if ( $row->type == 'Item' )
|
|
{
|
|
$record->processItemFieldData( $row );
|
|
}
|
|
}
|
|
Output::i()->redirect( $record->url() );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( $record->record_meta_keywords )
|
|
{
|
|
Output::i()->metaTags['keywords'] = $record->record_meta_keywords;
|
|
}
|
|
|
|
if ( $record->record_meta_description )
|
|
{
|
|
Output::i()->metaTags['description'] = $record->record_meta_description;
|
|
Output::i()->metaTags['og:description'] = $record->record_meta_description;
|
|
}
|
|
|
|
/* Set record URL as canonical tag */
|
|
if ( $record::database()->canonical_flag == 1 and ( isset( Request::i()->page ) and Request::i()->page > 1 ) )
|
|
{
|
|
Output::i()->linkTags['canonical'] = $record->url()->setPage( 'page', Request::i()->page );
|
|
}
|
|
else
|
|
{
|
|
Output::i()->linkTags['canonical'] = $record->url();
|
|
}
|
|
|
|
/* Update location */
|
|
if( $record->database()->use_categories )
|
|
{
|
|
Session::i()->setLocation( $record->url(), $record->onlineListPermissions(), 'loc_cms_viewing_db_record', array( $record->_title => FALSE, 'content_db_' . $record->database()->id => TRUE ,'content_cat_name_' . $category->id => TRUE ) );
|
|
}
|
|
else
|
|
{
|
|
Session::i()->setLocation( $record->url(), $record->onlineListPermissions(), 'loc_cms_viewing_db_record_no_cats', array( $record->_title => FALSE, 'content_db_' . $record->database()->id => TRUE ) );
|
|
}
|
|
|
|
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'records/record.css', 'cms', 'front' ) );
|
|
|
|
/* Next unread */
|
|
try
|
|
{
|
|
$nextUnread = $record->containerHasUnread();
|
|
}
|
|
catch( Exception $e )
|
|
{
|
|
$nextUnread = NULL;
|
|
}
|
|
|
|
if ( $record->record_image )
|
|
{
|
|
Output::i()->metaTags['og:image'] = (string) File::get( 'cms_Records', $record->record_image )->url;
|
|
}
|
|
|
|
/* Add Json-LD */
|
|
$jsonLdText = $record->truncated( TRUE, NULL );
|
|
|
|
Output::i()->jsonLd['article'] = array(
|
|
'@context' => "https://schema.org",
|
|
'@type' => "Article",
|
|
'url' => (string) $record->url(),
|
|
'discussionUrl' => (string) $record->url(),
|
|
'mainEntityOfPage' => (string) $record->url(),
|
|
'name' => $record->_title,
|
|
'headline' => $record->_title,
|
|
'text' => $jsonLdText,
|
|
'articleBody' => $jsonLdText,
|
|
'dateCreated' => DateTime::ts( $record->record_saved )->format( DateTimeInterface::ATOM ),
|
|
'datePublished' => DateTime::ts( $record->record_publish_date ?: $record->record_saved )->format( DateTimeInterface::ATOM ),
|
|
'dateModified' => DateTime::ts( $record->record_edit_time ?: ( $record->record_publish_date ?: $record->record_saved ) )->format( DateTimeInterface::ATOM ),
|
|
'pageStart' => 1,
|
|
'pageEnd' => $record->commentPageCount(),
|
|
'author' => array(
|
|
'@type' => 'Person',
|
|
'name' => (string) Member::load( $record->member_id )->name,
|
|
'image' => (string) Member::load( $record->member_id )->get_photo( TRUE, TRUE )
|
|
),
|
|
'publisher' => array(
|
|
'@id' => Settings::i()->base_url . '#organization',
|
|
'member' => array(
|
|
'@type' => "Person",
|
|
'name' => Member::load( $record->member_id )->name,
|
|
'image' => (string) Member::load( $record->member_id )->get_photo( TRUE, TRUE ),
|
|
)
|
|
),
|
|
'interactionStatistic' => array(
|
|
array(
|
|
'@type' => 'InteractionCounter',
|
|
'interactionType' => "https://schema.org/ViewAction",
|
|
'userInteractionCount' => $record->record_views
|
|
),
|
|
array(
|
|
'@type' => 'InteractionCounter',
|
|
'interactionType' => "https://schema.org/FollowAction",
|
|
'userInteractionCount' => $record::containerFollowerCount( $record->container() )
|
|
),
|
|
),
|
|
);
|
|
|
|
/* Do we have a real author? */
|
|
if( $record->member_id )
|
|
{
|
|
Output::i()->jsonLd['article']['author']['url'] = (string) Member::load( $record->member_id )->url();
|
|
Output::i()->jsonLd['article']['publisher']['member']['url'] = (string) Member::load( $record->member_id )->url();
|
|
}
|
|
|
|
$logo = NULL;
|
|
if( !empty( Theme::i()->logo['front']['url'] ) )
|
|
{
|
|
try
|
|
{
|
|
$logo = Theme::i()->logo['front']['url'];
|
|
}
|
|
/* File doesn't exist */
|
|
catch ( RuntimeException | DomainException $e ){}
|
|
}
|
|
Output::i()->jsonLd['article']['publisher']['logo'] = array(
|
|
'@type' => 'ImageObject',
|
|
'url' => $logo ? (string) $logo : (string) Member::load( $record->member_id )->get_photo( TRUE, TRUE ),
|
|
);
|
|
|
|
/* Image is required */
|
|
if( $record->record_image )
|
|
{
|
|
try
|
|
{
|
|
$imageObj = File::get( 'cms_Records', $record->record_image );
|
|
|
|
Output::i()->jsonLd['article']['image'] = array(
|
|
'@type' => 'ImageObject',
|
|
'url' => (string) $imageObj->url
|
|
);
|
|
}
|
|
/* File doesn't exist */
|
|
catch ( RuntimeException | DomainException $e ){}
|
|
}
|
|
else
|
|
{
|
|
$photoVars = explode( 'x', THUMBNAIL_SIZE );
|
|
|
|
Output::i()->jsonLd['article']['image'] = array(
|
|
'@type' => 'ImageObject',
|
|
'url' => Member::load( $record->member_id )->get_photo( TRUE, TRUE ),
|
|
'width' => $photoVars[0],
|
|
'height' => $photoVars[1]
|
|
);
|
|
}
|
|
|
|
if( $record::database()->options['reviews'] or $record->container()->allow_rating )
|
|
{
|
|
Output::i()->jsonLd['article']['interactionStatistic'][] = array(
|
|
'@type' => 'InteractionCounter',
|
|
'interactionType' => "https://schema.org/ReviewAction",
|
|
'userInteractionCount' => $record->record_reviews
|
|
);
|
|
}
|
|
|
|
if( $record::database()->options['comments'] )
|
|
{
|
|
Output::i()->jsonLd['article']['commentCount'] = $record->record_comments;
|
|
Output::i()->jsonLd['article']['interactionStatistic'][] = array(
|
|
'@type' => 'InteractionCounter',
|
|
'interactionType' => "https://schema.org/CommentAction",
|
|
'userInteractionCount' => $record->record_comments
|
|
);
|
|
}
|
|
|
|
$this->showClubHeader();
|
|
/* Set default search to this record */
|
|
if ( ! $record::database()->search )
|
|
{
|
|
Output::i()->defaultSearchOption = array( 'all', 'search_everything' );
|
|
}
|
|
else
|
|
{
|
|
$type = mb_strtolower( str_replace( '\\', '_', mb_substr( get_class( $record ), 4 ) ) );
|
|
Output::i()->defaultSearchOption = array( $type, "{$type}_pl" );
|
|
|
|
Output::i()->contextualSearchOptions = array();
|
|
Output::i()->contextualSearchOptions[ Member::loggedIn()->language()->addToStack( 'search_contextual_item', FALSE, array( 'sprintf' => array( Member::loggedIn()->language()->addToStack( $record::$title ) ) ) ) ] = array( 'type' => $type, 'item' => $record->_id );
|
|
|
|
try
|
|
{
|
|
$container = $record->container();
|
|
Output::i()->contextualSearchOptions[ Member::loggedIn()->language()->addToStack( 'search_contextual_item_categories' ) ] = array( 'type' => mb_strtolower( str_replace( '\\', '_', mb_substr( get_class( $record ), 4 ) ) ), 'nodes' => $container->_id );
|
|
}
|
|
catch ( BadMethodCallException $e ) { }
|
|
}
|
|
|
|
Dispatcher::i()->output .= \IPS\cms\Theme::i()->getTemplate( $record->container()->_template_display, 'cms', 'database' )->record( $record, $comments, $form, $nextUnread );
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Set the breadcrumb and title
|
|
*
|
|
* @param Item $item Content item
|
|
* @param bool $link Link the content item element in the breadcrumb
|
|
* @return void
|
|
*/
|
|
protected function _setBreadcrumbAndTitle( Item $item, bool $link=TRUE ): void
|
|
{
|
|
$database = Databases::load( Dispatcher::i()->databaseId );
|
|
if ( $database->use_categories )
|
|
{
|
|
parent::_setBreadcrumbAndTitle( $item, $link );
|
|
}
|
|
else
|
|
{
|
|
Output::i()->breadcrumb[] = array( $link ? $item->url() : NULL, $item->mapped('title') );
|
|
|
|
$title = ( isset( Request::i()->page ) and Request::i()->page > 1 ) ? Member::loggedIn()->language()->addToStack( 'title_with_page_number', FALSE, array( 'sprintf' => array( $item->mapped('title') . ' - ' . $database->pageTitle(), Request::i()->page ) ) ) : $item->mapped('title') . ' - ' . $database->pageTitle();
|
|
Output::i()->title = $title;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* View check
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function _doViewCheck() : void
|
|
{
|
|
try
|
|
{
|
|
/* @var Item $class */
|
|
$class = static::$contentModel;
|
|
$topic = $class::loadAndCheckPerms( Request::i()->id );
|
|
|
|
switch( Request::i()->view )
|
|
{
|
|
case 'getnewpost':
|
|
Output::i()->redirect( $topic->url( 'getNewComment' ) );
|
|
break;
|
|
|
|
case 'getlastpost':
|
|
Output::i()->redirect( $topic->url( 'getLastComment' ) );
|
|
break;
|
|
}
|
|
}
|
|
catch( OutOfRangeException $e )
|
|
{
|
|
Output::i()->error( 'node_error', '2F173/F', 403, '' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Revisions
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function revisions() : void
|
|
{
|
|
/* @var Records $recordClass */
|
|
$recordClass = '\IPS\cms\Records' . Dispatcher::i()->databaseId;
|
|
|
|
try
|
|
{
|
|
$record = $recordClass::loadAndCheckPerms( Request::i()->id );
|
|
$category = $record->container();
|
|
}
|
|
catch ( OutOfRangeException $e )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/4', 403, '' );
|
|
}
|
|
|
|
if ( ! $record->canManageRevisions() )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/5', 403, '' );
|
|
}
|
|
|
|
$title = Member::loggedIn()->language()->addToStack('content_revision_record_title', FALSE, array( 'sprintf' => array( $record->_title ) ) );
|
|
|
|
$table = new Db( 'cms_database_revisions', $record->url('revisions'), array( 'revision_database_id=? and revision_record_id=?', $record::$customDatabaseId, $record->_id ) );
|
|
$table->tableTemplate = array( Theme::i()->getTemplate( 'revisions', 'cms', 'front' ), 'table' );
|
|
$table->rowsTemplate = array( Theme::i()->getTemplate( 'revisions', 'cms', 'front' ), 'rows' );
|
|
$table->title = $title;
|
|
$table->include = array( 'revision_id', 'revision_date', 'revision_data', 'revision_member_id' );
|
|
$table->mainColumn = 'revision_date';
|
|
$table->sortBy = $table->sortBy ?: 'revision_date';
|
|
$table->sortDirection = $table->sortDirection ?: 'desc';
|
|
|
|
/* Parsers */
|
|
$table->parsers = array(
|
|
'revision_member_id' => function( $val )
|
|
{
|
|
return Member::load( $val );
|
|
},
|
|
'revision_date' => function( $val )
|
|
{
|
|
return DateTime::ts( $val )->relative();
|
|
},
|
|
'revision_data' => function( $val, $row ) use ( $record )
|
|
{
|
|
return Revisions::load( $row['revision_id'] )->getDiffHtmlTables( $record::$customDatabaseId, $record, true );
|
|
}
|
|
);
|
|
|
|
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'system/diff.css', 'core', 'admin' ) );
|
|
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'codemirror/diff_match_patch.js', 'core', 'interface' ) );
|
|
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js('front_records.js', 'cms' ) );
|
|
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'codemirror/codemirror.js', 'core', 'interface' ) );
|
|
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'codemirror/codemirror.css', 'core', 'interface' ) );
|
|
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'records/record.css', 'cms', 'front' ) );
|
|
|
|
/* Output */
|
|
if ( Request::i()->isAjax() )
|
|
{
|
|
Output::i()->sendOutput( Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $table ) );
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
foreach( $category->parents() AS $parent )
|
|
{
|
|
Output::i()->breadcrumb[] = array( $parent->url(), $parent->_title );
|
|
}
|
|
Output::i()->breadcrumb[] = array( $category->url(), $category->_title );
|
|
}
|
|
catch( Exception $e ) {}
|
|
|
|
Output::i()->breadcrumb[] = array( $record->url(), $record->_title );
|
|
|
|
Output::i()->title = $title;
|
|
Dispatcher::i()->output .= (string) $table;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete Revision
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function revisionDelete() : void
|
|
{
|
|
Session::i()->csrfCheck();
|
|
|
|
/* Make sure the user confirmed the deletion */
|
|
Request::i()->confirmedDelete();
|
|
|
|
/* @var Records $recordClass */
|
|
$recordClass = '\IPS\cms\Records' . Dispatcher::i()->databaseId;
|
|
|
|
try
|
|
{
|
|
$record = $recordClass::loadAndCheckPerms( Request::i()->id );
|
|
$category = $record->container();
|
|
}
|
|
catch ( OutOfRangeException $e )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/6', 403, '' );
|
|
}
|
|
|
|
try
|
|
{
|
|
$revision = Revisions::load( Request::i()->revision_id );
|
|
}
|
|
catch ( OutOfRangeException $e )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/7', 403, '' );
|
|
}
|
|
|
|
if ( ! $record->canManageRevisions() )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/8', 403, '' );
|
|
}
|
|
|
|
$revision->delete();
|
|
|
|
if ( isset( Request::i()->ajax ) )
|
|
{
|
|
Output::i()->redirect( $record->url() );
|
|
}
|
|
else
|
|
{
|
|
Output::i()->redirect( $record->url('revisions') );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* View Revision
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function revisionView() : void
|
|
{
|
|
/* @var Records $recordClass */
|
|
$recordClass = '\IPS\cms\Records' . Dispatcher::i()->databaseId;
|
|
|
|
try
|
|
{
|
|
$record = $recordClass::loadAndCheckPerms( Request::i()->id );
|
|
$category = $record->container();
|
|
}
|
|
catch ( OutOfRangeException $e )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/9', 403, '' );
|
|
}
|
|
|
|
try
|
|
{
|
|
$revision = Revisions::load( Request::i()->revision_id );
|
|
}
|
|
catch ( OutOfRangeException $e )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/A', 403, '' );
|
|
}
|
|
|
|
if ( ! $record->canManageRevisions() )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/B', 403, '' );
|
|
}
|
|
|
|
$title = Member::loggedIn()->language()->addToStack('content_revision_record_title', FALSE, array( 'sprintf' => array( $record->_title ) ) );
|
|
|
|
/* @var Fields $fieldsClass */
|
|
$fieldsClass = 'IPS\cms\Fields' . $record::$customDatabaseId;
|
|
$customFields = $fieldsClass::data( 'view', $category );
|
|
$conflicts = array();
|
|
$form = new Form( 'form', 'content_revision_restore' );
|
|
|
|
/* Add a "cancel" button that will take you back to the previous page */
|
|
array_unshift( $form->actionButtons, Theme::i()->getTemplate( 'forms', 'core', 'global' )->button( 'cancel', 'link', $record->url()->setQueryString( array( 'do' => 'revisions', 'd' => $record::$customDatabaseId ) ), 'ipsButton ipsButton--text', array( 'tabindex' => '3', 'accesskey' => 'c' ) ) );
|
|
|
|
/* Build up our data set */
|
|
$conflicts = $revision->getDiffHtmlTables( $record::$customDatabaseId, $record, true );
|
|
|
|
/* If there is only one change, then clicking restore naturally means to revert that single change, so we don't need a form */
|
|
if( count( $conflicts ) === 1 )
|
|
{
|
|
foreach( $conflicts as $conflict )
|
|
{
|
|
$form->hiddenValues[ 'conflict_' . $conflict['field']->id ] = 'old';
|
|
}
|
|
}
|
|
/* Otherwise if multiple fields have changes to compare, let the admin decide what to do */
|
|
else
|
|
{
|
|
foreach( $conflicts as $conflict )
|
|
{
|
|
$form->add( new Radio( 'conflict_' . $conflict['field']->id, 'no', false, array( 'options' => array( 'old' => '', 'new' => '' ) ) ) );
|
|
}
|
|
}
|
|
|
|
if ( $values = $form->values() )
|
|
{
|
|
foreach( $values as $k => $v )
|
|
{
|
|
if ( $v === 'old' )
|
|
{
|
|
$fieldId = mb_substr( $k, 9 );
|
|
$key = 'field_' . $fieldId;
|
|
$record->$key = $revision->get( $key );
|
|
}
|
|
|
|
Session::i()->modLog( 'modlog__content_revision_restored', array( $record->_title => FALSE, $revision->id => FALSE ) );
|
|
|
|
$record->save();
|
|
$revision->delete();
|
|
|
|
Output::i()->redirect( $record->url(), 'content_revision_restored' );
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
foreach( $category->parents() AS $parent )
|
|
{
|
|
Output::i()->breadcrumb[] = array( $parent->url(), $parent->_title );
|
|
}
|
|
Output::i()->breadcrumb[] = array( $category->url(), $category->_title );
|
|
}
|
|
catch( Exception $e ) {}
|
|
|
|
Output::i()->breadcrumb[] = array( $record->url(), $record->_title );
|
|
Output::i()->breadcrumb[] = array( $record->url()->setQueryString( array( 'do' => 'revisions', 'd' => $record::$customDatabaseId ) ), $title );
|
|
|
|
Output::i()->title = $title;
|
|
|
|
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'system/diff.css', 'core', 'admin' ) );
|
|
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'codemirror/diff_match_patch.js', 'core', 'interface' ) );
|
|
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js('front_records.js', 'cms' ) );
|
|
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js( 'codemirror/codemirror.js', 'core', 'interface' ) );
|
|
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'codemirror/codemirror.css', 'core', 'interface' ) );
|
|
|
|
Dispatcher::i()->output = $form->customTemplate( array( Theme::i()->getTemplate( 'revisions', 'cms' ), 'view' ), $record, $revision, $conflicts );
|
|
}
|
|
|
|
/**
|
|
* Edit Item
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function edit() : void
|
|
{
|
|
Output::i()->jsFiles = array_merge( Output::i()->jsFiles, Output::i()->js('front_records.js', 'cms' ) );
|
|
|
|
/* @var Records $recordClass */
|
|
$recordClass = '\IPS\cms\Records' . Dispatcher::i()->databaseId;
|
|
$fieldsClass = '\IPS\cms\Fields' . Dispatcher::i()->databaseId;
|
|
$database = Databases::load( Dispatcher::i()->databaseId );
|
|
try
|
|
{
|
|
$record = $recordClass::loadAndCheckPerms( Request::i()->id );
|
|
$category = $record->container();
|
|
|
|
}
|
|
catch ( OutOfRangeException $e )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/C', 403, '' );
|
|
}
|
|
|
|
$title = Member::loggedIn()->language()->addToStack( 'content_record_form_edit_record', FALSE, array( 'sprintf' => array( $record->_title ) ) );
|
|
$formElements = $recordClass::formElements( $record, $category );
|
|
|
|
// We check if the form has been submitted to prevent the user loosing their content
|
|
if ( isset( Request::i()->form_submitted ) )
|
|
{
|
|
if ( ! $record->couldEdit() )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/G', 403, '' );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ! $record->canEdit() )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/D', 403, '' );
|
|
}
|
|
}
|
|
|
|
$form = new Form( 'form', isset( Member::loggedIn()->language()->words[ $recordClass::$formLangPrefix . '_save' ] ) ? $recordClass::$formLangPrefix . '_save' : 'save' );
|
|
$form->class = 'ipsForm--vertical ipsForm--edit-record';
|
|
|
|
foreach( $formElements as $name => $field )
|
|
{
|
|
$form->add( $field );
|
|
}
|
|
|
|
/* Now loop through and add all the elements to the form */
|
|
foreach( UiExtension::i()->run( $record, 'formElements', array( $record->container() ) ) as $element )
|
|
{
|
|
$form->add( $element );
|
|
}
|
|
|
|
$hasModOptions = FALSE;
|
|
|
|
/* @var Fields $fieldsClass */
|
|
if ( $recordClass::modPermission( 'lock', NULL, $category ) or
|
|
$recordClass::modPermission( 'pin', NULL, $category ) or
|
|
$record->canHide() or
|
|
$recordClass::modPermission( 'feature', NULL, $category ) or
|
|
$fieldsClass::fixedFieldFormShow( 'record_allow_comments' ) or
|
|
$fieldsClass::fixedFieldFormShow( 'record_expiry_date' ) or
|
|
$fieldsClass::fixedFieldFormShow( 'record_comment_cutoff' ) or
|
|
Member::loggedIn()->modPermission('can_content_edit_meta_tags') )
|
|
{
|
|
$hasModOptions = TRUE;
|
|
}
|
|
|
|
if ( $values = $form->values() )
|
|
{
|
|
$record->processBeforeEdit( $values );
|
|
$record->processForm( $values );
|
|
$record->processAfterEdit( $values );
|
|
|
|
if ( isset( $recordClass::$databaseColumnMap['date'] ) and isset( $values[ $recordClass::$formLangPrefix . 'date' ] ) )
|
|
{
|
|
$column = $recordClass::$databaseColumnMap['date'];
|
|
|
|
if ( $values[ $recordClass::$formLangPrefix . 'date' ] instanceof DateTime )
|
|
{
|
|
$record->$column = $values[ $recordClass::$formLangPrefix . 'date' ]->getTimestamp();
|
|
}
|
|
}
|
|
|
|
$record->save();
|
|
|
|
Session::i()->modLog( 'modlog__item_edit', array( $record::$title => FALSE, $record->url()->__toString() => FALSE, $record::$title => TRUE, $record->mapped( 'title' ) => FALSE ), $record );
|
|
|
|
Output::i()->redirect( $record->url() );
|
|
}
|
|
|
|
Output::i()->sidebar['enabled'] = FALSE;
|
|
Output::i()->title = Member::loggedIn()->language()->addToStack( $title );
|
|
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'records/form.css', 'cms', 'front' ) );
|
|
|
|
try
|
|
{
|
|
if ( $database->use_categories )
|
|
{
|
|
foreach( $category->parents() AS $parent )
|
|
{
|
|
Output::i()->breadcrumb[] = array( $parent->url(), $parent->_title );
|
|
}
|
|
Output::i()->breadcrumb[] = array( $category->url(), $category->_title );
|
|
}
|
|
}
|
|
catch( Exception $e ) {}
|
|
|
|
Output::i()->breadcrumb[] = array( $record->url(), $record->mapped('title') );
|
|
|
|
$this->showClubHeader();
|
|
Dispatcher::i()->output = $form->customTemplate( array( \IPS\cms\Theme::i()->getTemplate( $database->template_form, 'cms', 'database' ), 'recordForm' ), NULL, $category, $database, Page::$currentPage, $title, $hasModOptions );
|
|
|
|
}
|
|
|
|
/**
|
|
* Mark Database Record Read
|
|
*
|
|
* @return void
|
|
*/
|
|
public function markRead() : void
|
|
{
|
|
Session::i()->csrfCheck();
|
|
|
|
try
|
|
{
|
|
$record = $this->_getRecord();
|
|
$record->markRead();
|
|
Output::i()->redirect( $record->url() );
|
|
}
|
|
catch ( OutOfRangeException $e )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2F173/C', 403, 'module_no_permission_guest' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return a record based on query string 'id' param
|
|
*
|
|
* @return Records
|
|
*/
|
|
public function _getRecord(): Records
|
|
{
|
|
/* @var Records $recordClass */
|
|
$recordClass = '\IPS\cms\Records' . Dispatcher::i()->databaseId;
|
|
|
|
try
|
|
{
|
|
$record = $recordClass::loadAndCheckPerms( Request::i()->id );
|
|
}
|
|
catch ( OutOfRangeException $e )
|
|
{
|
|
Output::i()->error( 'module_no_permission', '2T252/E', 403, '' );
|
|
}
|
|
|
|
return $record;
|
|
}
|
|
|
|
/* IP.Board integration */
|
|
|
|
/**
|
|
* Hide Comment/Review
|
|
*
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
public function _hide( string $commentClass, Comment $comment, Item $item ): void
|
|
{
|
|
$this->_doSomething( '_hide', $commentClass, $comment, $item );
|
|
}
|
|
|
|
/**
|
|
* Unhide Comment/Review
|
|
*
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
public function _unhide( string $commentClass, Comment $comment, Item $item ): void
|
|
{
|
|
$this->_doSomething( '_unhide', $commentClass, $comment, $item );
|
|
}
|
|
|
|
/**
|
|
* Split Comment
|
|
*
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
protected function _split( string $commentClass, Comment $comment, Item $item ): void
|
|
{
|
|
$this->_doSomething( '_split', $commentClass, $comment, $item );
|
|
}
|
|
|
|
/**
|
|
* Edit Comment/Review
|
|
*
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
protected function _edit( string $commentClass, Comment $comment, Item $item ) : void
|
|
{
|
|
$this->_doSomething( '_edit', $commentClass, $comment, $item );
|
|
}
|
|
|
|
/**
|
|
* Report Comment/Review
|
|
*
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
protected function _report( string $commentClass, Comment $comment, Item $item ) : void
|
|
{
|
|
$this->_doSomething( '_report', $commentClass, $comment, $item );
|
|
}
|
|
|
|
/**
|
|
* Edit Log
|
|
*
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
public function _editlog( string $commentClass, Comment $comment, Item $item ) : void
|
|
{
|
|
$this->_doSomething( '_editlog', $commentClass, $comment, $item );
|
|
}
|
|
|
|
/**
|
|
* Delete Comment/Review
|
|
*
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
protected function _delete( string $commentClass, Comment $comment, Item $item ) : void
|
|
{
|
|
$this->_doSomething( '_delete', $commentClass, $comment, $item );
|
|
}
|
|
|
|
/**
|
|
* Rep Comment/Review
|
|
*
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
protected function _react( string $commentClass, Comment $comment, Item $item ): void
|
|
{
|
|
$this->_doSomething( '_react', $commentClass, $comment, $item );
|
|
}
|
|
|
|
/**
|
|
* Show Comment/Review Rep
|
|
*
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
protected function _showReactions( string $commentClass, Comment $comment, Item $item ): void
|
|
{
|
|
$this->_doSomething( '_showReactions', $commentClass, $comment, $item );
|
|
}
|
|
|
|
/**
|
|
* Do something that needs to be overridden from the Content controller
|
|
*
|
|
* @param string $method The method name
|
|
* @param string $commentClass The comment/review class
|
|
* @param Comment $comment The comment/review
|
|
* @param Item $item The item
|
|
* @return void
|
|
* @throws LogicException
|
|
*/
|
|
protected function _doSomething( string $method, string $commentClass, Comment $comment, Item $item ) : void
|
|
{
|
|
$record = $this->_getRecord();
|
|
|
|
if ( $record->useForumComments() AND isset( Request::i()->comment) )
|
|
{
|
|
/* @var Records\CommentTopicSync $commentClass */
|
|
$commentClass = 'IPS\cms\Records\CommentTopicSync' . $record::$customDatabaseId;
|
|
$comment = $commentClass::load( Request::i()->comment );
|
|
$item = $record;
|
|
}
|
|
|
|
try
|
|
{
|
|
parent::$method( $commentClass, $comment, $item );
|
|
}
|
|
catch( LogicException $e )
|
|
{
|
|
Output::i()->error( 'node_error', '2T252/F', 403, '' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add the club header to \IPS\Output
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function showClubHeader() : void
|
|
{
|
|
$record = $this->_getRecord();
|
|
if ( $record and $club = $record->club )
|
|
{
|
|
$category = $record->container();
|
|
|
|
Output::i()->sidebar['contextual'] = '';
|
|
|
|
/* Club info in sidebar */
|
|
if ( Settings::i()->clubs_header == 'sidebar' )
|
|
{
|
|
Output::i()->sidebar['enabled'] = true;
|
|
Output::i()->sidebar['contextual'] .= Theme::i()->getTemplate( 'clubs', 'core', 'front' )->header( $club, $category, 'sidebar' );
|
|
}
|
|
else
|
|
{
|
|
Dispatcher::i()->output .= Theme::i()->getTemplate( 'clubs', 'core', 'front' )->header( $club, $record->container(), 'full' );
|
|
}
|
|
|
|
if( ( GeoLocation::enabled() and Settings::i()->clubs_locations AND $location = $club->location() ) )
|
|
{
|
|
Output::i()->sidebar['enabled'] = true;
|
|
Output::i()->sidebar['contextual'] .= Theme::i()->getTemplate( 'clubs', 'core', 'front' )->clubLocationBox( $club, $location );
|
|
}
|
|
}
|
|
}
|
|
} |