Seditio Source
Root |
./othercms/ips_4.3.4/system/Content/Controller.php
<?php
/**
 * @brief        Content Controller
 * @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
 * @since        5 Jul 2013
 */

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
{
   
/**
     * @brief    Should views and item markers be updated by AJAX requests?
     */
   
protected $updateViewsAndMarkersOnAjax = FALSE;
   
   
/**
     * 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 );

           
/* Have we moved? If a user has loaded a forum, and a moderator merges two topics leaving a link, then we need to account for this if the
                user happens to hover over the topic that was removed before refreshing the page. */
           
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' OR $item->$stateColumn == 'merged' )
                {
                    try
                    {
                       
$moved = $class::loadAndCheckPerms( $movedTo[0] );

                        if ( \
IPS\Request::i()->isAjax() AND \IPS\Request::i()->preview )
                        {
                            return
NULL;
                        }

                        \
IPS\Output::i()->redirect( $moved->url(), '', 301 );
                    }
                    catch( \
OutOfRangeException $e ) { }
                }
            }
           
           
/* 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() and !$this->updateViewsAndMarkersOnAjax )
            {
               
/* 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();
                }

               
/* We also want to update the views if we have a page parameter */
               
if( $item instanceof \IPS\Content\ReadMarkers AND isset( \IPS\Request::i()->page ) AND \IPS\Request::i()->page )
                {

                   
$idColumn = $class::$databaseColumnId;
                    if (
in_array( 'IPS\Content\Views', class_implements( $class ) ) AND isset( $class::$databaseColumnMap['views'] ) )
                    {
                       
$countUpdated = false;
                        if ( \
IPS\REDIS_ENABLED and \IPS\CACHE_METHOD == 'Redis' and \IPS\CACHE_CONFIG )
                        {
                            try
                            {
                                \
IPS\Redis::i()->zIncrBy( 'topic_views', 1, $class .'__' . $item->$idColumn );
                               
$countUpdated = true;
                            }
                            catch( \
Exception $e ) {}
                        }
                       
                        if ( !
$countUpdated )
                        {
                            \
IPS\Db::i()->insert( 'core_view_updates', array(
                                   
'classname'    => $class,
                                   
'id'        => $item->$idColumn
                           
) );
                        }
                    }
                }

                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 */
           
$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 ( isset( \
IPS\Request::i()->page ) )
            {
               
$paginationType    = NULL;
               
$container        = ( isset( $item::$databaseColumnMap['container'] ) ) ? $item->container() : NULL;

                if(
$item::supportsComments( NULL, $container ) )
                {
                   
$paginationType    = 'comment';
                }

                if( ( isset( \
IPS\Request::i()->tab ) and \IPS\Request::i()->tab === 'reviews' AND $item::supportsReviews( NULL, $container ) ) OR
                    (
$item::supportsReviews( NULL, $container ) AND $paginationType === NULL ) )
                {
                   
$paginationType = 'review';
                }

                if(
$paginationType !== NULL )
                {
                   
$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' . mb_ucfirst( $paginationType ) . 'PageUrl' ) ), NULL, 303 );
                    }
                }

               
/* Add rel tags */
               
if( \IPS\Request::i()->page != 1 )
                {
                    \
IPS\Output::i()->linkTags['first'] = (string) $item->url();

                    if( \
IPS\Request::i()->page - 1 > 1 )
                    {
                        \
IPS\Output::i()->linkTags['prev'] = (string) $item->url()->setQueryString( 'page', \IPS\Request::i()->page - 1 );
                    }
                    else
                    {
                        \
IPS\Output::i()->linkTags['prev'] = (string) $item->url();
                    }
                }
               
/* If we literally requested ?page=1 add canonical tag to get rid of the page query string param */
               
elseif( isset( $item->url()->data[ \IPS\Http\Url::COMPONENT_QUERY ]['page' ] ) )
                {
                    \
IPS\Output::i()->linkTags['canonical'] = (string) $item->url();
                }
            }

           
/* Add rel tags */
           
if ( $pageCount > 1 AND ( !\IPS\Request::i()->page OR $pageCount > \IPS\Request::i()->page ) )
            {
                \
IPS\Output::i()->linkTags['next'] = (string) $item->url()->setQueryString( 'page', ( \IPS\Request::i()->page ?: 1 ) + 1 );
            }
            if (
$pageCount > 1 AND ( !\IPS\Request::i()->page OR $pageCount != \IPS\Request::i()->page ) )
            {
                \
IPS\Output::i()->linkTags['last'] = (string) $item->url()->setQueryString( 'page', $pageCount );
            }

           
/* Update Views */
           
$idColumn = $class::$databaseColumnId;
            if (
in_array( 'IPS\Content\Views', class_implements( $class ) ) AND isset( $class::$databaseColumnMap['views'] ) )
            {
               
$countUpdated = false;
                if ( \
IPS\REDIS_ENABLED and \IPS\CACHE_METHOD == 'Redis' and \IPS\CACHE_CONFIG )
                {
                    try
                    {
                        \
IPS\Redis::i()->zIncrBy( 'topic_views', 1, $class .'__' . $item->$idColumn );
                       
$countUpdated = true;
                    }
                    catch( \
Exception $e ) {}
                }
               
                if ( !
$countUpdated )
                {
                    \
IPS\Db::i()->insert( 'core_view_updates', array(
                       
'classname'    => $class,
                       
'id'        => $item->$idColumn
                   
) );
                }
            }
                       
           
/* 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();
                }
            }
           
           
/* 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();
           
           
/* Facebook Pixel */
           
$itemId = $class::$databaseColumnId;
            \
IPS\core\Facebook\Pixel::i()->PageView = array(
               
'item_id' => $item->$itemId,
               
'item_name' => $item->mapped( 'title' ),
               
'item_type' => isset( $class::$contentType ) ? $class::$contentType : $class::$title,
               
'category_name' => isset( $item::$databaseColumnMap['container'] ) ? $item->container()->_title : NULL
           
);
           
            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();
            }

           
$tags = array();

            if(
$item->prefix() !== NULL )
            {
               
$tags[]    = $item->prefix();
            }

            if(
$item->tags() !== NULL )
            {
               
$tags = array_merge( $tags, $item->tags() );
            }

            if(
count( $tags ) )
            {
                \
IPS\Output::i()->metaTags['keywords'] = implode( ', ', $tags );
            }
           
           
/* Add contextual search options */
           
\IPS\Output::i()->contextualSearchOptions[ \IPS\Member::loggedIn()->language()->addToStack( 'search_contextual_item_' . $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_' . $container::$nodeTitle ) ] = 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()
    {
        \
IPS\Session::i()->csrfCheck();

       
/* 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
        {
           
/* no need for polling for embeds */
           
if ( !isset( static::$contentModel ) )
            {
                \
IPS\Output::i()->json( array( 'error' => 'auto_polling_disabled' ) );
                return;
            }

           
$class = static::$contentModel;

           
/* no need for polling if the content item doesn't have comments */
           
if ( !isset( $class::$commentClass ) )
            {
                \
IPS\Output::i()->json( array( 'error' => 'auto_polling_disabled' ) );
                return;
            }

           
$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 */
           
$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, ENT_DISALLOWED | ENT_QUOTES, '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;
                }

               
/* Only mark as read if we'll be staying on this page, otherise we'll be marking it read despite
                    the user not having seen everything */
               
if( (int) \IPS\Request::i()->showing + (int) count( $newComments ) <= $class::getCommentsPerPage() )
                {
                   
$item->markRead();
                }
            }
           
            \
IPS\Output::i()->json( array(
               
'content' => \IPS\Output::i()->replaceEmojiWithImages( $output ),
               
'id' => $lastId,
               
'totalCount' => $item->mapped( 'num_comments' ),
               
'totalNewCount' => (int) count( $newComments ),
               
'perPage' => $class::getCommentsPerPage(),
               
'spillOverUrl' => $item->url( 'getNewComment' )
            ) );
        }
        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;
            }
           
           
$container = NULL;
            try
            {
               
$container = $item->container();
            }
            catch ( \
BadMethodCallException $e ) {}

           
/* Build the form */
           
$form = $item->buildEditForm();

            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 );

                   
/* Moderator log */
                   
\IPS\Session::i()->modLog( 'modlog__item_edit', array( $item::$title => FALSE, $item->url()->__toString() => FALSE, $item::$title => TRUE, $item->mapped( 'title' ) => FALSE ), $item );

                    \
IPS\Output::i()->redirect( $item->url() );
                }
                else
                {
                   
$form->error = \IPS\Member::loggedIn()->language()->addToStack( 'edit_no_perm_err' );
                }
            }
           
           
