r32821 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r32820‎ | r32821 | r32822 >
Date:18:45, 5 April 2008
Author:skizzerz
Status:old
Tags:
Comment:
*Attributing original author of EditSubpages upon request
*Committing unaltered WatchSubpages extension
Modified paths:
  • /trunk/extensions/EditSubpages/EditSubpages.php (modified) (history)
  • /trunk/extensions/WatchSubpages (added) (history)
  • /trunk/extensions/WatchSubpages/WatchSubpages.i18n.php (added) (history)
  • /trunk/extensions/WatchSubpages/WatchSubpages.php (added) (history)
  • /trunk/extensions/WatchSubpages/WatchSubpages_body.php (added) (history)

Diff [purge]

Index: trunk/extensions/EditSubpages/EditSubpages.php
@@ -15,7 +15,7 @@
1616 'description' => "Allows sysops to unlock a page and all subpages of that page
1717 for anonymous editing via [[MediaWiki:Unlockedpages]]",
1818 'descriptionmsg' => 'editsubpages-desc',
19 -'author' => "Ryan Schmidt",
 19+'author' => "<span class=\"plainlinks\">[http://strategywiki.org/wiki/User:Ryan Schmidt Ryan Schmidt] and [http://strategywiki.org/wiki/User:Prod Prod]</span>",
