Current File : /home/jvzmxxx/wiki1/extensions/Flow/includes/Api/ApiFlowSearch.php
<?php

namespace Flow\Api;

use ApiBase;
use Flow\Container;
use Flow\Exception\InvalidDataException;
use Flow\Formatter\TopicListFormatter;
use Flow\Formatter\TopicListQuery;
use Flow\Model\UUID;
use Flow\Search\Connection;
use Flow\Search\SearchEngine;
use Flow\Search\Searcher;
use MWNamespace;
use Status;

class ApiFlowSearch extends ApiFlowBaseGet {
	/**
	 * @var SearchEngine
	 */
	protected $searchEngine;

	public function __construct( $api, $modName ) {
		parent::__construct( $api, $modName, 'q' );
		$this->searchEngine = new SearchEngine();
	}

	public function execute() {
		$params = $this->extractRequestParams();

		if ( $params['type'] ) {
			$this->searchEngine->setType( $params['type'] );
		}

		$this->searchEngine->setLimitOffset( $params['limit'], $params['offset'] );
		$this->searchEngine->setSort( $params['sort'] );

		// pages are optional; if not provided, all pages will be searched
		$pageIds = $this->getPageIds( $params );
		if ( $pageIds ) {
			$this->searchEngine->setPageIds( $pageIds );
		}

		// namespaces are optional; if not provided, all namespaces will be searched
		if ( $params['namespaces'] ) {
			$this->searchEngine->setNamespaces( $params['namespaces'] );
		}

		// moderationStates are optional; if not provided, all moderation states will be searched
		$moderationState = $this->getModerationState( $params );
		if ( $moderationState ) {
			$this->searchEngine->setModerationStates( $moderationState );
		}

		/** @var Status $status */
		$status = $this->searchEngine->searchText( $params['term'] );
		if ( !$status->isGood() ) {
			throw new InvalidDataException( $status->getMessage(), 'fail-search' );
		}

		/** @var \Elastica\ResultSet|null $resultSet */
		$resultSet = $status->getValue();
		// $resultSet can be null, if nothing was found
		$results = $resultSet === null ? array() : $resultSet->getResults();

		// list of highlighted words
		$highlights = array();
		/** @var \Elastica\Result $result */
		foreach ( $results as $result ) {
			// there'll always be exactly 1 excerpt
			// see Searcher.php, ...->setHighlight() config
			$excerpt = $result->getHighlights();
			$excerpt = $excerpt[Searcher::HIGHLIGHT_FIELD][0];

			$pre = preg_quote( Searcher::HIGHLIGHT_PRE, '/' );
			$post = preg_quote( Searcher::HIGHLIGHT_POST, '/' );
			if ( preg_match_all( '/' . $pre . '(.*?)' . $post . '/', $excerpt, $matches ) ) {
				$highlights += array_flip( $matches[1] );
			}
		}
		$highlights = array_keys( $highlights );

		// total term frequency
		$ttf = $resultSet->getAggregation( 'ttf' );
		$ttf = $ttf['value'];

		$topicIds = array();
		foreach ( $results as $topic ) {
			$topicIds[] = UUID::create( $topic->getId() );
		}

		// output similar to view-topiclist
		$results = $this->formatApi( $topicIds );
		// search-specific output
		$results['total'] = $resultSet->getTotalHits();
		$results['highlights'] = $highlights;
		$results['ttf'] = $ttf;

		$this->getResult()->addValue( null, $this->getModuleName(), $results );
	}

	/**
	 * Given an array of topic UUIDs, we'll use TopicListQuery & TopicListFormatter
	 * to return API output very similar to ApiFlowViewTopicList.
	 *
	 * @param array $topicIds
	 * @return array
	 */
	protected function formatApi( array $topicIds ) {
		/** @var TopicListQuery $query */
		$query = Container::get( 'query.topiclist' );
		$found = $query->getResults( $topicIds );

		$storage = Container::get( 'storage' );
		$workflows = $storage->getMulti( 'Workflow', $topicIds );

		/** @var TopicListFormatter $serializer */
		$serializer = Container::get( 'formatter.topiclist' );
		return $serializer->buildResult( $workflows, $found, $this->getContext() );
	}