$this->_setBreadcrumbAndTitle( $item );

            if( \
IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
            }
            else
            {
                \
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, $container );
            }
        }
        catch ( \
Exception $e )
        {
            \
IPS\Log::debug( $e, 'content_debug' );
            \
IPS\Output::i()->error( 'edit_no_perm_err', '2S136/E', 404, '' );
        }
    }

   
/**
     * Edit item's tags
     *
     * @return    void
     */
   
protected function editTags()
    {
        try
        {
           
$class = static::$contentModel;
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );

           
/* Make sure tagging is supported in this class */
           
if ( !in_array( 'IPS\Content\Tags', class_implements( $class ) ) )
            {
                throw new \
DomainException;
            }

           
/* Get the container as we'll need it for permission checking */
           
$container = NULL;
            try
            {
               
$container = $item->container();
            }
            catch ( \
BadMethodCallException $e ) {}

           
/* Make sure we can edit and tag */
           
if ( ( !$item->canEdit() OR !$item::canTag( NULL, $container ) ) )
            {
                throw new \
OutOfRangeException;
            }

           
/* If the tag form field is generated, create the form, otherwise throw an exception */
           
if( $tagsField = $item::tagsFormField( $item, $container, TRUE ) )
            {
               
$form = new \IPS\Helpers\Form( 'form', \IPS\Member::loggedIn()->language()->checkKeyExists( $item::$formLangPrefix . '_save' ) ? $item::$formLangPrefix . '_save' : 'save' );
               
$form->class = 'ipsForm_vertical ipsForm_fullWidth';

               
$form->add( $tagsField );
            }
            else
            {
                throw new \
DomainException;
            }

           
/* If we are simply removing a tag, do it */
           
if( isset( \IPS\Request::i()->removeTag ) )
            {
               
/* Get our current tags */
               
$existingTags = ( $item->prefix() ? array_merge( array( 'prefix' => $item->prefix() ), $item->tags() ) : $item->tags() );

               
/* Remove the tag from the array. Preserve index since it could be 'prefix' which we need to remember. */
               
foreach( $existingTags as $index => $tag )
                {
                    if(
$tag == \IPS\Request::i()->removeTag )
                    {
                        unset(
$existingTags[ $index ] );
                        break;
                    }
                }

               
/* Now set the tags */
               
$name = $tagsField->name;
                \
IPS\Request::i()->$name = implode( "\n", $existingTags );

                if( isset(
$existingTags['prefix'] ) )
                {
                   
$prefix = $tagsField->name . '_prefix';
                    \
IPS\Request::i()->$prefix = $existingTags['prefix'];

                   
$prefix = $tagsField->name . '_freechoice_prefix';
                    \
IPS\Request::i()->$prefix = 'on';
                }

               
$tagsField->setValue( FALSE, TRUE );

               
$submittedKey = $form->id . "_submitted";
                \
IPS\Request::i()->$submittedKey = 1;
            }

           
/* Process the values */
           
if ( $values = $form->values() )
            {
               
$item->setTags( $values[ $item::$formLangPrefix . 'tags' ] ?: array() );

                if ( \
IPS\Request::i()->isAjax() )
                {
                   
/* Build html we'll need to display */
                   
$toReturn = array( 'tags' => '', 'prefix' => '' );

                    foreach (
$item->tags() as $tag )
                    {
                       
$toReturn['tags'] .= \IPS\Theme::i()->getTemplate( 'global', 'core' )->tag( $tag, $item->url() );
                    }

                    if(
$item->prefix() )
                    {
                       
$toReturn['prefix'] = \IPS\Theme::i()->getTemplate( 'global', 'core' )->prefix( $item->prefix( TRUE ), $item->prefix() );
                    }

                    \
IPS\Output::i()->json( $toReturn );
                }
                else
                {
                    \
IPS\Output::i()->redirect( $item->url() );    
                }                
            }

           
/* If we tried to delete a tag and this is an AJAX request, just return the error. If it's not AJAX, we
                can just let the regular form output which will show the error. */
           
if( $tagsField->error AND \IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->error( $tagsField->error, '1S136/13', 403, '' );
            }
           
           
/* Show the output */
           
$this->_setBreadcrumbAndTitle( $item );
            \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->editTagsForm( $form );
        }
        catch ( \
DomainException $e )
        {
            \
IPS\Output::i()->error( 'edit_no_tags_defined', '2S131/3', 403, 'edit_no_tags_defined_admin' );
        }
        catch ( \
Exception $e )
        {
            \
IPS\Output::i()->error( 'edit_no_perm_err', '2S136/12', 403, '' );
        }
    }
   
   
/**
     * 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->canEditTitle() )
            {
                throw new \
RuntimeException;
            }
           
           
$oldTitle = $item->mapped( 'title' );

           
$maxLength    = \IPS\Settings::i()->max_title_length ?: 255;
           
$titleField    = $item::$databaseColumnMap['title'];

            if( \
strlen( \IPS\Request::i()->newTitle ) > $maxLength )
            {
                throw new \
LengthException( \IPS\Member::loggedIn()->language()->addToStack( 'form_maxlength', FALSE, array( 'pluralize' => array( $maxLength ) ) ) );
            }
            elseif( !
trim( \IPS\Request::i()->newTitle ) )
            {
                throw new \
InvalidArgumentException('form_required');
            }

           
$item->$titleField = \IPS\Request::i()->newTitle;
           
$idField = $item::$databaseColumnId;
           
$item->save();

           
/* rebuild the container last item data */
           
if ( ! $item->hidden() and ( $item->$idField === $item->container()->last_id ) )
            {
               
$item->container()->seo_last_title = $item->title_seo;
               
$item->container()->last_title     = $item->title;
               
$item->container()->save();

                foreach(
$item->container()->parents() AS $parent )
                {
                    if ( (
$item::$databaseColumnId === $parent->last_id ) )
                    {
                       
$parent->seo_last_title        = $item->title_seo;
                       
$parent->last_title            = $item->title;
                       
$parent->save();
                    }
                }
            }

            if (
$item instanceof \IPS\Content\Searchable )
            {
                \
IPS\Content\Search\Index::i()->index( $item );
            }
           
           
/* Only add the mod log entry if we actually changed the title */
           
if( $item->$titleField !== $oldTitle )
            {
                \
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( \
LogicException $e )
        {
            \
IPS\Output::i()->error( $e->getMessage(), '2S136/1M', 403, '' );
        }
        catch ( \
Exception $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2S136/11', 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();
            if ( \
IPS\IPS::classUsesTrait( $container, 'IPS\Content\ClubContainer' ) and $club = $container->club() )
            {
                \
IPS\core\FrontNavigation::$clubTabActive = TRUE;
                \
IPS\Output::i()->breadcrumb = array();
                \
IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' ), \IPS\Member::loggedIn()->language()->addToStack('module__core_clubs') );
                \
IPS\Output::i()->breadcrumb[] = array( $club->url(), $club->name );
                \
IPS\Output::i()->breadcrumb[] = array( $container->url(), $container->_title );
               
                if ( \
IPS\Settings::i()->clubs_header == 'sidebar' )
                {
                    \
IPS\Output::i()->sidebar['contextual'] = \IPS\Theme::i()->getTemplate( 'clubs', 'core' )->header( $club, $container, 'sidebar' );
                }
            }
            else
            {
                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;
    }
   
   
/**
     * Toggle a poll status
     *
     * @return void
     */
   
protected function pollStatus()
    {
        try
        {
            \
IPS\Session::i()->csrfCheck();
                       
           
$class = static::$contentModel;
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );
           
            if (
$poll = $item->getPoll() )
            {
                if ( !
$poll->canClose() )
                {
                    \
IPS\Output::i()->error( 'no_module_permission', '2S136/Z', 403, '' );
                }

               
$newStatus = ( \IPS\Request::i()->value == 1 ? 0 : 1 );
               
$poll->poll_closed = $newStatus;
               
$redirectMessage = $newStatus == 0 ? 'poll_status_opened' : 'poll_status_closed';

               
/* If opening the poll (after it has closed) remove the auto-close date */
               
if( !$newStatus and ( $poll->poll_close_date instanceof \IPS\DateTime ) )
                {
                   
$poll->poll_close_date = -1;
                   
$redirectMessage .= '_no_date';
                }

               
$poll->save();
               
                \
IPS\Output::i()->redirect( $item->url(), $redirectMessage );
            }
            else
            {
                throw new \
UnderflowException;
            }
        }
        catch ( \
Exception $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2S136/Y', 404, '' );
        }
    }
   
   
/**
     * 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' )
            {
               
/* If this is an AJAX request, and we're coming from the approval queue, just do it. */
               
if ( \IPS\Request::i()->isAjax() AND isset( \IPS\Request::i()->_fromApproval ) )
                {
                   
$item->modAction( \IPS\Request::i()->action );
                    \
IPS\Output::i()->json( 'OK' );
                }
               
               
$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 if ( \
IPS\Request::i()->action === 'delete' AND isset( \IPS\Request::i()->immediate ) )
            {
               
$item->modAction( \IPS\Request::i()->action, NULL, NULL, TRUE );
            }
            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( \
InvalidArgumentException $e )
        {
            \
IPS\Output::i()->error( 'mod_error_invalid_action', '3S136/1A', 403, 'mod_error_invalid_action_admin' );
        }
        catch ( \
OutOfRangeException $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;
            }

           
$container = $item->container();
           
           
$form = new \IPS\Helpers\Form( 'form', \IPS\Member::loggedIn()->language()->addToStack( 'move_send_to_container', FALSE, array( 'sprintf' => $container->_title ) ), NULL, array( 'data-bypassValidation' => true ) );
           
$form->actionButtons[] = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->button( \IPS\Member::loggedIn()->language()->addToStack( 'move_send_to_item', FALSE, array( 'sprintf' => $item->definiteArticle() ) ), 'submit', null, 'ipsButton ipsButton_link', array( 'tabindex' => '3', 'accesskey' => 'i', 'value' => 'item', 'name' => 'returnto' ) );
           
$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 the item is in a club, only allow moving to other clubs that you moderate */
                           
if ( \IPS\IPS::classUsesTrait( $item->container(), 'IPS\Content\ClubContainer' ) and $item->container()->club()  )
                            {
                                return
$item::modPermission( 'move', \IPS\Member::loggedIn(), $node ) and $node->can( 'add' ) ;
                            }
                           
                            if (
$node->can( 'add' ) )
                            {
                                return
true;
                            }
                        }
                        catch( \
OutOfBoundsException $e ) { }
                    }
                   
                    return
