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 21 Aug 2014 * @version SVN_VERSION_NUMBER */ namespace IPS\Content\Search; /* 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; } /** * Abstract Search Query */ abstract class _Query { const TERM_OR_TAGS = 1; const TERM_AND_TAGS = 2; const HIDDEN_VISIBLE = 0; const HIDDEN_UNAPPROVED = 1; const HIDDEN_HIDDEN = -1; const HIDDEN_PARENT_HIDDEN = 2; const ORDER_NEWEST_UPDATED = 1; const ORDER_NEWEST_CREATED = 2; const ORDER_RELEVANCY = 3; const SUPPORTS_JOIN_FILTERS = TRUE; /** * Create new query * * @param \IPS\Member $member The member performing the search (NULL for currently logged in member) * @return \IPS\Content\Search */ public static function init( \IPS\Member $member = NULL ) { $class = '\\IPS\\Content\\Search\\' . ucfirst( \IPS\Settings::i()->search_engine ) . '\\Query'; return new $class( $member ?: \IPS\Member::loggedIn() ); } /** * @brief Number of results to get */ public $resultsToGet = 25; /** * @brief The member performing the search */ protected $member; /** * Constructor * * @param \IPS\Member $member The member performing the search * @return void */ public function __construct( \IPS\Member $member ) { $this->member = $member; if ( !$member->modPermission('can_view_hidden_content') ) { $this->setHiddenFilter( static::HIDDEN_VISIBLE ); } } /** * Exclude "first post" content items * Useful for cases such as activity streams where you do not want both the topic and the first post to show * * @return \IPS\Content\Search\Query (for daisy chaining) */ public function excludeFirstPostContentItems() { $classes = array(); foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class ) { if ( in_array( 'IPS\Content\Searchable', class_implements( $class ) ) and $class::$firstCommentRequired ) { $classes[] = $class; } } if ( !empty( $classes ) ) { return $this->filterByExcludeContent( $classes ); } else { return $this; } } /** * Exclude disabled applications * * @return \IPS\Content\Search\Query (for daisy chaining) */ public function excludeDisabledApps() { $includeClasses = array(); foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class ) { if ( in_array( 'IPS\Content\Searchable', class_implements( $class ) ) ) { $includeClasses[] = $class; if ( isset( $class::$commentClass ) ) { $includeClasses[] = $class::$commentClass; } if ( isset( $class::$reviewClass ) ) { $includeClasses[] = $class::$reviewClass; } } } $allClasses = array(); foreach ( \IPS\Content::routedClasses( FALSE, FALSE, TRUE ) as $class ) { if ( in_array( 'IPS\Content\Searchable', class_implements( $class ) ) ) { $allClasses[] = $class; if ( isset( $class::$commentClass ) ) { $allClasses[] = $class::$commentClass; } if ( isset( $class::$reviewClass ) ) { $allClasses[] = $class::$reviewClass; } } } $classes = array_diff( $allClasses, $includeClasses ); if ( !empty( $classes ) ) { return $this->filterByExcludeContent( $classes ); } else { return $this; } } /** * Filter by content type * * @param string $class The type of content to search (including all comment/review classes) * @param array|NULL $containers An array of container IDs to filter by, or NULL to not filter by containers * @param int|NULL $minimumComments The minimum number of comments * @param int|NULL $minimumReviews The minimum number of reviews * @param int|NULL $minimumViews The minimum number of views * @return \IPS\Content\Search\Query (for daisy chaining) */ abstract public function filterByContent( $class, $containers = NULL, $minimumComments = NULL, $minimumReviews = NULL, $minimumViews = NULL ); /** * Filter by content type to exclude * * @param string $classes The types of content to search (not including all comment/review classes) * @param bool $allowedContiners If TRUE, $class should be an array of allowed containers for that class - e.g. array( 'IPS\forums\Topic' => array( 1, 2 ) - otherwise, a simple array - e.g. array( 'IPS\forums\Topic' ) * @return \IPS\Content\Search\Query (for daisy chaining) */ abstract public function filterByExcludeContent( array $classes, $allowedContiners=FALSE ); /** * Filter by content item * * @param \IPS\Content\Item $item The item * @return \IPS\Content\Search\Query (for daisy chaining) */ abstract public function filterByItem( \IPS\Content\Item $item ); /** * Filter by author * * @param \IPS\Member $author The author * @return \IPS\Content\Search\Query (for daisy chaining) */ abstract public function filterByAuthor( \IPS\Member $author ); /** * Filter by start date * * @param \IPS\DateTime|NULL $start The start date (only results AFTER this date will be returned) * @param \IPS\DateTime|NULL $end The end date (only results BEFORE this date will be returned) * @return \IPS\Content\Search\Query (for daisy chaining) */ abstract public function filterByCreateDate( \IPS\DateTime $start = NULL, \IPS\DateTime $end = NULL ); /** * Filter by last updated date * * @param \IPS\DateTime|NULL $start The start date (only results AFTER this date will be returned) * @param \IPS\DateTime|NULL $end The end date (only results BEFORE this date will be returned) * @return \IPS\Content\Search\Query (for daisy chaining) */ abstract public function filterByLastUpdatedDate( \IPS\DateTime $start = NULL, \IPS\DateTime $end = NULL ); /** * Set hidden status * * @param int|array|NULL $statuses The statuses (see HIDDEN_ constants) or NULL for any * @return \IPS\Content\Search\Query (for daisy chaining) */ abstract public function setHiddenFilter( $statuses ); /** * Set limit * * @param int $limit Number per page * @return \IPS\Content\Search\Query (for daisy chaining) */ public function setLimit( $limit ) { $this->resultsToGet = $limit; return $this; } /** * Set page * * @param int $page The page number * @return \IPS\Content\Search\Query (for daisy chaining) */ abstract public function setPage( $page ); /** * Set order * * @param int $order Order (see ORDER_ constants) * @return \IPS\Content\Search\Query (for daisy chaining) */ abstract public function setOrder( $order ); /** * Permission Array * * @return array */ public function permissionArray() { return $this->member->permissionArray(); } /** * Search * * @param string|null $term The term to search for * @param array|null $tags The tags to search for * @param int $method \IPS\Content\Search\Index::i()->TERM_OR_TAGS or \IPS\Content\Search\Index::i()->TERM_AND_TAGS * @return \Traversable */ abstract public function search( $term = NULL, $tags = NULL, $method = 1 ); /** * Get count * * @param string|null $term The term to search for * @param array|null $tags The tags to search for * @param int $method \IPS\Content\Search\Index::i()->TERM_OR_TAGS or \IPS\Content\Search\Index::i()->TERM_AND_TAGS * @return int */ public function count( $term = NULL, $tags = NULL, $method = 1 ) { return $this->search( $term, $tags, $method )->count( TRUE ); } }