/**
  * Render the special page
  *
  * @param string $subpage
  */
 public function execute($subpage)
 {
     $out = $this->getOutput();
     $out->addModules(array('ext.gather.special', 'ext.gather.moderation'));
     $out->addModuleStyles(array('mobile.special.pagefeed.styles', 'mediawiki.ui.anchor', 'mediawiki.ui.icon', 'ext.gather.icons', 'ext.gather.styles', 'ext.gather.menu.icon'));
     if (!isset($subpage) || $subpage === '') {
         // Root subpage. For anons will be active collections, for logged in,
         // their own.
         $user = $this->getUser();
         if (!$user->isAnon()) {
             $page = SpecialPage::getTitleFor('Gather')->getSubPage('by')->getSubPage($user->getName());
         } else {
             $page = SpecialPage::getTitleFor('Gather')->getSubPage('all')->getSubPage('recent');
         }
         $out->redirect($page->getLocalUrl());
     } elseif ($subpage === 'by' || $subpage === 'by/') {
         // User owned collections.
         // For listing own lists, you need to be logged in
         $this->requireLogin('gather-anon-view-lists');
         $user = $this->getUser();
         $url = SpecialPage::getTitleFor('Gather')->getSubPage('by')->getSubPage($user->getName())->getLocalUrl();
         $out->redirect($url);
     } elseif (preg_match('/^explore\\/(?<key>.*)$/', $subpage, $matches)) {
         // Special explorable collections
         // /explore/:type & /explore/
         $key = $matches['key'];
         if ($key) {
             // /explore/:type -> Show the collection of type :type
             if (isset($this->specialCollections[$key])) {
                 $args = $this->specialCollections[$key];
                 $c = new models\Collection(0, null, $args['title'], $args['description']);
                 $c = models\Collection::newFromApi($c, $args['params'], $args['limit'], isset($args['continue']) ? $args['continue'] : array());
                 $c->setUrl(SpecialPage::getTitleFor('Gather')->getSubpage('explore')->getSubpage($key)->getLocalUrl());
                 $this->renderCollection($c);
             } else {
                 $this->renderError(new views\NotFound());
             }
         } else {
             // /explore/ -> List all explorable collections
             $collectionList = new models\CollectionsList();
             foreach ($this->specialCollections as $key => $definition) {
                 $ci = new models\CollectionInfo(null, null, $definition['title'], $definition['description']);
                 $ci->setCount('∞');
                 $ci->setUrl(SpecialPage::getTitleFor('Gather')->getSubpage('explore')->getSubpage($key)->getLocalUrl());
                 $collectionList->add($ci);
             }
             $this->renderCollectionsList($collectionList);
         }
     } elseif (preg_match('/^by\\/(?<user>[^\\/]+)\\/?$/', $subpage, $matches)) {
         // User's collections
         // /by/:user = /by/:user/
         $user = User::newFromName($matches['user']);
         if (!($user && $user->getId())) {
             // Invalid user
             $this->renderError(new views\NotFound());
         } else {
             $this->renderUserCollectionsList($user);
         }
     } elseif (preg_match('/^id\\/(?<id>\\d+)(\\/.*$|$)/', $subpage, $matches)) {
         // Collection page
         // /id/:id
         $id = (int) $matches['id'];
         $this->renderUserCollection($id);
     } elseif (preg_match('/^by\\/(?<user>[^\\/]+)\\/(?<id>\\d+)$/', $subpage, $matches)) {
         // Collection page
         // /by/:user/:id -- Deprecated -- Redirects to /id/:id
         $id = (int) $matches['id'];
         $this->getOutput()->redirect(SpecialPage::getTitleFor('Gather')->getSubPage('id')->getSubPage($id)->getLocalURL());
     } elseif (preg_match('/^all(\\/(?<cond>[^\\/]+))?\\/?$/', $subpage, $matches)) {
         // All collections. Public or hidden or pending review
         // /all = /all/ = /all/public = /all/public/
         // /all/hidden = /all/hidden/
         // /all/recent = /all/recent/
         // /all/review = /all/review/
         $apiParams = array();
         $displayAsTable = true;
         // Mode defaults to public
         $allowedModes = array('public', 'hidden', 'recent', 'review');
         if (!isset($matches['cond'])) {
             $mode = 'public';
         } elseif (in_array($matches['cond'], $allowedModes)) {
             $mode = $matches['cond'];
         } else {
             $mode = 'error';
         }
         $originalMode = $mode;
         if ($mode === 'recent') {
             // Fancy list of collections with a certain amount of items.
             $displayAsTable = false;
             $apiParams = array('lstminitems' => 4);
             // Active is a concrete view of public with different params.
             $mode = 'public';
         } elseif ($mode === 'hidden' || $mode === 'review') {
             // Table list of hidden collections.
             // If user doesn't have permissions, bail out.
             if (!$this->canHideLists()) {
                 $view = new views\NotFound();
                 $this->renderError($view);
                 return;
             }
             $out->addSubtitle($this->getSubTitle());
         } elseif ($mode === 'public') {
             // Table list of public collections.
             // If the user has permissions to hide add a link to the /all/hidden
             // view for switching there.
             if ($this->canHideLists()) {
                 $out->addSubtitle($this->getSubTitle(true));
             }
         } else {
             $this->renderError(new views\NotFound());
             return;
         }
         $req = $this->getRequest();
         $apiParams = array_merge($apiParams, $req->getValues());
         $cList = models\CollectionsList::newFromApi(null, $mode, false, $apiParams, $mode, 100);
         $cList->setMode($originalMode);
         if ($displayAsTable) {
             $this->renderRows($cList, $mode === 'hidden' ? 'show' : 'hide');
         } else {
             $this->renderTabs(0);
             $this->renderCollectionsList($cList);
         }
     } else {
         // Unknown subpage
         $this->renderError(new views\NotFound());
     }
 }
 /**
  * @param CollectionInfo $info
  * @return models\Collection
  */
 public static function newFromCollectionInfo($info)
 {
     return new Collection($info->getId(), $info->getOwner(), $info->getTitle(), $info->getDescription(), $info->isPublic(), $info->getFile());
 }
 /**
  * Adds a item to the collection.
  * If the collection to add is private, and this collection list does not include
  * private items, the collection won't be added
  * @param CollectionInfo $collection
  */
 public function add(CollectionInfo $collection)
 {
     if ($this->includePrivate || !$this->includePrivate && $collection->isPublic()) {
         $this->collections[] = $collection;
     }
 }
 /**
  * Renders a html link for a collection page
  * @param models\CollectionInfo $collection
  * @return string
  */
 private function collectionLink($collection)
 {
     return Html::element('a', array('href' => $collection->getUrl()), $collection->getTitle());
 }
 /**
  * Generate UserPageCollectionsList from api result
  * @param User $user collection list owner (currently ignored)
  * @param boolean [$includePrivate] if the list should show private collections or not
  * @param string|boolean [$memberTitle] title of member to check for
  * @param array [$params] generate collection list with additional api parameters
  * @param string [$mode] listing mode (public/private/review)
  * @param integer [$limit] of number of collections to show
  * @return models\CollectionsList List of collections.
  */
 public static function newFromApi($user = null, $includePrivate = false, $memberTitle = false, $params = array(), $mode = null, $limit = 50)
 {
     $collectionsList = new CollectionsList($user, $includePrivate);
     $query = array_merge($params, array('action' => 'query', 'list' => 'lists', 'lstprop' => 'label|description|public|image|count|updated|owner', 'lstlimit' => $limit, 'continue' => ''));
     if ($memberTitle) {
         $query['lsttitle'] = $memberTitle;
     }
     if ($user) {
         $query['lstowner'] = $user->getName();
     }
     if ($mode) {
         $query['lstmode'] = $mode === 'review' ? $mode : "all{$mode}";
     } else {
         $mode = '';
     }
     $api = new ApiMain(new FauxRequest($query));
     $api->execute();
     $data = $api->getResult()->getResultData(null, array('Strip' => 'all'));
     if (isset($data['query']['lists'])) {
         $lists = $data['query']['lists'];
         foreach ($lists as $list) {
             $public = $list['perm'] === 'public';
             if ($public || $includePrivate) {
                 $image = isset($list['image']) ? wfFindFile($list['image']) : false;
                 $info = new models\CollectionInfo($list['id'], User::newFromName($list['owner']), $list['label'], $list['description'], $public, $image);
                 $info->setCount($list['count']);
                 $info->setUpdated($list['updated']);
                 if ($list['perm'] === 'hidden') {
                     $info->setHidden();
                 }
                 if ($memberTitle) {
                     $info->setMember($memberTitle, $list['title']);
                 }
                 $collectionsList->add($info);
             }
         }
     }
     if (isset($data['continue'])) {
         $collectionsList->setContinueQueryString($data['continue'], $mode);
     }
     return $collectionsList;
 }