false;
                },
               
'clubs'    => TRUE
           
) ) );
           
            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, '' );
                }

               
/* If this item is read, we need to re-mark it as such after moving */
               
if( $item instanceof \IPS\Content\ReadMarkers )
                {
                   
$unread = $item->unread();
                }

               
$item->move( $values['move_to'], isset( $values['move_keep_link'] ) ? $values['move_keep_link'] : FALSE );

               
/* Mark it as read */
               
if( $item instanceof \IPS\Content\ReadMarkers and $unread == 0 )
                {
                   
$item->markRead();
                }

                \
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( ( isset( \IPS\Request::i()->returnto ) AND \IPS\Request::i()->returnto == 'item' ) ? $item->url() : $container->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 = $item->mergeForm();

            if (
$values = $form->values() )
            {
               
$item->mergeIn( array( $class::loadFromUrl( $values['merge_with'] ) ), isset( $values['move_keep_link'] ) ? $values['move_keep_link'] : FALSE );
                \
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() )
                {
                   
$fromName    = ( isset( $values['mail_from_name'] ) ) ? $values['mail_from_name'] : \IPS\Member::loggedIn()->name;
                   
$email = \IPS\Email::buildFromContent( $values['email_subject'], \IPS\Email::staticParseTextForEmail( $values['email_content'], \IPS\Member::loggedIn()->language() ) )
                        ->
send( $values['email_email'], array(), array(), NULL, $fromName, array(
                           
'Reply-To'    =>  \IPS\Email::encodeHeader( $fromName, isset( $values['mail_from_email'] ) ? $values['mail_from_email'] : \IPS\Member::loggedIn()->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' );
                    }
                }

               
/* Don't index the share by email page */
               
\IPS\Output::i()->metaTags['robots'] = 'noindex';
   
               
$this->_setBreadcrumbAndTitle( $item );
                \
IPS\Output::i()->title        = \IPS\Member::loggedIn()->language()->addToStack( 'send_email_form_with_title', FALSE, array( 'sprintf' => array( $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 \
BadMethodCallException;
            }
        }
        catch ( \
Exception $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2S136/5', 404, '' );
        }
    }
   
   
/**
     * Delete a report
     *
     * @return    void
     */
   
protected function deleteReport()
    {
        \
IPS\Session::i()->csrfCheck();

       
/* Make sure the user confirmed the deletion */
       
\IPS\Request::i()->confirmedDelete();

        try
        {
           
$report = \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'id=? AND report_by=? AND date_reported > ?', \IPS\Request::i()->cid, \IPS\Member::loggedIn()->member_id, time() - ( \IPS\Settings::i()->automoderation_report_again_mins * 60 ) ) )->first();
        }
        catch( \
UnderflowException $e )
        {
            \
IPS\Output::i()->error( 'automoderation_cannot_find_report', '2S136/1G', 404, '' );
        }
       
        try
        {
           
$index = \IPS\core\Reports\Report::load( $report['rid'] );
        }
        catch( \
OutofRangeException $e )
        {
            \
IPS\Output::i()->error( 'automoderation_cannot_find_report', '2S136/1H', 404, '' );
        }
       
       
$class = $index->class;
       
        \
IPS\Db::i()->delete( 'core_rc_reports', array( 'id=?', \IPS\Request::i()->cid ) );
       
       
/* Recalculate, we may have dropped below the threshold needed to hide a thing */
       
$index->runAutomaticModeration();
       
        \
IPS\Output::i()->redirect( $class::load( $index->content_id )->url(), 'automoderation_deleted' );
    }
   
   
/**
     * 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 AND !( $canReport == 'report_err_already_reported' AND \IPS\Settings::i()->automoderation_enabled ) )
            {
                \
IPS\Output::i()->error( $canReport, '2S136/6', 403, '' );
            }
           
           
/* Show form */
           
$form = new \IPS\Helpers\Form( NULL, 'report_submit' );
           
$form->class = 'ipsForm_vertical';
           
$idColumn = $class::$databaseColumnId;
           
           
/* As we group by user id to determine if max points have been reached, guests cannot contribute to counts */
           
if ( \IPS\Member::loggedIn()->member_id and \IPS\Settings::i()->automoderation_enabled )
            {
               
/* Has this member already reported this in the past 24 hours */
               
try
                {
                   
$index = \IPS\core\Reports\Report::loadByClassAndId( get_class( $item ), $item->$idColumn );
                   
$report = \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'rid=? and report_by=? and date_reported > ?', $index->id, \IPS\Member::loggedIn()->member_id, time() - ( \IPS\Settings::i()->automoderation_report_again_mins * 60 ) ) )->first();
                   
                    \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system', 'core' )->reportedAlready( $index, $report, $item );
                    return;
                }
                catch( \
Exception $e ) { }
               
               
$options = array( \IPS\core\Reports\Report::TYPE_MESSAGE => \IPS\Member::loggedIn()->language()->addToStack('report_message_item') );
                foreach( \
IPS\core\Reports\Types::roots() as $type )
                {
                   
$options[ $type->id ] = $type->_title;
                }
               
               
$form->add( new \IPS\Helpers\Form\Radio( 'report_type', NULL, FALSE, array( 'options' => $options ) ) );
            }
           
           
$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( !\
IPS\Member::loggedIn()->member_id )
            {
               
$form->add( new \IPS\Helpers\Form\Captcha );
            }
            if (
$values = $form->values() )
            {
               
$report = $item->report( $values['report_message'], ( isset( $values['report_type'] ) ) ? $values['report_type'] : 0 );
                \
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, '' );
        }
    }
   
   
/**
     * React
     *
     * @return    void
     */
   