	/**
	 * @param array $params
	 * @return int[]
	 */
	protected function getPageIds( $params ) {
		$pageIds = array();
		// page is optional - if not provided, all pages will be searched
		if ( $params['title'] || $params['pageid'] ) {
			$page = $this->getTitleOrPageId( $params );

			// validate the page that was passed in
			if ( !$page->exists() ) {
				$this->dieUsage( 'Invalid page provided', 'invalid-page' );
			}

			if ( $page->getTitle()->getContentModel() !== CONTENT_MODEL_FLOW_BOARD ) {
				$this->dieUsage( 'Page provided does not have Flow enabled', 'invalid-page' );
			}

			$pageIds[] = $page->getId();
		}

		return $pageIds;
	}

	/**
	 * If only state AbstractRevision::MODERATED_NONE (which is '') is
	 * passed in, this param will be an empty array. Let's fix this and
	 * turn it into an array with exactly 1 value: ''
	 * Other scenarios are fine: if multiple states are passed in, ''
	 * is correctly recognized. If no state has been passed in, this
	 * parameter value is null, which will by default search all
	 * moderation states.
	 * Regardless of which status a user searches in, we'll never
	 * display results the user has no permissions for.
	 *
	 * @param array $params
	 * @return string[]|null
	 */
	protected function getModerationState( $params ) {
		$moderationState = $params['moderationState'];
		if ( $moderationState !== null && count( $moderationState ) === 0 ) {
			$moderationState = array( '' );
		}

		return $moderationState;
	}

	public function getAllowedParams() {
		global $wgFlowDefaultLimit;

		$sorts = $this->searchEngine->getValidSorts();

		return array(
			'term' => array(
				ApiBase::PARAM_REQUIRED => true,
				ApiBase::PARAM_TYPE => 'string',
			),
			'title' => null,
			'pageid' => array(
				ApiBase::PARAM_ISMULTI => false,
				ApiBase::PARAM_TYPE => 'integer'
			),
			'namespaces' => array(
				ApiBase::PARAM_ISMULTI => true,
				ApiBase::PARAM_TYPE => MWNamespace::getValidNamespaces(),
			),
			'moderationState' => array(
				ApiBase::PARAM_ISMULTI => true,
				ApiBase::PARAM_TYPE => $this->getModerationStates( false ),
			),
			'sort' => array(
				ApiBase::PARAM_TYPE => $sorts,
				ApiBase::PARAM_DFLT => reset( $sorts ),
			),
			'type' => array(
				// false is allowed (means we'll search *all* types)
				ApiBase::PARAM_TYPE => array_merge( Connection::getAllTypes(), array( false ) ),
				ApiBase::PARAM_DFLT => false,
			),
			'offset' => array(
				ApiBase::PARAM_TYPE => 'integer',
				ApiBase::PARAM_DFLT => 0,
			),
			'limit' => array(
				ApiBase::PARAM_TYPE => 'limit',
				ApiBase::PARAM_DFLT => $wgFlowDefaultLimit,
				ApiBase::PARAM_MAX => $wgFlowDefaultLimit,
				ApiBase::PARAM_MAX2 => $wgFlowDefaultLimit,
			),
		);
	}

	public function getParamDescription() {
		$p = $this->getModulePrefix();
		return array(
			'term' => 'Search term',
			'title' => "Title of the boards to search in. Cannot be used together with {$p}pageid",
			'pageid' => "ID of the boards to search in. Cannot be used together with {$p}title",
			'namespaces' => 'Namespaces to search in',
			'moderationState' => 'Search for revisions in (a) particular moderation state(s)',
			'sort' => 'What to order the search results by',
			'type' => 'Desired type of results (' . implode( '|', Connection::getAllTypes() ) . ')',
			'offset' => 'Offset value to start fetching results at',
			'limit' => 'Amount of results to fetch',
		);
	}

	public function getDescription() {
		return 'Search within Flow boards';
	}

	public function getExamples() {
		return array(
			'api.php?action=flow&submodule=search&qterm=keyword&qtitle=Main_Page',
		);
	}

	public function needsPage() {
		// irrelevant for search API
		return false;
	}

	protected function getBlockParams() {
		// irrelevant for search API
		return array();
	}

	protected function getAction() {
		// irrelevant for search API
		return '';
	}
}