r93154 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r93153‎ | r93154 | r93155 >
Date:00:50, 26 July 2011
Author:jlemley
Status:deferred
Tags:
Comment:
Added Favorites extension
Modified paths:
  • /trunk/extensions/Favorites (added) (history)
  • /trunk/extensions/Favorites/FavArticle.php (added) (history)
  • /trunk/extensions/Favorites/FavParser.php (added) (history)
  • /trunk/extensions/Favorites/FavTitle.php (added) (history)
  • /trunk/extensions/Favorites/FavUser.php (added) (history)
  • /trunk/extensions/Favorites/FavoritedItem.php (added) (history)
  • /trunk/extensions/Favorites/FavoritelistEditor.php (added) (history)
  • /trunk/extensions/Favorites/Favorites.php (added) (history)
  • /trunk/extensions/Favorites/Favorites_body.php (added) (history)
  • /trunk/extensions/Favorites/SpecialFavoritelist.php (added) (history)
  • /trunk/extensions/Favorites/favorites.css (added) (history)
  • /trunk/extensions/Favorites/favorites.i18n.php (added) (history)
  • /trunk/extensions/Favorites/favorites.sql (added) (history)
  • /trunk/extensions/Favorites/images (added) (history)
  • /trunk/extensions/Favorites/images/fav-icon-loading.gif (added) (history)
  • /trunk/extensions/Favorites/images/fav-icons.png (added) (history)

Diff [purge]

Index: trunk/extensions/Favorites/FavParser.php
@@ -0,0 +1,266 @@
 2+<?php
 3+
 4+class FavParser {
 5+
 6+function wfSpecialFavoritelist() {
 7+
 8+ global $wgUser, $wgOut, $wgLang, $wgRequest;
 9+ global $wgRCShowFavoritingUsers, $wgEnotifFavoritelist, $wgShowUpdatedMarker;
 10+ $output = '';
 11+
 12+ $skin = $wgUser->getSkin();
 13+ $specialTitle = SpecialPage::getTitleFor( 'Favoritelist' );
 14+ //$wgOut->setRobotPolicy( 'noindex,nofollow' );
 15+
 16+ # Anons don't get a favoritelist
 17+ if( $wgUser->isAnon() ) {
 18+ //$wgOut->setPageTitle( wfMsg( 'favoritenologin' ) );
 19+ $llink = $skin->linkKnown(
 20+ SpecialPage::getTitleFor( 'Userlogin' ),
 21+ wfMsg( 'loginreqlink' ),
 22+ array(),
 23+ array( 'returnto' => $specialTitle->getPrefixedText() )
 24+ );
 25+ $output = wfMsgHtml( 'favoritelistanontext', $llink ) ;
 26+ return $output ;
 27+
 28+ }
 29+
 30+
 31+
 32+
 33+ $output = $this->viewFavList($wgUser, $output, $wgRequest);
 34+ return $output ;
 35+}
 36+
 37+
 38+ private function viewFavList ($user, $output, $request) {
 39+ global $wgUser, $wgOut, $wgLang, $wgRequest;
 40+ $uid = $wgUser->getId();
 41+ $output = $this->showNormalForm( $output, $user );
 42+
 43+ $dbr = wfGetDB( DB_SLAVE, 'favoritelist' );
 44+
 45+
 46+ $favoritelistCount = $dbr->selectField( 'favoritelist', 'COUNT(*)',
 47+ array( 'fl_user' => $uid ), __METHOD__ );
 48+ // Adjust for page X, talk:page X, which are both stored separately,
 49+ // but treated together
 50+ $nitems = floor($favoritelistCount / 2);
 51+
 52+ if( $nitems == 0 ) {
 53+ $output = wfmsg('nofavoritelist');
 54+
 55+ }
 56+ return $output;
 57+}
 58+
 59+
 60+ /**
 61+ * Extract a list of titles from a blob of text, returning
 62+ * (prefixed) strings; unfavoritable titles are ignored
 63+ *
 64+ * @param $list mixed
 65+ * @return array
 66+ */
 67+ private function extractTitles( $list ) {
 68+ $titles = array();
 69+ if( !is_array( $list ) ) {
 70+ $list = explode( "\n", trim( $list ) );
 71+ if( !is_array( $list ) )
 72+ return array();
 73+ }
 74+ foreach( $list as $text ) {
 75+ $text = trim( $text );
 76+ if( strlen( $text ) > 0 ) {
 77+ $title = Title::newFromText( $text );
 78+ //if( $title instanceof Title && $title->isFavoritable() )
 79+ $titles[] = $title->getPrefixedText();
 80+ }
 81+ }
 82+ return array_unique( $titles );
 83+ }
 84+
 85+
 86+ /**
 87+ * Count the number of titles on a user's favoritelist, excluding talk pages
 88+ *
 89+ * @param $user User
 90+ * @return int
 91+ */
 92+ private function countFavoritelist( $user ) {
 93+ $dbr = wfGetDB( DB_MASTER );
 94+ $res = $dbr->select( 'favoritelist', 'COUNT(*) AS count', array( 'fl_user' => $user->getId() ), __METHOD__ );
 95+ $row = $dbr->fetchObject( $res );
 96+ return ceil( $row->count / 2 ); // Paranoia
 97+ }
 98+
 99+ /**
 100+ * Prepare a list of titles on a user's favoritelist (excluding talk pages)
 101+ * and return an array of (prefixed) strings
 102+ *
 103+ * @param $user User
 104+ * @return array
 105+ */
 106+ private function getFavoritelist( $user ) {
 107+ $list = array();
 108+ $dbr = wfGetDB( DB_MASTER );
 109+ $res = $dbr->select(
 110+ 'favoritelist',
 111+ '*',
 112+ array(
 113+ 'fl_user' => $user->getId(),
 114+ ),
 115+ __METHOD__
 116+ );
 117+ if( $res->numRows() > 0 ) {
 118+ while( $row = $res->fetchObject() ) {
 119+ $title = Title::makeTitleSafe( $row->fl_namespace, $row->fl_title );
 120+ if( $title instanceof Title && !$title->isTalkPage() )
 121+ $list[] = $title->getPrefixedText();
 122+ }
 123+ $res->free();
 124+ }
 125+ return $list;
 126+ }
 127+
 128+ /**
 129+ * Get a list of titles on a user's favoritelist, excluding talk pages,
 130+ * and return as a two-dimensional array with namespace, title and
 131+ * redirect status
 132+ *
 133+ * @param $user User
 134+ * @return array
 135+ */
 136+ private function getFavoritelistInfo( $user ) {
 137+ $titles = array();
 138+ $dbr = wfGetDB( DB_MASTER );
 139+ $uid = intval( $user->getId() );
 140+ list( $favoritelist, $page ) = $dbr->tableNamesN( 'favoritelist', 'page' );
 141+ $sql = "SELECT fl_namespace, fl_title, page_id, page_len, page_is_redirect
 142+ FROM {$favoritelist} LEFT JOIN {$page} ON ( fl_namespace = page_namespace
 143+ AND fl_title = page_title ) WHERE fl_user = {$uid}";
 144+ $res = $dbr->query( $sql, __METHOD__ );
 145+ if( $res && $dbr->numRows( $res ) > 0 ) {
 146+ $cache = LinkCache::singleton();
 147+ while( $row = $dbr->fetchObject( $res ) ) {
 148+ $title = Title::makeTitleSafe( $row->fl_namespace, $row->fl_title );
 149+ if( $title instanceof Title ) {
 150+ // Update the link cache while we're at it
 151+ if( $row->page_id ) {
 152+ $cache->addGoodLinkObj( $row->page_id, $title, $row->page_len, $row->page_is_redirect );
 153+ } else {
 154+ $cache->addBadLinkObj( $title );
 155+ }
 156+ // Ignore non-talk
 157+ if( !$title->isTalkPage() )
 158+ $titles[$row->fl_namespace][$row->fl_title] = $row->page_is_redirect;
 159+ }
 160+ }
 161+ }
 162+ return $titles;
 163+ }
 164+
 165+ /**
 166+ * Show a message indicating the number of items on the user's favoritelist,
 167+ * and return this count for additional checking
 168+ *
 169+ * @param $output OutputPage
 170+ * @param $user User
 171+ * @return int
 172+ */
 173+ private function showItemCount( $output, $user ) {
 174+ if( ( $count = $this->countFavoritelist( $user ) ) > 0 ) {
 175+ //$output->addHTML( wfMsgExt( 'favoritelistedit-numitems', 'parse',
 176+ // $GLOBALS['wgLang']->formatNum( $count ) ) );
 177+ } else {
 178+ //$output->addHTML( wfMsg( 'favoritelistedit-noitems', 'parse' ) );
 179+ }
 180+ return $count;
 181+ }
 182+
 183+ /**
 184+ * Remove all titles from a user's favoritelist
 185+ *
 186+ * @param $user User
 187+// */
 188+
 189+
 190+ /**
 191+ * Show the standard favoritelist
 192+ *
 193+ * @param $output OutputPage
 194+ * @param $user User
 195+ */
 196+ private function showNormalForm( $output, $user ) {
 197+ global $wgUser;
 198+ if( ( $count = $this->showItemCount( $output, $user ) ) > 0 ) {
 199+ $form = $this->buildRemoveList( $user, $wgUser->getSkin() );
 200+ $output .= $form ;
 201+ return $output;
 202+ }
 203+ }
 204+
 205+ /**
 206+ * Build part of the standard favoritelist
 207+ *
 208+ * @param $user User
 209+ * @param $skin Skin (really, Linker)
 210+ */
 211+ private function buildRemoveList( $user, $skin ) {
 212+ $list = "";
 213+ foreach( $this->getFavoritelistInfo( $user ) as $namespace => $pages ) {
 214+
 215+ $list .= "<ul>\n";
 216+ foreach( $pages as $dbkey => $redirect ) {
 217+ $title = Title::makeTitleSafe( $namespace, $dbkey );
 218+ $list .= $this->buildRemoveLine( $title, $redirect, $skin );
 219+ }
 220+ $list .= "</ul>\n";
 221+ }
 222+ return $list;
 223+ }
 224+
 225+
 226+
 227+ /**
 228+ * Build a single list item containing a link
 229+ *
 230+ * @param $title Title
 231+ * @param $redirect bool
 232+ * @param $skin Skin
 233+ * @return string
 234+ */
 235+ private function buildRemoveLine( $title, $redirect, $skin ) {
 236+ global $wgLang;
 237+
 238+ $link = $skin->link( $title );
 239+ if( $redirect )
 240+ $link = '<span class="favoritelistredir">' . $link . '</span>';
 241+
 242+ return "<li>" . $link . "</li>\n";
 243+ }
 244+
 245+}
 246+/**
 247+ * Count the number of items on a user's favoritelist
 248+ *
 249+ * @param $talk Include talk pages
 250+ * @return integer
 251+ */
 252+function flCountItems( &$user, $talk = true ) {
 253+ $dbr = wfGetDB( DB_SLAVE, 'favoritelist' );
 254+
 255+ # Fetch the raw count
 256+ $res = $dbr->select( 'favoritelist', 'COUNT(*) AS count',
 257+ array( 'fl_user' => $user->mId ), 'flCountItems' );
 258+ $row = $dbr->fetchObject( $res );
 259+ $count = $row->count;
 260+ $dbr->freeResult( $res );
 261+
 262+ # Halve to remove talk pages if needed
 263+ if( !$talk )
 264+ $count = floor( $count / 2 );
 265+
 266+ return( $count );
 267+}