protected function react()
    {
        try
        {
            \
IPS\Session::i()->csrfCheck();
           
           
$class = static::$contentModel;
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );
           
$item->react( \IPS\Content\Reaction::load( \IPS\Request::i()->reaction ) );
           
            if ( \
IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->json( array(
                   
'status' => 'ok',
                   
'count' => count( $item->reactions() ),
                   
'score' => $item->reactionCount(),
                   
'blurb' => ( \IPS\Settings::i()->reaction_count_display == 'count' ) ? '' : \IPS\Theme::i()->getTemplate( 'global', 'core' )->reactionBlurb( $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/14', 403, '' );
            }
        }
    }
   
   
/**
     * Unreact
     *
     * @return    void
     */
   
protected function unreact()
    {
        try
        {
            \
IPS\Session::i()->csrfCheck();

           
$member = ( isset( \IPS\Request::i()->member ) and \IPS\Member::loggedIn()->modPermission('can_remove_reactions') ) ? \IPS\Member::load( \IPS\Request::i()->member ) : \IPS\Member::loggedIn();
           
           
$class = static::$contentModel;
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );
           
$item->removeReaction( $member );

           
/* Log */
           
if( $member->member_id !== \IPS\Member::loggedIn()->member_id )
            {
                \
IPS\Session::i()->modLog( 'modlog__reaction_delete', array( $member->url()->__toString() => FALSE, $member->name => FALSE, $item::$title => TRUE, $item->url()->__toString() => FALSE, $item->mapped( 'title' ) => FALSE ), $item );
            }

            if ( \
IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->json( array(
                   
'status' => 'ok',
                   
'count' => count( $item->reactions() ),
                   
'score' => $item->reactionCount(),
                   
'blurb' => ( \IPS\Settings::i()->reaction_count_display == 'count' ) ? '' : \IPS\Theme::i()->getTemplate( 'global', 'core' )->reactionBlurb( $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/15', 403, '' );
            }
        }
    }
   
   
/**
     * Show Reactions
     *
     * @return    void
     */
   
protected function showReactions()
    {
        try
        {
           
$class = static::$contentModel;
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );
           
$blurb = $item->reactBlurb();
           
           
$this->_setBreadcrumbAndTitle( $item );
           
            \
IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'see_who_reacted' ) . ' - ' . \IPS\Output::i()->title;
           
           
$tabs = array();
           
$tabs['all'] = array( 'title' => \IPS\Member::loggedIn()->language()->addToStack('all'), 'count' => count( $item->reactions() ) );
            foreach( \
IPS\Content\Reaction::roots() AS $reaction )
            {
                if (
$reaction->_enabled !== FALSE )
                {
                   
$tabs[ $reaction->id ] = array( 'title' => $reaction->_title, 'icon' => $reaction->_icon, 'count' => isset( $blurb[ $reaction->id ] ) ? $blurb[ $reaction->id ] : 0 );
                }
            }
           
           
$activeTab = 'all';
            if ( isset( \
IPS\Request::i()->reaction ) )
            {
               
$activeTab = \IPS\Request::i()->reaction;
            }
           
           
$url = $item->url('showReactions');
           
$url = $url->setQueryString( 'changed', 1 );
           
            if (
$activeTab !== 'all' )
            {
               
$url = $url->setQueryString( 'reaction', $activeTab );
            }

            \
IPS\Output::i()->metaTags['robots'] = 'noindex';
           
            if ( \
IPS\Request::i()->isAjax() AND isset( \IPS\Request::i()->changed ) )
            {
                \
IPS\Output::i()->output = $item->reactionTable( $activeTab !== 'all' ? $activeTab : NULL, $url, 'reaction', FALSE );
            }
            else
            {
                \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->reactionTabs( $tabs, $activeTab, $item->reactionTable( $activeTab !== 'all' ? $activeTab : NULL ), $url, 'reaction', FALSE );
            }
        }
        catch( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2S136/18', 404, '' );
        }
        catch( \
DomainException $e )
        {
            \
IPS\Output::i()->error( 'no_module_permission', '2S136/19', 403, '' );
        }
    }
   
   
/**
     * Moderator Log
     *
     * @return    void
     */
   
protected function modLog()
    {
        if( !\
IPS\Member::loggedIn()->modPermission( 'can_view_moderation_log' ) )
        {
            \
IPS\Output::i()->error( 'no_module_permission', '2S136/1F', 403, '' );
        }
       
        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 = NULL;
                if( \
IPS\DateTime::ts( $item->mapped('date') ) < $timeLastRead )
                {
                   
$comment = $item->comments( 1, NULL, 'date', 'asc', NULL, NULL, $timeLastRead );
                }

               
/* If we don't have any unread comments... */
               
if ( !$comment and $class::$firstCommentRequired )
                {
                   
/* If we haven't read the item at all, go there */
                   
if ( $item->unread() )
                    {
                        \
IPS\Output::i()->redirect( $item->url() );
                    }
                   
/* Otherwise, go to the last comment */
                   
else
                    {
                       
$comment = $item->comments( 1, NULL, 'date', 'desc' );
                    }
                }

                \
IPS\Output::i()->redirect( $comment ? $comment->url() : $item->url() );
            }
            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 ) and $item->mapped('date') < $resetTimes[ $item->container()->_id ] )
                    {
                       
$comment = $item->comments( 1, NULL, 'date', 'asc', NULL, NULL, \IPS\DateTime::ts( $resetTimes[ $item->container()->_id ] ) );
                       
                        if (
$class::$firstCommentRequired and $comment->isFirst() )
                        {
                            \
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 )
            {
               
$this->_find( get_class( $comment ), $comment, $item );
            }
            else
            {
                \
IPS\Output::i()->redirect( $item->url() );
            }
        }
        catch( \
BadMethodCallException $e )
        {
            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/K', 404, '' );
        }
        catch( \
LogicException $e )
        {
            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/Q', 404, '' );
        }
    }
   
   
/**
     * Go to first comment
     *
     * @return    void
     */
   
public function getFirstComment()
    {
        try
        {
           
$class    = static::$contentModel;
           
$item    = $class::loadAndCheckPerms( \IPS\Request::i()->id );
           
            if (
$class::$firstCommentRequired )
            {
               
$comments = $item->comments( 2, NULL, 'date', 'asc' );
               
$comment  = array_pop( $comments );
                unset(
$comments );
            }
            else
            {
               
$comment = $item->comments( 1, NULL, 'date', 'asc' );
            }
           
            if (
$comment !== NULL )
            {
               
$this->_find( get_class( $comment ), $comment, $item );
            }
            else
            {
                \
IPS\Output::i()->redirect( $item->url() );
            }
        }
        catch( \
BadMethodCallException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2S136/W', 404, '' );
        }
        catch( \
LogicException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2S136/X', 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, '' );
        }
    }

   
/**
     * Allow the author of a content item to reply to a review
     *
     * @param    bool    $editing    TRUE if we are editing our response
     * @return    void
     */
   
protected function _respond( $editing=FALSE )
    {
        try
        {
           
/* Init */
           
$class = static::$contentModel;
           
$reviewClass = $class::$reviewClass;
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );
           
$review = $reviewClass::loadAndCheckPerms( \IPS\Request::i()->review );
           
           
/* Are we allowed to respond? */
           
if( $editing === TRUE )
            {
                if ( !
$review->canEditResponse() )
                {
                    throw new \
DomainException;
                }
            }
            else
            {
                if ( !
$review->canRespond() )
                {
                    throw new \
DomainException;
                }
            }
           
           
$form = new \IPS\Helpers\Form;
           
$form->add( new \IPS\Helpers\Form\Editor( 'reviewResponse', $editing ? $review->mapped('author_response') : NULL, TRUE, array(
               
'app'            => 'core',
               
'key'            => 'ReviewResponses',
               
'autoSaveKey'     => 'reviewResponse-' . $class::$application . '/' . $class::$module . '-' . \IPS\Request::i()->review,
               
'attachIds'        => array( \IPS\Request::i()->id, \IPS\Request::i()->review, get_class( $item ) )
            ) ) );
           
            if (
$values = $form->values() )
            {
               
$review->author_response = $values['reviewResponse'];
               
$review->save();
               
                \
IPS\Output::i()->redirect( $review->url() );
            }

            \
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/1I', 403, '' );
        }
    }

   
/**
     * Allow the author of a content item to edit their review response
     *
     * @return    void
     */
   
protected function _editResponse()
    {
        return
$this->_respond( TRUE );
    }

   
/**
     * Delete a review response
     *
     * @return    void
     */
   
protected function _deleteResponse()
    {
        \
IPS\Session::i()->csrfCheck();

        try
        {
           
/* Init */
           
$class = static::$contentModel;
           
$reviewClass = $class::$reviewClass;
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );
           
$review = $reviewClass::loadAndCheckPerms( \IPS\Request::i()->review );
           
           
/* Are we allowed to delete responses? */
           
if ( !$review->canDeleteResponse() )
            {
                throw new \
DomainException;
            }
           
           
$review->author_response = NULL;
           
$review->save();

            \
IPS\Output::i()->redirect( $review->url() );
        }
        catch ( \
LogicException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2S136/1J', 403, '' );
        }
    }
   
   
/**
     * Message Form
     *
     * @return    void
     */
   
protected function messageForm()
    {
       
$class = static::$contentModel;
        try
        {
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );

           
$current = NULL;
           
$metaData = $item->getMeta();
            if ( isset( \
IPS\Request::i()->meta_id ) AND isset( $metaData['core_ContentMessages'][ \IPS\Request::i()->meta_id ] ) )
            {
               
$current = $metaData['core_ContentMessages'][ \IPS\Request::i()->meta_id ];
            }
           
            if ( !
$item->canOnMessage( $current ? 'edit' : 'add' ) )
            {
                throw new \
OutOfRangeException;
            }
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2S136/1D', 404, '' );
        }

       
