MediaWiki
REL1_23
|
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 00077 protected function getTitleNs() { 00078 return $this->getTitle()->getNamespace(); 00079 } 00080 00085 protected function getTitleDBkey() { 00086 return $this->getTitle()->getDBkey(); 00087 } 00088 00093 protected function getUserId() { 00094 return $this->mUser->getId(); 00095 } 00096 00103 private function dbCond() { 00104 return array( 00105 'wl_user' => $this->getUserId(), 00106 'wl_namespace' => $this->getTitleNs(), 00107 'wl_title' => $this->getTitleDBkey(), 00108 ); 00109 } 00110 00114 private function load() { 00115 if ( $this->loaded ) { 00116 return; 00117 } 00118 $this->loaded = true; 00119 00120 // Only loggedin user can have a watchlist 00121 if ( $this->mUser->isAnon() ) { 00122 $this->watched = false; 00123 return; 00124 } 00125 00126 // some pages cannot be watched 00127 if ( !$this->getTitle()->isWatchable() ) { 00128 $this->watched = false; 00129 return; 00130 } 00131 00132 # Pages and their talk pages are considered equivalent for watching; 00133 # remember that talk namespaces are numbered as page namespace+1. 00134 00135 $dbr = wfGetDB( DB_SLAVE ); 00136 $row = $dbr->selectRow( 'watchlist', 'wl_notificationtimestamp', 00137 $this->dbCond(), __METHOD__ ); 00138 00139 if ( $row === false ) { 00140 $this->watched = false; 00141 } else { 00142 $this->watched = true; 00143 $this->timestamp = $row->wl_notificationtimestamp; 00144 } 00145 } 00146 00151 private function isAllowed( $what ) { 00152 return !$this->mCheckRights || $this->mUser->isAllowed( $what ); 00153 } 00154 00159 public function isWatched() { 00160 if ( !$this->isAllowed( 'viewmywatchlist' ) ) { 00161 return false; 00162 } 00163 00164 $this->load(); 00165 return $this->watched; 00166 } 00167 00174 public function getNotificationTimestamp() { 00175 if ( !$this->isAllowed( 'viewmywatchlist' ) ) { 00176 return false; 00177 } 00178 00179 $this->load(); 00180 if ( $this->watched ) { 00181 return $this->timestamp; 00182 } else { 00183 return false; 00184 } 00185 } 00186 00194 public function resetNotificationTimestamp( $force = '', $oldid = 0 ) { 00195 // Only loggedin user can have a watchlist 00196 if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) { 00197 return; 00198 } 00199 00200 if ( $force != 'force' ) { 00201 $this->load(); 00202 if ( !$this->watched || $this->timestamp === null ) { 00203 return; 00204 } 00205 } 00206 00207 $title = $this->getTitle(); 00208 if ( !$oldid ) { 00209 // No oldid given, assuming latest revision; clear the timestamp. 00210 $notificationTimestamp = null; 00211 } elseif ( !$title->getNextRevisionID( $oldid ) ) { 00212 // Oldid given and is the latest revision for this title; clear the timestamp. 00213 $notificationTimestamp = null; 00214 } else { 00215 // See if the version marked as read is more recent than the one we're viewing. 00216 // Call load() if it wasn't called before due to $force. 00217 $this->load(); 00218 00219 if ( $this->timestamp === null ) { 00220 // This can only happen if $force is enabled. 00221 $notificationTimestamp = null; 00222 } else { 00223 // Oldid given and isn't the latest; update the timestamp. 00224 // This will result in no further notification emails being sent! 00225 $dbr = wfGetDB( DB_SLAVE ); 00226 $notificationTimestamp = $dbr->selectField( 00227 'revision', 'rev_timestamp', 00228 array( 'rev_page' => $title->getArticleID(), 'rev_id' => $oldid ) 00229 ); 00230 // We need to go one second to the future because of various strict comparisons 00231 // throughout the codebase 00232 $ts = new MWTimestamp( $notificationTimestamp ); 00233 $ts->timestamp->add( new DateInterval( 'PT1S' ) ); 00234 $notificationTimestamp = $ts->getTimestamp( TS_MW ); 00235 00236 if ( $notificationTimestamp < $this->timestamp ) { 00237 if ( $force != 'force' ) { 00238 return; 00239 } else { 00240 // This is a little silly… 00241 $notificationTimestamp = $this->timestamp; 00242 } 00243 } 00244 } 00245 } 00246 00247 // If the page is watched by the user (or may be watched), update the timestamp on any 00248 // any matching rows 00249 $dbw = wfGetDB( DB_MASTER ); 00250 $dbw->update( 'watchlist', array( 'wl_notificationtimestamp' => $notificationTimestamp ), 00251 $this->dbCond(), __METHOD__ ); 00252 $this->timestamp = null; 00253 } 00254 00260 public function addWatch() { 00261 wfProfileIn( __METHOD__ ); 00262 00263 // Only loggedin user can have a watchlist 00264 if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) { 00265 wfProfileOut( __METHOD__ ); 00266 return false; 00267 } 00268 00269 // Use INSERT IGNORE to avoid overwriting the notification timestamp 00270 // if there's already an entry for this page 00271 $dbw = wfGetDB( DB_MASTER ); 00272 $dbw->insert( 'watchlist', 00273 array( 00274 'wl_user' => $this->getUserId(), 00275 'wl_namespace' => MWNamespace::getSubject( $this->getTitleNs() ), 00276 'wl_title' => $this->getTitleDBkey(), 00277 'wl_notificationtimestamp' => null 00278 ), __METHOD__, 'IGNORE' ); 00279 00280 // Every single watched page needs now to be listed in watchlist; 00281 // namespace:page and namespace_talk:page need separate entries: 00282 $dbw->insert( 'watchlist', 00283 array( 00284 'wl_user' => $this->getUserId(), 00285 'wl_namespace' => MWNamespace::getTalk( $this->getTitleNs() ), 00286 'wl_title' => $this->getTitleDBkey(), 00287 'wl_notificationtimestamp' => null 00288 ), __METHOD__, 'IGNORE' ); 00289 00290 $this->watched = true; 00291 00292 wfProfileOut( __METHOD__ ); 00293 return true; 00294 } 00295 00300 public function removeWatch() { 00301 wfProfileIn( __METHOD__ ); 00302 00303 // Only loggedin user can have a watchlist 00304 if ( wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed( 'editmywatchlist' ) ) { 00305 wfProfileOut( __METHOD__ ); 00306 return false; 00307 } 00308 00309 $success = false; 00310 $dbw = wfGetDB( DB_MASTER ); 00311 $dbw->delete( 'watchlist', 00312 array( 00313 'wl_user' => $this->getUserId(), 00314 'wl_namespace' => MWNamespace::getSubject( $this->getTitleNs() ), 00315 'wl_title' => $this->getTitleDBkey(), 00316 ), __METHOD__ 00317 ); 00318 if ( $dbw->affectedRows() ) { 00319 $success = true; 00320 } 00321 00322 # the following code compensates the new behavior, introduced by the 00323 # enotif patch, that every single watched page needs now to be listed 00324 # in watchlist namespace:page and namespace_talk:page had separate 00325 # entries: clear them 00326 $dbw->delete( 'watchlist', 00327 array( 00328 'wl_user' => $this->getUserId(), 00329 'wl_namespace' => MWNamespace::getTalk( $this->getTitleNs() ), 00330 'wl_title' => $this->getTitleDBkey(), 00331 ), __METHOD__ 00332 ); 00333 00334 if ( $dbw->affectedRows() ) { 00335 $success = true; 00336 } 00337 00338 $this->watched = false; 00339 00340 wfProfileOut( __METHOD__ ); 00341 return $success; 00342 } 00343 00351 public static function duplicateEntries( $ot, $nt ) { 00352 WatchedItem::doDuplicateEntries( $ot->getSubjectPage(), $nt->getSubjectPage() ); 00353 WatchedItem::doDuplicateEntries( $ot->getTalkPage(), $nt->getTalkPage() ); 00354 } 00355 00364 private static function doDuplicateEntries( $ot, $nt ) { 00365 $oldnamespace = $ot->getNamespace(); 00366 $newnamespace = $nt->getNamespace(); 00367 $oldtitle = $ot->getDBkey(); 00368 $newtitle = $nt->getDBkey(); 00369 00370 $dbw = wfGetDB( DB_MASTER ); 00371 $res = $dbw->select( 'watchlist', 'wl_user', 00372 array( 'wl_namespace' => $oldnamespace, 'wl_title' => $oldtitle ), 00373 __METHOD__, 'FOR UPDATE' 00374 ); 00375 # Construct array to replace into the watchlist 00376 $values = array(); 00377 foreach ( $res as $s ) { 00378 $values[] = array( 00379 'wl_user' => $s->wl_user, 00380 'wl_namespace' => $newnamespace, 00381 'wl_title' => $newtitle 00382 ); 00383 } 00384 00385 if ( empty( $values ) ) { 00386 // Nothing to do 00387 return true; 00388 } 00389 00390 # Perform replace 00391 # Note that multi-row replace is very efficient for MySQL but may be inefficient for 00392 # some other DBMSes, mostly due to poor simulation by us 00393 $dbw->replace( 'watchlist', array( array( 'wl_user', 'wl_namespace', 'wl_title' ) ), $values, __METHOD__ ); 00394 return true; 00395 } 00396 }