Property changes on: trunk/extensions/Favorites/FavParser.php
___________________________________________________________________
Added: svn:eol-style
1268 + native
Index: trunk/extensions/Favorites/favorites.sql
@@ -0,0 +1,13 @@
 2+--
 3+-- Table structure for table `favoritelist`
 4+--
 5+-- Replace /*_*/ with the proper prefix
 6+
 7+CREATE TABLE IF NOT EXISTS /*_*/favoritelist (
 8+ fl_user int(10) unsigned NOT NULL,
 9+ fl_namespace int(11) NOT NULL DEFAULT '0',
 10+ fl_title varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
 11+ fl_notificationtimestamp varbinary(14) DEFAULT NULL,
 12+ UNIQUE KEY fl_user (fl_user,fl_namespace,fl_title),
 13+ KEY namespace_title (fl_namespace,fl_title)
 14+)
Property changes on: trunk/extensions/Favorites/favorites.sql
___________________________________________________________________
Added: svn:eol-style
115 + native
Index: trunk/extensions/Favorites/FavUser.php
@@ -0,0 +1,36 @@
 2+<?php
 3+class FavUser extends User {
 4+ /**
 5+ * Check the Favorited status of an article.
 6+ * @param $title \type{Title} Title of the article to look at
 7+ * @return \bool True if article is Favorited
 8+ */
 9+ function isFavorited( $title ) {
 10+ global $wgUser;
 11+ $fl = FavoritedItem::fromUserTitle( $wgUser, $title );
 12+ return $fl->isFavorited();
 13+
 14+ }
 15+
 16+ /**
 17+ * Favorite an article.
 18+ * @param $title \type{Title} Title of the article to look at
 19+ */
 20+ function addFavorite( $title ) {
 21+ global $wgUser;
 22+ $fl = FavoritedItem::fromUserTitle( $wgUser, $title );
 23+ $fl->addFavorite();
 24+ $title->invalidateCache();
 25+ }
 26+
 27+ /**
 28+ * Stop Favoriting an article.
 29+ * @param $title \type{Title} Title of the article to look at
 30+ */
 31+ function removeFavorite( $title ) {
 32+ global $wgUser;
 33+ $fl = FavoritedItem::fromUserTitle( $wgUser, $title );
 34+ $fl->removeFavorite();
 35+ $title->invalidateCache();
 36+ }
 37+}
