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

Reply via email to