MediaWiki  REL1_22
WatchedItem.php
Go to the documentation of this file.
00001 <?php
00029 class WatchedItem {
00035     const IGNORE_USER_RIGHTS = 0;
00036 
00042     const CHECK_USER_RIGHTS = 1;
00043 
00044     var $mTitle, $mUser, $mCheckRights;
00045     private $loaded = false, $watched, $timestamp;
00046 
00056     public static function fromUserTitle( $user, $title, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) {
00057         $wl = new WatchedItem;
00058         $wl->mUser = $user;
00059         $wl->mTitle = $title;
00060         $wl->mCheckRights = $checkRights;
00061 
00062         return $wl;
00063     }
00064 
00069     protected function getTitle() {
00070         return $this->mTitle;
00071     }
00072 
00074     protected function getTitleNs() {
00075         return $this->getTitle()->getNamespace();
00076     }
00077 
00079     protected function getTitleDBkey() {
00080         return $this->getTitle()->getDBkey();
00081     }
00083     protected function getUserId() {
00084         return $this->mUser->getId();
00085     }
00086 
00093     private function dbCond() {
00094         return array(
00095             'wl_user' => $this->getUserId(),
00096             'wl_namespace' => $this->getTitleNs(),
00097             'wl_title' => $this->getTitleDBkey(),
00098         );
00099     }
00100 
00104     private function load() {
00105         if ( $this->loaded ) {
00106             return;
00107         }
00108         $this->loaded = true;
00109 
00110         // Only loggedin user can have a watchlist
00111         if ( $this->mUser->isAnon() ) {
00112             $this->watched = false;
00113             return;
00114         }
00115 
00116         # Pages and their talk pages are considered equivalent for watching;
00117         # remember that talk namespaces are numbered as page namespace+1.
00118 
00119         $dbr = wfGetDB( DB_SLAVE );
00120         $row = $dbr->selectRow( 'watchlist', 'wl_notificationtimestamp',
00121             $this->dbCond(), __METHOD__ );
00122 
00123         if ( $row === false ) {
00124             $this->watched = false;
00125         } else {
00126             $this->watched = true;
00127             $this->timestamp = $row->wl_notificationtimestamp;
00128         }
00129     }
00130 
00135     private function isAllowed( $what ) {
00136         return !$this->mCheckRights || $this->mUser->isAllowed( $what );
00137     }
00138 
00143     public function isWatched() {
00144         if ( !$this->isAllowed( 'viewmywatchlist' ) ) {
00145             return false;
00146         }
00147 
00148         $this->load();
00149         return $this->watched;
00150     }
00151 
00158     public function getNotificationTimestamp() {
00159         if ( !$this->isAllowed( 'viewmywatchlist' ) ) {
00160             return false;
00161         }
00162 
00163         $this->load();
00164         if ( $this->watched ) {
00165             return $this->timestamp;
00166         } else {
00167             return false;
00168         }
00169     }
00170 
00177     public function resetNotificationTimestamp( $force = '' ) {
00178         // Only loggedin user can have a watchlist
00179         if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) {
00180             return;
00181         }
00182 
00183         if ( $force != 'force' ) {
00184             $this->load();
00185             if ( !$this->watched || $this->timestamp === null ) {
00186                 return;
00187             }
00188         }
00189 
00190         // If the page is watched by the user (or may be watched), update the timestamp on any
00191         // any matching rows
00192         $dbw = wfGetDB( DB_MASTER );
00193         $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => null ),
00194             $this->dbCond(), __METHOD__ );
00195         $this->timestamp = null;
00196     }
00197 
00203     public function addWatch() {
00204         wfProfileIn( __METHOD__ );
00205 
00206         // Only loggedin user can have a watchlist
00207         if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) {
00208             wfProfileOut( __METHOD__ );
00209             return false;
00210         }
00211 
00212         // Use INSERT IGNORE to avoid overwriting the notification timestamp
00213         // if there's already an entry for this page
00214         $dbw = wfGetDB( DB_MASTER );
00215         $dbw->insert( 'watchlist',
00216             array(
00217                 'wl_user' => $this->getUserId(),
00218                 'wl_namespace' => MWNamespace::getSubject( $this->getTitleNs() ),
00219                 'wl_title' => $this->getTitleDBkey(),
00220                 'wl_notificationtimestamp' => null
00221             ), __METHOD__, 'IGNORE' );
00222 
00223         // Every single watched page needs now to be listed in watchlist;
00224         // namespace:page and namespace_talk:page need separate entries:
00225         $dbw->insert( 'watchlist',
00226             array(
00227                 'wl_user' => $this->getUserId(),
00228                 'wl_namespace' => MWNamespace::getTalk( $this->getTitleNs() ),
00229                 'wl_title' => $this->getTitleDBkey(),
00230                 'wl_notificationtimestamp' => null
00231             ), __METHOD__, 'IGNORE' );
00232 
00233         $this->watched = true;
00234 
00235         wfProfileOut( __METHOD__ );
00236         return true;
00237     }
00238 
00243     public function removeWatch() {
00244         wfProfileIn( __METHOD__ );
00245 
00246         // Only loggedin user can have a watchlist
00247         if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) {
00248             wfProfileOut( __METHOD__ );
00249             return false;
00250         }
00251 
00252         $success = false;
00253         $dbw = wfGetDB( DB_MASTER );
00254         $dbw->delete( 'watchlist',
00255             array(
00256                 'wl_user' => $this->getUserId(),
00257                 'wl_namespace' => MWNamespace::getSubject( $this->getTitleNs() ),
00258                 'wl_title' => $this->getTitleDBkey(),
00259             ), __METHOD__
00260         );
00261         if ( $dbw->affectedRows() ) {
00262             $success = true;
00263         }
00264 
00265         # the following code compensates the new behavior, introduced by the
00266         # enotif patch, that every single watched page needs now to be listed
00267         # in watchlist namespace:page and namespace_talk:page had separate
00268         # entries: clear them
00269         $dbw->delete( 'watchlist',
00270             array(
00271                 'wl_user' => $this->getUserId(),
00272                 'wl_namespace' => MWNamespace::getTalk( $this->getTitleNs() ),
00273                 'wl_title' => $this->getTitleDBkey(),
00274             ), __METHOD__
00275         );
00276 
00277         if ( $dbw->affectedRows() ) {
00278             $success = true;
00279         }
00280 
00281         $this->watched = false;
00282 
00283         wfProfileOut( __METHOD__ );
00284         return $success;
00285     }
00286 
00294     public static function duplicateEntries( $ot, $nt ) {
00295         WatchedItem::doDuplicateEntries( $ot->getSubjectPage(), $nt->getSubjectPage() );
00296         WatchedItem::doDuplicateEntries( $ot->getTalkPage(), $nt->getTalkPage() );
00297     }
00298 
00307     private static function doDuplicateEntries( $ot, $nt ) {
00308         $oldnamespace = $ot->getNamespace();
00309         $newnamespace = $nt->getNamespace();
00310         $oldtitle = $ot->getDBkey();
00311         $newtitle = $nt->getDBkey();
00312 
00313         $dbw = wfGetDB( DB_MASTER );
00314         $res = $dbw->select( 'watchlist', 'wl_user',
00315             array( 'wl_namespace' => $oldnamespace, 'wl_title' => $oldtitle ),
00316             __METHOD__, 'FOR UPDATE'
00317         );
00318         # Construct array to replace into the watchlist
00319         $values = array();
00320         foreach ( $res as $s ) {
00321             $values[] = array(
00322                 'wl_user' => $s->wl_user,
00323                 'wl_namespace' => $newnamespace,
00324                 'wl_title' => $newtitle
00325             );
00326         }
00327 
00328         if ( empty( $values ) ) {
00329             // Nothing to do
00330             return true;
00331         }
00332 
00333         # Perform replace
00334         # Note that multi-row replace is very efficient for MySQL but may be inefficient for
00335         # some other DBMSes, mostly due to poor simulation by us
00336         $dbw->replace( 'watchlist', array( array( 'wl_user', 'wl_namespace', 'wl_title' ) ), $values, __METHOD__ );
00337         return true;
00338     }
00339 }