2020 'url' => "http://www.mediawiki.org/wiki/Extension:EditSubpages",
2121 'version' => "2.0",
2222 );
Index: trunk/extensions/WatchSubpages/WatchSubpages_body.php
@@ -0,0 +1,329 @@
 2+<?php
 3+#restrict to subpages
 4+class WatchSubpages extends SpecialPage
 5+{
 6+ function WatchSubpages() {
 7+ SpecialPage::SpecialPage( 'Watchsubpages', 'watchsubpages' );
 8+ }
 9+
 10+ function execute( $par ) {
 11+ global $wgRequest, $wgOut, $wgUser;
 12+
 13+ $namespace = $wgRequest->getInt( 'namespace' );
 14+ $guide = $wgRequest->getVal( 'guide' );
 15+ if ( isset( $guide ) ) {
 16+ $guidename = $guide;
 17+ } elseif ( isset( $par ) ) {
 18+ $guidename = $par;
 19+ }
 20+
 21+ $this->setHeaders();
 22+
 23+# $guidename = Title::newFromText( $guidename , NS_MAIN );
 24+
 25+ if ( wfReadOnly() ) {
 26+ $wgOut->readOnlyPage();
 27+ return;
 28+ }
 29+ if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal( 'token' ), 'watchsubpages' ) ) {
 30+ $titles = $this->extractTitles( $wgRequest->getArray( 'titles' ) );
 31+ $current = $this->getWatchlist( $wgUser );
 32+ $toWatch = array_diff( $titles, $current );
 33+ $this->watchTitles( $toWatch, $wgUser );
 34+ $wgUser->invalidateCache();
 35+ $wgOut->addHtml( 'The following has been added to your watchlist.' );
 36+ $this->showTitles( $toWatch, $wgOut, $wgUser->getSkin() );
 37+ }
 38+ $this->showForm( $wgOut, $wgUser, $namespace, trim($guidename, "/") );
 39+ }
 40+
 41+ /**
 42+ * Extract a list of titles from a blob of text, returning
 43+ * (prefixed) strings; unwatchable titles are ignored
 44+ *
 45+ * @param mixed $list
 46+ * @return array
 47+ */
 48+ private function extractTitles( $list ) {
 49+ $titles = array();
 50+ if( !is_array( $list ) ) {
 51+ $list = explode( "\n", trim( $list ) );
 52+ if( !is_array( $list ) )
 53+ return array();
 54+ }
 55+ foreach( $list as $text ) {
 56+ $text = trim( $text );
 57+ if( strlen( $text ) > 0 ) {
 58+ $title = Title::newFromText( $text );
 59+ if( $title instanceof Title && $title->isWatchable() )
 60+ $titles[] = $title->getPrefixedText();
 61+ }
 62+ }
 63+ return array_unique( $titles );
 64+ }
 65+
 66+ /**
 67+ * Prepare a list of titles on a user's watchlist (excluding talk pages)
 68+ * and return an array of (prefixed) strings
 69+ *
 70+ * @param User $user
 71+ * @return array
 72+ */
 73+ private function getWatchlist( $user ) {
 74+ $list = array();
 75+ $dbr = wfGetDB( DB_MASTER );
 76+ $res = $dbr->select(
 77+ 'watchlist',
 78+ '*',
 79+ array(
 80+ 'wl_user' => $user->getId(),
 81+ ),
 82+ __METHOD__
 83+ );
 84+ if( $dbr->numRows( $res ) > 0 ) {
 85+ while( $row = $dbr->fetchObject( $res ) ) {
 86+ $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title );
 87+ if( $title instanceof Title && !$title->isTalkPage() )
 88+ $list[] = $title->getPrefixedText();
 89+ }
 90+ $res->free();
 91+ }
 92+ return $list;
 93+ }
 94+
 95+ /**
 96+ * Add a list of titles to a user's watchlist
 97+ *
 98+ * $titles can be an array of strings or Title objects; the former
 99+ * is preferred, since Titles are very memory-heavy
 100+ *
 101+ * @param array $titles An array of strings, or Title objects
 102+ * @param User $user
 103+ */
 104+ private function watchTitles( $titles, $user ) {
 105+ $dbw = wfGetDB( DB_MASTER );
 106+ $rows = array();
 107+ foreach( $titles as $title ) {
 108+ if( !$title instanceof Title )
 109+ $title = Title::newFromText( $title );
 110+ if( $title instanceof Title ) {
 111+ $rows[] = array(
 112+ 'wl_user' => $user->getId(),
 113+ 'wl_namespace' => ( $title->getNamespace() & ~1 ),
 114+ 'wl_title' => $title->getDBkey(),
 115+ 'wl_notificationtimestamp' => null,
 116+ );
 117+ $rows[] = array(
 118+ 'wl_user' => $user->getId(),
 119+ 'wl_namespace' => ( $title->getNamespace() | 1 ),
 120+ 'wl_title' => $title->getDBkey(),
 121+ 'wl_notificationtimestamp' => null,
 122+ );
 123+ }
 124+ }
 125+ $dbw->insert( 'watchlist', $rows, __METHOD__, 'IGNORE' );
 126+ }
 127+
 128+ /**
 129+ * Print out a list of linked titles
 130+ *
 131+ * $titles can be an array of strings or Title objects; the former
 132+ * is preferred, since Titles are very memory-heavy
 133+ *
 134+ * @param array $titles An array of strings, or Title objects
 135+ * @param OutputPage $output
 136+ * @param Skin $skin
 137+ */
 138+ private function showTitles( $titles, $output, $skin ) {
 139+ $talk = wfMsgHtml( 'talkpagelinktext' );
 140+ // Do a batch existence check
 141+ $batch = new LinkBatch();
 142+ foreach( $titles as $title ) {
 143+ if( !$title instanceof Title )
 144+ $title = Title::newFromText( $title );
 145+ if( $title instanceof Title ) {
 146+ $batch->addObj( $title );
 147+ $batch->addObj( $title->getTalkPage() );
 148+ }
 149+ }
 150+ $batch->execute();
 151+ // Print out the list
 152+ $output->addHtml( "<ul>\n" );
 153+ foreach( $titles as $title ) {
 154+ if( !$title instanceof Title )
 155+ $title = Title::newFromText( $title );
 156+ if( $title instanceof Title ) {
 157+ $output->addHtml( "<li>" . $skin->makeLinkObj( $title )
 158+ . ' (' . $skin->makeLinkObj( $title->getTalkPage(), $talk ) . ")</li>\n" );
 159+ }
 160+ }
 161+ $output->addHtml( "</ul>\n" );
 162+ }
 163+
 164+ /**
 165+ * Show the standard watchlist editing form
 166+ *
 167+ * @param OutputPage $output
 168+ * @param User $user
 169+ * @param GuideName $guide
 170+ */
 171+ private function showForm( $output, $user, $namespace, $guide ) {
 172+ global $wgScript, $wgContLang;
 173+
 174+ $self = SpecialPage::getTitleFor( 'Watchsubpages' );
 175+ # Input boxes at the top
 176+ $form .= Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
 177+ $form .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) );
 178+ $form .= Xml::hidden( 'title', $self->getPrefixedText() );
 179+ $form .= Xml::openElement( 'table', array( 'id' => 'nsselect', 'class' => 'allpages' ) );
 180+ $form .= "<tr>
 181+ <td>" .
 182+ Xml::label( 'Guide name:', 'nsfrom' ) .
 183+ "</td>
 184+ <td>" .
 185+ Xml::input( 'guide', 20, htmlspecialchars ( $guide . '/' ), array( 'id' => 'nsfrom' ) ) .
 186+ "</td>
 187+ </tr>
 188+ <tr>
 189+ <td>" .
 190+ Xml::label( wfMsg( 'namespace' ), 'namespace' ) .
 191+ "</td>
 192+ <td>" .
 193+ Xml::namespaceSelector( $namespace, null ) .
 194+ Xml::submitButton( wfMsg( 'allpagessubmit' ) ) .
 195+ "</td>
 196+ </tr>";
 197+ $form .= Xml::closeElement( 'table' );
 198+ $form .= Xml::closeElement( 'form' );
 199+ $form .= Xml::closeElement( 'div' );
 200+
 201+ $form .= Xml::openElement( 'form', array( 'method' => 'post',
 202+ 'action' => $self->getLocalUrl( 'guide=' . $guide ) ) );
 203+ $form .= Xml::hidden( 'token', $user->editToken( 'watchsubpages' ) );
 204+ $form .= '<fieldset><legend>Add titles to watchlist</legend>';
 205+ $form .= 'Select the titles to add to your watchlist below. To add a title, check the box next to it, and click Add Titles.<br /><br />When checking or unchecking multiple titles, holding the shift key allows you to select consecutive checkboxes by clicking each end of the range to be checked.<br /><br />';
 206+ foreach( $this->getPrefixlistInfo( $namespace, $guide . '/' ) as $namespace => $pages ) {
 207+ $form .= '<h2>' . $this->getNamespaceHeading( $namespace ) . '</h2>';
 208+ $form .= '<ul>';
 209+ foreach( $pages as $dbkey => $redirect ) {
 210+ $title = Title::makeTitleSafe( $namespace, $dbkey );
 211+ $form .= $this->buildLine( $title, $redirect, $user->getSkin() );
 212+ }
 213+ $form .= '</ul>';
 214+ }
 215+ $form .= '<p>' . Xml::submitButton( 'Add Titles' ) . '</p>';
 216+ $form .= '</fieldset></form>';
 217+ $output->addHtml( $form );
 218+ }
 219+
 220+ /**
 221+ * Get a list of titles that are subpages of a given title, excluding talk pages,
 222+ * and return as a two-dimensional array with namespace, title and
 223+ * redirect status
 224+ *
 225+ * @param GuideName $guide
 226+ * @return array
 227+ */
 228+ private function getPrefixlistInfo( $namespace = NS_MAIN, $guide ) {
 229+ $prefixList = $this->getNamespaceKeyAndText($namespace, $guide);
 230+
 231+ $titles = array();
 232+ list( $prefixNS, $prefixKey, $guide ) = $prefixList;
 233+ $dbr = wfGetDB( DB_MASTER );
 234+ $res = $dbr->select( 'page',
 235+ array( 'page_namespace', 'page_title', 'page_id', 'page_is_redirect' ),
 236+ array(
 237+ 'page_namespace' => $prefixNS,
 238+ 'page_title LIKE \'' . $dbr->escapeLike( $prefixKey ) .'%\'',
 239+ ),
 240+ __METHOD__,
 241+ array(
 242+ 'ORDER BY' => 'page_title',
 243+ 'USE INDEX' => 'name_title',
 244+ )
 245+ );
 246+ if( $res && $dbr->numRows( $res ) > 0 ) {
 247+ $cache = LinkCache::singleton();
 248+ while( $row = $dbr->fetchObject( $res ) ) {
 249+ $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
 250+ if( $title instanceof Title ) {
 251+ // Update the link cache while we're at it
 252+ if( $row->page_id ) {
 253+ $cache->addGoodLinkObj( $row->page_id, $title );
 254+ } else {
 255+ $cache->addBadLinkObj( $title );
 256+ }
 257+ // Ignore non-talk
 258+ if( !$title->isTalkPage() )
 259+ $titles[$row->page_namespace][$row->page_title] = $row->page_is_redirect;
 260+ }
 261+ }
 262+ }
 263+ return $titles;
 264+ }
 265+
 266+ /**
 267+ * Get the correct "heading" for a namespace
 268+ *
 269+ * @param int $namespace
 270+ * @return string
 271+ */
 272+ private function getNamespaceHeading( $namespace ) {
 273+ return $namespace == NS_MAIN
 274+ ? wfMsgHtml( 'blanknamespace' )
 275+ : htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( $namespace ) );
 276+ }
 277+
 278+ /**
 279+ * @param int $ns the namespace of the article
 280+ * @param string $text the name of the article
 281+ * @return array( int namespace, string dbkey, string pagename ) or NULL on error
 282+ * @static (sort of)
 283+ * @access private
 284+ */
 285+ function getNamespaceKeyAndText ($ns, $text) {
 286+ if ( $text == '' )
 287+ return array( $ns, '', '' ); # shortcut for common case
 288+
 289+ $t = Title::makeTitleSafe($ns, $text);
 290+ if ( $t && $t->isLocal() ) {
 291+ return array( $t->getNamespace(), $t->getDBkey(), $t->getText() );
 292+ } else if ( $t ) {
 293+ return NULL;
 294+ }
 295+
 296+ # try again, in case the problem was an empty pagename
 297+ $text = preg_replace('/(#|$)/', 'X$1', $text);
 298+ $t = Title::makeTitleSafe($ns, $text);
 299+ if ( $t && $t->isLocal() ) {
 300+ return array( $t->getNamespace(), '', '' );
 301+ } else {
 302+ return NULL;
 303+ }
 304+ }
 305+
 306+ /**
 307+ * Build a single list item containing a check box selecting a title
 308+ * and a link to that title, with various additional bits
 309+ *
 310+ * @param Title $title
 311+ * @param bool $redirect
 312+ * @param Skin $skin
 313+ * @return string
 314+ */
 315+ private function buildLine( $title, $redirect, $skin ) {
 316+ $link = $skin->makeLinkObj( $title );
 317+ if( $redirect )
 318+ $link = '<span class="watchlistredir">' . $link . '</span>';
 319+ $tools[] = $skin->makeLinkObj( $title->getTalkPage(), wfMsgHtml( 'talkpagelinktext' ) );
 320+ if( $title->exists() ) {
 321+ $tools[] = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'history_short' ), 'action=history' );
 322+ }
 323+ if( $title->getNamespace() == NS_USER && !$title->isSubpage() ) {
 324+ $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Contributions', $title->getText() ), wfMsgHtml( 'contributions' ) );
 325+ }
 326+ return '<li>'
 327+ . Xml::check( 'titles[]', true, array( 'value' => $title->getPrefixedText() ) )
 328+ . $link . ' (' . implode( ' | ', $tools ) . ')' . '</li>';
 329+ }
 330+}
\ No newline at end of file
Property changes on: trunk/extensions/WatchSubpages/WatchSubpages_body.php
___________________________________________________________________
Added: svn:eol-style
1331 + native
Index: trunk/extensions/WatchSubpages/WatchSubpages.i18n.php
@@ -0,0 +1,6 @@
 2+<?php
 3+$allMessages = array(
 4+ 'en' => array(
 5+ 'watchsubpages' => 'Watch Guide Subpages'
 6+ )
 7+);
\ No newline at end of file
Property changes on: trunk/extensions/WatchSubpages/WatchSubpages.i18n.php
___________________________________________________________________
Added: svn:eol-style
18 + native
Index: trunk/extensions/WatchSubpages/WatchSubpages.php
@@ -0,0 +1,19 @@
 2+<?php
 3+# Not a valid entry point, skip unless MEDIAWIKI is defined
 4+if (!defined('MEDIAWIKI')) {
 5+ echo <<<EOT
 6+To install my extension, put the following line in LocalSettings.php:
 7+require_once( "$IP/extensions/WatchSubpages/WatchSubpages.php" );
 8+EOT;
 9+ exit( 1 );
 10+}
 11+
 12+$wgExtensionCredits['specialpage'][] = array(
 13+ 'author' => '[http://www.strategywiki.org/wiki/User:Prod User:Prod]',
 14+ 'name' => 'Watch Guide Subpages',
 15+ 'url' => 'http://www.strategywiki.org/wiki/User:Prod',
 16+ 'description' => 'Quickly add all subpages of a guide to the users watchlist'
 17+);
 18+
 19+$wgAutoloadClasses['WatchSubpages'] = dirname(__FILE__) . '/WatchSubpages_body.php';
 20+$wgSpecialPages['WatchSubpages'] = 'WatchSubpages';
\ No newline at end of file
Property changes on: trunk/extensions/WatchSubpages/WatchSubpages.php
___________________________________________________________________
Added: svn:eol-style
121 + native

Status & tagging log