Version 4.1.2

This commit is contained in:
Neo committed 2025-12-19 05:32:19 -08:00
1 parent 3ba79295ee
commit 28bd025b35
1131 files changed
+63658 -20579

No files matched your search

+116 -105
View File
@@ -25,6 +25,8 @@ abstract class _Query
{
const TERM_OR_TAGS = 1;
const TERM_AND_TAGS = 2;
const TERM_OR_MODE = 4;
const TERM_TITLES_ONLY = 8;
const HIDDEN_VISIBLE = 0;
const HIDDEN_UNAPPROVED = 1;
@@ -34,6 +36,8 @@ abstract class _Query
const ORDER_NEWEST_UPDATED = 1;
const ORDER_NEWEST_CREATED = 2;
const ORDER_RELEVANCY = 3;
const ORDER_OLDEST_UPDATED = 4;
const ORDER_OLDEST_CREATED = 5;
const SUPPORTS_JOIN_FILTERS = TRUE;
@@ -45,8 +49,7 @@ abstract class _Query
*/
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() );
return new \IPS\Content\Search\Mysql\Query( $member ?: \IPS\Member::loggedIn() );
}
/**
@@ -58,7 +61,7 @@ abstract class _Query
* @brief The member performing the search
*/
protected $member;
/**
* Constructor
*
@@ -69,129 +72,90 @@ abstract class _Query
{
$this->member = $member;
/* Exclude hidden items */
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 ) )
/* Exclude disabled applications */
$filters = array();
foreach ( \IPS\Application::applications() as $application )
{
return $this->filterByExcludeContent( $classes );
if ( !$application->enabled )
{
foreach ( $application->extensions( 'core', 'ContentRouter' ) as $extension )
{
foreach ( $extension->classes as $class )
{
$filters[] = \IPS\Content\Search\ContentFilter::init( $class );
}
}
}
}
else
if ( !empty( $filters ) )
{
return $this;
$this->filterByContent( $filters, FALSE );
}
}
/**
* Exclude disabled applications
* Filter by multiple content types
*
* @param array $contentFilters Array of \IPS\Content\Search\ContentFilter objects
* @param bool $type TRUE means only include results matching the filters, FALSE means exclude all results matching the filters
* @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 );
abstract public function filterByContent( array $contentFilters, $type = TRUE );
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|int|array $author The author, or an array of author IDs
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByAuthor( $author );
/**
* Filter for profile
*
* @param \IPS\Member $member The member whose profile is being viewed
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterForProfile( \IPS\Member $member );
/**
* Filter by item author
*
* @param \IPS\Member $author The author
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByAuthor( \IPS\Member $author );
abstract public function filterByItemAuthor( \IPS\Member $author );
/**
* Filter by content the user follows
*
* @param bool $includeContainers Include content in containers the user follows?
* @param bool $includeItems Include items and comments/reviews on items the user follows?
* @param bool $includeContainers Include content posted by members the user follows?
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByFollowed( $includeContainers, $includeItems, $includeMembers );
/**
* Filter by content the user has posted in
*
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByItemsIPostedIn();
/**
* Filter by content the user has not read
*
* @note If applicable, it is more efficient to call filterByContent() before calling this method
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByUnread();
/**
* Filter by start date
@@ -279,4 +243,51 @@ abstract class _Query
{
return $this->search( $term, $tags, $method )->count( TRUE );
}
/**
* Is this term a phrase?
*
* @param string $term The term to search for
* @return boolean
*/
public static function termIsPhrase( $term )
{
return (boolean) preg_match( '#^".*"$#', $term );
}
/**
* Term as words array
* Returns an array of words. A word is considered as either an entire phrase (eg "sticks stones") when $ignorePhrase is FALSE, or a sequence of characters delimited by a space.
* Words smaller than 2 characters are ignored.
*
* @param string $term The term to search for
* @param boolean $ignorePhrase When true, phrases are stripped of quotes and treated as normal words
* @param int $minLength The minimum length a sequence of characters has to be before it is considered a word
* @return array
*/
public static function termAsWordsArray( $term, $ignorePhrase=FALSE, $minLength=3 )
{
if ( static::termIsPhrase( $term ) )
{
if ( ! $ignorePhrase )
{
return array( $term );
}
else
{
$term = str_replace( '"', '', $term );
}
}
$theWords = array();
foreach( explode( ' ', $term ) as $_term )
{
if ( mb_strlen( $_term ) > $minLength )
{
$theWords[] = $_term;
}
}
return $theWords;
}
}