Property changes on: trunk/extensions/Favorites/FavUser.php
___________________________________________________________________
Added: svn:eol-style
138 + native
Index: trunk/extensions/Favorites/SpecialFavoritelist.php
@@ -0,0 +1,556 @@
 2+<?php
 3+/**
 4+ * @file
 5+ * @ingroup SpecialPage Favoritelist
 6+ */
 7+
 8+/**
 9+ * Constructor
 10+ *
 11+ * @param $par Parameter passed to the page
 12+ */
 13+
 14+class SpecialFavoritelist extends SpecialPage {
 15+ function __construct() {
 16+ parent::__construct( 'Favoritelist' );
 17+ wfLoadExtensionMessages('Favoritelist');
 18+ }
 19+
 20+ function execute( $par ) {
 21+ global $wgRequest, $wgOut;
 22+ $vwfav = new ViewFavorites();
 23+
 24+ $this->setHeaders();
 25+ $param = $wgRequest->getText('param');
 26+
 27+ $vwfav->wfSpecialFavoritelist( $par );
 28+ }
 29+
 30+}
 31+
 32+class ViewFavorites {
 33+
 34+function wfSpecialFavoritelist( $par ) {
 35+ global $wgUser, $wgOut, $wgLang, $wgRequest;
 36+ global $wgRCShowFavoritingUsers, $wgEnotifFavoritelist, $wgShowUpdatedMarker;
 37+
 38+ // Add feed links
 39+ $flToken = $wgUser->getOption( 'favoritelisttoken' );
 40+ if (!$flToken) {
 41+ $flToken = sha1( mt_rand() . microtime( true ) );
 42+ $wgUser->setOption( 'favoritelisttoken', $flToken );
 43+ $wgUser->saveSettings();
 44+ }
 45+
 46+ global $wgServer, $wgScriptPath, $wgFeedClasses;
 47+ $apiParams = array( 'action' => 'feedfavoritelist', 'allrev' => 'allrev',
 48+ 'flowner' => $wgUser->getName(), 'fltoken' => $flToken );
 49+ $feedTemplate = wfScript('api').'?';
 50+
 51+ foreach( $wgFeedClasses as $format => $class ) {
 52+ $theseParams = $apiParams + array( 'feedformat' => $format );
 53+ $url = $feedTemplate . wfArrayToCGI( $theseParams );
 54+ $wgOut->addFeedLink( $format, $url );
 55+ }
 56+
 57+ $skin = $wgUser->getSkin();
 58+ $specialTitle = SpecialPage::getTitleFor( 'Favoritelist' );
 59+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 60+
 61+ # Anons don't get a favoritelist
 62+ if( $wgUser->isAnon() ) {
 63+ $wgOut->setPageTitle( wfMsg( 'favoritenologin' ) );
 64+ $llink = $skin->linkKnown(
 65+ SpecialPage::getTitleFor( 'Userlogin' ),
 66+ wfMsgHtml( 'loginreqlink' ),
 67+ array(),
 68+ array( 'returnto' => $specialTitle->getPrefixedText() )
 69+ );
 70+ $wgOut->addHTML( wfMsgWikiHtml( 'favoritelistanontext', $llink ) );
 71+ return;
 72+ }
 73+
 74+ $wgOut->setPageTitle( wfMsg( 'favoritelist' ) );
 75+
 76+ $sub = wfMsgExt( 'favoritelistfor', 'parseinline', $wgUser->getName() );
 77+ $sub .= '<br />' . FavoritelistEditor::buildTools( $wgUser->getSkin() );
 78+ $wgOut->setSubtitle( $sub );
 79+
 80+ if( ( $mode = FavoritelistEditor::getMode( $wgRequest, $par ) ) !== false ) {
 81+ $editor = new FavoritelistEditor();
 82+ $editor->execute( $wgUser, $wgOut, $wgRequest, $mode );
 83+ return;
 84+ }
 85+
 86+
 87+ $this->viewFavList($wgUser, $wgOut, $wgRequest, $mode);
 88+
 89+}
 90+
 91+
 92+private function viewFavList ($user, $output, $request, $mode) {
 93+ global $wgUser, $wgOut, $wgLang, $wgRequest;
 94+ $uid = $wgUser->getId();
 95+ $output->setPageTitle( wfMsg( 'favoritelist' ) );
 96+ if( $request->wasPosted() && $this->checkToken( $request, $wgUser ) ) {
 97+ $titles = $this->extractTitles( $request->getArray( 'titles' ) );
 98+ $this->unfavoriteTitles( $titles, $user );
 99+ $user->invalidateCache();
 100+ $output->addHTML( wfMsgExt( 'favoritelistedit-normal-done', 'parse',
 101+ $GLOBALS['wgLang']->formatNum( count( $titles ) ) ) );
 102+ $this->showTitles( $titles, $output, $wgUser->getSkin() );
 103+ }
 104+ $this->showNormalForm( $output, $user );
 105+
 106+ $dbr = wfGetDB( DB_SLAVE, 'favoritelist' );
 107+// $recentchanges = $dbr->tableName( 'recentchanges' );
 108+
 109+ $favoritelistCount = $dbr->selectField( 'favoritelist', 'COUNT(*)',
 110+ array( 'fl_user' => $uid ), __METHOD__ );
 111+ // Adjust for page X, talk:page X, which are both stored separately,
 112+ // but treated together
 113+ //$nitems = floor($favoritelistCount / 2);
 114+ $nitems = $favoritelistCount;
 115+ if( $nitems == 0 ) {
 116+ $wgOut->addWikiMsg( 'nofavoritelist' );
 117+ return;
 118+ }
 119+
 120+}
 121+
 122+ /**
 123+ * Check the edit token from a form submission
 124+ *
 125+ * @param $request WebRequest
 126+ * @param $user User
 127+ * @return bool
 128+ */
 129+ private function checkToken( $request, $user ) {
 130+ return $user->matchEditToken( $request->getVal( 'token' ), 'favoritelistedit' );
 131+ }
 132+
 133+ /**
 134+ * Extract a list of titles from a blob of text, returning
 135+ * (prefixed) strings; unfavoritable titles are ignored
 136+ *
 137+ * @param $list mixed
 138+ * @return array
 139+ */
 140+ private function extractTitles( $list ) {
 141+ $titles = array();
 142+ if( !is_array( $list ) ) {
 143+ $list = explode( "\n", trim( $list ) );
 144+ if( !is_array( $list ) )
 145+ return array();
 146+ }
 147+ foreach( $list as $text ) {
 148+ $text = trim( $text );
 149+ if( strlen( $text ) > 0 ) {
 150+ $title = Title::newFromText( $text );
 151+ //if( $title instanceof Title && $title->isFavoritable() )
 152+ $titles[] = $title->getPrefixedText();
 153+ }
 154+ }
 155+ return array_unique( $titles );
 156+ }
 157+
 158+ /**
 159+ * Print out a list of linked titles
 160+ *
 161+ * $titles can be an array of strings or Title objects; the former
 162+ * is preferred, since Titles are very memory-heavy
 163+ *
 164+ * @param $titles An array of strings, or Title objects
 165+ * @param $output OutputPage
 166+ * @param $skin Skin
 167+ */
 168+ private function showTitles( $titles, $output, $skin ) {
 169+ $talk = wfMsgHtml( 'talkpagelinktext' );
 170+ // Do a batch existence check
 171+ $batch = new LinkBatch();
 172+ foreach( $titles as $title ) {
 173+ if( !$title instanceof Title )
 174+ $title = Title::newFromText( $title );
 175+ //if( $title instanceof Title ) {
 176+ // $batch->addObj( $title );
 177+ // $batch->addObj( $title->getTalkPage() );
 178+ //}
 179+ }
 180+ $batch->execute();
 181+ // Print out the list
 182+ $output->addHTML( "<ul>\n" );
 183+ foreach( $titles as $title ) {
 184+ if( !$title instanceof Title )
 185+ $title = Title::newFromText( $title );
 186+ if( $title instanceof Title ) {
 187+ $output->addHTML( "<li>" . $skin->link( $title )
 188+ //. ' (' . $skin->link( $title->getTalkPage(), $talk ) . ")</li>\n" );
 189+ . "</li>\n" );
 190+ }
 191+ }
 192+ $output->addHTML( "</ul>\n" );
 193+ }
 194+
 195+ /**
 196+ * Count the number of titles on a user's favoritelist, excluding talk pages
 197+ *
 198+ * @param $user User
 199+ * @return int
 200+ */
 201+ private function countFavoritelist( $user ) {
 202+ $dbr = wfGetDB( DB_MASTER );
 203+ $res = $dbr->select( 'favoritelist', 'COUNT(*) AS count', array( 'fl_user' => $user->getId() ), __METHOD__ );
 204+ $row = $dbr->fetchObject( $res );
 205+ return ceil( $row->count ); // Paranoia
 206+ }
 207+
 208+ /**
 209+ * Prepare a list of titles on a user's favoritelist (excluding talk pages)
 210+ * and return an array of (prefixed) strings
 211+ *
 212+ * @param $user User
 213+ * @return array
 214+ */
 215+ private function getFavoritelist( $user ) {
 216+ $list = array();
 217+ $dbr = wfGetDB( DB_MASTER );
 218+ $res = $dbr->select(
 219+ 'favoritelist',
 220+ '*',
 221+ array(
 222+ 'fl_user' => $user->getId(),
 223+ ),
 224+ __METHOD__
 225+ );
 226+ if( $res->numRows() > 0 ) {
 227+ while( $row = $res->fetchObject() ) {
 228+ $title = Title::makeTitleSafe( $row->fl_namespace, $row->fl_title );
 229+ if( $title instanceof Title && !$title->isTalkPage() )
 230+ $list[] = $title->getPrefixedText();
 231+ }
 232+ $res->free();
 233+ }
 234+ return $list;
 235+ }
 236+
 237+ /**
 238+ * Get a list of titles on a user's favoritelist, excluding talk pages,
 239+ * and return as a two-dimensional array with namespace, title and
 240+ * redirect status
 241+ *
 242+ * @param $user User
 243+ * @return array
 244+ */
 245+ private function getFavoritelistInfo( $user ) {
 246+ $titles = array();
 247+ $dbr = wfGetDB( DB_MASTER );
 248+ $uid = intval( $user->getId() );
 249+ list( $favoritelist, $page ) = $dbr->tableNamesN( 'favoritelist', 'page' );
 250+ $sql = "SELECT fl_namespace, fl_title, page_id, page_len, page_is_redirect
 251+ FROM {$favoritelist} LEFT JOIN {$page} ON ( fl_namespace = page_namespace
 252+ AND fl_title = page_title ) WHERE fl_user = {$uid}";
 253+ $res = $dbr->query( $sql, __METHOD__ );
 254+ if( $res && $dbr->numRows( $res ) > 0 ) {
 255+ $cache = LinkCache::singleton();
 256+ while( $row = $dbr->fetchObject( $res ) ) {
 257+ $title = Title::makeTitleSafe( $row->fl_namespace, $row->fl_title );
 258+ if( $title instanceof Title ) {
 259+ // Update the link cache while we're at it
 260+ if( $row->page_id ) {
 261+ $cache->addGoodLinkObj( $row->page_id, $title, $row->page_len, $row->page_is_redirect );
 262+ } else {
 263+ $cache->addBadLinkObj( $title );
 264+ }
 265+ // Ignore non-talk
 266+ if( !$title->isTalkPage() )
 267+ $titles[$row->fl_namespace][$row->fl_title] = $row->page_is_redirect;
 268+ }
 269+ }
 270+ }
 271+ return $titles;
 272+ }
 273+
 274+ /**
 275+ * Show a message indicating the number of items on the user's favoritelist,
 276+ * and return this count for additional checking
 277+ *
 278+ * @param $output OutputPage
 279+ * @param $user User
 280+ * @return int
 281+ */
 282+ private function showItemCount( $output, $user ) {
 283+ if( ( $count = $this->countFavoritelist( $user ) ) > 0 ) {
 284+ //$output->addHTML( wfMsgExt( 'favoritelistedit-numitems', 'parse',
 285+ // $GLOBALS['wgLang']->formatNum( $count ) ) );
 286+ } else {
 287+ //$output->addHTML( wfMsgExt( 'favoritelistedit-noitems', 'parse' ) );
 288+ }
 289+ return $count;
 290+ }
 291+
 292+ /**
 293+ * Remove all titles from a user's favoritelist
 294+ *
 295+ * @param $user User
 296+ */
 297+ private function clearFavoritelist( $user ) {
 298+ $dbw = wfGetDB( DB_MASTER );
 299+ $dbw->delete( 'favoritelist', array( 'fl_user' => $user->getId() ), __METHOD__ );
 300+ }
 301+
 302+ /**
 303+ * Add a list of titles to a user's favoritelist
 304+ *
 305+ * $titles can be an array of strings or Title objects; the former
 306+ * is preferred, since Titles are very memory-heavy
 307+ *
 308+ * @param $titles An array of strings, or Title objects
 309+ * @param $user User
 310+ */
 311+ private function favoriteTitles( $titles, $user ) {
 312+ $dbw = wfGetDB( DB_MASTER );
 313+ $rows = array();
 314+ foreach( $titles as $title ) {
 315+ if( !$title instanceof Title )
 316+ $title = Title::newFromText( $title );
 317+ if( $title instanceof Title ) {
 318+ $rows[] = array(
 319+ 'fl_user' => $user->getId(),
 320+ 'fl_namespace' => ( $title->getNamespace() & ~1 ),
 321+ 'fl_title' => $title->getDBkey(),
 322+ 'fl_notificationtimestamp' => null,
 323+ );
 324+ $rows[] = array(
 325+ 'fl_user' => $user->getId(),
 326+ 'fl_namespace' => ( $title->getNamespace() | 1 ),
 327+ 'fl_title' => $title->getDBkey(),
 328+ 'fl_notificationtimestamp' => null,
 329+ );
 330+ }
 331+ }
 332+ $dbw->insert( 'favoritelist', $rows, __METHOD__, 'IGNORE' );
 333+ }
 334+
 335+ /**
 336+ * Remove a list of titles from a user's favoritelist
 337+ *
 338+ * $titles can be an array of strings or Title objects; the former
 339+ * is preferred, since Titles are very memory-heavy
 340+ *
 341+ * @param $titles An array of strings, or Title objects
 342+ * @param $user User
 343+ */
 344+ private function unfavoriteTitles( $titles, $user ) {
 345+ $dbw = wfGetDB( DB_MASTER );
 346+ foreach( $titles as $title ) {
 347+ if( !$title instanceof Title )
 348+ $title = Title::newFromText( $title );
 349+ if( $title instanceof Title ) {
 350+ $dbw->delete(
 351+ 'favoritelist',
 352+ array(
 353+ 'fl_user' => $user->getId(),
 354+ 'fl_namespace' => ( $title->getNamespace() & ~1 ),
 355+ 'fl_title' => $title->getDBkey(),
 356+ ),
 357+ __METHOD__
 358+ );
 359+ $dbw->delete(
 360+ 'favoritelist',
 361+ array(
 362+ 'fl_user' => $user->getId(),
 363+ 'fl_namespace' => ( $title->getNamespace() | 1 ),
 364+ 'fl_title' => $title->getDBkey(),
 365+ ),
 366+ __METHOD__
 367+ );
 368+ $article = new Article($title);
 369+ wfRunHooks('UnfavoriteArticleComplete',array(&$user,&$article));
 370+ }
 371+ }
 372+ }
 373+
 374+ /**
 375+ * Show the standard favoritelist editing form
 376+ *
 377+ * @param $output OutputPage
 378+ * @param $user User
 379+ */
 380+ private function showNormalForm( $output, $user ) {
 381+ global $wgUser;
 382+ if( ( $count = $this->showItemCount( $output, $user ) ) > 0 ) {
 383+ $self = SpecialPage::getTitleFor( 'Favoritelist' );
 384+ $form = Xml::openElement( 'form', array( 'method' => 'post',
 385+ 'action' => $self->getLocalUrl( array( 'action' => 'edit' ) ) ) );
 386+ $form .= Xml::hidden( 'token', $wgUser->editToken( 'favoritelistedit' ) );
 387+ //$form .= "<fieldset>\n<legend>" . wfMsgHtml( 'favoritelistedit-normal-legend' ) . "</legend>";
 388+ //$form .= wfMsgExt( 'favoritelistedit-normal-explain', 'parse' );
 389+ $form .= $this->buildRemoveList( $user, $wgUser->getSkin() );
 390+ //$form .= '<p>' . Xml::submitButton( wfMsg( 'favoritelistedit-normal-submit' ) ) . '</p>';
 391+ $form .= '</fieldset></form>';
 392+ $output->addHTML( $form );
 393+ }
 394+ }
 395+
 396+ /**
 397+ * Build the part of the standard favoritelist editing form with the actual
 398+ * title selection checkboxes and stuff. Also generates a table of
 399+ * contents if there's more than one heading.
 400+ *
 401+ * @param $user User
 402+ * @param $skin Skin (really, Linker)
 403+ */
 404+ private function buildRemoveList( $user, $skin ) {
 405+ $list = "";
 406+ $toc = $skin->tocIndent();
 407+ $tocLength = 0;
 408+ foreach( $this->getFavoritelistInfo( $user ) as $namespace => $pages ) {
 409+ $tocLength++;
 410+ $heading = htmlspecialchars( $this->getNamespaceHeading( $namespace ) );
 411+ $anchor = "editfavoritelist-ns" . $namespace;
 412+
 413+ $list .= $skin->makeHeadLine( 2, ">", $anchor, $heading, "" );
 414+ $toc .= $skin->tocLine( $anchor, $heading, $tocLength, 1 ) . $skin->tocLineEnd();
 415+
 416+ $list .= "<ul>\n";
 417+ foreach( $pages as $dbkey => $redirect ) {
 418+ $title = Title::makeTitleSafe( $namespace, $dbkey );
 419+ $list .= $this->buildRemoveLine( $title, $redirect, $skin );
 420+ }
 421+ $list .= "</ul>\n";
 422+ }
 423+ // ISSUE: omit the TOC if the total number of titles is low?
 424+ if( $tocLength > 10 ) {
 425+ $list = $skin->tocList( $toc ) . $list;
 426+ }
 427+ return $list;
 428+ }
 429+
 430+ /**
 431+ * Get the correct "heading" for a namespace
 432+ *
 433+ * @param $namespace int
 434+ * @return string
 435+ */
 436+ private function getNamespaceHeading( $namespace ) {
 437+ return $namespace == NS_MAIN
 438+ ? wfMsgHtml( 'blanknamespace' )
 439+ : htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( $namespace ) );
 440+ }
 441+
 442+ /**
 443+ * Build a single list item containing a check box selecting a title
 444+ * and a link to that title, with various additional bits
 445+ *
 446+ * @param $title Title
 447+ * @param $redirect bool
 448+ * @param $skin Skin
 449+ * @return string
 450+ */
 451+ private function buildRemoveLine( $title, $redirect, $skin ) {
 452+ global $wgLang;
 453+
 454+ $link = $skin->link( $title );
 455+ if( $redirect )
 456+ $link = '<span class="favoritelistredir">' . $link . '</span>';
 457+ $tools[] = $skin->link( $title->getTalkPage(), wfMsgHtml( 'talkpagelinktext' ) );
 458+ if( $title->exists() ) {
 459+ $tools[] = $skin->link(
 460+ $title,
 461+ wfMsgHtml( 'history_short' ),
 462+ array(),
 463+ array( 'action' => 'history' ),
 464+ array( 'known', 'noclasses' )
 465+ );
 466+ }
 467+ if( $title->getNamespace() == NS_USER && !$title->isSubpage() ) {
 468+ $tools[] = $skin->link(
 469+ SpecialPage::getTitleFor( 'Contributions', $title->getText() ),
 470+ wfMsgHtml( 'contributions' ),
 471+ array(),
 472+ array(),
 473+ array( 'known', 'noclasses' )
 474+ );
 475+ }
 476+ return "<li>"
 477+ //. Xml::check( 'titles[]', false, array( 'value' => $title->getPrefixedText() ) )
 478+ . $link . " (" . $wgLang->pipeList( $tools ) . ")" . "</li>\n";
 479+ }
 480+
 481+ /**
 482+ * Show a form for editing the favoritelist in "raw" mode
 483+ *
 484+ * @param $output OutputPage
 485+ * @param $user User
 486+ */
 487+ public function showRawForm( $output, $user ) {
 488+ global $wgUser;
 489+ $this->showItemCount( $output, $user );
 490+ $self = SpecialPage::getTitleFor( 'Favoritelist' );
 491+ $form = Xml::openElement( 'form', array( 'method' => 'post',
 492+ 'action' => $self->getLocalUrl( array( 'action' => 'raw' ) ) ) );
 493+ $form .= Xml::hidden( 'token', $wgUser->editToken( 'favoritelistedit' ) );
 494+ $form .= '<fieldset><legend>' . wfMsgHtml( 'favoritelistedit-raw-legend' ) . '</legend>';
 495+ $form .= wfMsgExt( 'favoritelistedit-raw-explain', 'parse' );
 496+ $form .= Xml::label( wfMsg( 'favoritelistedit-raw-titles' ), 'titles' );
 497+ $form .= "<br />\n";
 498+ $form .= Xml::openElement( 'textarea', array( 'id' => 'titles', 'name' => 'titles',
 499+ 'rows' => $wgUser->getIntOption( 'rows' ), 'cols' => $wgUser->getIntOption( 'cols' ) ) );
 500+ $titles = $this->getFavoritelist( $user );
 501+ foreach( $titles as $title )
 502+ $form .= htmlspecialchars( $title ) . "\n";
 503+ $form .= '</textarea>';
 504+ $form .= '<p>' . Xml::submitButton( wfMsg( 'favoritelistedit-raw-submit' ) ) . '</p>';
 505+ $form .= '</fieldset></form>';
 506+ $output->addHTML( $form );
 507+ }
 508+
 509+ /**
 510+ * Determine whether we are editing the favoritelist, and if so, what
 511+ * kind of editing operation
 512+ *
 513+ * @param $request WebRequest
 514+ * @param $par mixed
 515+ * @return int
 516+ */
 517+ public static function getMode( $request, $par ) {
 518+ $mode = strtolower( $request->getVal( 'action', $par ) );
 519+ switch( $mode ) {
 520+ case 'clear':
 521+ return self::EDIT_CLEAR;
 522+ case 'raw':
 523+ return self::EDIT_RAW;
 524+ case 'edit':
 525+ return self::EDIT_NORMAL;
 526+ default:
 527+ return false;
 528+ }
 529+ }
 530+
 531+ /**
 532+ * Build a set of links for convenient navigation
 533+ * between favoritelist viewing and editing modes
 534+ *
 535+ * @param $skin Skin to use
 536+ * @return string
 537+ */
 538+ public static function buildTools( $skin ) {
 539+ global $wgLang;
 540+
 541+ $tools = array();
 542+ $modes = array( 'view' => false, 'edit' => 'edit', 'raw' => 'raw' );
 543+ foreach( $modes as $mode => $subpage ) {
 544+ // can use messages 'favoritelisttools-view', 'favoritelisttools-edit', 'favoritelisttools-raw'
 545+ $tools[] = $skin->link(
 546+ SpecialPage::getTitleFor( 'Favoritelist', $subpage ),
 547+ wfMsgHtml( "favoritelisttools-{$mode}" ),
 548+ array(),
 549+ array(),
 550+ array( 'known', 'noclasses' )
 551+ );
 552+ }
 553+ return $wgLang->pipeList( $tools );
 554+ }
 555+}
 556+
 557+
