Invision Power Services, Inc. * @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc. * @license http://www.invisionpower.com/legal/standards/ * @package IPS Social Suite * @since 18 Apr 2014 * @version SVN_VERSION_NUMBER */ namespace IPS\core\modules\front\system; /* 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; } /** * New Content */ class _vnc extends \IPS\Dispatcher\Controller { /** * New Content * * @return void */ public function manage() { /* Flood control */ \IPS\Request::floodCheck(); if( isset( \IPS\Request::i()->nodes ) ) { \IPS\Request::i()->nodes = urldecode( \IPS\Request::i()->nodes ); } /* Logged in only */ if ( !\IPS\Member::loggedIn()->member_id ) { \IPS\Output::i()->error( 'no_module_permission_guest', '2C232/1', 403, '' ); } elseif( !\IPS\Request::i()->updateFilters ) { $preferences = json_decode( \IPS\Member::loggedIn()->vnc_preferences, TRUE ); if( isset( $preferences['onlyFollowed'] ) AND !isset( \IPS\Request::i()->onlyFollowed ) ) { \IPS\Request::i()->onlyFollowed = $preferences['onlyFollowed']; } if( isset( $preferences['onlyParticipated'] ) AND !isset( \IPS\Request::i()->onlyParticipated ) ) { \IPS\Request::i()->onlyParticipated = $preferences['onlyParticipated']; } if( isset( $preferences['onlyStarted'] ) AND !isset( \IPS\Request::i()->onlyStarted ) ) { \IPS\Request::i()->onlyStarted = $preferences['onlyStarted']; } if( isset( $preferences['onlyUnread'] ) AND !isset( \IPS\Request::i()->onlyUnread ) ) { \IPS\Request::i()->onlyUnread = $preferences['onlyUnread']; } if( isset( $preferences['searchType'] ) AND !isset( \IPS\Request::i()->type ) ) { \IPS\Request::i()->type = $preferences['searchType']; } if( isset( $preferences['vncTimePeriod'] ) AND !isset( \IPS\Request::i()->vncTimePeriod ) ) { \IPS\Request::i()->vncTimePeriod = $preferences['vncTimePeriod']; } if( isset( $preferences['container'] ) AND !isset( \IPS\Request::i()->nodes ) ) { \IPS\Request::i()->nodes = $preferences['container']; } } /* Though this isn't part of search, disabling the search module will disable it */ if ( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'search' ) ) ) { \IPS\Output::i()->error( 'no_module_permission', '2C232/2', 403, '' ); } /* Init */ $baseUrl = \IPS\Http\Url::internal( 'app=core&module=system&controller=vnc&updateFilters=1', 'front', 'vnc' ); $nodeSelect = NULL; $nodeTitle = NULL; /* Get types */ $types = array(); foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class ) { if( is_subclass_of( $class, 'IPS\Content\ReadMarkers' ) ) { $key = mb_strtolower( str_replace( '\\', '_', mb_substr( $class, 4 ) ) ); $types[ $key ] = $class; } } /* If a user last filtered by "downloads_file" and then the download manager app was disabled we need to clear this otherwise you get a fatal error */ if ( \IPS\Request::i()->type AND \IPS\Request::i()->type !== 'all' AND !isset( $types[ \IPS\Request::i()->type ] ) ) { \IPS\Request::i()->type = ''; } /* Force a type if only one available */ if( count( $types ) == 1 ) { $keys = array_keys( $types ); \IPS\Request::i()->type = array_shift( $keys ); } /* Set filters */ $where = array(); $start = NULL; $end = NULL; $onlyUnread = FALSE; if ( isset( \IPS\Request::i()->onlyUnread ) and \IPS\Request::i()->onlyUnread ) { $baseUrl = $baseUrl->setQueryString( 'onlyUnread', 1 ); $onlyUnread = TRUE; } $onlyFollowed = FALSE; if ( isset( \IPS\Request::i()->onlyFollowed ) and \IPS\Request::i()->onlyFollowed ) { $baseUrl = $baseUrl->setQueryString( 'onlyFollowed', 1 ); $onlyFollowed = TRUE; $where[] = array( 'follow_id IS NOT NULL AND follow_member_id =?', \IPS\Member::loggedIn()->member_id ); } $onlyParticipated = FALSE; if ( isset( \IPS\Request::i()->onlyParticipated ) and \IPS\Request::i()->onlyParticipated ) { $baseUrl = $baseUrl->setQueryString( 'onlyParticipated', 1 ); $onlyParticipated = TRUE; } $onlyStarted = FALSE; if ( isset( \IPS\Request::i()->onlyStarted ) and \IPS\Request::i()->onlyStarted ) { $baseUrl = $baseUrl->setQueryString( 'onlyStarted', 1 ); $onlyStarted = TRUE; } /* Get cutoff */ $interval = NULL; $vncTimePeriod = NULL; if ( isset( \IPS\Request::i()->vncTimePeriod ) ) { $vncTimePeriod = \IPS\Request::i()->vncTimePeriod; $baseUrl = $baseUrl->setQueryString( 'vncTimePeriod', $vncTimePeriod ); if ( isset( \IPS\Request::i()->vncTime ) ) { $start = \IPS\DateTime::ts( \IPS\Request::i()->vncTime, TRUE ); } switch ( $vncTimePeriod ) { case 'all': $cutoff = NULL; break; case 'day': $interval = new \DateInterval( 'P1D' ); if ( !$start ) { $start = \IPS\DateTime::ts( \IPS\DateTime::create()->getTimestamp(), TRUE )->sub( $interval ); } else { $end = \IPS\DateTime::ts( $start->getTimestamp(), TRUE )->add( $interval ); } break; case 'week': $interval = new \DateInterval( 'P1W' ); if ( !$start ) { $start = \IPS\DateTime::ts( \IPS\DateTime::create()->getTimestamp(), TRUE )->sub( $interval ); } else { $end = \IPS\DateTime::ts( $start->getTimestamp(), TRUE )->add( $interval ); } break; case 'month': $interval = new \DateInterval( 'P1M' ); if ( !$start ) { $start = \IPS\DateTime::create()->sub( $interval ); } else { $end = \IPS\DateTime::ts( $start->getTimestamp(), TRUE )->add( $interval ); } break; default: $start = \IPS\DateTime::ts( \IPS\Member::loggedIn()->last_visit, TRUE ); break; } } else { $start = \IPS\DateTime::ts( \IPS\Member::loggedIn()->last_visit, TRUE ); } if( \IPS\Member::loggedIn()->member_id and isset( \IPS\Request::i()->updateFilters ) ) { $vncPreferences = json_encode( array( 'onlyFollowed' => $onlyFollowed, 'onlyParticipated' => $onlyParticipated, 'onlyStarted' => $onlyStarted, 'onlyUnread' => $onlyUnread, 'searchType' => \IPS\Request::i()->type ?: NULL, 'vncTimePeriod' => $vncTimePeriod, 'container' => \IPS\Request::i()->nodes ?: NULL, ) ); if ( $vncPreferences != \IPS\Member::loggedIn()->vnc_preferences ) { \IPS\Member::loggedIn()->vnc_preferences = $vncPreferences; \IPS\Member::loggedIn()->save(); } } /* Get results - if we're just searching one type, it's a bit simpler */ if ( \IPS\Request::i()->type AND \IPS\Request::i()->type !== 'all' ) { /* Init */ $baseUrl = $baseUrl->setQueryString( 'type', \IPS\Request::i()->type ); $class = $types[ \IPS\Request::i()->type ]; if ( $onlyUnread ) { $unreadWhere = $this->_getUnreadWhere( $class ); if ( count( $unreadWhere ) ) { foreach( $unreadWhere as $id => $item ) { $where[] = $item; } } } /* Where */ if ( $onlyParticipated or $onlyStarted ) { if( isset( $class::$databaseColumnMap['author'] ) ) { $participatedWhere[] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['author'] . '=' . \IPS\Member::loggedIn()->member_id; } if ( !$onlyStarted ) { if( isset( $class::$commentClass ) ) { $commentClass = $class::$commentClass; $participatedWhere[] = '(' . str_replace( "`", '', (string) \IPS\Db::i()->select( 'COUNT(*)', $commentClass::$databaseTable, $commentClass::$databaseTable . '.' . $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['author'] . '=' . \IPS\Member::loggedIn()->member_id . ' AND ' . $commentClass::$databaseTable . '.' . $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['item'] . '=' . $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId ) ) . ') > 0'; } if( isset( $class::$reviewClass ) ) { $reviewClass = $class::$reviewClass; $participatedWhere[] = '(' . str_replace( "`", '', (string) \IPS\Db::i()->select( 'COUNT(*)', $reviewClass::$databaseTable, $reviewClass::$databaseTable . '.' . $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['author'] . '=' . \IPS\Member::loggedIn()->member_id . ' AND ' . $reviewClass::$databaseTable . '.' . $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['item'] . '=' . $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId ) ) . ') > 0'; } } $where[] = array( '( ' . implode( ' OR ', $participatedWhere ) . ' )' ); } /* Node Filter */ if ( $class::$containerNodeClass ) { $node = NULL; $nodeClass = $class::$containerNodeClass; $nodeTitle = \IPS\Member::loggedIn()->language()->addToStack( $nodeClass::$nodeTitle ); if ( isset( \IPS\Request::i()->nodes ) and \IPS\Request::i()->nodes ) { try { $baseUrl = $baseUrl->setQueryString( 'nodes', \IPS\Request::i()->nodes ); $where[] = array( \IPS\Db::i()->in( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['container'], is_array( \IPS\Request::i()->nodes ) ? \IPS\Request::i()->nodes : explode( ',', \IPS\Request::i()->nodes ) ) ); } catch ( \OutOfRangeException $e ) { } } $nodeSelect = new \IPS\Helpers\Form\Node( 'nodes', ( is_array( \IPS\Request::i()->nodes ) ) ? \IPS\Request::i()->nodes : explode( ',', \IPS\Request::i()->nodes ), FALSE, array( 'class' => $class::$containerNodeClass, 'permissionCheck' => 'view', 'url' => $baseUrl, 'multiple' => TRUE ) ); } /* Cutoff */ $dateColumnExpression = $this->_getDateExpression( $class ); if ( $start ) { $where[] = array( $dateColumnExpression . '>?', $start->getTimestamp() ); } if ( $end ) { $where[] = array( $dateColumnExpression . 'getTimestamp() ); } if ( isset( $class::$databaseColumnMap['state'] ) ) { $where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['state'] . "!=?", 'link' ); } /* Create table */ $table = new \IPS\Helpers\Table\Content( $class, $baseUrl, $where, NULL, NULL, 'read', FALSE ); $table->classes[] = 'ipsDataList_zebra'; $table->sortOptions = array(); $table->noModerate = TRUE; /* Joins */ if ( $onlyFollowed ) { $followArea = mb_strtolower( mb_substr( $class, mb_strrpos( $class, '\\' ) + 1 ) ); $table->joins[] = array( 'from' => 'core_follow', 'where' => array( "follow_app=? AND follow_area=? AND follow_member_id=? AND follow_rel_id={$class::$databasePrefix}{$class::$databaseColumnId}", $class::$application, $followArea, \IPS\Member::loggedIn()->member_id ) ); } } /* If we're searching them all, we need to go all union */ else { /* Build the selects */ $selects = array(); foreach ( $types as $key => $class ) { $dateColumnExpression = $this->_getDateExpression( $class ); /* Normalize the columns */ $contentWhere = array(); $columns = array( $class::$databaseTable . "." . $class::$databasePrefix.$class::$databaseColumnId => 'id', $dateColumnExpression => 'date', $class::$databaseTable . "." . $class::$databasePrefix.$class::$databaseColumnMap['author'] => 'author' ); if ( in_array( 'IPS\Content\Hideable', class_implements( $class ) ) and !\IPS\Member::loggedIn()->modPermission( "can_view_hidden_content" ) ) { if ( $class::$databaseColumnMap['approved'] ) { $columns[ $class::$databaseTable . "." . $class::$databasePrefix.$class::$databaseColumnMap['approved'] . '-1' ] = 'hidden'; $contentWhere[] = array( $class::$databaseTable . "." . $class::$databasePrefix.$class::$databaseColumnMap['approved'] . '-1=0' ); } else { $columns[ $class::$databaseTable . "." . $class::$databasePrefix.$class::$databaseColumnMap['hidden'] ] = 'hidden'; $contentWhere[] = array( $class::$databaseTable . "." . $class::$databasePrefix.$class::$databaseColumnMap['hidden'] . '=0' ); } } $select = array( "'".str_replace( '\\', '\\\\' , $class ) ."' AS class" ); foreach ( $columns as $local => $normalized ) { $select[] = "{$local} AS {$normalized}"; } if ( $onlyFollowed ) { $select[] = "'".$class::$application."' AS _app"; $followArea = mb_strtolower( mb_substr( $class, mb_strrpos( $class, '\\' ) + 1 ) ); $select[] = "'{$followArea}' AS _follow_area"; } $select = implode( ', ', $select ); /* Permissions */ if ( in_array( 'IPS\Content\Permissions', class_implements( $class ) ) ) { $containerClass = $class::$containerNodeClass; $categories = array(); foreach( \IPS\Db::i()->select( 'perm_type_id', 'core_permission_index', array( "core_permission_index.app='" . $containerClass::$permApp . "' AND core_permission_index.perm_type='" . $containerClass::$permType . "' AND (" . \IPS\Db::i()->findInSet( 'perm_' . $containerClass::$permissionMap['read'], \IPS\Member::loggedIn()->groups ) . ' OR ' . 'perm_' . $containerClass::$permissionMap['read'] . "='*' )" ) ) as $result ) { $categories[] = $result; } if( count( $categories ) ) { $contentWhere[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['container'] . ' IN(' . implode( ',', $categories ) . ')' ); } else { $contentWhere[] = array( $class::$databaseTable . "." . $class::$databasePrefix . $class::$databaseColumnMap['container'] . '=0' ); } } /* Future */ if ( in_array( 'IPS\Content\FuturePublishing', class_implements( $class ) ) and !$class::canViewFutureItems() ) { $authorCol = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['author']; $col = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['is_future_entry']; $contentWhere[] = array( "( {$col}=0 OR ( {$col}=1 AND {$authorCol}=" . \IPS\Member::loggedIn()->member_id . " ) )" ); } /* Add app-sepcific wheres */ $joinContainer = FALSE; $joins = array(); $vncWhere = $class::vncWhere( $joinContainer, $joins ); if ( is_array( $vncWhere ) ) { foreach( $vncWhere as $key => $value ) { if ( $key ==='item' ) { $contentWhere = array_merge( $contentWhere, $value ); } else if ( $key === 'container' ) { $contentWhere = array_merge( $contentWhere, $value ); } else { $contentWhere[] = $value; } } } /* Only unread */ if ( $onlyUnread ) { $unreadWhere = $this->_getUnreadWhere( $class ); if ( count( $unreadWhere ) ) { foreach( $unreadWhere as $id => $item ) { $contentWhere[] = $item; } } } /* Participated Only */ if ( $onlyParticipated or $onlyStarted ) { $participatedWhere = array(); if( isset( $class::$databaseColumnMap['author'] ) ) { $participatedWhere[] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['author'] . '=' . \IPS\Member::loggedIn()->member_id; } if ( !$onlyStarted ) { if( isset( $class::$commentClass ) ) { $commentClass = $class::$commentClass; $participatedWhere[] = '(' . str_replace( "`", '', (string) \IPS\Db::i()->select( 'COUNT(*)', $commentClass::$databaseTable, $commentClass::$databaseTable . '.' . $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['author'] . '=' . \IPS\Member::loggedIn()->member_id . ' AND ' . $commentClass::$databaseTable . '.' . $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['item'] . '=' . $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId ) ) . ') > 0'; } if( isset( $class::$reviewClass ) ) { $reviewClass = $class::$reviewClass; $participatedWhere[] = '(' . str_replace( "`", '', (string) \IPS\Db::i()->select( 'COUNT(*)', $reviewClass::$databaseTable, $reviewClass::$databaseTable . '.' . $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['author'] . '=' . \IPS\Member::loggedIn()->member_id . ' AND ' . $reviewClass::$databaseTable . '.' . $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['item'] . '=' . $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId ) ) . ') > 0'; } } $contentWhere[] = '( ' . implode( ' OR ', $participatedWhere ) . ' )'; } if ( isset( $class::$databaseColumnMap['state'] ) ) { $contentWhere[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['state'] . "!='link'" ); } /* Add to the list */ $select = \IPS\Db::i()->select( $select, $class::$databaseTable, $contentWhere ); if ( $joinContainer ) { $containerClass = $class::$containerNodeClass; $select->join( $containerClass::$databaseTable, $class::$databaseTable . "." . $class::$databasePrefix . $class::$databaseColumnMap['container'] . '=' . $containerClass::$databaseTable . "." . $containerClass::$databasePrefix . $containerClass::$databaseColumnId ); } if ( count( $joins ) ) { foreach ( $joins as $join ) { $select->join( $join['from'], $join['where'] ); } } $selects[] = $select; } /* Cutoff */ if ( $start ) { $where[] = array( 'date>?', $start->getTimestamp() ); } if ( $end ) { $where[] = array( 'dategetTimestamp() ); } $page = isset( \IPS\Request::i()->page ) ? intval( \IPS\Request::i()->page ) : 1; if( $page < 1 ) { $page = 1; } /* Run */ $results = \IPS\Db::i()->union( $selects, 'date DESC', array( ( ( $page - 1 ) * 25 ), 25 ), ( $onlyFollowed ) ? '_app, _follow_area, id' : NULL, FALSE, \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS, $where ); /* Joins */ if ( $onlyFollowed ) { $results->join( 'core_follow', "follow_app=_app AND follow_area=_follow_area AND follow_rel_id=id" ); } $resultPages = ceil( $results->count( TRUE ) / 25 ); /* Build a table */ $table = new \IPS\Helpers\Table\Custom( iterator_to_array( $results ), $baseUrl ); $table->classes[] = 'ipsDataList_zebra'; $table->pages = $resultPages; $table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'vnc' ), 'genericRows' ); } /* Display */ if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->json( array( 'sidebar' => \IPS\Theme::i()->getTemplate('vnc')->filters( $baseUrl, $types, $nodeSelect, $nodeTitle ), 'content' => \IPS\Theme::i()->getTemplate('vnc')->results( $baseUrl, (string) $table, \IPS\Request::i()->vncTimePeriod, $interval, $start, $end ?: \IPS\DateTime::create() ), ) ); } else { \IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('front_vnc.js', 'core' ) ); \IPS\Output::i()->sidebar['enabled'] = FALSE; \IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack('search_vnc_title') ); \IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('search_vnc_title'); \IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('vnc')->template( $baseUrl, $types, (string) $table, \IPS\Request::i()->vncTimePeriod, $interval, $start, $end ?: \IPS\DateTime::create(), $nodeSelect, $nodeTitle ); } } /** * Get the 'unread' where SQL * * @param string $class Content class (\IPS\forums\Forum) * @return array */ protected function _getUnreadWhere( $class ) { $classBits = explode( "\\", $class ); $application = $classBits[1]; $resetTimes = \IPS\Member::loggedIn()->markersResetTimes( $application ); $oldestTime = time(); $markers = array(); $excludeIds = array(); $where = array(); $unreadWheres = array(); $containerIds = array(); $containerClass = ( $class::$containerNodeClass ) ? $class::$containerNodeClass : NULL; /* What is the best date column? */ $dateColumns = array(); foreach ( array( 'updated', 'last_comment', 'last_review' ) as $k ) { if ( isset( $class::$databaseColumnMap[ $k ] ) ) { if ( is_array( $class::$databaseColumnMap[ $k ] ) ) { foreach ( $class::$databaseColumnMap[ $k ] as $v ) { $dateColumns[] = " IFNULL( " . $class::$databaseTable . '.'. $class::$databasePrefix . $v . ", 0 )"; } } else { $dateColumns[] = " IFNULL( " . $class::$databaseTable . '.'. $class::$databasePrefix . $class::$databaseColumnMap[ $k ] . ", 0 )"; } } } $dateColumnExpression = count( $dateColumns ) > 1 ? ( 'GREATEST(' . implode( ',', $dateColumns ) . ')' ) : array_pop( $dateColumns ); foreach( $resetTimes as $containerId => $timestamp ) { $container = NULL; if( $containerId AND $containerClass ) { try { $container = $containerClass::load( $containerId ); } catch ( \OutOfRangeException $e) { continue; } } $timestamp = $timestamp ?: \IPS\Member::loggedIn()->marked_site_read; $containerIds[] = $containerId; $unreadWheres[] = '( ' . $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['container'] . '=' . $containerId . ' AND ' . $dateColumnExpression . ' > ' . (int) $timestamp . ')'; $items = \IPS\Member::loggedIn()->markersItems( $application, \IPS\Content\Item::makeMarkerKey( $container ) ); if ( count( $items ) ) { foreach( $items as $mid => $mtime ) { if ( $mtime > $timestamp ) { $markers[ $mtime . '.' . $mid ] = $mid; } } } } if( count( $containerIds ) ) { $unreadWheres[] = "( " . $dateColumnExpression . " > " . \IPS\Member::loggedIn()->marked_site_read . " AND ( " . $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['container'] . " NOT IN(" . implode( ',', $containerIds ) . ") ) )"; } else { $unreadWheres[] = "( " . $dateColumnExpression . " > " . \IPS\Member::loggedIn()->marked_site_read . ")"; } if( count( $unreadWheres ) ) { $where[] = array( "(" . implode( " OR ", $unreadWheres ) . ")" ); } if ( count( $markers ) ) { /* Avoid packet issues */ krsort( $markers ); $useIds = array_flip( array_slice( $markers, 0, 500, TRUE ) ); $select = ''; $from = ''; $notIn = array(); foreach( \IPS\Db::i()->select( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId. ' as _id, ' . $dateColumnExpression . ' as _date', $class::$databaseTable, \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnId, array_keys( $useIds ) ) ) as $row ) { if ( isset( $useIds[ $row['_id'] ] ) ) { if ( $useIds[ $row['_id'] ] >= $row['_date'] ) { /* Still read */ $notIn[] = intval( $row['_id'] ); } } } if ( count( $notIn ) ) { $where[] = array( "( " . $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId . " NOT IN (" . implode( ',', $notIn ) . ") )" ); } } return $where; } /** * Get the date Column expression * * @param string $class Content class (\IPS\forums\Forum) * @return string */ protected function _getDateExpression( $class ) { /* What is the best date column? */ $dateColumns = array(); foreach ( array( 'updated', 'last_comment', 'last_review' ) as $k ) { if ( isset( $class::$databaseColumnMap[ $k ] ) ) { if ( is_array( $class::$databaseColumnMap[ $k ] ) ) { foreach ( $class::$databaseColumnMap[ $k ] as $v ) { $dateColumns[] = " IFNULL( " . $class::$databaseTable . '.'. $class::$databasePrefix . $v . ", 0 )"; } } else { $dateColumns[] = " IFNULL( " . $class::$databaseTable . '.'. $class::$databasePrefix . $class::$databaseColumnMap[ $k ] . ", 0 )"; } } } return count( $dateColumns ) > 1 ? ( 'GREATEST(' . implode( ',', $dateColumns ) . ')' ) : array_pop( $dateColumns ); } }