$form = new \IPS\Helpers\Form;
       
$form->add( new \IPS\Helpers\Form\Editor( 'message', $current ? $current['message'] : NULL , TRUE, array( 'app' => 'core', 'key' => 'Meta', 'autoSaveKey' => $current ? "meta-message-" . \IPS\Request::i()->meta_id : "meta-message-new", 'attachIds' => $current ? array( \IPS\Request::i()->meta_id, NULL, 'message' ) : NULL ) ) );
       
$form->add( new \IPS\Helpers\Form\Custom( 'message_color', isset( $current['color'] ) ? $current['color'] : 'none', FALSE, array( 'getHtml' => function( $element )
        {
            return \
IPS\Theme::i()->getTemplate( 'forms', 'core', 'front' )->colorSelection( $element->name, $element->value );
        } ),
NULL, NULL, NULL, 'message_color' ) );
       
        if (
$values = $form->values() )
        {
            if (
$current )
            {
               
$item->editMessage( \IPS\Request::i()->meta_id, $values['message'], $values['message_color'] );
                \
IPS\File::claimAttachments( "meta-message-" . \IPS\Request::i()->meta_id, \IPS\Request::i()->meta_id, NULL, 'core_ContentMessages' );
               
                \
IPS\Session::i()->modLog( 'modlog__message_edit', array(
                    (string)
$item->url()    => FALSE,
                   
$item->mapped('title')    => FALSE
               
), $item );
            }
            else
            {
               
$id = $item->addMessage( $values['message'], $values['message_color'] );
                \
IPS\File::claimAttachments( "meta-message-new", $id, NULL, 'core_ContentMessages' );
               
                \
IPS\Session::i()->modLog( 'modlog__message_add', array(
                    (string)
$item->url()    => FALSE,
                   
$item->mapped('title')    => FALSE
               
), $item );
            }
           
            \
IPS\Output::i()->redirect( $item->url() );
        }
       
        \
IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
    }
   
   
/**
     * Message Delete
     *
     * @return    void
     */
   
protected function messageDelete()
    {
        \
IPS\Session::i()->csrfCheck();
       
        try
        {
           
$class = static::$contentModel;
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );
            if ( !
$item->canOnMessage('delete') )
            {
                throw new \
OutOfRangeException;
            }
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2S136/1E', 404, '' );
        }
       
       
$item->deleteMessage( \IPS\Request::i()->meta_id );
       
        \
IPS\File::unclaimAttachments( 'core_Meta', \IPS\Request::i()->meta_id, NULL, 'core_ContentMessages' );
       
        \
IPS\Session::i()->modLog( 'modlog__message_delete', array(
            (string)
$item->url()    => FALSE,
           
$item->mapped('title')    => FALSE
       
), $item );
       
        if ( \
IPS\Request::i()->isAjax() )
        {
            \
IPS\Output::i()->json( 'OK' );
        }
        else
        {
            \
IPS\Output::i()->redirect( $item->url() );
        }
    }
       
   
/**
     * 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' )
            {
                if ( isset(
$class::$commentClass ) )
                {
                   
$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' )
            {
                if ( isset(
$class::$reviewClass ) )
                {
                   
$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::$databaseColumnId;
       
$itemColumn = $commentClass::$databaseColumnMap['item'];
       
       
/* Note in the session that we were looking for this comment. This can be used
            to set appropriate meta tag descriptions */
       
$_SESSION['_findComment']    = $comment->$idColumn;
       
       
/* Work out where the comment is in the item */    
       
$directional = ( in_array( 'IPS\Content\Review', class_parents( $commentClass ) ) ) ? '>=?' : '<=?';
       
$where = array(
            array(
$commentClass::$databasePrefix . $itemColumn . '=?', $comment->$itemColumn ),
            array(
$commentClass::$databasePrefix . $idColumn . $directional, $comment->$idColumn )
        );

       
/* Exclude content pending deletion, as it will not be shown inline  */
       
if ( isset( $commentClass::$databaseColumnMap['approved'] ) )
        {
           
$where[] = array( $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['approved'] . '<>?', -2 );
        }
        elseif( isset(
$commentClass::$databaseColumnMap['hidden'] ) )
        {
           
$where[] = array( $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['hidden'] . '<>?', -2 );
        }

        if (
$commentClass::commentWhere() !== NULL )
        {
           
$where[] = $commentClass::commentWhere();
        }
        if (
$container = $item->containerWrapper() )
        {
            if (
$commentClass::modPermission( 'view_hidden', NULL, $container ) === FALSE )
            {
                if ( isset(
$commentClass::$databaseColumnMap['approved'] ) )
                {
                   
$where[] = array( $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['approved'] . '=?', 1 );
                }
                elseif( isset(
$commentClass::$databaseColumnMap['hidden'] ) )
                {
                   
$where[] = array( $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['hidden'] . '=?', 0 );
                }
            }
        }
       
$commentPosition = \IPS\Db::i()->select( 'COUNT(*) AS position', $commentClass::$databaseTable, $where )->first();
       
       
/* Now work out what page that makes it */
       
$url = $item->url();

        if (
in_array( 'IPS\Content\Review', class_parents( $commentClass ) ) )
        {
           
$perPage = $item::$reviewsPerPage;
        }
        else
        {
           
$perPage = $item::getCommentsPerPage();
        }

       
$page = ceil( $commentPosition / $perPage );
        if (
$page != 1 )
        {
           
$url = $url->setQueryString( 'page', $page );
        }

        if (
in_array( 'IPS\Content\Review', class_parents( $commentClass ) ) )
        {
           
$url = $url->setQueryString( array( 'sort' => 'newest', 'tab' => 'reviews' ) );
           
$fragment = 'review';
        }
        else
        {
           
$url = $url->setQueryString( array( 'tab' => 'comments' ) );
           
$fragment = 'comment';
        }

        if ( isset( \
IPS\Request::i()->showDeleted ) )
        {
           
$url = $url->setQueryString( 'showDeleted', 1 );
        }
       
       
/* And redirect */
       
\IPS\Output::i()->redirect( $url->setFragment( $fragment . '-' . $comment->$idColumn ) );
    }
   
   
/**
     * 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 this is an AJAX request, and we're coming from the approval queue, just do it. */
       
if ( \IPS\Request::i()->isAjax() AND isset( \IPS\Request::i()->_fromApproval ) )
        {
           
$comment->modAction( 'hide' );
            \
IPS\Output::i()->json( 'OK' );
        }
       
        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() );
        }
    }
   
   
/**
     * Restore a 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 _restore( $commentClass, $comment, $item )
    {
        \
IPS\Session::i()->csrfCheck();
       
        if ( isset( \
IPS\Request::i()->restoreAsHidden ) )
        {
           
$comment->modAction( 'restoreAsHidden' );
        }
        else
        {
           
$comment->modAction( 'restore' );
        }
       
        \
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', false );
           
$form->class = 'ipsForm_vertical';
           
            if (
in_array( 'IPS\Content\Review', class_parents( $comment ) ) )
            {
               
$ratingField = $commentClass::$databaseColumnMap['rating'];
               
$form->add( new \IPS\Helpers\Form\Rating( 'rating_value', $comment->$ratingField, TRUE, array( 'max' => \IPS\Settings::i()->reviews_rating_out_of ) ) );
            }
           
           
$form->add( new \IPS\Helpers\Form\Editor( 'comment_value', $comment->$valueField, TRUE, array(
               
'app'            => $class::$application,
               
'key'            => mb_ucfirst( $class::$module ),
               
'autoSaveKey'     => 'editComment-' . $class::$application . '/' . $class::$module . '-' . $comment->$idField,
               
'attachIds'        => $comment->attachmentIds()
            ) ) );

           
$form->addButton( 'save', 'submit', null, 'ipsButton ipsButton_medium ipsButton_primary ipsButton_fullWidth', array( 'tabindex' => '2', 'accesskey' => 's' ) );

           
$form->addButton( 'cancel', 'link', $item->url()->setQueryString( in_array( 'IPS\Content\Review', class_parents( $comment ) ) ? array( 'do' => 'findReview', 'review' => $comment->$idField ) : array( 'do' => 'findComment', 'comment' => $comment->$idField ) ), 'ipsButton ipsButton_medium ipsButton_link ipsButton_fullWidth', 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;
                    }
                }

               
/* Determine if the comment is hidden to start with */
               
$isHidden = $comment->hidden();
               
               
/* Do it */
               
$comment->editContents( $values['comment_value'] );
               
               
/* Edit rating */
               