Property changes on: trunk/extensions/Favorites/SpecialFavoritelist.php
___________________________________________________________________
Added: svn:eol-style
1558 + native
Index: trunk/extensions/Favorites/FavTitle.php
@@ -0,0 +1,42 @@
 2+<?php
 3+
 4+
 5+class FavTitle extends Title {
 6+
 7+
 8+ var $mFav = null;
 9+ var $mTextform;
 10+ var $mTitle;
 11+
 12+ function __construct() {}
 13+
 14+ /**
 15+ * Is $wgUser watching this page?
 16+ * @return \type{\bool}
 17+ */
 18+ public function userIsFavoriting() {
 19+
 20+ global $wgUser, $wgArticle;
 21+ $favUser = new FavUser();
 22+ if ( is_null( $this->mFav ) ) {
 23+ if ( NS_SPECIAL == $this->mNamespace || !$wgUser->isLoggedIn()) {
 24+ $this->mFav = false;
 25+ } else {
 26+ $this->mFav = $favUser->isFavorited( $wgArticle->mTitle );
 27+ }
 28+ }
 29+ return $this->mFav;
 30+ }
 31+
 32+ public function moveToFav($title, &$nt ) {
 33+ # Update watchlists
 34+ $oldnamespace = $title->getNamespace() & ~1;
 35+ $newnamespace = $nt->getNamespace() & ~1;
 36+ $oldtitle = $title->getDBkey();
 37+ $newtitle = $nt->getDBkey();
 38+
 39+ if( $oldnamespace != $newnamespace || $oldtitle != $newtitle ) {
 40+ FavoritedItem::duplicateEntries( $title, $nt );
 41+ }
 42+ }
 43+}
Property changes on: trunk/extensions/Favorites/FavTitle.php
___________________________________________________________________
Added: svn:eol-style
144 + native
Index: trunk/extensions/Favorites/Favorites_body.php
@@ -0,0 +1,48 @@
 2+<?php
 3+
 4+class Favorites extends QuickTemplate {
 5+
 6+ var $mTitle;
 7+
 8+ function execute() {
 9+
 10+ }
 11+
 12+ function favoritesIcon( &$sktemplate, &$links ) {
 13+
 14+ global $wgUseIconFavorite, $wgRequest, $wgArticle;
 15+
 16+ //$sktemplate->skin = $sktemplate->data['skin'];
 17+ $action = $wgRequest->getText( 'action' );
 18+
 19+ // See if this object even exists - if the user can't read it, the object doesn't get created.
 20+ if ($wgArticle) {
 21+
 22+ if ( $wgUseIconFavorite ) {
 23+
 24+ $class = 'icon ';
 25+ $place = 'views';
 26+ } else {
 27+ $class = '';
 28+ $place = 'actions';
 29+ }
 30+ $favTitle = new FavTitle();
 31+
 32+ //$mode = $this->mTitle->userIsFavoriting() ? 'unfavorite' : 'favorite';
 33+ $mode = $favTitle->userIsFavoriting() ? 'unfavorite' : 'favorite';
 34+ $links[$place][$mode] = array(
 35+ 'class' => $class . ( ( $action == 'favorite' || $action == 'unfavorite' ) ? ' selected' : false ),
 36+ 'text' => wfMsg( $mode ), // uses 'favorite' or 'unfavorite' message
 37+ 'href' => $wgArticle->mTitle->getLocalUrl( 'action=' . $mode )
 38+ );
 39+
 40+
 41+ return false;
 42+ }
 43+
 44+}
 45+
 46+}
 47+
 48+
 49+
