Invision Power Services, Inc. * @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc. * @license http://www.invisionpower.com/legal/standards/ * @package IPS Social Suite * @since 5 Jul 2013 * @version SVN_VERSION_NUMBER */ namespace IPS\Content; /* To prevent PHP errors (extending class does not exist) revealing path */ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) ) { header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' ); exit; } /** * Content Controller */ class _Controller extends \IPS\Helpers\CoverPhoto\Controller { /** * Execute * * @return void */ public function execute() { /* We do this to prevent SQL errors with page offsets */ if ( isset( \IPS\Request::i()->page ) ) { \IPS\Request::i()->page = intval( \IPS\Request::i()->page ); if ( !\IPS\Request::i()->page OR \IPS\Request::i()->page < 1 ) { \IPS\Request::i()->page = 1; } } /* Ensure JS loaded for forms/content functions such as moderation */ \IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_core.js', 'core' ) ); return parent::execute(); } /** * View Item * * @return \IPS\Content\Item|NULL */ protected function manage() { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); /* If this is an AJAX request (like the topic hovercard), we don't want to do any of the below like update views and mark read */ if ( \IPS\Request::i()->isAjax() ) { /* But we do want to mark read if we are paging through the content */ if( $item instanceof \IPS\Content\ReadMarkers AND isset( \IPS\Request::i()->page ) AND \IPS\Request::i()->page AND $item->isLastPage() ) { $item->markRead(); } return $item; } /* Do we need to convert any legacy URL parameters? */ if( $redirectToUrl = $item->checkForLegacyParameters() ) { \IPS\Output::i()->redirect( $redirectToUrl ); } /* Check we're on a valid page */ if ( isset( \IPS\Request::i()->page ) ) { $paginationType = 'comment'; if ( isset( \IPS\Request::i()->tab ) and \IPS\Request::i()->tab === 'reviews' ) { $paginationType = 'review'; } $pageCount = call_user_func( array( $item, "{$paginationType}PageCount" ) ); if ( $pageCount and \IPS\Request::i()->page > $pageCount ) { \IPS\Output::i()->redirect( call_user_func( array( $item, 'last' . ucfirst( $paginationType ) . 'PageUrl' ) ), NULL, 303 ); } } /* Update Views */ $idColumn = $class::$databaseColumnId; if ( in_array( 'IPS\Content\Views', class_implements( $class ) ) AND isset( $class::$databaseColumnMap['views'] ) ) { \IPS\Db::i()->update( $class::$databaseTable, "`{$class::$databasePrefix}{$class::$databaseColumnMap['views']}`=`{$class::$databasePrefix}{$class::$databaseColumnMap['views']}`+1", array( "{$class::$databasePrefix}{$class::$databaseColumnId}=?", $item->$idColumn ), array(), NULL, \IPS\Db::LOW_PRIORITY ); } /* Mark read */ if( $item instanceof \IPS\Content\ReadMarkers ) { /* Note time last read before we mark it read so that the line is in the right place */ $item->timeLastRead(); if ( $item->isLastPage() ) { $item->markRead(); } } /* Have we moved? */ if ( isset( $class::$databaseColumnMap['state'] ) AND isset( $class::$databaseColumnMap['moved_to'] ) ) { $stateColumn = $class::$databaseColumnMap['state']; $movedToColumn = $class::$databaseColumnMap['moved_to']; $movedTo = explode( '&', $item->$movedToColumn ); if ( $item->$stateColumn == 'link' ) { try { $moved = $class::loadAndCheckPerms( $movedTo[0] ); \IPS\Output::i()->redirect( $moved->url(), '', 301 ); } catch( \OutOfRangeException $e ) { } } } /* Set navigation and title */ $this->_setBreadcrumbAndTitle( $item, FALSE ); /* Set meta tags */ \IPS\Output::i()->linkTags['canonical'] = (string) ( \IPS\Request::i()->page > 1 ) ? $item->url()->setQueryString( 'page', \IPS\Request::i()->page ) : $item->url() ; \IPS\Output::i()->metaTags['description'] = $item->metaDescription(); \IPS\Output::i()->metaTags['og:title'] = $item->mapped('title'); \IPS\Output::i()->metaTags['og:type'] = 'object'; \IPS\Output::i()->metaTags['og:url'] = (string) $item->url(); \IPS\Output::i()->metaTags['og:description'] = $item->metaDescription(); if( $item->mapped('updated') OR $item->mapped('last_comment') OR $item->mapped('last_review') ) { \IPS\Output::i()->metaTags['og:updated_time'] = \IPS\DateTime::ts( $item->mapped('updated') ? $item->mapped('updated') : ( $item->mapped('last_comment') ? $item->mapped('last_comment') : $item->mapped('last_review') ) )->rfc3339(); } /* Add contextual search options */ \IPS\Output::i()->contextualSearchOptions[ \IPS\Member::loggedIn()->language()->addToStack( 'search_contextual_item', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $item::$title ) ) ) ) ] = array( 'type' => mb_strtolower( str_replace( '\\', '_', mb_substr( $class, 4 ) ) ), 'item' => $item->$idColumn ); try { $container = $item->container(); \IPS\Output::i()->contextualSearchOptions[ \IPS\Member::loggedIn()->language()->addToStack( 'search_contextual_item', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $container::$nodeTitle . '_sg' ) ) ) ) ] = array( 'type' => mb_strtolower( str_replace( '\\', '_', mb_substr( $class, 4 ) ) ), 'nodes' => $container->_id ); } catch ( \BadMethodCallException $e ) { } /* Return */ return $item; } catch ( \LogicException $e ) { return NULL; } } /** * AJAX - check for new replies * * @return \IPS\Content\Item|NULL */ protected function checkForNewReplies() { /* If auto-polling isn't enabled, kill the polling now */ if ( !\IPS\Settings::i()->auto_polling_enabled ) { \IPS\Output::i()->json( array( 'error' => 'auto_polling_disabled' ) ); return; } try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); $commentClass = $class::$commentClass; $commentIdColumn = $commentClass::$databaseColumnId; $commentDateColumn = $commentClass::$databaseColumnMap['date']; /* The form field has an underscore, but this value is sent in a query string value without an underscore via AJAX */ if( ! \IPS\Request::i()->lastSeenID or ! \IPS\Member::loggedIn()->member_id ) { \IPS\Output::i()->json( array( 'count' => 0 ) ); } $lastComment = $commentClass::load( \IPS\Request::i()->lastSeenID ); $authorColumn = $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['author']; $where = array(); /* Ignored Users */ if( ! \IPS\Member::loggedIn()->members_bitoptions['has_no_ignored_users'] ) { $ignored = iterator_to_array( \IPS\Db::i()->select( 'ignore_ignore_id', 'core_ignored_users', array( 'ignore_owner_id=? and ignore_messages=?', \IPS\Member::loggedIn()->member_id, 1 ) ) ); if( count( $ignored ) ) { $where[] = array( \IPS\Db::i()->in( $authorColumn, $ignored, TRUE ) ); } } /* We will fetch up to 200 comments - anything over this is excessive. @see https://community.invisionpower.com/4bugtrack/active-reports/x-new-replies-doesnt-show-more-than-15-replies-r7192/ */ $newComments = $item->comments( 200, 0, 'date', 'asc', NULL, NULL, \IPS\DateTime::ts( $lastComment->$commentDateColumn ), array_merge( $where, array( "{$authorColumn} != " . \IPS\Member::loggedIn()->member_id ) ) ); if ( \IPS\Request::i()->type === 'count' ) { $data = array( 'totalNewCount' => (int) count( $newComments ), 'count' => (int) count( $newComments ), /* This is here for legacy purposes only */ 'perPage' => $class::getCommentsPerPage(), 'totalCount' => $item->mapped('num_comments'), 'title' => $item->mapped('title') , 'spillOverUrl' => $item->url( 'getNewComment' ) ); if( $data['count'] === 1 ){ $itemData = reset( $newComments ); $author = $itemData->author(); $data['name'] = htmlspecialchars( $author->name, \IPS\HTMLENTITIES, 'UTF-8', FALSE ); $data['photo'] = (string) $author->photo; } \IPS\Output::i()->json( $data ); } else { $output = array(); $lastId = 0; foreach ( $newComments as $newComment ) { $output[] = $newComment->html(); $lastId = ( $newComment->$commentIdColumn > $lastId ) ? $newComment->$commentIdColumn : $lastId; } $item->markRead(); } \IPS\Output::i()->json( array( 'content' => $output, 'id' => $lastId, 'totalCount' => $item->mapped('num_comments') ) ); } catch ( \Exception $e ) { \IPS\Output::i()->json( $e->getMessage(), 500 ); } } /** * Edit Item * * @return void */ protected function edit() { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); if ( !$item->canEdit() and !\IPS\Request::i()->form_submitted ) // We check if the form has been submitted to prevent the user loosing their content { throw new \OutOfRangeException; } $form = new \IPS\Helpers\Form( 'form', \IPS\Member::loggedIn()->language()->addToStack( $class::$formLangPrefix . '_save' ) ? $class::$formLangPrefix . '_save' : 'save' ); $form->class = 'ipsForm_vertical'; $container = NULL; try { $container = $item->container(); } catch ( \BadMethodCallException $e ) {} $formElements = $class::formElements( $item, $container ); if ( isset( $formElements['poll'] ) AND !\IPS\Request::i()->_report ) { $form->addTab( $class::$formLangPrefix . 'mainTab' ); } foreach ( $formElements as $key => $object ) { if ( \IPS\Request::i()->_report AND !in_array( $key, array( 'title', 'content', 'description' ) ) ) { continue; } if ( $key === 'poll' ) { $form->addTab( $class::$formLangPrefix . 'pollTab' ); } $form->add( $object ); } if ( $values = $form->values() ) { if ( $item->canEdit() ) { $item->processForm( $values ); if ( isset( $item::$databaseColumnMap['updated'] ) ) { $column = $item::$databaseColumnMap['updated']; $item->$column = time(); } if ( isset( $item::$databaseColumnMap['date'] ) and isset( $values[ $item::$formLangPrefix . 'date' ] ) ) { $column = $item::$databaseColumnMap['date']; if ( $values[ $item::$formLangPrefix . 'date' ] instanceof \IPS\DateTime ) { $item->$column = $values[ $item::$formLangPrefix . 'date' ]->getTimestamp(); } else { $item->$column = time(); } } $item->save(); $item->processAfterEdit( $values ); \IPS\Output::i()->redirect( $item->url() ); } else { $form->error = \IPS\Member::loggedIn()->language()->addToStack('edit_no_perm_err'); } } $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->editContentForm( \IPS\Member::loggedIn()->language()->addToStack( 'edit_title', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $item::$title ) ) ) ), $form ); } catch ( \Exception $e ) { \IPS\Output::i()->error( 'edit_no_perm_err', '2S136/E', 404, '' ); } } /** * Quick Edit Title * * @return void */ public function ajaxEditTitle() { try { \IPS\Session::i()->csrfCheck(); $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); if ( !$item->canEdit() ) { throw new \DomainException; } $oldTitle = $item->mapped('title'); $titleField = $item::$databaseColumnMap['title']; $item->$titleField = \IPS\Request::i()->newTitle; $item->save(); if ( $item instanceof \IPS\Content\Searchable ) { \IPS\Content\Search\Index::i()->index( $item ); } \IPS\Session::i()->modLog( 'modlog__comment_edit_title', array( (string) $item->url() => FALSE, $item->$titleField => FALSE, $oldTitle => FALSE ), $item ); \IPS\Output::i()->json('OK'); } catch ( \Exception $e ) { \IPS\Output::i()->error( 'node_error', '2S136/E', 404, '' ); } } /** * Set the breadcrumb and title * * @param \IPS\Content\Item $item Content item * @param bool $link Link the content item element in the breadcrumb * @return void */ protected function _setBreadcrumbAndTitle( $item, $link=TRUE ) { $container = NULL; try { $container = $item->container(); foreach ( $container->parents() as $parent ) { \IPS\Output::i()->breadcrumb[] = array( $parent->url(), $parent->_title ); } \IPS\Output::i()->breadcrumb[] = array( $container->url(), $container->_title ); } catch ( \Exception $e ) { } \IPS\Output::i()->breadcrumb[] = array( $link ? $item->url() : NULL, $item->mapped('title') ); $title = ( isset( \IPS\Request::i()->page ) and \IPS\Request::i()->page > 1 ) ? \IPS\Member::loggedIn()->language()->addToStack( 'title_with_page_number', FALSE, array( 'sprintf' => array( $item->mapped('title'), \IPS\Request::i()->page ) ) ) : $item->mapped('title'); \IPS\Output::i()->title = $container ? ( $title . ' - ' . $container->_title ) : $title; } /** * Moderate * * @return void */ protected function moderate() { try { \IPS\Session::i()->csrfCheck(); $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); if ( $item::$hideLogKey and \IPS\Request::i()->action === 'hide' ) { $this->_setBreadcrumbAndTitle( $item ); $form = new \IPS\Helpers\Form; $form->add( new \IPS\Helpers\Form\Text( 'hide_reason' ) ); if ( $values = $form->values() ) { $item->modAction( \IPS\Request::i()->action, NULL, $values['hide_reason'] ); } else { \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); return; } } else { $item->modAction( \IPS\Request::i()->action ); } if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->json( 'OK' ); } else { if( \IPS\Request::i()->action == 'delete' ) { \IPS\Output::i()->redirect( $item->container()->url() ); } else { \IPS\Output::i()->redirect( $item->url(), 'mod_confirm_' . \IPS\Request::i()->action ); } } } catch ( \Exception $e ) { \IPS\Output::i()->error( 'node_error', '2S136/1', 404, '' ); } } /** * Move * * @return void */ protected function move() { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); if ( !$item->canMove() ) { throw new \DomainException; } $form = new \IPS\Helpers\Form( 'form', 'move' ); $form->class = 'ipsForm_vertical'; $form->add( new \IPS\Helpers\Form\Node( 'move_to', NULL, TRUE, array( 'class' => get_class( $item->container() ), 'permissionCheck' => function( $node ) use ( $item ) { if ( $node->id != $item->container()->id ) { try { if ( $node->can( 'add' ) ) { return true; } } catch( \OutOfBoundsException $e ) { } } return false; } ) ) ); if ( isset( $class::$databaseColumnMap['moved_to'] ) ) { $form->add( new \IPS\Helpers\Form\Checkbox( 'move_keep_link' ) ); if ( \IPS\Settings::i()->topic_redirect_prune ) { \IPS\Member::loggedIn()->language()->words['move_keep_link_desc'] = \IPS\Member::loggedIn()->language()->addToStack( '_move_keep_link_desc', FALSE, array( 'pluralize' => array( \IPS\Settings::i()->topic_redirect_prune ) ) ); } } if ( $values = $form->values() ) { if ( $values['move_to'] === NULL OR !$values['move_to']->can('add') OR $values['move_to']->id == $item->container()->id ) { \IPS\Output::i()->error( 'node_move_invalid', '1S136/L', 403, '' ); } $item->move( $values['move_to'], isset( $values['move_keep_link'] ) ? $values['move_keep_link'] : FALSE ); \IPS\Session::i()->modLog( 'modlog__action_move', array( $item::$title => TRUE, $item->url()->__toString() => FALSE, $item->mapped('title') ?: ( method_exists( $item, 'item' ) ? $item->item()->mapped('title') : NULL ) => FALSE ), $item ); \IPS\Output::i()->redirect( $item->url() ); } $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'move_item', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $class::$title ) ) ) ); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); } catch ( \Exception $e ) { \IPS\Output::i()->error( 'node_error', '2S136/D', 403, '' ); } } /** * Merge * * @return void */ protected function merge() { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); if ( !$item->canMerge() ) { throw new \DomainException; } $form = new \IPS\Helpers\Form( 'form', 'merge' ); $form->class = 'ipsForm_vertical'; $form->add( new \IPS\Helpers\Form\Url( 'merge_with', NULL, TRUE, array(), function ( $val ) use ( $class, $item ) { try { $toMerge = $class::loadFromUrl( $val ); if ( $toMerge == $item ) { throw new \DomainException; } } catch ( \Exception $e ) { throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_url_bad_item', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $class::$title, FALSE, array( 'strtolower' => TRUE ) ) ) ) ) ); } } ) ); \IPS\Member::loggedIn()->language()->words['merge_with_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'merge_with__desc', FALSE, array( 'sprintf' => array( $item->indefiniteArticle(), $item->mapped('title') ) ) ); if ( $values = $form->values() ) { $item->mergeIn( array( $class::loadFromUrl( $values['merge_with'] ) ) ); \IPS\Output::i()->redirect( $item->url() ); } $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'merge_item', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $class::$title ) ) ) ); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); } catch ( \Exception $e ) { \IPS\Output::i()->error( 'node_error', '2S136/G', 403, '' ); } } /** * Send email * * @return void */ protected function email() { /* Share link enabled */ $node = \IPS\core\ShareLinks\Service::load( 'email', 'share_key' ); if( !( $node->enabled and ( $node->groups === "*" or \IPS\Member::loggedIn()->inGroup( explode( ',', $node->groups ) ) ) ) ) { \IPS\Output::i()->error( 'no_module_permission', '2S136/N', 403, '' ); } try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); if ( $item instanceof \IPS\Content\Shareable ) { $form = new \IPS\Helpers\Form( 'form', 'send' ); $form->class = 'ipsForm_vertical'; if( !\IPS\Member::loggedIn()->member_id ) { $form->add( new \IPS\Helpers\Form\Text( 'mail_from_name', NULL, TRUE ) ); $form->add( new \IPS\Helpers\Form\Email( 'mail_from_email', NULL, TRUE ) ); } $form->add( new \IPS\Helpers\Form\Text( 'email_subject', $item->mapped('title'), TRUE, array( 'maxLength' => 255 ) ) ); $form->add( new \IPS\Helpers\Form\Email( 'email_email', NULL, TRUE ) ); $idColumn = $class::$databaseColumnId; $url = $item->url(); if ( \IPS\Request::i()->comment ) { $url = $url->setQueryString( array( 'do' => 'findComment', 'comment' => \IPS\Request::i()->comment ) ); } $defaultEmail = \IPS\Member::loggedIn()->language()->addToStack('default_email_content', FALSE, array( 'sprintf' => array( $item->mapped('title'), $url ) ) ); $form->add( new \IPS\Helpers\Form\Editor( 'email_content', $defaultEmail, TRUE, array( 'app' => 'core', 'key' => 'ShareLinks', 'autoSaveKey' => 'contentEdit-' . $class::$application . '/' . $class::$module . '-' . $item->$idColumn ) ) ); if( !\IPS\Member::loggedIn()->member_id ) { $form->add( new \IPS\Helpers\Form\Captcha ); } if( $values = $form->values() ) { $email = \IPS\Email::buildFromContent( $values['email_subject'], $values['email_content'] ); $fromName = ( isset( $values['mail_from_name'] ) ) ? $values['mail_from_name'] : \IPS\Member::loggedIn()->name; /* Set contact email as Reply-To instead of From, so that it will pass Spam/SPF checks */ $email->headers['Reply-To'] = '=?UTF-8?B?' . base64_encode( $fromName ) . "?= <" . ( isset( $values['mail_from_email'] ) ? $values['mail_from_email'] : \IPS\Member::loggedIn()->email ) . '>'; $email->emailBody['html'] = $email->parseTextForEmail( $email->emailBody['html'] ); $email->send( $values['email_email'] ); if ( \IPS\Request::i()->isAjax() ) { return; } else { $url = $item->url(); if ( \IPS\Request::i()->comment ) { $url = $url->setQueryString( array( 'do' => 'findComment', 'comment' => \IPS\Request::i()->comment ) ); } \IPS\Output::i()->redirect( $url, 'email_sent' ); } } $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('send_email_form'); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); } else { throw new \BadMethodCallException; } } catch ( \Exception $e ) { \IPS\Output::i()->error( 'node_error', '2S136/5', 404, '' ); } } /** * Report Item * * @return void */ protected function report() { try { /* Init */ $class = static::$contentModel; $commentClass = $class::$commentClass; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); /* Permission check */ $canReport = $item->canReport(); if ( $canReport !== TRUE ) { \IPS\Output::i()->error( $canReport, '2S136/6', 404, '' ); } /* Show form */ $form = new \IPS\Helpers\Form( NULL, 'report_submit' ); $form->class = 'ipsForm_vertical'; $idColumn = $class::$databaseColumnId; $form->add( new \IPS\Helpers\Form\Editor( 'report_message', NULL, FALSE, array( 'app' => 'core', 'key' => 'Reports', 'autoSaveKey' => "report-{$class::$application}-{$class::$module}-{$item->$idColumn}", 'minimize' => 'report_message_placeholder' ) ) ); if ( $values = $form->values() ) { $report = $item->report( $values['report_message'] ); \IPS\File::claimAttachments( "report-{$class::$application}-{$class::$module}-{$item->$idColumn}", $report->id ); if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( \IPS\Member::loggedIn()->language()->addToStack('report_submit_success') ); } else { \IPS\Output::i()->redirect( $item->url(), 'report_submit_success' ); } } $this->_setBreadcrumbAndTitle( $item ); /* Even if guests can report something, we don't want the report form indexed in Google */ \IPS\Output::i()->metaTags['robots'] = 'noindex'; \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); } catch ( \LogicException $e ) { \IPS\Output::i()->error( 'node_error', '2S136/7', 404, '' ); } } /** * Get Next Unread Item * * @return void */ protected function nextUnread() { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); $next = $item->nextUnread(); if ( $next instanceof \IPS\Content\Item ) { \IPS\Output::i()->redirect( $next->url()->setQueryString( array( 'do' => 'getNewComment' ) ) ); } } catch( \Exception $e ) { \IPS\Output::i()->error( 'next_unread_not_found', '2S136/J', 404, '' ); } } /** * Rep Item * * @return void */ protected function rep() { try { \IPS\Session::i()->csrfCheck(); $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); $type = intval( \IPS\Request::i()->rep ) === 1 ? 1 : -1; $item->giveReputation( $type ); if ( \IPS\Request::i()->isAjax() ) { if ( \IPS\Request::i()->mini ) { \IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->reputationMini( $item->reputation(), $item->canGiveReputation( 1 ), $item->canGiveReputation( -1 ), $item->url('showRep'), $item->url('rep') ) ); } else { \IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->reputation( $item ) ); } } else { \IPS\Output::i()->redirect( $item->url() ); } } catch ( \DomainException $e ) { if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->json( array( 'error' => \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage() ) ), 403 ); } else { \IPS\Output::i()->error( $e->getMessage(), '1S136/H', 403, '' ); } } } /** * Show Comment/Review Rep * * @return void */ protected function showRep() { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); /* Set navigation */ $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->output = $item->reputationTable(); } catch ( \OutOfRangeException $e ) { \IPS\Output::i()->error( 'node_error', '2S136/O', 404, '' ); } catch( \DomainException $e ) { \IPS\Output::i()->error( 'no_module_permission', '2S136/P', 403, '' ); } } /** * Moderator Log * * @return void */ protected function modLog() { if( !\IPS\Member::loggedIn()->modPermission('can_view_moderation_log') ) { throw new \DomainException; } try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); /* Set navigation */ $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->output = $item->moderationTable(); } catch ( \OutOfRangeException $e ) { \IPS\Output::i()->error( 'node_error', '2S136/T', 404, '' ); } catch( \DomainException $e ) { \IPS\Output::i()->error( 'no_module_permission', '2S136/U', 403, '' ); } } /** * Go to new comment. * * @return void */ public function getNewComment() { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); $timeLastRead = $item->timeLastRead(); if ( $timeLastRead instanceof \IPS\DateTime ) { $comment = $item->comments( 1, NULL, 'date', 'asc', NULL, NULL, $timeLastRead ); \IPS\Output::i()->redirect( $comment ? $comment->url() : $item->url( 'getLastComment' ) ); } else { if ( $item->unread() ) { /* If we do not have a time last read set for this content, fallback to the reset time */ $resetTimes = \IPS\Member::loggedIn()->markersResetTimes( $class::$application ); if ( array_key_exists( $item->container()->_id, $resetTimes ) ) { $comment = $item->comments( 1, NULL, 'date', 'asc', NULL, NULL, \IPS\DateTime::ts( $resetTimes[ $item->container()->_id ] ) ); if ( $class::$firstCommentRequired and $comment->isFirst() ) { /* link https://community.invisionpower.com/4bugtrack/active-reports/go-to-unread-link-also-links-to-the-fist-postcomment-even-for-totally-unread-content-r7864/ */ \IPS\Output::i()->redirect( $item->url() ); } \IPS\Output::i()->redirect( $comment ? $comment->url() : $item->url() ); } else { \IPS\Output::i()->redirect( $item->url() ); } } else { \IPS\Output::i()->redirect( $item->url() ); } } } catch( \BadMethodCallException $e ) { \IPS\Output::i()->error( 'node_error', '2S136/I', 404, '' ); } catch( \OutOfRangeException $e ) { $class = static::$contentModel; try { $item = $class::load( \IPS\Request::i()->id ); $error = ( !$item->canView() and ( $item->containerWrapper( TRUE ) and method_exists( $item->container(), 'errorMessage' ) ) ) ? $item->container()->errorMessage() : 'node_error'; } catch( \OutOfRangeException $e ) { $error = 'node_error'; } \IPS\Output::i()->error( $error, '2S136/V', 404, '' ); } catch( \LogicException $e ) { $class = static::$contentModel; try { $item = $class::load( \IPS\Request::i()->id ); $error = ( !$item->canView() and ( $item->containerWrapper( TRUE ) and method_exists( $item->container(), 'errorMessage' ) ) ) ? $item->container()->errorMessage() : 'node_error'; } catch( \OutOfRangeException $e ) { $error = 'node_error'; } \IPS\Output::i()->error( $error, '2S136/R', 404, '' ); } } /** * Go to last comment * * @return void */ public function getLastComment() { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); $comment = $item->comments( 1, NULL, 'date', 'desc' ); if ( $comment !== NULL ) { \IPS\Output::i()->redirect( $comment->url() ); } else { \IPS\Output::i()->redirect( $item->url() ); } } catch( \BadMethodCallException $e ) { \IPS\Output::i()->error( 'node_error', '2S136/K', 404, '' ); } catch( \LogicException $e ) { \IPS\Output::i()->error( 'node_error', '2S136/Q', 404, '' ); } } /** * Rate Review as helpful/unhelpful * * @return void */ public function rateReview() { try { \IPS\Session::i()->csrfCheck(); /* Only logged in members */ if ( !\IPS\Member::loggedIn()->member_id ) { throw new \DomainException; } /* Init */ $class = static::$contentModel; $reviewClass = $class::$reviewClass; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); $review = $reviewClass::load( \IPS\Request::i()->review ); /* Review authors can't rate their own reviews */ if ( $review->author()->member_id === \IPS\Member::loggedIn()->member_id ) { throw new \DomainException; } /* Have we already rated? */ $dataColumn = $reviewClass::$databaseColumnMap['votes_data']; $votesData = $review->mapped('votes_data') ? json_decode( $review->mapped('votes_data'), TRUE ) : array(); if ( array_key_exists( \IPS\Member::loggedIn()->member_id, $votesData ) ) { \IPS\Output::i()->error( 'you_have_already_rated', '2S136/A', 403, '' ); } /* Add it */ $votesData[ \IPS\Member::loggedIn()->member_id ] = intval( \IPS\Request::i()->helpful ); if ( \IPS\Request::i()->helpful ) { $helpful = $reviewClass::$databaseColumnMap['votes_helpful']; $review->$helpful++; } $total = $reviewClass::$databaseColumnMap['votes_total']; $review->$total++; $review->$dataColumn = json_encode( $votesData ); $review->save(); /* Boink */ if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->json( $review->html() ); } else { \IPS\Output::i()->redirect( $review->url() ); } } catch ( \LogicException $e ) { \IPS\Output::i()->error( 'node_error', '2S136/9', 404, '' ); } } /** * Stuff that applies to both comments and reviews * * @param string $method Desired method * @param array $args Arguments * @return void */ public function __call( $method, $args ) { $class = static::$contentModel; try { $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); $comment = NULL; if ( mb_substr( $method, -7 ) === 'Comment' ) { $class = $class::$commentClass; $method = '_' . mb_substr( $method, 0, mb_strlen( $method ) - 7 ); $comment = $method === '_multimod' ? NULL : $class::load( \IPS\Request::i()->comment ); } elseif ( mb_substr( $method, -6 ) === 'Review' ) { $class = $class::$reviewClass; $method = '_' . mb_substr( $method, 0, mb_strlen( $method ) - 6 ); $comment = $method === '_multimod' ? NULL : $class::load( \IPS\Request::i()->review ); } if ( $method === '_multimod' ) { $this->_multimod( $class, $item ); } if ( !$comment or !method_exists( $this, $method ) ) { \IPS\Output::i()->error( 'page_not_found', '2S136/B', 404, '' ); } else { $this->$method( $class, $comment, $item ); } } catch ( \LogicException $e ) { \IPS\Output::i()->error( 'node_error', '2S136/C', 404, '' ); } } /** * Find a Comment / Review (do=findComment/findReview) * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void */ public function _find( $commentClass, $comment, $item ) { $idColumn = $commentClass::$databasePrefix . $commentClass::$databaseColumnId; $_SESSION['_findComment'] = $comment->$idColumn; \IPS\Output::i()->redirect( $comment->url() ); } /** * Hide Comment/Review * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ public function _hide( $commentClass, $comment, $item ) { \IPS\Session::i()->csrfCheck(); if ( $comment::$hideLogKey ) { $form = new \IPS\Helpers\Form; $form->add( new \IPS\Helpers\Form\Text( 'hide_reason' ) ); if ( $values = $form->values() ) { $comment->modAction( 'hide', NULL, $values['hide_reason'] ); } else { $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); return; } } else { $comment->modAction('hide'); } \IPS\Output::i()->redirect( $comment->url() ); } /** * Unhide Comment/Review * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ public function _unhide( $commentClass, $comment, $item ) { \IPS\Session::i()->csrfCheck(); $comment->modAction('unhide'); if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( $comment->html(), 200, 'text/html' ); return; } else { \IPS\Output::i()->redirect( $comment->url() ); } } /** * Edit Comment/Review * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ protected function _edit( $commentClass, $comment, $item ) { $class = static::$contentModel; $valueField = $commentClass::$databaseColumnMap['content']; $idField = $commentClass::$databaseColumnId; $itemIdField = $item::$databaseColumnId; if ( $comment->canEdit() ) { $form = new \IPS\Helpers\Form; $form->class = 'ipsForm_vertical'; $form->add( new \IPS\Helpers\Form\Editor( 'comment_value', $comment->$valueField, TRUE, array( 'app' => $class::$application, 'key' => ucfirst( $class::$module ), 'autoSaveKey' => 'editComment-' . $class::$application . '/' . $class::$module . '-' . $comment->$idField, 'attachIds' => $comment->attachmentIds() ) ) ); $form->addButton( 'cancel', 'link', $item->url()->setQueryString( array( 'do' => 'findComment', 'comment' => $comment->$idField ) ), 'ipsButton ipsButton_link', array( 'data-action' => 'cancelEditComment', 'data-comment-id' => $comment->$idField ) ); if ( $comment instanceof \IPS\Content\EditHistory and \IPS\Settings::i()->edit_log ) { if ( \IPS\Settings::i()->edit_log == 2 or isset( $commentClass::$databaseColumnMap['edit_reason'] ) ) { $form->add( new \IPS\Helpers\Form\Text( 'comment_edit_reason', ( isset( $commentClass::$databaseColumnMap['edit_reason'] ) ) ? $comment->mapped('edit_reason') : NULL, FALSE, array( 'maxLength' => 255 ) ) ); } if ( \IPS\Member::loggedIn()->group['g_append_edit'] ) { $form->add( new \IPS\Helpers\Form\Checkbox( 'comment_log_edit', FALSE ) ); } } if ( $values = $form->values() ) { /* Log History */ if ( $comment instanceof \IPS\Content\EditHistory and \IPS\Settings::i()->edit_log ) { $editIsPublic = \IPS\Member::loggedIn()->group['g_append_edit'] ? $values['comment_log_edit'] : TRUE; if ( \IPS\Settings::i()->edit_log == 2 ) { \IPS\Db::i()->insert( 'core_edit_history', array( 'class' => get_class( $comment ), 'comment_id' => $comment->$idField, 'member' => \IPS\Member::loggedIn()->member_id, 'time' => time(), 'old' => $comment->$valueField, 'new' => $values['comment_value'], 'public' => $editIsPublic, 'reason' => isset( $values['comment_edit_reason'] ) ? $values['comment_edit_reason'] : NULL, ) ); } if ( isset( $commentClass::$databaseColumnMap['edit_reason'] ) and isset( $values['comment_edit_reason'] ) ) { $field = $commentClass::$databaseColumnMap['edit_reason']; $comment->$field = $values['comment_edit_reason']; } if ( isset( $commentClass::$databaseColumnMap['edit_time'] ) ) { $field = $commentClass::$databaseColumnMap['edit_time']; $comment->$field = time(); } if ( isset( $commentClass::$databaseColumnMap['edit_member_id'] ) ) { $field = $commentClass::$databaseColumnMap['edit_member_id']; $comment->$field = \IPS\Member::loggedIn()->member_id; } if ( isset( $commentClass::$databaseColumnMap['edit_member_name'] ) ) { $field = $commentClass::$databaseColumnMap['edit_member_name']; $comment->$field = \IPS\Member::loggedIn()->name; } if ( isset( $commentClass::$databaseColumnMap['edit_show'] ) and $editIsPublic ) { $field = $commentClass::$databaseColumnMap['edit_show']; $comment->$field = \IPS\Member::loggedIn()->group['g_append_edit'] ? $values['comment_log_edit'] : TRUE; } else if( isset( $commentClass::$databaseColumnMap['edit_show'] ) ) { $field = $commentClass::$databaseColumnMap['edit_show']; $comment->$field = 0; } } /* Do it */ $comment->editContents( $values['comment_value'] ); /* Moderator log */ \IPS\Session::i()->modLog( 'modlog__comment_edit', array( $comment->url()->__toString() => FALSE, $item::$title => TRUE, $item->url()->__toString() => FALSE, $item->mapped('title') => FALSE ), $item ); /* Display */ if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->output = $comment->html(); return; } else { \IPS\Output::i()->redirect( $comment->url() ); } } $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('edit_comment') ); \IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('edit_comment') . ' - ' . $item->mapped('title'); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); } else { throw new \UnexpectedValueException; } } /** * Delete Comment/Review * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ protected function _delete( $commentClass, $comment, $item ) { \IPS\Session::i()->csrfCheck(); $currentPageCount = $item->commentPageCount(); $valueField = $commentClass::$databaseColumnMap['content']; if ( $item::$firstCommentRequired and $comment->mapped('first') ) { if ( $item->canDelete() ) { $item->delete(); } else { throw new \UnderflowException; } } else { if ( $comment->canDelete() ) { $comment->delete(); /* Log */ \IPS\Session::i()->modLog( 'modlog__comment_delete', array( $item::$title => TRUE, $item->url()->__toString() => FALSE, $item->mapped('title') => FALSE ), $item ); } else { throw new \UnexpectedValueException; } } if ( \IPS\Request::i()->isAjax() ) { $currentPageCount = \IPS\Request::i()->currentPage; $newPageCount = $item->commentPageCount( TRUE ); if ( $currentPageCount != $newPageCount ) { \IPS\Output::i()->json( array( 'type' => 'redirect', 'total' => $item->mapped( 'num_comments' ), 'url' => (string) $item->lastCommentPageUrl() ) ); } else { \IPS\Output::i()->json( array( 'page' => $newPageCount, 'total' => $item->mapped( 'num_comments' ) ) ); } } else { \IPS\Output::i()->redirect( $item->url() ); } } /** * Split Comment * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ protected function _split( $commentClass, $comment, $item ) { if ( $comment->canSplit() ) { $itemClass = $comment::$itemClass; $idColumn = $itemClass::$databaseColumnId; $commentIdColumn = $comment::$databaseColumnId; /* Create a copy of the old item for logging */ $oldItem = $item; /* Construct a form */ $form = $this->_splitForm( $item ); /* Handle submissions */ if ( $values = $form->values() ) { /* Are we creating or using an existing? */ if ( isset( $values['_split_type'] ) and $values['_split_type'] === 'new' ) { $item = $itemClass::createItem( $comment->author(), $comment->mapped('ip_address'), \IPS\DateTime::ts( $comment->mapped('date') ), isset( $values[ $itemClass::$formLangPrefix . 'container' ] ) ? $values[ $itemClass::$formLangPrefix . 'container' ] : NULL ); $item->processForm( $values ); if ( isset( $itemClass::$databaseColumnMap['first_comment_id'] ) ) { $firstCommentIdColumn = $itemClass::$databaseColumnMap['first_comment_id']; $item->$firstCommentIdColumn = $comment->$commentIdColumn; } $item->save(); } else { $item = $itemClass::loadFromUrl( $values['_split_into_url'] ); } $comment->move( $item ); $oldItem->rebuildFirstAndLastCommentData(); /* Log it */ \IPS\Session::i()->modLog( 'modlog__action_split', array( $item::$title => FALSE, $item->url()->__toString() => FALSE, $item->mapped('title') => FALSE, $oldItem->url()->__toString() => FALSE, $oldItem->mapped('title') => FALSE ), $item ); /* Redirect to it */ \IPS\Output::i()->redirect( $item->url() ); } /* Display */ $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); } else { throw new \DomainException; } } /** * Edit Log * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ public function _editlog( $commentClass, $comment, $item ) { /* Permission check */ if ( \IPS\Settings::i()->edit_log != 2 or ( !\IPS\Settings::i()->edit_log_public and !\IPS\Member::loggedIn()->modPermission('can_view_editlog') ) ) { throw new \DomainException; } /* Display */ $container = NULL; try { $container = $item->container(); foreach ( $container->parents() as $parent ) { \IPS\Output::i()->breadcrumb[] = array( $parent->url(), $parent->_title ); } \IPS\Output::i()->breadcrumb[] = array( $container->url(), $container->_title ); } catch ( \Exception $e ) { } \IPS\Output::i()->breadcrumb[] = array( $comment->url(), $item->mapped('title') ); \IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack( 'edit_history_title' ) ); \IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'edit_history_title' ); \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->commentEditHistory( $comment->editHistory( \IPS\Member::loggedIn()->modPermission('can_view_editlog') ), $comment ); } /** * Report Comment/Review * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ protected function _report( $commentClass, $comment, $item ) { $class = static::$contentModel; /* Permission check */ $canReport = $comment->canReport(); if ( $canReport !== TRUE ) { \IPS\Output::i()->error( $canReport, '2S136/4', 404, '' ); } /* Show form */ $form = new \IPS\Helpers\Form( NULL, 'report_submit' ); $form->class = 'ipsForm_vertical'; $form->add( new \IPS\Helpers\Form\Editor( 'report_message', NULL, FALSE, array( 'app' => 'core', 'key' => 'Reports', 'autoSaveKey' => "report-{$class::$application}-{$class::$module}-{$item->id}-{$comment->id}", 'minimize' => 'report_message_placeholder' ) ) ); if( !\IPS\Member::loggedIn()->member_id ) { $form->add( new \IPS\Helpers\Form\Captcha ); } if ( $values = $form->values() ) { $report = $comment->report( $values['report_message'] ); \IPS\File::claimAttachments( "report-{$class::$application}-{$class::$module}-{$item->id}-{$comment->id}", $report->id ); if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( \IPS\Member::loggedIn()->language()->addToStack('report_submit_success') ); } else { \IPS\Output::i()->redirect( $comment->url(), 'report_submit_success' ); } } $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); } /** * Rep Comment/Review * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ protected function _rep( $commentClass, $comment, $item ) { \IPS\Session::i()->csrfCheck(); $type = intval( \IPS\Request::i()->rep ) === 1 ? 1 : -1; try { $comment->giveReputation( $type ); if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->reputation( $comment ) ); } else { \IPS\Output::i()->redirect( $comment->url() ); } } catch ( \DomainException $e ) { if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->json( array( 'error' => \IPS\Member::loggedIn()->language()->addToStack( $e->getMessage() ) ), 403 ); } else { \IPS\Output::i()->error( $e->getMessage(), '1S136/M', 403, '' ); } } } /** * Show Comment/Review Rep * * @param string $commentClass The comment/review class * @param \IPS\Content\Comment $comment The comment/review * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ protected function _showRep( $commentClass, $comment, $item ) { /* Set navigation */ $this->_setBreadcrumbAndTitle( $item ); if( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->output = $comment->reputationTable(); } else { \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->genericBlock( $comment->reputationTable(), \IPS\Member::loggedIn()->language()->addToStack('see_who_repped') ); } } /** * Multimod * * @param string $commentClass The comment/review class * @param \IPS\Content\Item $item The item * @return void * @throws \LogicException */ protected function _multimod( $commentClass, $item ) { \IPS\Session::i()->csrfCheck(); if ( \IPS\Request::i()->modaction == 'split' ) { $form = $this->_splitForm( $item ); $form->hiddenValues['modaction'] = 'split'; foreach ( \IPS\Request::i()->multimod as $k => $v ) { $form->hiddenValues['multimod['.$k.']'] = $v; } if ( $values = $form->values() ) { $itemIdColumn = $item::$databaseColumnId; $commentIdColumn = $commentClass::$databaseColumnId; /* Create a copy of the old item for logging */ $oldItem = $item; $comments = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', $commentClass::$databaseTable, array( array( $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['item'] . '=?', $item->$itemIdColumn ), \IPS\Db::i()->in( $commentClass::$databasePrefix . $commentClass::$databaseColumnId, array_keys( \IPS\Request::i()->multimod ) ) ), $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['date'] ), $commentClass ); if ( isset( $values['_split_type'] ) and $values['_split_type'] === 'new' ) { foreach ( $comments as $comment ) { $firstComment = $comment; break; } $item = $item::createItem( $firstComment->author(), $firstComment->mapped('ip_address'), \IPS\DateTime::ts( $firstComment->mapped('date') ), $values[ $item::$formLangPrefix . 'container' ] ); $item->processForm( $values ); if ( isset( $item::$databaseColumnMap['first_comment_id'] ) ) { $firstCommentIdColumn = $item::$databaseColumnMap['first_comment_id']; $item->$firstCommentIdColumn = $comment->$commentIdColumn; } $item->save(); } else { $item = $item::loadFromUrl( $values['_split_into_url'] ); } foreach ( $comments as $comment ) { $comment->move( $item ); } $item->rebuildFirstAndLastCommentData(); $oldItem->rebuildFirstAndLastCommentData(); /* Log it */ \IPS\Session::i()->modLog( 'modlog__action_split', array( $item::$title => FALSE, $item->url()->__toString() => FALSE, $item->mapped('title') => FALSE, $oldItem->url()->__toString() => FALSE, $oldItem->mapped('title') => FALSE ), $item ); \IPS\Output::i()->redirect( $item->url() ); } else { $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( \IPS\Output::i()->output ); } else { \IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders ); } return; } } elseif ( \IPS\Request::i()->modaction == 'merge' ) { if ( !( count( \IPS\Request::i()->multimod ) > 1 ) ) { \IPS\Output::i()->error( 'cannot_merge_one_post', '1S136/S', 403, '' ); } $comments = array(); $authors = array(); $content = array(); foreach( array_keys( \IPS\Request::i()->multimod ) AS $id ) { try { $comments[$id] = $commentClass::loadAndCheckPerms( $id ); $content[] = $comments[$id]->mapped('content'); } catch( \Exception $e ) {} } $form = new \IPS\Helpers\Form; $form->class = 'ipsForm_vertical'; $form->add( new \IPS\Helpers\Form\Editor( 'final_comment_content', implode( '
', $content ), TRUE, array( 'app' => $item::$application, 'key' => ucwords( $item::$module ), 'autoSaveKey' => 'mod-merge-' . implode( '-', array_keys( $comments ) ), ) ) ); if ( $values = $form->values() ) { $idColumn = $item::$databaseColumnId; $commentIdColumn = $commentClass::$databaseColumnId; $commentIds = array_keys( \IPS\Request::i()->multimod ); $firstComment = $commentClass::loadAndCheckPerms( array_shift( $commentIds ) ); $contentColumn = $commentClass::$databaseColumnMap['content']; $firstComment->$contentColumn = $values['final_comment_content']; $firstComment->save(); foreach( $commentIds AS $id ) { try { $comment = $commentClass::loadAndCheckPerms( $id ); \IPS\Db::i()->update( 'core_attachments_map', array( 'id1' => $item->$idColumn, 'id2' => $firstComment->$commentIdColumn, ), array( 'location_key=? AND id1=? AND id2=?', (string) $item::$application . '_' . ucfirst( $item::$module ), $item->$idColumn, $comment->$commentIdColumn ) ); $comment->delete(); } catch( \Exception $e ) {} } $item->rebuildFirstAndLastCommentData(); \IPS\Output::i()->redirect( $firstComment->url() ); } else { $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( \IPS\Output::i()->output ); } else { \IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders ); } return; } } elseif ( \IPS\Request::i()->modaction == 'hide' ) { if ( $commentClass::$hideLogKey ) { $form = new \IPS\Helpers\Form; $form->class = 'ipsForm_vertical'; $form->add( new \IPS\Helpers\Form\Text( 'hide_reason') ); if ( $values = $form->values() ) { foreach( array_keys( \IPS\Request::i()->multimod ) AS $id ) { try { $comment = $commentClass::loadAndCheckPerms( $id ); $comment->modAction( 'hide', NULL, $values['hide_reason'] ); } catch( \Exception $e ) { } } $item->rebuildFirstAndLastCommentData(); \IPS\Output::i()->redirect( $item->url() ); } else { $this->_setBreadcrumbAndTitle( $item ); \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( \IPS\Output::i()->output ); } else { \IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders ); } return; } } else { foreach( array_keys( \IPS\Request::i()->multimod ) AS $id ) { try { $comment = $commentClass::loadAndCheckPerms( $id ); $comment->modAction( 'hide' ); } catch( \Exception $e ) { } } } } else { $object = NULL; if( isset( \IPS\Request::i()->multimod ) AND is_array( \IPS\Request::i()->multimod ) ) { foreach ( array_keys( \IPS\Request::i()->multimod ) as $id ) { try { $object = $commentClass::loadAndCheckPerms( $id ); $object->modAction( \IPS\Request::i()->modaction, \IPS\Member::loggedIn() ); } catch ( \Exception $e ) {} } } $item->resyncCommentCounts(); $item->save(); if ( $object and \IPS\Request::i()->multimod != 'delete' ) { \IPS\Output::i()->redirect( $object->url() ); } elseif ( in_array( 'IPS\Content\Review', class_parents( $commentClass ) ) ) { \IPS\Output::i()->redirect( $item->lastReviewPageUrl() ); } else { \IPS\Output::i()->redirect( $item->lastCommentPageUrl() ); } } } /** * Form for splitting * * @param \IPS\Content\Item $item The item * @return \IPS\Helpers\Form */ protected function _splitForm( \IPS\Content\Item $item ) { try { $container = $item->container(); } catch ( \Exception $e ) { $container = NULL; } $form = new \IPS\Helpers\Form; if ( $item::canCreate( \IPS\Member::loggedIn(), $container ) ) { $toAdd = array(); $toggles = array(); foreach ( $item::formElements( $item ) as $k => $field ) { if ( !in_array( $k, array( 'poll', 'content', 'comment_edit_reason', 'comment_log_edit' ) ) ) { if ( $k === 'container' ) { $field->defaultValue = $container; if ( !$field->value ) { $field->value = $field->defaultValue; } } if ( !$field->htmlId ) { $field->htmlId = $field->name; } $toggles[] = $field->htmlId; $toAdd[] = $field; } } $form->add( new \IPS\Helpers\Form\Radio( '_split_type', 'new', FALSE, array( 'options' => array( 'new' => \IPS\Member::loggedIn()->language()->addToStack( 'split_type_new', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $item::$title ) ) ) ), 'existing' => \IPS\Member::loggedIn()->language()->addToStack( 'split_type_existing', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $item::$title ) ) ) ) ), 'toggles' => array( 'new' => $toggles, 'existing' => array( 'split_into_url' ) ), ) ) ); foreach ( $toAdd as $field ) { $form->add( $field ); } } $form->add( new \IPS\Helpers\Form\Url( '_split_into_url', NULL, FALSE, array(), function ( $val ) use ( $item ) { try { if ( $val ) { $item::loadFromUrl( $val ); } } catch ( \Exception $e ) { throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_url_bad_item', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $item::$title ) ) ) ) ); } }, NULL, NULL, 'split_into_url' ) ); return $form; } /** * Retrieve content tagged the same * * @note Used with a widget, but can be used elsewhere too * @return array|NULL */ public function getSimilarContent() { if( !isset( static::$contentModel ) ) { return NULL; } try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); if( $item->tags() === NULL ) { return NULL; } $select = \IPS\Db::i()->select( 'tag_meta_app,tag_meta_area,tag_meta_id', 'core_tags', array( array( '(' . \IPS\Db::i()->in( 'tag_text', $item->tags() ) . ')' ), array( '!(tag_meta_app=? and tag_meta_area=? and tag_meta_id=?)', $class::$application, $class::$module, \IPS\Request::i()->id ), array( '(' . \IPS\Db::i()->findInSet( 'tag_perm_text', \IPS\Member::loggedIn()->groups ) . ' OR ' . 'tag_perm_text=? )', '*' ), array( 'tag_perm_visible=1' ) ), 'tag_added DESC', array( 0, 5 ), array( 'tag_aai_lookup' ) )->join( 'core_tags_perms', array( 'tag_perm_aai_lookup=tag_aai_lookup' ) ); $items = array(); foreach( $select as $result ) { foreach( \IPS\Application::load( $result['tag_meta_app'] )->extensions( 'core', 'ContentRouter' ) as $key => $router ) { foreach( $router->classes AS $itemClass ) { if( $itemClass::$module == $result['tag_meta_area'] ) { try { $items[] = $itemClass::loadAndCheckPerms( $result['tag_meta_id'] ); break; } catch( \Exception $e ){} } } } } return $items; } catch ( \Exception $e ) { return NULL; } } /** * Get Cover Photo Storage Extension * * @return string */ protected function _coverPhotoStorageExtension() { $class = static::$contentModel; return $class::$coverPhotoStorageExtension; } /** * Set Cover Photo * * @param \IPS\Helpers\CoverPhoto $photo New Photo * @return void */ protected function _coverPhotoSet( \IPS\Helpers\CoverPhoto $photo ) { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); $photoColumn = $class::$databaseColumnMap['cover_photo']; $item->$photoColumn = (string) $photo->file; $offsetColumn = $class::$databaseColumnMap['cover_photo_offset']; $item->$offsetColumn = $photo->offset; $item->save(); } catch ( \OutOfRangeException $e ){} } /** * Get Cover Photo * * @return \IPS\Helpers\CoverPhoto */ protected function _coverPhotoGet() { try { $class = static::$contentModel; $item = $class::loadAndCheckPerms( \IPS\Request::i()->id ); return $item->coverPhoto(); } catch ( \OutOfRangeException $e ) { return new \IPS\Helpers\CoverPhoto; } } }