$reloadPage = false;
                if ( isset(
$values['rating_value'] ) and in_array( 'IPS\Content\Review', class_parents( $comment ) ) )
                {
                   
/* The star rating changes but is outside of JS scope to change when editing a comment */
                   
$ratingField = $comment::$databaseColumnMap['rating'];
                    if (
$comment->$ratingField != $values['rating_value'] )
                    {
                       
$reloadPage = true;
                    }
                   
                   
$comment->editRating( $values['rating_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 );
               
               
/* If this is an AJAX request and the comment hidden status has not changed just output the comment HTML */
               
if ( \IPS\Request::i()->isAjax() AND $isHidden == $comment->hidden() AND $reloadPage === false )
                {
                    \
IPS\Output::i()->output = $comment->html();
                    return;
                }
                else
                {
                    \
IPS\Output::i()->redirect( $comment->url() );
                }
            }
           
           
$this->_setBreadcrumbAndTitle( $item );
            \
IPS\Output::i()->breadcrumb[] = array( NULL, in_array( 'IPS\Content\Review', class_parents( $commentClass ) )? \IPS\Member::loggedIn()->language()->addToStack( 'edit_review' ) : \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 \
InvalidArgumentException;
        }
    }
   
   
/**
     * 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() )
            {
               
/* If we are retaining content for a period of time, we need to just hide it instead for deleting later - this only works, though, with items that implement \IPS\Content\Hideable */
               
if ( \IPS\Settings::i()->dellog_retention_period AND ( $item instanceof \IPS\Content\Hideable ) AND !isset( \IPS\Request::i()->immediately ) )
                {
                   
$log = new \IPS\core\DeletionLog;
                   
$log->setContentAndMember( $item, \IPS\Member::loggedIn() );
                   
$log->save();
                   
                    if ( isset(
$item::$databaseColumnMap['hidden'] ) )
                    {
                       
$column = $item::$databaseColumnMap['hidden'];
                    }
                    else if ( isset(
$item::$databaseColumnMap['approved'] ) )
                    {
                       
$column = $item::$databaseColumnMap['approved'];
                    }
                   
                    try
                    {
                        if (
$item->container() )
                        {
                            if (
$item->container()->_comments !== NULL )
                            {
                               
/* Hidden content already decrements */
                               
if ( !$item->hidden() )
                                {
                                   
$item->container()->_comments = ( $item->container()->_comments - $item->mapped('num_comments') );
                                }
                            }
                           
                            if (
$item->container()->_unapprovedComments !== NULL )
                            {
                               
$item->container()->_unapprovedComments = ( $item->container()->_unapprovedComments - $item->mapped( 'unapproved_comments' ) );
                            }
                        }
                    }
                    catch( \
BadMethodCallException $e ) {}
                   
                   
$item->$column = -2;
                   
$item->save();
                   
                    try
                    {
                        if (
$item->container() )
                        {
                           
$item->container()->setLastComment();
                           
$this->container()->setLastReview();
                           
$item->container()->save();
                        }
                    }
                    catch( \
BadMethodCallException $e ) {}
                }
                else
                {
                   
$item->delete();
                }
            }
            else
            {
                \
IPS\Output::i()->error( 'node_noperm_delete', '3S136/1L', 403, '' );
            }
        }
        else
        {
            if (
$comment->canDelete() )
            {
               
/* If we are retaining content for a period of time, we need to just hide it instead for deleting later - this only works, though, with items that implement \IPS\Content\Hideable */
               
if ( \IPS\Settings::i()->dellog_retention_period AND ( $item instanceof \IPS\Content\Hideable ) AND !isset( \IPS\Request::i()->immediately ) )
                {
                   
$log = new \IPS\core\DeletionLog;
                   
$log->setContentAndMember( $comment, \IPS\Member::loggedIn() );
                   
$log->save();
                   
                    if ( isset(
$comment::$databaseColumnMap['hidden'] ) )
                    {
                       
$column = $comment::$databaseColumnMap['hidden'];
                    }
                    else if ( isset(
$comment::$databaseColumnMap['approved'] ) )
                    {
                       
$column = $comment::$databaseColumnMap['approved'];
                    }
                   
                    try
                    {
                        if (
$comment->container() )
                        {
                            if (
$comment->container()->_comments !== NULL )
                            {
                               
/* Hidden content already decrements */
                               
if ( !$comment->hidden() )
                                {
                                   
$comment->container()->_comments = $comment->container()->_comments - 1;
                                }
                            }
                        }
                    }
                    catch( \
BadMethodCallException $e ) {}
                   
                   
$comment->$column = -2;
                   
$comment->save();
                   
                    try
                    {
                       
$comment->item()->rebuildFirstAndLastCommentData();
                    }
                    catch( \
BadMethodCallException $e ) {}

                   
/* Remove featured comment associations */
                   
if( $comment->isFeatured() )
                    {
                        \
IPS\Application::load('core')->extensions( 'core', 'MetaData' )['FeaturedComments']->unfeatureComment( $comment->item(), $comment );
                    }
                }
                else
                {
                   
$comment->delete();
                }
               
               
/* Log */
               
\IPS\Session::i()->modLog( 'modlog__comment_delete', array( $item::$title => TRUE, $item->url()->__toString() => FALSE, $item->mapped( 'title' ) => FALSE ), $item );
            }
            else
            {
                \
IPS\Output::i()->error( 'node_noperm_delete', '3S136/1K', 403, '' );
            }
        }
       
        if ( \
IPS\Request::i()->isAjax() )
        {
           
$currentPageCount = \IPS\Request::i()->page;
           
$newPageCount = $item->commentPageCount( TRUE );
            if ( isset( \
IPS\Request::i()->page ) AND $currentPageCount != $newPageCount )
            {
               
/* If we are on page 2 and delete a comment, and there are 3 pages, we don't want to be sent to page 3 (that makes no sense).
                    Instead, we'll send you to the page requested. If it exists you'll be on the same page. If it doesn't, the controller will
                    handle sending you to the correct location */
               
\IPS\Output::i()->json( array( 'type' => 'redirect', 'total' => $item->mapped( 'num_comments' ), 'url' => (string) $item->url()->setQueryString( 'page', (int) \IPS\Request::i()->page ) ) );
            }
            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;
                    }

                   
/* Does the first post require moderator approval? */
                   
if ( $comment->hidden() === 1 )
                    {
                        if ( isset(
$item::$databaseColumnMap['hidden'] ) )
                        {
                           
$column = $item::$databaseColumnMap['hidden'];
                           
$item->$column = 1;
                        }
                        elseif ( isset(
$item::$databaseColumnMap['approved'] ) )
                        {
                           
$column = $item::$databaseColumnMap['approved'];
                           
$item->$column = 0;
                        }
                    }
                   
/* Or is it hidden? */
                   
elseif ( $comment->hidden() === -1 )
                    {
                        if ( isset(
$item::$databaseColumnMap['hidden'] ) )
                        {
                           
$column = $item::$databaseColumnMap['hidden'];
                        }
                        elseif ( isset(
$item::$databaseColumnMap['approved'] ) )
                        {
                           
$column = $item::$databaseColumnMap['approved'];
                        }

                       
$item->$column = -1;
                    }

                   
$item->save();

                    if(
$comment->hidden() !== 0 )
                    {
                        if ( isset(
$comment::$databaseColumnMap['hidden'] ) )
                        {
                           
$column = $comment::$databaseColumnMap['hidden'];
                           
$comment->$column = 0;
                        }
                        elseif ( isset(
$comment::$databaseColumnMap['approved'] ) )
                        {
                           
$column = $comment::$databaseColumnMap['approved'];
                           
$comment->$column = 1;
                        }

                       
$comment->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 )
    {
        try
        {
           
$class = static::$contentModel;

           
/* Permission check */
           
$canReport = $comment->canReport();
            if (
$canReport !== TRUE AND !( $canReport == 'report_err_already_reported' AND \IPS\Settings::i()->automoderation_enabled ) )
            {
                \
IPS\Output::i()->error( $canReport, '2S136/4', 403, '' );
            }

           
/* Show form */
           
$form = new \IPS\Helpers\Form( NULL, 'report_submit' );
           
$form->class = 'ipsForm_vertical';
           
$itemIdColumn = $class::$databaseColumnId;
           
$idColumn = $comment::$databaseColumnId;
           
           
/* As we group by user id to determine if max points have been reached, guests cannot contribute to counts */
           
if ( \IPS\Member::loggedIn()->member_id and \IPS\Settings::i()->automoderation_enabled )
            {
               
/* Has this member already reported this in the past 24 hours */
               
try
                {
                   
$index = \IPS\core\Reports\Report::loadByClassAndId( get_class( $comment ), $comment->$idColumn );
                   
$report = \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'rid=? and report_by=? and date_reported > ?', $index->id, \IPS\Member::loggedIn()->member_id, time() - ( \IPS\Settings::i()->automoderation_report_again_mins * 60 ) ) )->first();
                   
                    \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'system', 'core' )->reportedAlready( $index, $report, $comment );
                    return;
                }
                catch( \
Exception $e ) { }
               
               
$options = array( \IPS\core\Reports\Report::TYPE_MESSAGE => \IPS\Member::loggedIn()->language()->addToStack('report_message_comment') );
                foreach( \
IPS\core\Reports\Types::roots() as $type )
                {
                   
$options[ $type->id ] = $type->_title;
                }
               
               