Property changes on: trunk/extensions/Favorites/Favorites_body.php
___________________________________________________________________
Added: svn:eol-style
150 + native
Index: trunk/extensions/Favorites/images/fav-icon-loading.gif
Cannot display: file marked as a binary type.
svn:mime-type = image/gif
Property changes on: trunk/extensions/Favorites/images/fav-icon-loading.gif
___________________________________________________________________
Added: svn:mime-type
251 + image/gif
Index: trunk/extensions/Favorites/images/fav-icons.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/Favorites/images/fav-icons.png
___________________________________________________________________
Added: svn:mime-type
352 + image/png
Index: trunk/extensions/Favorites/FavArticle.php
@@ -0,0 +1,96 @@
 2+<?php
 3+
 4+
 5+class FavArticle extends Article {
 6+
 7+
 8+ var $mTitle;
 9+
 10+
 11+
 12+ /**
 13+ * User-interface handler for the "favorite" action
 14+ */
 15+ public function favorite() {
 16+ global $wgUser, $wgOut, $wgArticle;
 17+
 18+ $this->mTitle = $wgArticle->mTitle;
 19+
 20+ if ( $wgUser->isAnon() ) {
 21+ $wgOut->showErrorPage( 'favoritenologin', 'favoritenologintext' );
 22+ return;
 23+ }
 24+ if ( wfReadOnly() ) {
 25+ $wgOut->readOnlyPage();
 26+ return;
 27+ }
 28+ if ( $this->doFavorite() ) {
 29+ $wgOut->setPagetitle( wfMsg( 'addedfavorite' ) );
 30+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 31+ $wgOut->addWikiMsg( 'addedfavoritetext', $this->mTitle->getPrefixedText() );
 32+ }
 33+ $wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
 34+ }
 35+
 36+ /**
 37+ * Add this page to $wgUser's favoritelist
 38+ * @return bool true on successful favorite operation
 39+ */
 40+ public function doFavorite() {
 41+ global $wgUser;
 42+ $favUser = new FavUser();
 43+ if ( $wgUser->isAnon() ) {
 44+ return false;
 45+ }
 46+ if ( wfRunHooks( 'FavoriteArticle', array( &$wgUser, &$this ) ) ) {
 47+ $favUser->addFavorite( $this->mTitle );
 48+ return wfRunHooks( 'FavoriteArticleComplete', array( &$wgUser, &$this ) );
 49+ }
 50+
 51+ }
 52+
 53+ /**
 54+ * User interface handler for the "unfavorite" action.
 55+ */
 56+ public function unfavorite($action, $wgArticle) {
 57+ global $wgUser, $wgOut, $wgArticle;
 58+ $this->mTitle = $wgArticle->mTitle;
 59+ if ( $wgUser->isAnon() ) {
 60+ $wgOut->showErrorPage( 'favoritenologin', 'favoritenologintext' );
 61+ return;
 62+ }
 63+ if ( wfReadOnly() ) {
 64+ $wgOut->readOnlyPage();
 65+ return;
 66+ }
 67+ if ( $this->doUnfavorite() ) {
 68+ $wgOut->setPagetitle( wfMsg( 'removedfavorite' ) );
 69+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 70+ $wgOut->addWikiMsg( 'removedfavoritetext', $wgArticle->mTitle->getPrefixedText() );
 71+ }
 72+ $wgOut->returnToMain( true, $wgArticle->mTitle->getPrefixedText() );
 73+
 74+ return false;
 75+
 76+ }
 77+
 78+ /**
 79+ * Stop favoriting a page
 80+ * @return bool true on successful unfavorite
 81+ */
 82+ public function doUnfavorite() {
 83+ global $wgUser;
 84+
 85+ $favUser = new FavUser();
 86+ if ( $wgUser->isAnon() ) {
 87+ return false;
 88+ }
 89+ if ( wfRunHooks( 'UnfavoriteArticle', array( &$wgUser, &$this ) ) ) {
 90+ $favUser->removeFavorite( $this->mTitle );
 91+ return wfRunHooks( 'UnfavoriteArticleComplete', array( &$wgUser, &$this ) );
 92+ }
 93+ return false;
 94+ }
 95+
 96+
 97+}
Property changes on: trunk/extensions/Favorites/FavArticle.php
___________________________________________________________________
Added: svn:eol-style
198 + native
Index: trunk/extensions/Favorites/favorites.i18n.php
@@ -0,0 +1,78 @@
 2+<?php
 3+$messages = array();
 4+$messages['en'] = array(
 5+
 6+
 7+# Edit pages
 8+'favoritethis' => 'Favorite this page',
 9+
 10+# Preferences page
 11+'prefs-favoritelist' => 'Favorites',
 12+'prefs-favoritelist-token' => 'Favorites token:',
 13+'prefs-advancedfavoritelist' => 'Advanced options',
 14+
 15+# Favoritelist
 16+'favoritelist' => 'My favorites',
 17+'myfavoritelist' => 'My favorites',
 18+'favoritelistfor' => "(for '''$1''')",
 19+'nofavoritelist' => 'You have no items in your favorites.',
 20+'favoritelistanontext' => 'Please $1 to view or edit items in your favorites.',
 21+'favoritenologin' => 'Not logged in',
 22+'favoritenologintext' => 'You must be [[Special:UserLogin|logged in]] to modify your favorites.',
 23+'addedfavorite' => 'Added to favorites',
 24+'addedfavoritetext' => 'The page "[[:$1]]" has been added to your [[Special:Favoritelist|favorites]].',
 25+'removedfavorite' => 'Removed from favorites',
 26+'removedfavoritetext' => 'The page "[[:$1]]" has been removed from [[Special:Favoritelist|your favorites]].',
 27+'favorite' => 'Favorite',
 28+'favoritethispage' => 'Favorite this page',
 29+'unfavorite' => 'Unfavorite',
 30+'unfavoritethispage' => 'Stop favoriteing',
 31+'favoritelist-options' => 'Favorites options',
 32+
 33+# Displayed when you click the "favorite" button and it is in the process of favoriteing
 34+'favoriteing' => 'Favoriteing...',
 35+'unfavoriteing' => 'Unfavoriteing...',
 36+
 37+# Keyboard access keys for power users
 38+'accesskey-pt-favoritelist' => 'l', # do not translate or duplicate this message to other languages
 39+'accesskey-ca-favorite' => 'w', # do not translate or duplicate this message to other languages
 40+'accesskey-ca-unfavorite' => 'w', # do not translate or duplicate this message to other languages
 41+'accesskey-favorite' => 'w', # do not translate or duplicate this message to other languages
 42+
 43+# Tooltip help for the actions
 44+'tooltip-pt-favoritelist' => 'The list of pages you are monitoring for changes',
 45+'tooltip-ca-favorite' => 'Add this page to your favorites',
 46+'tooltip-ca-unfavorite' => 'Remove this page from your favorites',
 47+'tooltip-favorite' => 'Add this page to your favorites',
 48+
 49+# 'all' in various places, this might be different for inflected languages
 50+'favoritelistall2' => 'all',
 51+
 52+# Favoritelist editor
 53+'favoritelistedit-numitems' => 'Your favorites contain {{PLURAL:$1|1 title|$1 titles}}, excluding talk pages.',
 54+'favoritelistedit-noitems' => 'Your favorites contain no titles.',
 55+'favoritelistedit-normal-title' => 'Edit favorites',
 56+'favoritelistedit-normal-legend' => 'Remove titles from favorites',
 57+'favoritelistedit-normal-explain' => 'Titles in your favorites are shown below.
 58+To remove a title, check the box next to it, and click "{{int:Favoritelistedit-normal-submit}}".
 59+You can also [[Special:Favoritelist/raw|edit the raw list]].',
 60+'favoritelistedit-normal-submit' => 'Remove titles',
 61+'favoritelistedit-normal-done' => '{{PLURAL:$1|1 title was|$1 titles were}} removed from your favorites:',
 62+'favoritelistedit-raw-title' => 'Edit raw favorites list',
 63+'favoritelistedit-raw-legend' => 'Edit raw favorites list',
 64+'favoritelistedit-raw-explain' => 'Titles in your favorites are shown below, and can be edited by adding to and removing from the list;
 65+one title per line.
 66+When finished, click "{{int:Favoritelistedit-raw-submit}}".
 67+You can also [[Special:Favoritelist/edit|use the standard editor]].',
 68+'favoritelistedit-raw-titles' => 'Titles:',
 69+'favoritelistedit-raw-submit' => 'Update favorites',
 70+'favoritelistedit-raw-done' => 'Your favorites have been updated.',
 71+'favoritelistedit-raw-added' => '{{PLURAL:$1|1 title was|$1 titles were}} added:',
 72+'favoritelistedit-raw-removed' => '{{PLURAL:$1|1 title was|$1 titles were}} removed:',
 73+
 74+# Favoritelist editing tools
 75+'favoritelisttools-view' => 'View Favorites',
 76+'favoritelisttools-edit' => 'View and edit favorites',
 77+'favoritelisttools-raw' => 'Edit raw favorites',
 78+
 79+);
\ No newline at end of file
Property changes on: trunk/extensions/Favorites/favorites.i18n.php
___________________________________________________________________
Added: svn:eol-style
180 + native
Index: trunk/extensions/Favorites/FavoritedItem.php
@@ -0,0 +1,131 @@
 2+<?php
 3+/**
 4+ * @file
 5+ * @ingroup Favoritelist
 6+ */
 7+
 8+/**
 9+ * @ingroup Favoritelist
 10+ */
 11+class FavoritedItem {
 12+ var $mTitle, $mUser, $id, $ns, $ti;
 13+
 14+ /**
 15+ * Create a FavoritedItem object with the given user and title
 16+ * @param $user User: the user to use for (un)favoriting
 17+ * @param $title Title: the title we're going to (un)favorite
 18+ * @return FavoritedItem object
 19+ */
 20+ public static function fromUserTitle( $user, $title ) {
 21+ $fl = new FavoritedItem;
 22+ $fl->mUser = $user;
 23+ $fl->mTitle = $title;
 24+ $fl->id = $user->getId();
 25+ $fl->ns = $title->getNamespace();
 26+ $fl->ti = $title->getDBkey();
 27+ return $fl;
 28+ }
 29+
 30+ /**
 31+ * Is mTitle being favorited by mUser?
 32+ * @return bool
 33+ */
 34+ public function isFavorited() {
 35+
 36+ $dbr = wfGetDB( DB_SLAVE );
 37+ $res = $dbr->select( 'favoritelist', 1, array( 'fl_user' => $this->id, 'fl_namespace' => $this->ns,
 38+ 'fl_title' => $this->ti ), __METHOD__ );
 39+ $isfavorited = ($dbr->numRows( $res ) > 0) ? 1 : 0;
 40+ return $isfavorited;
 41+ }
 42+
 43+ /**
 44+ * Given a title and user (assumes the object is setup), add the favorite to the
 45+ * database.
 46+ * @return bool (always true)
 47+ */
 48+ public function addFavorite() {
 49+ wfProfileIn( __METHOD__ );
 50+
 51+ $dbw = wfGetDB( DB_MASTER );
 52+ $dbw->insert( 'favoritelist',
 53+ array(
 54+ 'fl_user' => $this->id,
 55+ 'fl_namespace' => MWNamespace::getSubject($this->ns),
 56+ 'fl_title' => $this->ti,
 57+ 'fl_notificationtimestamp' => null
 58+ ), __METHOD__, 'IGNORE' );
 59+
 60+ wfProfileOut( __METHOD__ );
 61+ return true;
 62+ }
 63+
 64+ /**
 65+ * Same as addFavorite, only the opposite.
 66+ * @return bool
 67+ */
 68+ public function removeFavorite() {
 69+ $success = false;
 70+ $dbw = wfGetDB( DB_MASTER );
 71+ $dbw->delete( 'favoritelist',
 72+ array(
 73+ 'fl_user' => $this->id,
 74+ 'fl_namespace' => MWNamespace::getSubject($this->ns),
 75+ 'fl_title' => $this->ti
 76+ ), __METHOD__
 77+ );
 78+ if ( $dbw->affectedRows() ) {
 79+ $success = true;
 80+ }
 81+ return $success;
 82+ }
 83+
 84+ /**
 85+ * Check if the given title already is favorited by the user, and if so
 86+ * add favorite on a new title. To be used for page renames and such.
 87+ *
 88+ * @param $ot Title: page title to duplicate entries from, if present
 89+ * @param $nt Title: page title to add favorite on
 90+ */
 91+ public static function duplicateEntries( $ot, $nt ) {
 92+ FavoritedItem::doDuplicateEntries( $ot->getSubjectPage(), $nt->getSubjectPage() );
 93+ FavoritedItem::doDuplicateEntries( $ot->getTalkPage(), $nt->getTalkPage() );
 94+ }
 95+
 96+ /**
 97+ * Handle duplicate entries. Backend for duplicateEntries().
 98+ */
 99+ private static function doDuplicateEntries( $ot, $nt ) {
 100+ $oldnamespace = $ot->getNamespace();
 101+ $newnamespace = $nt->getNamespace();
 102+ $oldtitle = $ot->getDBkey();
 103+ $newtitle = $nt->getDBkey();
 104+
 105+ $dbw = wfGetDB( DB_MASTER );
 106+ $res = $dbw->select( 'favoritelist', 'fl_user',
 107+ array( 'fl_namespace' => $oldnamespace, 'fl_title' => $oldtitle ),
 108+ __METHOD__, 'FOR UPDATE'
 109+ );
 110+ # Construct array to replace into the favoritelist
 111+ $values = array();
 112+ while ( $s = $dbw->fetchObject( $res ) ) {
 113+ $values[] = array(
 114+ 'fl_user' => $s->fl_user,
 115+ 'fl_namespace' => $newnamespace,
 116+ 'fl_title' => $newtitle
 117+ );
 118+ }
 119+ $dbw->freeResult( $res );
 120+
 121+ if( empty( $values ) ) {
 122+ // Nothing to do
 123+ return true;
 124+ }
 125+
 126+ # Perform replace
 127+ # Note that multi-row replace is very efficient for MySQL but may be inefficient for
 128+ # some other DBMSes, mostly due to poor simulation by us
 129+ $dbw->replace( 'favoritelist', array( array( 'fl_user', 'fl_namespace', 'fl_title' ) ), $values, __METHOD__ );
 130+ return true;
 131+ }
 132+}
