jenkins-bot has submitted this change and it was merged.
Change subject: Implemented api action=editcollection
......................................................................
Implemented api action=editcollection
The eastiest way to use it - create a file settings.d/00.php (in vagrant):
if ( class_exists( 'ExtensionRegistry' ) && file_exists(
"$IP/extensions/Gather/extension.json" ) ) {
ExtensionRegistry::getInstance()->queue(
"$IP/extensions/Gather/extension.json" );
} else {
include_once "$IP/extensions/Gather/Gather.php";
}
$wgDebugAPI = true; // This allows GET requests to be treated as POST for easy
debugging
Copy token number from
http://localhost:8080/w/api.php?action=query&meta=tokens&type=watch
Replace token number in this URL (leave the %2B\ intact) and run:
http://localhost:8080/w/api.php?action=editcollection&token=833e0b1f14270c04b7c2daa3caf742b154f9a8fe%2B\&generator=allpages&label=mylist
Which will take first 10 pages of your wiki and add them to the new collection.
See result in http://localhost:8080/wiki/User:Admin/GatherCollections.json
API doc http://localhost:8080/w/api.php?action=help&modules=editcollection
If id=0, uses built-in watchlist, otherwise stores items as part of the manifest
Bug: T91304
Change-Id: Iaf6cc4553f2563fb2c9ffce2eac40eb13122253a
---
M Gather.php
M extension.json
A includes/api/ApiEditCollection.php
3 files changed, 392 insertions(+), 0 deletions(-)
Approvals:
Yurik: Looks good to me, approved
jenkins-bot: Verified
diff --git a/Gather.php b/Gather.php
index 31f747b..7dc2ab6 100644
--- a/Gather.php
+++ b/Gather.php
@@ -63,6 +63,7 @@
'Gather\SpecialGather' => 'specials/SpecialGather',
+ 'Gather\api\ApiEditCollection' => 'api/ApiEditCollection',
'Gather\api\CollectionsListApi' => 'api/CollectionsListApi',
);
@@ -85,6 +86,7 @@
// Api
$wgAPIModules['gather'] = 'Gather\api\CollectionsListApi';
+$wgAPIModules['editcollection'] = 'Gather\api\ApiEditCollection';
// ResourceLoader modules
require_once __DIR__ . "/resources/Resources.php";
diff --git a/extension.json b/extension.json
index c39b4e5..8afbf63 100644
--- a/extension.json
+++ b/extension.json
@@ -47,8 +47,13 @@
"Gather\\views\\CollectionsListItemCard":
"includes/views/CollectionsListItemCard.php",
"Gather\\views\\helpers\\CSS": "includes/views/helpers/CSS.php",
"Gather\\SpecialGather": "includes/specials/SpecialGather.php",
+ "Gather\\api\\ApiEditCollection":
"includes/api/ApiEditCollection.php",
"Gather\\api\\CollectionsListApi":
"includes/api/CollectionsListApi.php"
},
+ "APIModules": {
+ "editcollection": "Gather\\api\\ApiEditCollection",
+ "gather": "Gather\\api\\CollectionsListApi"
+ },
"ResourceModules": {
"ext.gather.icons": {
"targets": [
diff --git a/includes/api/ApiEditCollection.php
b/includes/api/ApiEditCollection.php
new file mode 100644
index 0000000..e242161
--- /dev/null
+++ b/includes/api/ApiEditCollection.php
@@ -0,0 +1,385 @@
+<?php
+/**
+ *
+ *
+ * Created on Mar 6, 2015
+ *
+ * Copyright © 2015 Yuri Astrakhan "<Firstname><Lastname>@gmail.com",
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace Gather\api;
+
+use ApiBase;
+use FormatJson;
+use JsonContent;
+use MWException;
+use Status;
+use stdClass;
+use Title;
+use UnwatchAction;
+use User;
+use ApiPageSet;
+use WatchAction;
+use WikiPage;
+
+/**
+ * API module to allow users to manage collections
+ *
+ * @ingroup API
+ */
+class ApiEditCollection extends ApiBase {
+ private $mPageSet = null;
+
+ /**
+ * @throws \UsageException
+ */
+ public function execute() {
+
+ $params = $this->extractRequestParams();
+
+ if ( $params['label'] !== null ) {
+ $params['label'] = trim( $params['label'] );
+ if ( $params['label'] === '' ) {
+ $this->dieUsage( 'If given, label must not be
empty', 'badlabel' );
+ }
+ }
+
+ $user = $this->getUser(); // TBD: We might want to allow other
users with getWatchlistUser()
+
+ if ( !$user->isLoggedIn() ) {
+ $this->dieUsage( 'You must be logged-in to have a
collection', 'notloggedin' );
+ }
+ if ( !$user->isAllowed( 'editmywatchlist' ) ) {
+ $this->dieUsage( 'You don\'t have permission to edit
your collection',
+ 'permissiondenied' );
+ }
+
+ $pageSet = $this->getPageSet();
+ $p = $this->getModulePrefix();
+
+ $remove = $params['remove'];
+ $id = $params['id'];
+ $isNew = $id === null;
+
+ // Validate 'deletecollection' parameters
+ if ( $params['deletecollection'] ) {
+
+ // ID == 0 is a watchlist
+ if ( $id === 0 ) {
+ $this->dieUsage( "Collection #0 (watchlist) may
not be deleted", 'badid' );
+ }
+ if ( $isNew ) {
+ $this->dieUsage( "Collection must be identified
with {$p}id when {$p}deletecollection is used", 'invalidparammix' );
+ }
+
+ // For deletecollection, disallow all parameters except
those unset
+ $tmp = $params + $pageSet->extractRequestParams();
+ unset( $tmp['deletecollection'] );
+ unset( $tmp['id'] );
+ unset( $tmp['token'] );
+ $extraParams =
+ array_keys( array_filter( $tmp, function ( $x )
{
+ return $x !== null && $x !== false;
+ } ) );
+ if ( $extraParams ) {
+ $this->dieUsage( "The parameter
{$p}deletecollection must not be used with " .
+ implode( ", ",
$extraParams ), 'invalidparammix' );
+ }
+ }
+
+ $manifest = self::loadManifest( $user );
+
+ /** @var stdClass $collection */
+ $collection = null;
+
+ if ( $isNew ) {
+ if ( $remove ) {
+ $this->dieUsage( "Collection must be identified
with {$p}id when {$p}remove is used", 'invalidparammix' );
+ }
+ if ( !$params['label'] ) {
+ $this->dieUsage( "Collection {$p}label must be
given for new collections", 'invalidparammix' );
+ }
+
+ // ACTION: add new collection to manifest.
+ // work out a new id
+ $id = 1;
+ if ( $manifest ) {
+ foreach ( $manifest as $c ) {
+ if ( $c->id > $id ) {
+ $id = $c->id + 1;
+ }
+ }
+ }
+ $collection = $this->createCollection( $id, $params,
$user );
+ $manifest[] = $collection;
+ } else {
+ $collection = $this->findCollection( $manifest, $id,
$user );
+ if ( $collection === null ) {
+ $this->dieUsage( "Collection {$p}id was not
found", 'badid' );
+ }
+
+ // ACTION: update existing collection
+ if ( $params['label'] !== null ) {
+ $collection->title = $params['label'];
+ }
+ if ( $params['description'] !== null ) {
+ $collection->description =
$params['description'];
+ }
+ }
+
+ if ( $params['deletecollection'] ) {
+ // ACTION: drop the collection from the manifest
+ $manifest = array_filter( $manifest, function ( $x )
use ( $id ) {
+ return $x->id !== $id;
+ } );
+ } else {
+
+ $this->getResult()->beginContinuation(
$params['continue'], array(), array() );
+
+ $pageSet->execute();
+ $res = $pageSet->getInvalidTitlesAndRevisions( array(
+ 'invalidTitles',
+ 'special',
+ 'missingIds',
+ 'missingRevIds',
+ 'interwikiTitles'
+ ) );
+
+ // If 0, use user's watchlist instead of our temp
collection
+ $col = $id === 0 ? null : $collection;
+
+ foreach ( $pageSet->getMissingTitles() as $title ) {
+ $r = $this->watchTitle( $col, $title, $user,
$remove );
+ $r['missing'] = 1;
+ $res[] = $r;
+ }
+
+ foreach ( $pageSet->getGoodTitles() as $title ) {
+ $r = $this->watchTitle( $col, $title, $user,
$remove );
+ $res[] = $r;
+ }
+ $this->getResult()->setIndexedTagName( $res, 'w' );
+ $this->getResult()->addValue( $this->getModuleName(),
'pages', $res );
+ $this->getResult()->endContinuation();
+
+ $collection->count = count( $collection->items );
+ }
+
+ self::save( $user, $manifest );
+ }
+
+ /**
+ * @param null|stdClass $collection
+ * @param Title $title
+ * @param User $user
+ * @param bool $remove
+ * @return array
+ * @throws MWException
+ */
+ private function watchTitle( $collection, Title $title, User $user,
$remove ) {
+ $titleStr = $title->getPrefixedText();
+
+ if ( !$title->isWatchable() ) {
+ return array( 'title' => $titleStr, 'watchable' => 0 );
+ }
+
+ $res = array( 'title' => $titleStr );
+
+ if ( $collection ) {
+ $key = array_search( $titleStr, $collection->items );
+ $status = Status::newGood();
+ if ( $remove && $key !== false ) {
+ unset( $collection->items[$key] );
+ // Must reset keys, otherwise will get
converted to an object on save
+ $collection->items = array_values(
$collection->items );
+ } elseif ( !$remove && $key === false ) {
+ $collection->items[] = $titleStr;
+ }
+ } else {
+ if ( $remove ) {
+ $status = UnwatchAction::doUnwatch( $title,
$user );
+ } else {
+ $status = WatchAction::doWatch( $title, $user );
+ }
+ }
+
+ if ( $status->isOK() ) {
+ $res[$remove ? 'removed' : 'added'] = '';
+ $res['message'] = $this->msg(
+ $remove ? 'removedwatchtext' : 'addedwatchtext',
+ $title->getPrefixedText()
+ )->title( $title )->parseAsBlock();
+ } else {
+ $res['error'] = $this->getErrorFromStatus( $status );
+ }
+
+ return $res;
+ }
+
+ /**
+ * Get a cached instance of an ApiPageSet object
+ * @return ApiPageSet
+ */
+ private function getPageSet() {
+ if ( $this->mPageSet === null ) {
+ $this->mPageSet = new ApiPageSet( $this );
+ }
+
+ return $this->mPageSet;
+ }
+
+ public function mustBePosted() {
+ return true;
+ }
+
+ public function isWriteMode() {
+ return true;
+ }
+
+ public function needsToken() {
+ return 'watch';
+ }
+
+ public function getAllowedParams( $flags = 0 ) {
+ $result = array(
+ 'id' => array(
+ ApiBase::PARAM_TYPE => 'integer',
+ ),
+ 'label' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ),
+ 'description' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ),
+ 'remove' => false,
+ 'deletecollection' => false,
+ 'continue' => array(
+ ApiBase::PARAM_HELP_MSG =>
'api-help-param-continue',
+ ),
+ );
+ if ( $flags ) {
+ $result += $this->getPageSet()->getFinalParams( $flags
);
+ }
+
+ return $result;
+ }
+
+ public function getHelpUrls() {
+ return '//www.mediawiki.org/wiki/Extension:Gather';
+ }
+
+
+ /**
+ * Temporary function to get data from a JSON blob stored on the user's
page
+ * @param User $user
+ * @return array
+ * @throws MWException
+ */
+ public static function loadManifest( $user ) {
+ $title = self::getStorageTitle( $user );
+ $page = WikiPage::factory( $title );
+ if ( $page->exists() ) {
+ $content = $page->getContent();
+ if ( method_exists( $content, 'getData' ) ) {
+ $status = $content->getData();
+ } else {
+ $status = FormatJson::parse(
$content->getNativeData() );
+ }
+ if ( !$status->isOK() ) {
+ throw new MWException( 'Internal error in ' .
__METHOD__ . ' loading ' . $title->getFullText() . ' : ' .
$status->getMessage() );
+ }
+ return $status->getValue();
+ } else {
+ // Page doesn't exist, return empty data
+ return array();
+ }
+ }
+
+ /**
+ * Temporary function to save data to the JSON blob stored on the
user's page
+ * @param User $user
+ * @param Array $manifest representation of all the user's existing
collections.
+ * @return Status
+ */
+ private function save( User $user, $manifest ) {
+ $title = self::getStorageTitle( $user );
+ $page = WikiPage::factory( $title );
+ $content = new JsonContent( FormatJson::encode( $manifest,
FormatJson::ALL_OK ) );
+ return $page->doEditContent( $content, 'ApiEditCollection.php'
);
+ }
+
+ /**
+ * Get formatted title of the page that contains the manifest
+ * @param User $user
+ * @return Title
+ */
+ private static function getStorageTitle( User $user ) {
+ $title = $user->getName() . '/GatherCollections.json';
+ return Title::makeTitleSafe( NS_USER, $title );
+ }
+
+ /**
+ * Returns representation of a new collection with the given id and
parameters.
+ * @param Integer $id
+ * @param Array $params
+ * @param User $user
+ * @return stdClass
+ */
+ private static function createCollection( $id, array $params, User
$user ) {
+ $collection = new stdClass();
+ $collection->id = $id;
+ $collection->owner = $user->getName();
+ $collection->title = $params['label'];
+ $collection->description = $params['description'];
+ $collection->public = true;
+ $collection->image = null;
+ $collection->items = array();
+ $collection->count = 0;
+ return $collection;
+ }
+
+ /**
+ * Retrieve the collection from the manifest of all the users existing
+ * collection using the given id, or null if not found.
+ * @param Array $manifest
+ * @param Integer $id
+ * @param User $user
+ * @return null|stdClass
+ */
+ public static function findCollection( &$manifest, $id, User $user ) {
+ // Find the collection with the given id.
+ foreach ( $manifest as $c ) {
+ if ( $c->id === $id ) {
+ return $c;
+ }
+ }
+ if ( $id === 0 ) {
+ // watchlist metadata should always exist
+ $params = array(
+ 'label' => wfMessage( 'mywatchlist' )->text(),
+ 'description' => '',
+ );
+ $c = self::createCollection( 0, $params, $user );
+ $manifest[] = $c;
+ return $c;
+ }
+ return null;
+ }
+}
--
To view, visit https://gerrit.wikimedia.org/r/194874
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Iaf6cc4553f2563fb2c9ffce2eac40eb13122253a
Gerrit-PatchSet: 5
Gerrit-Project: mediawiki/extensions/Gather
Gerrit-Branch: master
Gerrit-Owner: Yurik <[email protected]>
Gerrit-Reviewer: Jdlrobson <[email protected]>
Gerrit-Reviewer: Jhernandez <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: Robmoen <[email protected]>
Gerrit-Reviewer: Yurik <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits