Index: trunk/extensions/ExportWatchlist/LICENSE |
— | — | @@ -0,0 +1,27 @@ |
| 2 | +Copyright © 2007 Rob Church. |
| 3 | +All rights reserved. |
| 4 | + |
| 5 | +Redistribution and use in source and binary forms, with or without |
| 6 | +modification, are permitted provided that the following conditions |
| 7 | +are met: |
| 8 | + |
| 9 | + 1. Redistributions of source code must retain the above copyright |
| 10 | + notice, this list of conditions and the following disclaimer. |
| 11 | + |
| 12 | + 2. Redistributions in binary form must reproduce the above |
| 13 | + copyright notice, this list of conditions and the following |
| 14 | + disclaimer in the documentation and/or other materials provided |
| 15 | + with the distribution. |
| 16 | + |
| 17 | +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 18 | +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 19 | +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 20 | +DISCLAIMED. |
| 21 | + |
| 22 | +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 23 | +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| 24 | +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| 25 | +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 26 | +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
| 27 | +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
| 28 | +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
\ No newline at end of file |
Property changes on: trunk/extensions/ExportWatchlist/LICENSE |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 29 | + native |
Index: trunk/extensions/ExportWatchlist/SpecialImportWatchlist.php |
— | — | @@ -0,0 +1,147 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Special page bulk-adds items from a list to the |
| 6 | + * current user's watchlist |
| 7 | + * |
| 8 | + * @addtogroup Extensions |
| 9 | + * @author Rob Church <robchur@gmail.com> |
| 10 | + */ |
| 11 | +class SpecialImportWatchlist extends SpecialPage { |
| 12 | + |
| 13 | + /** |
| 14 | + * Constructor |
| 15 | + */ |
| 16 | + public function __construct() { |
| 17 | + parent::__construct( 'ImportWatchlist' ); |
| 18 | + } |
| 19 | + |
| 20 | + /** |
| 21 | + * Main execution point |
| 22 | + * |
| 23 | + * @param mixed $par Parameter passed to the page |
| 24 | + */ |
| 25 | + public function execute( $par = false ) { |
| 26 | + global $wgUser, $wgRequest, $wgOut, $wgLang; |
| 27 | + $this->setHeaders(); |
| 28 | + |
| 29 | + if( wfReadOnly() ) { |
| 30 | + $wgOut->readOnlyPage(); |
| 31 | + return; |
| 32 | + } |
| 33 | + |
| 34 | + $wgOut->addHtml( wfMsgExt( 'importwatchlist-header', 'parse' ) ); |
| 35 | + $wgOut->addHtml( $this->buildForm() ); |
| 36 | + |
| 37 | + if( $wgRequest->wasPosted() ) { |
| 38 | + $list = $this->extractTitles( $wgRequest->getText( 'titles' ) ); |
| 39 | + if( ( $count = count( $list ) ) > 0 ) { |
| 40 | + $count = $wgLang->formatNum( $count ); |
| 41 | + $this->bulkWatch( $wgUser, $list ); |
| 42 | + $wgOut->addHtml( wfMsgExt( 'importwatchlist-success', 'parse', $count ) ); |
| 43 | + $this->showTitles( $wgOut, $wgUser->getSkin(), $list ); |
| 44 | + } |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + /** |
| 49 | + * Explode a blob of text into a list of titles |
| 50 | + * |
| 51 | + * @param string $text List of titles |
| 52 | + * @return array |
| 53 | + */ |
| 54 | + private function extractTitles( $text ) { |
| 55 | + $titles = explode( "\n", $text ); |
| 56 | + for( $i = 0; $i < count( $titles ); $i++ ) { |
| 57 | + $titles[$i] = Title::newFromText( $titles[$i] ); |
| 58 | + if( !$titles[$i] instanceof Title ) |
| 59 | + unset( $titles[$i] ); |
| 60 | + } |
| 61 | + return $titles; |
| 62 | + } |
| 63 | + |
| 64 | + /** |
| 65 | + * Bulk-insert titles into a specified user's watchlist |
| 66 | + * |
| 67 | + * @param User $user User to insert for |
| 68 | + * @param array $titles Titles to watch |
| 69 | + */ |
| 70 | + private function bulkWatch( $user, $titles ) { |
| 71 | + $dbw = wfGetDB( DB_MASTER ); |
| 72 | + $values = array(); |
| 73 | + foreach( $titles as $title ) { |
| 74 | + $values[] = array( |
| 75 | + 'wl_user' => $user->getId(), |
| 76 | + 'wl_namespace' => ( $title->getNamespace() & ~1 ), |
| 77 | + 'wl_title' => $title->getDBkey(), |
| 78 | + 'wl_notificationtimestamp' => null, |
| 79 | + ); |
| 80 | + $values[] = array( |
| 81 | + 'wl_user' => $user->getId(), |
| 82 | + 'wl_namespace' => ( $title->getNamespace() | 1 ), |
| 83 | + 'wl_title' => $title->getDBkey(), |
| 84 | + 'wl_notificationtimestamp' => null, |
| 85 | + ); |
| 86 | + } |
| 87 | + $dbw->insert( |
| 88 | + 'watchlist', |
| 89 | + $values, |
| 90 | + __METHOD__, |
| 91 | + 'IGNORE' |
| 92 | + ); |
| 93 | + $user->invalidateCache(); |
| 94 | + } |
| 95 | + |
| 96 | + /** |
| 97 | + * Build the title input form |
| 98 | + * |
| 99 | + * @return string |
| 100 | + */ |
| 101 | + private function buildForm() { |
| 102 | + $form = Xml::openElement( 'form', array( 'method' => 'post', |
| 103 | + 'action' => $this->getTitle()->getLocalUrl() ) ); |
| 104 | + $form .= '<fieldset><legend>' . wfMsgHtml( 'importwatchlist-legend' ) . '</legend>'; |
| 105 | + $form .= Xml::label( wfMsg( 'importwatchlist-titles' ), 'titles' ) . '<br />'; |
| 106 | + $form .= Xml::openElement( 'textarea', array( 'id' => 'titles', 'name' => 'titles', |
| 107 | + 'rows' => 8, 'cols' => 80 ) ) . Xml::closeElement( 'textarea' ); |
| 108 | + $form .= '<p>' . Xml::submitButton( wfMsg( 'importwatchlist-submit' ) ) . '</p>'; |
| 109 | + $form .= '</fieldset></form>'; |
| 110 | + return $form; |
| 111 | + } |
| 112 | + |
| 113 | + /** |
| 114 | + * Print out a list of titles |
| 115 | + * |
| 116 | + * @param OutputPage $out |
| 117 | + * @param Skin $skin |
| 118 | + * @param array $titles |
| 119 | + */ |
| 120 | + private function showTitles( $out, $skin, $titles ) { |
| 121 | + $batch = new LinkBatch(); |
| 122 | + foreach( $titles as $title ) |
| 123 | + $batch->addObj( $title ); |
| 124 | + $batch->execute(); |
| 125 | + $out->addHtml( '<ul>' ); |
| 126 | + foreach( $titles as $title ) |
| 127 | + $out->addHtml( '<li>' . $skin->makeLinkObj( $title ) . '</li>' ); |
| 128 | + $out->addHtml( '</ul>' ); |
| 129 | + } |
| 130 | + |
| 131 | + /** |
| 132 | + * Show a "please log in" page |
| 133 | + * |
| 134 | + * @param OutputPage $out |
| 135 | + * @param User $user |
| 136 | + */ |
| 137 | + private function showLoginPage( $out, $user ) { |
| 138 | + $link = $user->getSkin()->makeKnownLinkObj( |
| 139 | + SpecialPage::getTitleFor( 'Userlogin' ), |
| 140 | + wfMsgHtml( 'exportwatchlist-login-link' ), |
| 141 | + 'returnto=' . $this->getTitle()->getPrefixedUrl() |
| 142 | + ); |
| 143 | + $out->addHtml( wfMsgHtml( 'importwatchlist-login', $link ) ); |
| 144 | + } |
| 145 | + |
| 146 | +} |
| 147 | + |
| 148 | +?> |
\ No newline at end of file |
Property changes on: trunk/extensions/ExportWatchlist/SpecialImportWatchlist.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 149 | + native |
Index: trunk/extensions/ExportWatchlist/ExportWatchlist.i18n.php |
— | — | @@ -0,0 +1,46 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Messages file for ExportWatchlist extension |
| 6 | + * |
| 7 | + * @addtogroup Extensions |
| 8 | + * @author Rob Church <robchur@gmail.com> |
| 9 | + */ |
| 10 | + |
| 11 | +/** |
| 12 | + * Prepare extension messages |
| 13 | + * |
| 14 | + * @return array |
| 15 | + */ |
| 16 | +function efExportWatchlistMessages() { |
| 17 | + $messages = array( |
| 18 | + |
| 19 | +/** |
| 20 | + * English |
| 21 | + */ |
| 22 | +'en' => array( |
| 23 | + 'exportwatchlist' => 'Export watchlist', |
| 24 | + 'exportwatchlist-header' => 'Use this page to export [[Special:Watchlist|your watchlist]] |
| 25 | + to a list of titles so you can move it to another account or share it with other users.', |
| 26 | + 'exportwatchlist-login' => 'You need to have an account and be $1 to export a watchlist.', |
| 27 | + 'exportwatchlist-login-link' => 'logged in', |
| 28 | + 'exportwatchlist-legend' => 'Export watchlist', |
| 29 | + 'exportwatchlist-namespace' => 'Namespace:', |
| 30 | + 'exportwatchlist-submit' => 'Export', |
| 31 | + 'exportwatchlist-none' => 'Your watchlist contains no pages.', |
| 32 | + 'exportwatchlist-none-ns' => 'Your watchlist contains no pages in this namespace.', |
| 33 | + 'importwatchlist' => 'Import watchlist', |
| 34 | + 'importwatchlist-header' => 'Use this page to bulk-add a list of titles to |
| 35 | + [[Special:Watchlist|your watchlist]].', |
| 36 | + 'importwatchlist-login' => 'You need to have an account and be $1 to import a watchlist.', |
| 37 | + 'importwatchlist-legend' => 'Import titles into watchlist', |
| 38 | + 'importwatchlist-titles' => 'Enter a list of titles below, one per line:', |
| 39 | + 'importwatchlist-submit' => 'Import', |
| 40 | + 'importwatchlist-success' => 'Imported $1 item(s) into the watchlist:', |
| 41 | +), |
| 42 | + |
| 43 | + ); |
| 44 | + return $messages; |
| 45 | +} |
| 46 | + |
| 47 | +?> |
\ No newline at end of file |
Property changes on: trunk/extensions/ExportWatchlist/ExportWatchlist.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 48 | + native |
Index: trunk/extensions/ExportWatchlist/ExportWatchlist.php |
— | — | @@ -0,0 +1,40 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Extension allows users to export their watchlist into |
| 6 | + * list format and import from a list into their watchlist |
| 7 | + * |
| 8 | + * @addtogroup Extensions |
| 9 | + * @author Rob Church <robchur@gmail.com> |
| 10 | + */ |
| 11 | +if( defined( 'MEDIAWIKI' ) ) { |
| 12 | + |
| 13 | + $wgExtensionCredits['specialpage'][] = array( |
| 14 | + 'name' => 'Export Watchlist', |
| 15 | + 'author' => 'Rob Church', |
| 16 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:Export_Watchlist', |
| 17 | + 'description' => 'Allows users to export/import watchlists as XML files', |
| 18 | + ); |
| 19 | + |
| 20 | + $wgAutoloadClasses['SpecialExportWatchlist'] = dirname( __FILE__ ) . '/SpecialExportWatchlist.php'; |
| 21 | + $wgAutoloadClasses['SpecialImportWatchlist'] = dirname( __FILE__ ) . '/SpecialImportWatchlist.php'; |
| 22 | + $wgSpecialPages['ExportWatchlist'] = 'SpecialExportWatchlist'; |
| 23 | + $wgSpecialPages['ImportWatchlist'] = 'SpecialImportWatchlist'; |
| 24 | + $wgExtensionFunctions[] = 'efExportWatchlist'; |
| 25 | + |
| 26 | + /** |
| 27 | + * Register messages with the message cache |
| 28 | + */ |
| 29 | + function efExportWatchlist() { |
| 30 | + global $wgMessageCache; |
| 31 | + require_once( dirname( __FILE__ ) . '/ExportWatchlist.i18n.php' ); |
| 32 | + foreach( efExportWatchlistMessages() as $lang => $messages ) |
| 33 | + $wgMessageCache->addMessages( $messages, $lang ); |
| 34 | + } |
| 35 | + |
| 36 | +} else { |
| 37 | + echo( "This file is an extension to the MediaWiki software and cannot be used standlone.\n" ); |
| 38 | + exit( 1 ); |
| 39 | +} |
| 40 | + |
| 41 | +?> |
\ No newline at end of file |
Property changes on: trunk/extensions/ExportWatchlist/ExportWatchlist.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 42 | + native |
Index: trunk/extensions/ExportWatchlist/SpecialExportWatchlist.php |
— | — | @@ -0,0 +1,140 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Special page exports the current user's watchlist |
| 6 | + * into list format |
| 7 | + * |
| 8 | + * @addtogroup Extensions |
| 9 | + * @author Rob Church <robchur@gmail.com> |
| 10 | + */ |
| 11 | +class SpecialExportWatchlist extends SpecialPage { |
| 12 | + |
| 13 | + /** |
| 14 | + * Constructor |
| 15 | + */ |
| 16 | + public function __construct() { |
| 17 | + parent::__construct( 'ExportWatchlist' ); |
| 18 | + } |
| 19 | + |
| 20 | + /** |
| 21 | + * Main execution point |
| 22 | + * |
| 23 | + * @param mixed $par Parameter passed to the page |
| 24 | + */ |
| 25 | + public function execute( $par = false ) { |
| 26 | + global $wgUser, $wgRequest, $wgOut; |
| 27 | + $this->setHeaders(); |
| 28 | + $namespace = $wgRequest->getIntOrNull( 'namespace' ); |
| 29 | + |
| 30 | + if( !$wgUser->isLoggedIn() ) { |
| 31 | + $this->showLoginPage( $wgOut, $wgUser ); |
| 32 | + return; |
| 33 | + } |
| 34 | + |
| 35 | + $wgOut->addHtml( wfMsgExt( 'exportwatchlist-header', 'parse' ) ); |
| 36 | + $wgOut->addHtml( $this->buildForm( $namespace ) ); |
| 37 | + |
| 38 | + if( $wgRequest->getCheck( 'export' ) ) { |
| 39 | + $list = $this->getWatchlist( $wgUser, $namespace ); |
| 40 | + if( $list->numRows() > 0 ) { |
| 41 | + $wgOut->addHtml( '<pre>' ); |
| 42 | + while( $row = $list->fetchObject() ) { |
| 43 | + $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title ); |
| 44 | + if( $title instanceof Title ) { |
| 45 | + if( !$title->isTalkPage() ) |
| 46 | + $wgOut->addHtml( htmlspecialchars( $title->getPrefixedText() ) . "\n" ); |
| 47 | + } else { |
| 48 | + $text = htmlspecialchars( $row->wl_title ); |
| 49 | + $wgOut->addHtml( "<!-- Invalid title: {$row->wl_namespace}, '{$text}' -->\n" ); |
| 50 | + } |
| 51 | + } |
| 52 | + $wgOut->addHtml( '</pre>' ); |
| 53 | + } else { |
| 54 | + $msg = $namespace === false |
| 55 | + ? 'exportwatchlist-none' |
| 56 | + : 'exportwatchlist-none-ns'; |
| 57 | + $wgOut->addHtml( wfMsgExt( $msg, 'parse' ) ); |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + } |
| 62 | + |
| 63 | + /** |
| 64 | + * Get all items on a user's watchlist |
| 65 | + * |
| 66 | + * @param User $user User to fetch for |
| 67 | + * @param mixed $namespace Namespace to fetch |
| 68 | + * @return ResultWrapper |
| 69 | + */ |
| 70 | + private function getWatchlist( $user, $namespace ) { |
| 71 | + $dbr = wfGetDB( DB_SLAVE ); |
| 72 | + $conds = array( 'wl_user' => $user->getId() ); |
| 73 | + if( !is_null( $namespace ) ) |
| 74 | + $conds['wl_namespace'] = $namespace; |
| 75 | + $res = $dbr->select( |
| 76 | + 'watchlist', |
| 77 | + '*', |
| 78 | + $conds, |
| 79 | + __METHOD__ |
| 80 | + ); |
| 81 | + return new ResultWrapper( $dbr, $res ); |
| 82 | + } |
| 83 | + |
| 84 | + /** |
| 85 | + * Build the options form |
| 86 | + * |
| 87 | + * @param mixed $namespace Pre-select namespace |
| 88 | + * @return string |
| 89 | + */ |
| 90 | + private function buildForm( $namespace ) { |
| 91 | + global $wgScript; |
| 92 | + $form = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); |
| 93 | + $form .= Xml::hidden( 'title', $this->getTitle()->getPrefixedUrl() ); |
| 94 | + $form .= Xml::hidden( 'export', 1 ); |
| 95 | + $form .= '<fieldset><legend>' . wfMsgHtml( 'exportwatchlist-legend' ) . '</legend>'; |
| 96 | + $form .= '<table><tr><td>' . Xml::label( wfMsg( 'exportwatchlist-namespace' ), 'namespace' ) . '</td>'; |
| 97 | + $form .= '<td>' . $this->buildNamespaceSelector( $namespace ) . '</td></tr>'; |
| 98 | + $form .= '<tr><td></td><td>' . Xml::submitButton( wfMsg( 'exportwatchlist-submit' ) ) . '</td></tr></table>'; |
| 99 | + $form .= '</fieldset>'; |
| 100 | + $form .= Xml::closeElement( 'form' ); |
| 101 | + return $form; |
| 102 | + } |
| 103 | + |
| 104 | + /** |
| 105 | + * Build a namespace selector which omits discussion namespaces |
| 106 | + * |
| 107 | + * @param mixed $namespace Pre-select namespace |
| 108 | + * @return string |
| 109 | + */ |
| 110 | + private function buildNamespaceSelector( $namespace ) { |
| 111 | + global $wgContLang; |
| 112 | + $options[] = Xml::option( wfMsg( 'namespacesall' ), '' ); |
| 113 | + foreach( $wgContLang->getFormattedNamespaces() as $index => $name ) { |
| 114 | + if( $index < 0 || Namespace::isTalk( $index ) ) |
| 115 | + continue; |
| 116 | + if( $index == NS_MAIN ) |
| 117 | + $name = wfMsg( 'blanknamespace' ); |
| 118 | + $options[] = Xml::option( $name, $index, $index === $namespace ); |
| 119 | + } |
| 120 | + return Xml::openElement( 'select', array( 'id' => 'namespace', 'name' => 'namespace' ) ) |
| 121 | + . implode( "\n", $options ) . Xml::closeElement( 'select' ); |
| 122 | + } |
| 123 | + |
| 124 | + /** |
| 125 | + * Show a "please log in" page |
| 126 | + * |
| 127 | + * @param OutputPage $out |
| 128 | + * @param User $user |
| 129 | + */ |
| 130 | + private function showLoginPage( $out, $user ) { |
| 131 | + $link = $user->getSkin()->makeKnownLinkObj( |
| 132 | + SpecialPage::getTitleFor( 'Userlogin' ), |
| 133 | + wfMsgHtml( 'exportwatchlist-login-link' ), |
| 134 | + 'returnto=' . $this->getTitle()->getPrefixedUrl() |
| 135 | + ); |
| 136 | + $out->addHtml( wfMsgHtml( 'exportwatchlist-login', $link ) ); |
| 137 | + } |
| 138 | + |
| 139 | +} |
| 140 | + |
| 141 | +?> |
\ No newline at end of file |
Property changes on: trunk/extensions/ExportWatchlist/SpecialExportWatchlist.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 142 | + native |
Index: trunk/extensions/ExportWatchlist/README |
— | — | @@ -0,0 +1,49 @@ |
| 2 | +Export Watchlist Extension |
| 3 | +© 2007 Rob Church |
| 4 | +See LICENSE file for full licencing information |
| 5 | + |
| 6 | +The Export Watchlist extension provides two special pages which can be |
| 7 | +used to dump one's watchlist to a plain text list of titles, and to bulk-add |
| 8 | +pages to the watchlist from such a list. |
| 9 | + |
| 10 | +This is convenient for keeping copies of watchlists, duplicating watchlists |
| 11 | +across accounts, sharing watchlists with other users (and making them public) |
| 12 | +and also for bulk-adding titles from a number of sources to the watchlist. |
| 13 | + |
| 14 | +== Requirements == |
| 15 | + |
| 16 | +The Export Watchlist extension requires MediaWiki 1.8.0 or above. |
| 17 | + |
| 18 | +== Installation == |
| 19 | + |
| 20 | +1. Place extension files into an "ExportWatchlist" directory in your |
| 21 | + MediaWiki "extensions/" directory |
| 22 | + |
| 23 | +2. Add the line |
| 24 | + `require_once( "{$IP}/extensions/ExportWatchlist/ExportWatchlist.php" );` |
| 25 | + to LocalSettings.php. |
| 26 | + |
| 27 | +Installation can be verified through the Special:Version page on the wiki. |
| 28 | + |
| 29 | +== Usage == |
| 30 | + |
| 31 | +The extension introduces two new special pages: |
| 32 | + |
| 33 | +* Special:ExportWatchlist |
| 34 | +* Special:ImportWatchlist |
| 35 | + |
| 36 | +=== Export === |
| 37 | + |
| 38 | +Logged-in users can export pages from their watchlist using the |
| 39 | +Special:ExportWatchlist page, and can filter according to namespace. |
| 40 | + |
| 41 | +Note: Discussion namespaces are not included in the exported list. This would be |
| 42 | +redundant, as a watch is considered to exist for both a page and the associated |
| 43 | +discussion page at the same time. |
| 44 | + |
| 45 | +=== Import === |
| 46 | + |
| 47 | +Logged-in users can bulk-add/import pages to their watchlist using the |
| 48 | +Special:ImportWatchlist page; when accessing the page, enter one or more titles, |
| 49 | +one per line, in the text box provided, then click "Import". A list of watched |
| 50 | +pages will be shown. |
\ No newline at end of file |
Property changes on: trunk/extensions/ExportWatchlist/README |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 51 | + native |