Property changes on: trunk/extensions/Favorites/FavoritedItem.php
___________________________________________________________________
Added: svn:eol-style
1133 + native
Index: trunk/extensions/Favorites/FavoritelistEditor.php
@@ -0,0 +1,512 @@
 2+<?php
 3+
 4+/**
 5+ * Provides the UI through which users can perform editing
 6+ * operations on their favoritelist
 7+ *
 8+ * @ingroup favoritelist
 9+ */
 10+class FavoritelistEditor {
 11+
 12+ /**
 13+ * Editing modes
 14+ */
 15+ const EDIT_CLEAR = 1;
 16+ const EDIT_RAW = 2;
 17+ const EDIT_NORMAL = 3;
 18+
 19+ /**
 20+ * Main execution point
 21+ *
 22+ * @param $user User
 23+ * @param $output OutputPage
 24+ * @param $request WebRequest
 25+ * @param $mode int
 26+ */
 27+ public function execute( $user, $output, $request, $mode ) {
 28+ global $wgUser;
 29+ if( wfReadOnly() ) {
 30+ $output->readOnlyPage();
 31+ return;
 32+ }
 33+ switch( $mode ) {
 34+ case self::EDIT_CLEAR:
 35+ // The "Clear" link scared people too much.
 36+ // Pass on to the raw editor, from which it's very easy to clear.
 37+ case self::EDIT_RAW:
 38+ $output->setPageTitle( wfMsg( 'favoritelistedit-raw-title' ) );
 39+ if( $request->wasPosted() && $this->checkToken( $request, $wgUser ) ) {
 40+ $wanted = $this->extractTitles( $request->getText( 'titles' ) );
 41+ $current = $this->getFavoritelist( $user );
 42+ if( count( $wanted ) > 0 ) {
 43+ $toFavorite = array_diff( $wanted, $current );
 44+ $toUnfavorite = array_diff( $current, $wanted );
 45+ $this->favoriteTitles( $toFavorite, $user );
 46+ $this->unfavoriteTitles( $toUnfavorite, $user );
 47+ $user->invalidateCache();
 48+ if( count( $toFavorite ) > 0 || count( $toUnfavorite ) > 0 )
 49+ $output->addHTML( wfMsgExt( 'favoritelistedit-raw-done', 'parse' ) );
 50+ if( ( $count = count( $toFavorite ) ) > 0 ) {
 51+ $output->addHTML( wfMsgExt( 'favoritelistedit-raw-added', 'parse', $count ) );
 52+ $this->showTitles( $toFavorite, $output, $wgUser->getSkin() );
 53+ }
 54+ if( ( $count = count( $toUnfavorite ) ) > 0 ) {
 55+ $output->addHTML( wfMsgExt( 'favoritelistedit-raw-removed', 'parse', $count ) );
 56+ $this->showTitles( $toUnfavorite, $output, $wgUser->getSkin() );
 57+ }
 58+ } else {
 59+ $this->clearFavoritelist( $user );
 60+ $user->invalidateCache();
 61+ $output->addHTML( wfMsgExt( 'favoritelistedit-raw-removed', 'parse', count( $current ) ) );
 62+ $this->showTitles( $current, $output, $wgUser->getSkin() );
 63+ }
 64+ }
 65+ $this->showRawForm( $output, $user );
 66+ break;
 67+ case self::EDIT_NORMAL:
 68+ $output->setPageTitle( wfMsg( 'favoritelistedit-normal-title' ) );
 69+ if( $request->wasPosted() && $this->checkToken( $request, $wgUser ) ) {
 70+ $titles = $this->extractTitles( $request->getArray( 'titles' ) );
 71+ $this->unfavoriteTitles( $titles, $user );
 72+ $user->invalidateCache();
 73+ $output->addHTML( wfMsgExt( 'favoritelistedit-normal-done', 'parse',
 74+ $GLOBALS['wgLang']->formatNum( count( $titles ) ) ) );
 75+ $this->showTitles( $titles, $output, $wgUser->getSkin() );
 76+ }
 77+ $this->showNormalForm( $output, $user );
 78+ }
 79+ }
 80+
 81+ /**
 82+ * Check the edit token from a form submission
 83+ *
 84+ * @param $request WebRequest
 85+ * @param $user User
 86+ * @return bool
 87+ */
 88+ private function checkToken( $request, $user ) {
 89+ return $user->matchEditToken( $request->getVal( 'token' ), 'favoritelistedit' );
 90+ }
 91+
 92+ /**
 93+ * Extract a list of titles from a blob of text, returning
 94+ * (prefixed) strings; unfavoritable titles are ignored
 95+ *
 96+ * @param $list mixed
 97+ * @return array
 98+ */
 99+ private function extractTitles( $list ) {
 100+ $titles = array();
 101+ if( !is_array( $list ) ) {
 102+ $list = explode( "\n", trim( $list ) );
 103+ if( !is_array( $list ) )
 104+ return array();
 105+ }
 106+ foreach( $list as $text ) {
 107+ $text = trim( $text );
 108+ if( strlen( $text ) > 0 ) {
 109+ $title = Title::newFromText( $text );
 110+ //if( $title instanceof Title && $title->isFavoritable() )
 111+ $titles[] = $title->getPrefixedText();
 112+ }
 113+ }
 114+ return array_unique( $titles );
 115+ }
 116+
 117+ /**
 118+ * Print out a list of linked titles
 119+ *
 120+ * $titles can be an array of strings or Title objects; the former
 121+ * is preferred, since Titles are very memory-heavy
 122+ *
 123+ * @param $titles An array of strings, or Title objects
 124+ * @param $output OutputPage
 125+ * @param $skin Skin
 126+ */
 127+ private function showTitles( $titles, $output, $skin ) {
 128+ $talk = wfMsgHtml( 'talkpagelinktext' );
 129+ // Do a batch existence check
 130+ $batch = new LinkBatch();
 131+ foreach( $titles as $title ) {
 132+ if( !$title instanceof Title )
 133+ $title = Title::newFromText( $title );
 134+ if( $title instanceof Title ) {
 135+ $batch->addObj( $title );
 136+ $batch->addObj( $title->getTalkPage() );
 137+ }
 138+ }
 139+ $batch->execute();
 140+ // Print out the list
 141+ $output->addHTML( "<ul>\n" );
 142+ foreach( $titles as $title ) {
 143+ if( !$title instanceof Title )
 144+ $title = Title::newFromText( $title );
 145+ if( $title instanceof Title ) {
 146+ $output->addHTML( "<li>" . $skin->link( $title )
 147+ . ' (' . $skin->link( $title->getTalkPage(), $talk ) . ")</li>\n" );
 148+ }
 149+ }
 150+ $output->addHTML( "</ul>\n" );
 151+ }
 152+
 153+ /**
 154+ * Count the number of titles on a user's favoritelist, excluding talk pages
 155+ *
 156+ * @param $user User
 157+ * @return int
 158+ */
 159+ private function countFavoritelist( $user ) {
 160+ $dbr = wfGetDB( DB_MASTER );
 161+ $res = $dbr->select( 'favoritelist', 'COUNT(*) AS count', array( 'fl_user' => $user->getId() ), __METHOD__ );
 162+ $row = $dbr->fetchObject( $res );
 163+ return ceil( $row->count / 2 ); // Paranoia
 164+ }
 165+
 166+ /**
 167+ * Prepare a list of titles on a user's favoritelist (excluding talk pages)
 168+ * and return an array of (prefixed) strings
 169+ *
 170+ * @param $user User
 171+ * @return array
 172+ */
 173+ private function getFavoritelist( $user ) {
 174+ $list = array();
 175+ $dbr = wfGetDB( DB_MASTER );
 176+ $res = $dbr->select(
 177+ 'favoritelist',
 178+ '*',
 179+ array(
 180+ 'fl_user' => $user->getId(),
 181+ ),
 182+ __METHOD__
 183+ );
 184+ if( $res->numRows() > 0 ) {
 185+ while( $row = $res->fetchObject() ) {
 186+ $title = Title::makeTitleSafe( $row->fl_namespace, $row->fl_title );
 187+ if( $title instanceof Title && !$title->isTalkPage() )
 188+ $list[] = $title->getPrefixedText();
 189+ }
 190+ $res->free();
 191+ }
 192+ return $list;
 193+ }
 194+
 195+ /**
 196+ * Get a list of titles on a user's favoritelist, excluding talk pages,
 197+ * and return as a two-dimensional array with namespace, title and
 198+ * redirect status
 199+ *
 200+ * @param $user User
 201+ * @return array
 202+ */
 203+ private function getFavoritelistInfo( $user ) {
 204+ $titles = array();
 205+ $dbr = wfGetDB( DB_MASTER );
 206+ $uid = intval( $user->getId() );
 207+ list( $favoritelist, $page ) = $dbr->tableNamesN( 'favoritelist', 'page' );
 208+ $sql = "SELECT fl_namespace, fl_title, page_id, page_len, page_is_redirect
 209+ FROM {$favoritelist} LEFT JOIN {$page} ON ( fl_namespace = page_namespace
 210+ AND fl_title = page_title ) WHERE fl_user = {$uid}";
 211+ $res = $dbr->query( $sql, __METHOD__ );
 212+ if( $res && $dbr->numRows( $res ) > 0 ) {
 213+ $cache = LinkCache::singleton();
 214+ while( $row = $dbr->fetchObject( $res ) ) {
 215+ $title = Title::makeTitleSafe( $row->fl_namespace, $row->fl_title );
 216+ if( $title instanceof Title ) {
 217+ // Update the link cache while we're at it
 218+ if( $row->page_id ) {
 219+ $cache->addGoodLinkObj( $row->page_id, $title, $row->page_len, $row->page_is_redirect );
 220+ } else {
 221+ $cache->addBadLinkObj( $title );
 222+ }
 223+ // Ignore non-talk
 224+ if( !$title->isTalkPage() )
 225+ $titles[$row->fl_namespace][$row->fl_title] = $row->page_is_redirect;
 226+ }
 227+ }
 228+ }
 229+ return $titles;
 230+ }
 231+
 232+ /**
 233+ * Show a message indicating the number of items on the user's favoritelist,
 234+ * and return this count for additional checking
 235+ *
 236+ * @param $output OutputPage
 237+ * @param $user User
 238+ * @return int
 239+ */
 240+ private function showItemCount( $output, $user ) {
 241+ if( ( $count = $this->countFavoritelist( $user ) ) > 0 ) {
 242+ $output->addHTML( wfMsgExt( 'favoritelistedit-numitems', 'parse',
 243+ $GLOBALS['wgLang']->formatNum( $count ) ) );
 244+ } else {
 245+ $output->addHTML( wfMsgExt( 'favoritelistedit-noitems', 'parse' ) );
 246+ }
 247+ return $count;
 248+ }
 249+
 250+ /**
 251+ * Remove all titles from a user's favoritelist
 252+ *
 253+ * @param $user User
 254+ */
 255+ private function clearFavoritelist( $user ) {
 256+ $dbw = wfGetDB( DB_MASTER );
 257+ $dbw->delete( 'favoritelist', array( 'fl_user' => $user->getId() ), __METHOD__ );
 258+ }
 259+
 260+ /**
 261+ * Add a list of titles to a user's favoritelist
 262+ *
 263+ * $titles can be an array of strings or Title objects; the former
 264+ * is preferred, since Titles are very memory-heavy
 265+ *
 266+ * @param $titles An array of strings, or Title objects
 267+ * @param $user User
 268+ */
 269+ private function favoriteTitles( $titles, $user ) {
 270+ $dbw = wfGetDB( DB_MASTER );
 271+ $rows = array();
 272+ foreach( $titles as $title ) {
 273+ if( !$title instanceof Title )
 274+ $title = Title::newFromText( $title );
 275+ if( $title instanceof Title ) {
 276+ $rows[] = array(
 277+ 'fl_user' => $user->getId(),
 278+ 'fl_namespace' => ( $title->getNamespace() & ~1 ),
 279+ 'fl_title' => $title->getDBkey(),
 280+ 'fl_notificationtimestamp' => null,
 281+ );
 282+ $rows[] = array(
 283+ 'fl_user' => $user->getId(),
 284+ 'fl_namespace' => ( $title->getNamespace() | 1 ),
 285+ 'fl_title' => $title->getDBkey(),
 286+ 'fl_notificationtimestamp' => null,
 287+ );
 288+ }
 289+ }
 290+ $dbw->insert( 'favoritelist', $rows, __METHOD__, 'IGNORE' );
 291+ }
 292+
 293+ /**
 294+ * Remove a list of titles from a user's favoritelist
 295+ *
 296+ * $titles can be an array of strings or Title objects; the former
 297+ * is preferred, since Titles are very memory-heavy
 298+ *
 299+ * @param $titles An array of strings, or Title objects
 300+ * @param $user User
 301+ */
 302+ private function unfavoriteTitles( $titles, $user ) {
 303+ $dbw = wfGetDB( DB_MASTER );
 304+ foreach( $titles as $title ) {
 305+ if( !$title instanceof Title )
 306+ $title = Title::newFromText( $title );
 307+ if( $title instanceof Title ) {
 308+ $dbw->delete(
 309+ 'favoritelist',
 310+ array(
 311+ 'fl_user' => $user->getId(),
 312+ 'fl_namespace' => ( $title->getNamespace() & ~1 ),
 313+ 'fl_title' => $title->getDBkey(),
 314+ ),
 315+ __METHOD__
 316+ );
 317+ $dbw->delete(
 318+ 'favoritelist',
 319+ array(
 320+ 'fl_user' => $user->getId(),
 321+ 'fl_namespace' => ( $title->getNamespace() | 1 ),
 322+ 'fl_title' => $title->getDBkey(),
 323+ ),
 324+ __METHOD__
 325+ );
 326+ $article = new Article($title);
 327+ wfRunHooks('UnfavoriteArticleComplete',array(&$user,&$article));
 328+ }
 329+ }
 330+ }
 331+
 332+ /**
 333+ * Show the standard favoritelist editing form
 334+ *
 335+ * @param $output OutputPage
 336+ * @param $user User
 337+ */
 338+ private function showNormalForm( $output, $user ) {
 339+ global $wgUser;
 340+ if( ( $count = $this->showItemCount( $output, $user ) ) > 0 ) {
 341+ $self = SpecialPage::getTitleFor( 'Favoritelist' );
 342+ $form = Xml::openElement( 'form', array( 'method' => 'post',
 343+ 'action' => $self->getLocalUrl( array( 'action' => 'edit' ) ) ) );
 344+ $form .= Xml::hidden( 'token', $wgUser->editToken( 'favoritelistedit' ) );
 345+ $form .= "<fieldset>\n<legend>" . wfMsgHtml( 'favoritelistedit-normal-legend' ) . "</legend>";
 346+ $form .= wfMsgExt( 'favoritelistedit-normal-explain', 'parse' );
 347+ $form .= $this->buildRemoveList( $user, $wgUser->getSkin() );
 348+ $form .= '<p>' . Xml::submitButton( wfMsg( 'favoritelistedit-normal-submit' ) ) . '</p>';
 349+ $form .= '</fieldset></form>';
 350+ $output->addHTML( $form );
 351+ }
 352+ }
 353+
 354+ /**
 355+ * Build the part of the standard favoritelist editing form with the actual
 356+ * title selection checkboxes and stuff. Also generates a table of
 357+ * contents if there's more than one heading.
 358+ *
 359+ * @param $user User
 360+ * @param $skin Skin (really, Linker)
 361+ */
 362+ private function buildRemoveList( $user, $skin ) {
 363+ $list = "";
 364+ $toc = $skin->tocIndent();
 365+ $tocLength = 0;
 366+ foreach( $this->getFavoritelistInfo( $user ) as $namespace => $pages ) {
 367+ $tocLength++;
 368+ $heading = htmlspecialchars( $this->getNamespaceHeading( $namespace ) );
 369+ $anchor = "editfavoritelist-ns" . $namespace;
 370+
 371+ $list .= $skin->makeHeadLine( 2, ">", $anchor, $heading, "" );
 372+ $toc .= $skin->tocLine( $anchor, $heading, $tocLength, 1 ) . $skin->tocLineEnd();
 373+
 374+ $list .= "<ul>\n";
 375+ foreach( $pages as $dbkey => $redirect ) {
 376+ $title = Title::makeTitleSafe( $namespace, $dbkey );
 377+ $list .= $this->buildRemoveLine( $title, $redirect, $skin );
 378+ }
 379+ $list .= "</ul>\n";
 380+ }
 381+ // ISSUE: omit the TOC if the total number of titles is low?
 382+ if( $tocLength > 1 ) {
 383+ $list = $skin->tocList( $toc ) . $list;
 384+ }
 385+ return $list;
 386+ }
 387+
 388+ /**
 389+ * Get the correct "heading" for a namespace
 390+ *
 391+ * @param $namespace int
 392+ * @return string
 393+ */
 394+ private function getNamespaceHeading( $namespace ) {
 395+ return $namespace == NS_MAIN
 396+ ? wfMsgHtml( 'blanknamespace' )
 397+ : htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( $namespace ) );
 398+ }
 399+
 400+ /**
 401+ * Build a single list item containing a check box selecting a title
 402+ * and a link to that title, with various additional bits
 403+ *
 404+ * @param $title Title
 405+ * @param $redirect bool
 406+ * @param $skin Skin
 407+ * @return string
 408+ */
 409+ private function buildRemoveLine( $title, $redirect, $skin ) {
 410+ global $wgLang;
 411+
 412+ $link = $skin->link( $title );
 413+ if( $redirect )
 414+ $link = '<span class="favoritelistredir">' . $link . '</span>';
 415+ $tools[] = $skin->link( $title->getTalkPage(), wfMsgHtml( 'talkpagelinktext' ) );
 416+ if( $title->exists() ) {
 417+ $tools[] = $skin->link(
 418+ $title,
 419+ wfMsgHtml( 'history_short' ),
 420+ array(),
 421+ array( 'action' => 'history' ),
 422+ array( 'known', 'noclasses' )
 423+ );
 424+ }
 425+ if( $title->getNamespace() == NS_USER && !$title->isSubpage() ) {
 426+ $tools[] = $skin->link(
 427+ SpecialPage::getTitleFor( 'Contributions', $title->getText() ),
 428+ wfMsgHtml( 'contributions' ),
 429+ array(),
 430+ array(),
 431+ array( 'known', 'noclasses' )
 432+ );
 433+ }
 434+ return "<li>"
 435+ . Xml::check( 'titles[]', false, array( 'value' => $title->getPrefixedText() ) )
 436+ . $link . " (" . $wgLang->pipeList( $tools ) . ")" . "</li>\n";
 437+ }
 438+
 439+ /**
 440+ * Show a form for editing the favoritelist in "raw" mode
 441+ *
 442+ * @param $output OutputPage
 443+ * @param $user User
 444+ */
 445+ public function showRawForm( $output, $user ) {
 446+ global $wgUser;
 447+ $this->showItemCount( $output, $user );
 448+ $self = SpecialPage::getTitleFor( 'Favoritelist' );
 449+ $form = Xml::openElement( 'form', array( 'method' => 'post',
 450+ 'action' => $self->getLocalUrl( array( 'action' => 'raw' ) ) ) );
 451+ $form .= Xml::hidden( 'token', $wgUser->editToken( 'favoritelistedit' ) );
 452+ $form .= '<fieldset><legend>' . wfMsgHtml( 'favoritelistedit-raw-legend' ) . '</legend>';
 453+ $form .= wfMsgExt( 'favoritelistedit-raw-explain', 'parse' );
 454+ $form .= Xml::label( wfMsg( 'favoritelistedit-raw-titles' ), 'titles' );
 455+ $form .= "<br />\n";
 456+ $form .= Xml::openElement( 'textarea', array( 'id' => 'titles', 'name' => 'titles',
 457+ 'rows' => $wgUser->getIntOption( 'rows' ), 'cols' => $wgUser->getIntOption( 'cols' ) ) );
 458+ $titles = $this->getFavoritelist( $user );
 459+ foreach( $titles as $title )
 460+ $form .= htmlspecialchars( $title ) . "\n";
 461+ $form .= '</textarea>';
 462+ $form .= '<p>' . Xml::submitButton( wfMsg( 'favoritelistedit-raw-submit' ) ) . '</p>';
 463+ $form .= '</fieldset></form>';
 464+ $output->addHTML( $form );
 465+ }
 466+
 467+ /**
 468+ * Determine whether we are editing the favoritelist, and if so, what
 469+ * kind of editing operation
 470+ *
 471+ * @param $request WebRequest
 472+ * @param $par mixed
 473+ * @return int
 474+ */
 475+ public static function getMode( $request, $par ) {
 476+ $mode = strtolower( $request->getVal( 'action', $par ) );
 477+ switch( $mode ) {
 478+ case 'clear':
 479+ return self::EDIT_CLEAR;
 480+ case 'raw':
 481+ return self::EDIT_RAW;
 482+ case 'edit':
 483+ return self::EDIT_NORMAL;
 484+ default:
 485+ return false;
 486+ }
 487+ }
 488+
 489+ /**
 490+ * Build a set of links for convenient navigation
 491+ * between favoritelist viewing and editing modes
 492+ *
 493+ * @param $skin Skin to use
 494+ * @return string
 495+ */
 496+ public static function buildTools( $skin ) {
 497+ global $wgLang;
 498+
 499+ $tools = array();
 500+ $modes = array( 'view' => false, 'edit' => 'edit', 'raw' => 'raw' );
 501+ foreach( $modes as $mode => $subpage ) {
 502+ // can use messages 'favoritelisttools-view', 'favoritelisttools-edit', 'favoritelisttools-raw'
 503+ $tools[] = $skin->link(
 504+ SpecialPage::getTitleFor( 'Favoritelist', $subpage ),
 505+ wfMsgHtml( "favoritelisttools-{$mode}" ),
 506+ array(),
 507+ array(),
 508+ array( 'known', 'noclasses' )
 509+ );
 510+ }
 511+ return $wgLang->pipeList( $tools );
 512+ }
 513+}
Property changes on: trunk/extensions/Favorites/FavoritelistEditor.php
___________________________________________________________________
Added: svn:eol-style
1514 + native
Index: trunk/extensions/Favorites/Favorites.php
@@ -0,0 +1,140 @@
 2+<?php
 3+/*
 4+Copyright (C) 2011 Jeremy Lemley
 5+
 6+ This program is free software: you can redistribute it and/or modify
 7+ it under the terms of the GNU General Public License as published by
 8+ the Free Software Foundation, either version 3 of the License, or
 9+ (at your option) any later version.
 10+
 11+ This program is distributed in the hope that it will be useful,
 12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
 13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14+ GNU General Public License for more details.
 15+
 16+ You should have received a copy of the GNU General Public License
 17+ along with this program. If not, see <http://www.gnu.org/licenses/>.
 18+
 19+*/
 20+
 21+$wgExtensionCredits['specialpage'][] = array(
 22+ 'path' => __FILE__,
 23+ 'name' => 'Favorites',
 24+ 'author' => 'Jeremy Lemley',
 25+ 'description' => 'A method of creating a favorites list',
 26+ 'version' => '0.0.2',
 27+ 'url' => "http://www.mediawiki.org/wiki/Extension:Favorites",
 28+);
 29+
 30+
 31+
 32+
 33+$dir = dirname(__FILE__) . '/';
 34+$wgExtensionMessagesFiles['Favorites'] = $dir . 'favorites.i18n.php';
 35+$wgAutoloadClasses['Favorites'] = $dir . 'Favorites_body.php';
 36+$wgAutoloadClasses['FavoritelistEditor'] = $dir . 'FavoritelistEditor.php';
 37+$wgAutoloadClasses['FavoritedItem'] = $dir . 'FavoritedItem.php';
 38+$wgAutoloadClasses['SpecialFavoritelist'] = $dir . 'SpecialFavoritelist.php';
 39+$wgAutoloadClasses['APIQueryFavoritelistRaw'] = $dir . 'APIQueryFavoritelistRaw.php';
 40+$wgAutoloadClasses['APIFavorite'] = $dir . 'APIFavorite.php';
 41+$wgAutoloadClasses['FavUser'] = $dir . 'FavUser.php';
 42+$wgAutoloadClasses['FavArticle'] = $dir . 'FavArticle.php';
 43+$wgAutoloadClasses['FavTitle'] = $dir . 'FavTitle.php';
 44+$wgAutoloadClasses['FavParser'] = $dir . 'FavParser.php';
 45+$wgHooks['LoadExtensionSchemaUpdates'][] = 'FavSQLUpdate';
 46+$wgSpecialPages['Favoritelist'] = 'SpecialFavoritelist';
 47+$wgSpecialPageGroups['Favoritelist'] = 'other';
 48+
 49+
 50+//tag hook
 51+$wgHooks['ParserFirstCallInit'][] = 'ParseFavorites';
 52+
 53+
 54+//add the icon / link
 55+$wgHooks['SkinTemplateNavigation'][] = 'fnNavUrls';
 56+
 57+//add or remove
 58+$wgHooks['UnknownAction'][] = 'fnAction';
 59+
 60+//handle page moves
 61+$wgHooks['TitleMoveComplete'][] = 'fnHookMoveToFav';
 62+
 63+//add CSS
 64+$wgHooks['BeforePageDisplay'][] = 'fnAddCss';
 65+
 66+
 67+function fnAction ($action, $article) {
 68+ $title = new Title();
 69+ $favArticle = new FavArticle($title);
 70+
 71+ if ($action == 'unfavorite') {
 72+ $favArticle->unfavorite($action, $article);
 73+
 74+ } elseif ($action == 'favorite') {
 75+ $favArticle->favorite($action, $article);
 76+ } else {
 77+ return true;
 78+ }
 79+ return false;
 80+}
 81+
 82+function fnNavUrls(&$sktemplate, &$links) {
 83+ $fNav = new Favorites();
 84+ $fNav->favoritesIcon($sktemplate, $links);
 85+ return true;
 86+}
 87+
 88+function fnHookMoveToFav(&$title, &$nt, &$wgUser, $pageid, $redirid ) {
 89+ $favTitle = new FavTitle();
 90+ $favTitle->moveToFav($title, $nt, $wgUser, $pageid, $redirid );
 91+ return true;
 92+}
 93+
 94+function fnAddCss (&$out) {
 95+ global $wgScriptPath;
 96+ $out->addStyle($wgScriptPath. '/extensions/favorites/favorites.css');
 97+ //$out->addInlineScript($wgScriptPath . '/extensions/favorites/ajaxfavorite.js');
 98+ return true;
 99+}
 100+
 101+function ParseFavorites(Parser &$parser) {
 102+
 103+ $parser->setHook( 'favorites', 'favParser_Render' );
 104+
 105+ return true;
 106+}
 107+
 108+
 109+$markerList = array();
 110+function favParser_Render ( $input, $argv, $parser) {
 111+ # The parser function itself
 112+ # The input parameters are wikitext with templates expanded
 113+ # The output should be wikitext too
 114+ $output = "Parser Output goes here.";
 115+
 116+ $favParse = new FavParser();
 117+ $output = $favParse->wfSpecialFavoritelist();
 118+ $parser->disableCache();
 119+ return $output;
 120+
 121+}
 122+
 123+
 124+# Schema updates for update.php
 125+function FavSQLUpdate( $updater = null ) {
 126+ if ( $updater === null ) {
 127+ // <= 1.16 support
 128+ global $wgExtNewTables, $wgExtModifiedFields;
 129+ $wgExtNewTables[] = array(
 130+ 'favoritelist',
 131+ dirname( __FILE__ ) . '/favorites.sql'
 132+ );
 133+
 134+ } else {
 135+ // >= 1.17 support
 136+ $updater->addExtensionUpdate( array( 'addTable', 'favoritelist',
 137+ dirname( __FILE__ ) . '/favorites.sql', true ) );
 138+
 139+ }
 140+ return true;
 141+}
\ No newline at end of file
Property changes on: trunk/extensions/Favorites/Favorites.php
___________________________________________________________________
Added: svn:eol-style
1142 + native
Index: trunk/extensions/Favorites/favorites.css
@@ -0,0 +1,43 @@
 2+
 3+/* Favorite/Unfavorite Icon Styling */
 4+
 5+#ca-unfavorite.icon,
 6+#ca-favorite.icon {
 7+ margin-right:3px;
 8+ margin-left:0px;
 9+}
 10+#ca-unfavorite.icon a,
 11+#ca-favorite.icon a {
 12+ margin: 0;
 13+ padding: 0;
 14+ outline: none;
 15+ display: block;
 16+ width: 22px;
 17+ height: 2.5em;
 18+
 19+}
 20+#ca-favorite.icon a {
 21+ background-image: url(images/fav-icons.png);
 22+ background-position: 3px 60%;
 23+}
 24+#ca-unfavorite.icon a {
 25+ background-image: url(images/fav-icons.png);
 26+ background-position: -45px 60%;
 27+}
 28+#ca-favorite.icon a:hover {
 29+ background-image: url(images/fav-icons.png);
 30+ background-position: -45px 60%;
 31+}
 32+#ca-unfavorite.icon a:hover {
 33+ background-image: url(images/fav-icons.png);
 34+ background-position: 3px 60%;
 35+}
 36+#ca-unfavorite.icon a.loading,
 37+#ca-favorite.icon a.loading {
 38+ background-image: url(images/fav-icon-loading.gif);
 39+ background-position: center 60%;
 40+}
 41+#ca-unfavorite.icon a span,
 42+#ca-favorite.icon a span {
 43+ display: none;
 44+}
Property changes on: trunk/extensions/Favorites/favorites.css
___________________________________________________________________
Added: svn:eol-style
145 + native

Follow-up revisions

RevisionCommit summaryAuthorDate
r93174Followup r93154: Tweak extension credits and message files for consisteny...raymond13:15, 26 July 2011

Status & tagging log