$form->add( new \IPS\Helpers\Form\Radio( 'report_type', NULL, FALSE, array( 'options' => $options ) ) );
            }
           
           
$form->add( new \IPS\Helpers\Form\Editor( 'report_message', NULL, FALSE, array( 'app' => 'core', 'key' => 'Reports', 'autoSaveKey' => "report-{$class::$application}-{$class::$module}-{$item->$itemIdColumn}-{$comment->$idColumn}", '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'], ( isset( $values['report_type'] ) ) ? $values['report_type'] : 0 );
                \
IPS\File::claimAttachments( "report-{$class::$application}-{$class::$module}-{$item->$itemIdColumn}-{$comment->$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( $comment->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/10', 404, '' );
        }
    }
   
   
/**
     * React to a 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 _react( $commentClass, $comment, $item )
    {
        try
        {
            \
IPS\Session::i()->csrfCheck();
           
           
$comment->react( \IPS\Content\Reaction::load( \IPS\Request::i()->reaction ) );
           
            if ( \
IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->json( array(
                   
'status' => 'ok',
                   
'count' => count( $comment->reactions() ),
                   
'score' => $comment->reactionCount(),
                   
'blurb' => ( \IPS\Settings::i()->reaction_count_display == 'count' ) ? '' : \IPS\Theme::i()->getTemplate( 'global', 'core' )->reactionBlurb( $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/16', 403, '' );
            }
        }
    }
   
   
/**
     * Feature a 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 _feature( $commentClass, $comment, $item )
    {
        \
IPS\Session::i()->csrfCheck();
        if ( !
$item->canFeatureComment() )
        {
            throw new \
DomainException;
        }
       
        try
        {
           
$form = new \IPS\Helpers\Form( 'form', 'add_recommend_content' );
           
$form->class = 'ipsForm_vertical ipsForm_fullWidth';
           
$form->add( new \IPS\Helpers\Form\Text( 'feature_mod_note', NULL, FALSE ) );
            if (
$values = $form->values() )
            {
               
$item->featureComment( $comment, $values['feature_mod_note'] );
               
                \
IPS\Session::i()->modLog( 'modlog__featured_comment', array(
                    (string)
$comment->url()    => FALSE,
                   
$item->mapped('title')        => FALSE
               
) );
               
                if ( \
IPS\Request::i()->isAjax() )
                {
                   
$idField = $comment::$databaseColumnId;
                   
$isReview = FALSE;

                    if (
$comment instanceof \IPS\Content\Review )
                    {
                       
$isReview = TRUE;
                    }

                   
/* Send the new recommend comment ID so that the JS can identify it */
                   
\IPS\Output::i()->json( array(
                       
'recommended' => $comment->$idField,
                       
'comment' => $comment->html()
                    ) );
                }
                else
                {
                    \
IPS\Output::i()->redirect( $comment->url() );
                }
            }
           
           
$this->_setBreadcrumbAndTitle( $item );
            \
IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core', 'front' ) ), 'recommendCommentTemplate' ) );
        }
        catch( \
BadMethodCallException $e )
        {
            throw new \
LogicException;
        }
    }
   
   
/**
     * Unreact to a 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 _unreact( $commentClass, $comment, $item )
    {
        try
        {
            \
IPS\Session::i()->csrfCheck();

           
$member = ( isset( \IPS\Request::i()->member ) and \IPS\Member::loggedIn()->modPermission('can_remove_reactions') ) ? \IPS\Member::load( \IPS\Request::i()->member ) : \IPS\Member::loggedIn();

           
$comment->removeReaction( $member );

           
/* Log */
           
if( $member->member_id !== \IPS\Member::loggedIn()->member_id )
            {
                \
IPS\Session::i()->modLog( 'modlog__comment_reaction_delete', array( $member->url()->__toString() => FALSE, $member->name => FALSE, $comment->url()->__toString() => FALSE, $item::$title => TRUE, $item->url()->__toString() => FALSE, $item->mapped( 'title' ) => FALSE ), $item );
            }
           
            if ( \
IPS\Request::i()->isAjax() )
            {
                \
IPS\Output::i()->json( array(
                   
'status' => 'ok',
                   
'count' => count( $comment->reactions() ),
                   
'score' => $comment->reactionCount(),
                   
'blurb' => ( \IPS\Settings::i()->reaction_count_display == 'count' ) ? '' : \IPS\Theme::i()->getTemplate( 'global', 'core' )->reactionBlurb( $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/17', 403, '' );
            }
        }
    }
   
   
/**
     * Unfeature a 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 _unfeature( $commentClass, $comment, $item )
    {
        \
IPS\Session::i()->csrfCheck();
        if ( !
$item->canUnfeatureComment() )
        {
            throw new \
DomainException;
        }
       
        try
        {
           
$item->unfeatureComment( $comment );
           
            \
IPS\Session::i()->modLog( 'modlog__unfeatured_comment', array(
                (string)
$comment->url()    => FALSE,
               
$item->mapped('title')        => FALSE
           
) );
           
            if ( \
IPS\Request::i()->isAjax() )
            {
               
$idField = $comment::$databaseColumnId;
               
$isReview = FALSE;

                if (
$comment instanceof \IPS\Content\Review )
                {
                   
$isReview = TRUE;
                }

               
/* Send the new recommend comment ID so that the JS can identify it */
               
\IPS\Output::i()->json( array(
                   
'unrecommended' => $comment->$idField,
                   
'comment' => $comment->html()
                ) );
            }
            else
            {
                \
IPS\Output::i()->redirect( $comment->url() );
            }
        }
        catch( \
DomainException $e )
        {
            throw new \
LogicException;
        }
    }
   
   
/**
     * Show Comment/Review Reactions
     *
     * @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 _showReactions( $commentClass, $comment, $item )
    {
       
$idColumn = $commentClass::$databaseColumnId;
       
$blurb = $comment->reactBlurb();

       
$this->_setBreadcrumbAndTitle( $item );
        \
IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'see_who_reacted' ) . ' (' . $comment->$idColumn . ') - ' . \IPS\Output::i()->title;
       
       
$tabs = array();
       
$tabs['all'] = array( 'title' => \IPS\Member::loggedIn()->language()->addToStack('all'), 'count' => count( $comment->reactions() ) );
        foreach( \
IPS\Content\Reaction::roots() AS $reaction )
        {
            if (
$reaction->_enabled !== FALSE )
            {
               
$tabs[ $reaction->id ] = array( 'title' => $reaction->_title, 'icon' => $reaction->_icon, 'count' => isset( $blurb[ $reaction->id ] ) ? $blurb[ $reaction->id ] : 0 );
            }
        }

       
$activeTab = 'all';
        if ( isset( \
IPS\Request::i()->reaction ) )
        {
           
$activeTab = \IPS\Request::i()->reaction;
        }
       
       
$url = $comment->url('showReactions');
       
$url = $url->setQueryString( 'changed', 1 );
       
        if (
$activeTab !== 'all' )
        {
           
$url = $url->setQueryString( 'reaction', $activeTab );
        }

        \
IPS\Output::i()->metaTags['robots'] = 'noindex';
       
        if ( \
IPS\Request::i()->isAjax() AND isset( \IPS\Request::i()->changed ) )
        {
            \
IPS\Output::i()->output = $comment->reactionTable( $activeTab !== 'all' ? $activeTab : NULL, $url, 'reaction', FALSE );
        }
        else
        {
            \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->reactionTabs( $tabs, $activeTab, $comment->reactionTable( $activeTab !== 'all' ? $activeTab : NULL ), $url, 'reaction', FALSE );
        }
    }
   
   
/**
     * 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();
       
       
$checkAgainst = \IPS\Request::i()->modaction;

        if(
$checkAgainst == 'split' OR $checkAgainst == 'merge' )
        {
           
$checkAgainst = 'split_merge';
        }

        if ( !
$item::modPermission( $checkAgainst, NULL, $item->containerWrapper() ) )
        {
            throw new \
DomainException;
        }
       
        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 );
               
                foreach (
$comments as $comment )
                {
                   
$firstComment = $comment;
                    break;
                }

               
/* If we don't have a $firstComment, something went wrong - perhaps the input multimod comment ids are all from a different topic for instance */
               
