summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php431
1 files changed, 431 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php
new file mode 100644
index 00000000..5943d005
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Api/Task.php
@@ -0,0 +1,431 @@
+<?php
+
+namespace SMW\MediaWiki\Api;
+
+use ApiBase;
+use Iterator;
+use SMW\ApplicationFactory;
+use SMW\DIWikiPage;
+use SMW\MediaWiki\Jobs\UpdateJob;
+use SMW\Enum;
+use SMWQueryProcessor as QueryProcessor;
+use SMWQuery as Query;
+
+/**
+ * Module to support various tasks initiate using the API interface
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class Task extends ApiBase {
+
+ const CACHE_NAMESPACE = 'smw:api:task';
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return string
+ */
+ public static function makeCacheKey( $key ) {
+ return smwfCacheKey( self::CACHE_NAMESPACE, [ $key ] );
+ }
+
+ /**
+ * @see ApiBase::execute
+ */
+ public function execute() {
+
+ $params = $this->extractRequestParams();
+
+ $parameters = json_decode(
+ $params['params'],
+ true
+ );
+
+ $results = [];
+
+ if ( $params['task'] === 'update' ) {
+ $results = $this->callUpdateTask( $parameters );
+ }
+
+ if ( $params['task'] === 'check-query' ) {
+ $results = $this->callCheckQueryTask( $parameters );
+ }
+
+ if ( $params['task'] === 'duplookup' ) {
+ $results = $this->callDupLookupTask( $parameters );
+ }
+
+ if ( $params['task'] === 'job' ) {
+ $results = $this->callGenericJobTask( $parameters );
+ }
+
+ if ( $params['task'] === 'run-joblist' ) {
+ $results = $this->callJobListTask( $parameters );
+ }
+
+ $this->getResult()->addValue(
+ null,
+ 'task',
+ $results
+ );
+ }
+
+ private function callDupLookupTask( $parameters ) {
+
+ $applicationFactory = ApplicationFactory::getInstance();
+ $cache = $applicationFactory->getCache();
+
+ $cacheUsage = $applicationFactory->getSettings()->get(
+ 'smwgCacheUsage'
+ );
+
+ $cacheTTL = 3600;
+
+ if ( isset( $cacheUsage['api.task'] ) ) {
+ $cacheTTL = $cacheUsage['api.task'];
+ }
+
+ $key = self::makeCacheKey( 'duplookup' );
+
+ // Guard against repeated API calls (or fuzzing)
+ if ( ( $result = $cache->fetch( $key ) ) !== false && $cacheTTL !== false ) {
+ return $result + ['isFromCache' => true ];
+ }
+
+ $rows = $applicationFactory->getStore()->getObjectIds()->findDuplicates();
+
+ // Avoid "Exception caught: Serialization of 'Closure' is not allowedException ..."
+ if ( $rows instanceof Iterator ) {
+ $rows = iterator_to_array( $rows );
+ }
+
+ $result = [
+ 'list' => $rows,
+ 'count' => count( $rows ),
+ 'time' => time()
+ ];
+
+ $cache->save( $key, $result, $cacheTTL );
+
+ return $result;
+ }
+
+ private function callCheckQueryTask( $parameters ) {
+
+ if ( $parameters['subject'] === '' || $parameters['query'] === '' ) {
+ return [ 'done' => false ];
+ }
+
+ $store = ApplicationFactory::getInstance()->getStore();
+
+ $subject = DIWikiPage::doUnserialize(
+ $parameters['subject']
+ );
+
+ foreach ( $parameters['query'] as $hash => $raw_query ) {
+
+ // @see PostProcHandler::addQuery
+ list( $query_hash, $result_hash ) = explode( '#', $hash );
+
+ // Doesn't influence the fingerprint (aka query cache) so just
+ // ignored it
+ $printouts = [];
+ $parameters = $raw_query['parameters'];
+
+ if ( isset( $parameters['sortkeys'] ) ) {
+ $order = [];
+ $sort = [];
+
+ foreach ( $parameters['sortkeys'] as $key => $order_by ) {
+ $order[] = strtolower( $order_by );
+ $sort[] = $key;
+ }
+
+ $parameters['sort'] = implode( ',', $sort );
+ $parameters['order'] = implode( ',', $order );
+ }
+
+ QueryProcessor::addThisPrintout( $printouts, $parameters );
+
+ $query = QueryProcessor::createQuery(
+ $raw_query['conditions'],
+ QueryProcessor::getProcessedParams( $parameters, $printouts ),
+ QueryProcessor::INLINE_QUERY,
+ '',
+ $printouts
+ );
+
+ $query->setLimit(
+ $parameters['limit']
+ );
+
+ $query->setOffset(
+ $parameters['offset']
+ );
+
+ $query->setQueryMode(
+ $parameters['querymode']
+ );
+
+ $query->setContextPage(
+ $subject
+ );
+
+ $query->setOption( Query::PROC_CONTEXT, 'task.api' );
+
+ $res = $store->getQueryResult(
+ $query
+ );
+
+ // If the result_hash from before the post-edit and the result_hash
+ // after the post-edit check are not the same then it means that the
+ // list of entities changed hence send a `reload` command to the
+ // API promise.
+ if ( $result_hash !== $res->getHash( 'quick' ) ) {
+ return [ 'done' => true, 'reload' => true ];
+ }
+ }
+
+ return [ 'done' => true ];
+ }
+
+ private function callGenericJobTask( $params ) {
+
+ $this->checkParameters( $params );
+
+ if ( $params['subject'] === '' ) {
+ return ['done' => false ];
+ }
+
+ $title = DIWikiPage::doUnserialize( $params['subject'] )->getTitle();
+
+ if ( $title === null ) {
+ return ['done' => false ];
+ }
+
+ if ( !isset( $params['job'] ) ) {
+ return ['done' => false ];
+ }
+
+ $parameters = [];
+
+ if ( isset( $params['parameters'] ) ) {
+ $parameters = $params['parameters'];
+ }
+
+ $jobFactory = ApplicationFactory::getInstance()->newJobFactory();
+
+ $job = $jobFactory->newByType(
+ $params['job'],
+ $title,
+ $parameters
+ );
+
+ $job->insert();
+ }
+
+ private function callUpdateTask( $parameters ) {
+
+ $this->checkParameters( $parameters );
+
+ if ( !isset( $parameters['subject'] ) || $parameters['subject'] === '' ) {
+ return [ 'done' => false ];
+ }
+
+ $subject = DIWikiPage::doUnserialize( $parameters['subject'] );
+ $title = $subject->getTitle();
+ $log = [];
+
+ if ( $title === null ) {
+ return ['done' => false ];
+ }
+
+ // Each single update is required to allow for a cascading computation
+ // where one query follows another to ensure that results are updated
+ // according to the value dependency of the referenced annotations that
+ // rely on a computed (#ask) value
+ if ( !isset( $parameters['ref'] ) ) {
+ $parameters['ref'] = [ $subject->getHash() ];
+ }
+
+ $jobFactory = ApplicationFactory::getInstance()->newJobFactory();
+ $isPost = isset( $parameters['post'] ) ? $parameters['post'] : false;
+ $origin = [];
+
+ if ( isset( $parameters['origin'] ) ) {
+ $origin = [ 'origin' => $parameters['origin'] ];
+ }
+
+ foreach ( $parameters['ref'] as $ref ) {
+ $updateJob = $jobFactory->newUpdateJob(
+ $title,
+ [
+ UpdateJob::FORCED_UPDATE => true,
+ Enum::OPT_SUSPEND_PURGE => false,
+ 'ref' => $ref
+ ] + $origin
+ );
+
+ if ( $isPost ) {
+ $updateJob->insert();
+ } else {
+ $updateJob->run();
+ }
+ }
+
+ return [ 'done' => true, 'log' => $log ];
+ }
+
+ private function callJobListTask( $parameters ) {
+
+ $this->checkParameters( $parameters );
+
+ if ( !isset( $parameters['subject'] ) || $parameters['subject'] === '' ) {
+ return [ 'done' => false ];
+ }
+
+ $subject = DIWikiPage::doUnserialize( $parameters['subject'] );
+ $title = $subject->getTitle();
+
+ if ( $title === null ) {
+ return [ 'done' => false ];
+ }
+
+ $jobQueue = ApplicationFactory::getInstance()->getJobQueue();
+ $jobList = [];
+
+ if ( isset( $parameters['jobs'] ) ) {
+ $jobList = $parameters['jobs'];
+ }
+
+ $log = $jobQueue->runFromQueue(
+ $jobList
+ );
+
+ return [ 'done' => true, 'log' => $log ];
+ }
+
+ private function checkParameters( $parameters ) {
+ if ( json_last_error() !== JSON_ERROR_NONE || !is_array( $parameters ) ) {
+
+ // 1.29+
+ if ( method_exists( $this, 'dieWithError' ) ) {
+ $this->dieWithError( [ 'smw-api-invalid-parameters' ] );
+ } else {
+ $this->dieUsageMsg( 'smw-api-invalid-parameters' );
+ }
+ }
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getAllowedParams
+ *
+ * @return array
+ */
+ public function getAllowedParams() {
+ return [
+ 'task' => [
+ ApiBase::PARAM_REQUIRED => true,
+ ApiBase::PARAM_TYPE => [
+
+ // Run update using the updateJob
+ 'update',
+
+ // Run a query check
+ 'check-query',
+
+ // Duplicate lookup support
+ 'duplookup',
+
+ // Insert/run a job
+ 'job',
+
+ // Run jobs from a list directly without the job scheduler
+ 'run-joblist'
+ ]
+ ],
+ 'params' => [
+ ApiBase::PARAM_TYPE => 'string',
+ ApiBase::PARAM_REQUIRED => false,
+ ],
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getParamDescription
+ *
+ * @return array
+ */
+ public function getParamDescription() {
+ return [
+ 'task' => 'Defines the task type',
+ 'params' => 'JSON encoded parameters that matches the selected type requirement'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getDescription
+ *
+ * @return array
+ */
+ public function getDescription() {
+ return [
+ 'Semantic MediaWiki API module to invoke and execute tasks (for internal use only)'
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::needsToken
+ */
+ public function needsToken() {
+ return 'csrf';
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::mustBePosted
+ */
+ public function mustBePosted() {
+ return true;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::isWriteMode
+ */
+ public function isWriteMode() {
+ return true;
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getExamples
+ *
+ * @return array
+ */
+ protected function getExamples() {
+ return [
+ 'api.php?action=smwtask&task=update&params={ "subject": "Foo" }',
+ ];
+ }
+
+ /**
+ * @codeCoverageIgnore
+ * @see ApiBase::getVersion
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return __CLASS__ . ':' . SMW_VERSION;
+ }
+
+}