if( !isset( $firstComment ) )
                {
                   
$form->error    = \IPS\Member::loggedIn()->language()->addToStack( 'mod_error_invalid_action' );
                    goto
splitFormError;
                }
                   
                if ( isset(
$values['_split_type'] ) and $values['_split_type'] === 'new' )
                {
                   
$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;
                    }

                   
/* Does the first post require moderator approval? */
                   
if ( $firstComment->hidden() === 1 )
                    {
                        if ( isset(
$item::$databaseColumnMap['hidden'] ) )
                        {
                           
$column = $item::$databaseColumnMap['hidden'];
                           
$item->$column = 1;
                        }
                        elseif ( isset(
$item::$databaseColumnMap['approved'] ) )
                        {
                           
$column = $item::$databaseColumnMap['approved'];
                           
$item->$column = 0;
                        }
                    }
                   
/* Or is it hidden? */
                   
elseif ( $firstComment->hidden() === -1 )
                    {
                        if ( isset(
$item::$databaseColumnMap['hidden'] ) )
                        {
                           
$column = $item::$databaseColumnMap['hidden'];
                        }
                        elseif ( isset(
$item::$databaseColumnMap['approved'] ) )
                        {
                           
$column = $item::$databaseColumnMap['approved'];
                        }

                       
$item->$column = -1;
                    }

                   
$item->save();

                }
                else
                {
                   
$item = $item::loadFromUrl( $values['_split_into_url'] );
                }
               
                foreach (
$comments as $comment )
                {
                    if(
$comment == $firstComment AND $comment->hidden() !== 0 )
                    {
                        if ( isset(
$comment::$databaseColumnMap['hidden'] ) )
                        {
                           
$column = $comment::$databaseColumnMap['hidden'];
                           
$comment->$column = 0;
                        }
                        elseif ( isset(
$comment::$databaseColumnMap['approved'] ) )
                        {
                           
$column = $comment::$databaseColumnMap['approved'];
                           
$comment->$column = 1;
                        }

                       
$comment->save();
                    }

                   
$comment->move( $item, TRUE );
                }

               
$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 );
               
               
$url = $item->url();
               
                if ( isset( \
IPS\Request::i()->page ) )
                {
                   
$url = $url->setQueryString( 'page', (int) \IPS\Request::i()->page );
                }
               
                \
IPS\Output::i()->redirect( $url );
            }
            else
            {
               
/* Label for goto command */
               
splitFormError:
               
$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( '<p>&nbsp;</p>', $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 . '_' . mb_ucfirst( $item::$module ), $item->$idColumn, $comment->$commentIdColumn ) );

                       
/* Merge likes */
                       
if ( \IPS\IPS::classUsesTrait( $commentClass, 'IPS\Content\Reactable' ) )
                        {
                            \
IPS\Db::i()->update( 'core_reputation_index', array( 'type_id' => $firstComment->$commentIdColumn ), array( 'app=? and type=? and type_id=?', $item::$application, $comment::reactionType(), $id ) );
                        }

                       
$comment->delete();
                    }
                    catch( \
Exception $e ) {}
                }
               
               
/* Fix duplicated reactions */
               
if ( \IPS\IPS::classUsesTrait( $commentClass, 'IPS\Content\Reactable' ) )
                {
                    \
IPS\Db::i()->query( 'DELETE row1 FROM ' . \IPS\Db::i()->prefix . 'core_reputation_index row1, ' . \IPS\Db::i()->prefix . 'core_reputation_index row2 WHERE row1.id > row2.id AND row1.member_id = row2.member_id AND row1.app = \'' . $item::$application . '\' AND row1.type = \'' . $comment::reactionType() . '\' AND row1.type_id = row2.type_id' );
                }

               
$item->rebuildFirstAndLastCommentData();

               
/* Log it */
               
\IPS\Session::i()->modLog( 'modlog__action_merge_comments', array(
                   
$firstComment::$title                    => FALSE,
                   
$firstComment->url()->__toString()        => FALSE,
                   
$firstComment->$commentIdColumn            => FALSE,
                ),
$item );
               
                \
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 ) { }
                    }

                    if( !
in_array( 'IPS\Content\Review', class_parents( $commentClass ) ) )
                    {
                       
$item->rebuildFirstAndLastCommentData();
                    }
                   
                   
$url = $item->url();
                   
                    if ( isset( \
IPS\Request::i()->page ) )
                    {
                       
$url = $url->setQueryString( 'page', (int) \IPS\Request::i()->page );
                    }
                   
                    \
IPS\Output::i()->redirect( $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()->modaction != 'delete' )
            {
               
$url = $object->url();
               
                if ( isset( \
IPS\Request::i()->page ) )
                {
                   
$url = $url->setQueryString( 'page', (int) \IPS\Request::i()->page );
                }
               
                \
IPS\Output::i()->redirect( $url );
            }
            else
            {
               
$url = $item->url();
               
                if ( isset( \
IPS\Request::i()->page ) )
                {
                   
$url = $url->setQueryString( 'page', (int) \IPS\Request::i()->page );
                }
               
                \
IPS\Output::i()->redirect( $url );
            }
        }
    }
   
   
/**
     * 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() ) )
        {
           
$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' AND ( $container AND $container->can( 'add' ) ) )
                    {
                       
$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 )
            {
                if (
$field->name == $item::$formLangPrefix . 'container' )
                {
                   
/* Add a custom permission check for splitting comments */
                   
$field->options['permissionCheck'] = function( $node ) use ( $item )
                    {
                        try
                        {
                           
/* If the item is in a club, only allow moving to other clubs that you moderate */
                           
if ( \IPS\IPS::classUsesTrait( $item->container(), 'IPS\Content\ClubContainer' ) and $item->container()->club()  )
                            {
                                return
$item::modPermission( 'move', \IPS\Member::loggedIn(), $node ) and $node->can( 'add' ) ;
                            }

                            if (
$node->can( 'add' ) )
                            {
                                return
true;
                            }
                        }
                        catch( \
OutOfBoundsException $e ) { }

                        return
false;
                    };
                }

               
$form->add( $field );
            }
        }
       
$form->add( new \IPS\Helpers\Form\Url( '_split_into_url', NULL, FALSE, array(), function ( $val ) use ( $item )
        {
            if ( \
IPS\Request::i()->_split_type == 'existing' OR !isset( \IPS\Request::i()->_split_type ) )
            {
                try
                {
                   
/* Validate the URL */
                   
$test = $item::loadFromUrl( $val );

                   
/* Make sure the URL matches the content type we're splitting */
                   
foreach( array( 'app', 'module', 'controller') as $index )
                    {
                        if(
$test->url()->hiddenQueryString[ $index ] != $val->hiddenQueryString[ $index ] )
                        {
                            throw new \
InvalidArgumentException;
                        }
                    }
                }
                catch ( \
Exception $e )
                {
                    throw new \
DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_url_bad_item', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( '__defart_' . $item::$title ) ) ) ) );
                }

                if( !
$test->canMerge() )
                {
                    throw new \
DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'no_merge_permission', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( '__defart_' . $item::$title ) ) ) ) );
                }
            }
        },
NULL, NULL, 'split_into_url' ) );
       
        return
$form;
    }

   
/**
     * Retrieve content tagged the same
     *
     * @param    \int    $limit    How many items should be returned
     *
     * @note    Used with a widget, but can be used elsewhere too
     * @return    array|NULL
     */
   
public function getSimilarContent( $limit = 5 )
    {
        if( !isset( static::
$contentModel ) )
        {
            return
NULL;
        }

        try
        {
           
$class = static::$contentModel;
           
$item = $class::loadAndCheckPerms( \IPS\Request::i()->id );

            if( !
$item instanceof \IPS\Content\Tags OR ( $item->tags() === NULL AND $item->prefix( FALSE ) === NULL ) )
            {
                return
NULL;
            }

           
/* Store tags in array, so that we can add a prefix if set */
           
$tags = $item->tags() ?: array();
           
$tags[] = $item->prefix( FALSE );

           
/* Build the where clause */
           
$where = array(
                array(
'(' . \IPS\Db::i()->in( 'tag_text', $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' )
            );

           
/* Allow the item to manipulate the query if needed */
           
if( $item->similarContentFilter() )
            {
               
$where = array_merge( $where, $item->similarContentFilter() );
            }

           
$select = \IPS\Db::i()->select(
               
'tag_meta_app,tag_meta_area,tag_meta_id',
               
'core_tags',
               
$where,
               
'tag_added DESC',
                array(
0, $limit )
            )->
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[ $result['tag_meta_id'] ] = $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 = (int) $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;
        }
    }
}