/** * Record a log entry on the review action * @param Title $title * @param array $dims * @param array $oldDims * @param string $comment * @param int $revId, revision ID * @param int $stableId, prior stable revision ID * @param bool $approve, approved? (otherwise unapproved) * @param bool $auto */ public static function updateReviewLog(Title $title, array $dims, array $oldDims, $comment, $revId, $stableId, $approve, $auto = false) { $log = new LogPage('review', false, $auto ? "skipUDP" : "UDP"); # Tag rating list (e.g. accuracy=x, depth=y, style=z) $ratings = array(); # Skip rating list if flagging is just an 0/1 feature... if (!FlaggedRevs::binaryFlagging()) { // Give grep a chance to find the usages: // revreview-accuracy, revreview-depth, revreview-style, // revreview-accuracy-0, revreview-accuracy-1, revreview-accuracy-2, revreview-accuracy-3, revreview-accuracy-4, // revreview-depth-0, revreview-depth-1, revreview-depth-2, revreview-depth-3, revreview-depth-4, // revreview-style-0, revreview-style-1, revreview-style-2, revreview-style-3, revreview-style-4 foreach ($dims as $quality => $level) { $ratings[] = wfMessage("revreview-{$quality}")->inContentLanguage()->text() . wfMessage('colon-separator')->inContentLanguage()->text() . wfMessage("revreview-{$quality}-{$level}")->inContentLanguage()->text(); } } $isAuto = $auto && !FlaggedRevs::isQuality($dims); // Paranoid check // Approved revisions if ($approve) { if ($isAuto) { $comment = wfMessage('revreview-auto')->inContentLanguage()->text(); // override this } # Make comma-separated list of ratings $rating = !empty($ratings) ? '[' . implode(', ', $ratings) . ']' : ''; # Append comment with ratings if ($rating != '') { $comment .= $comment ? " {$rating}" : $rating; } # Sort into the proper action (useful for filtering) $action = FlaggedRevs::isQuality($dims) || FlaggedRevs::isQuality($oldDims) ? 'approve2' : 'approve'; if (!$stableId) { // first time $action .= $isAuto ? "-ia" : "-i"; } elseif ($isAuto) { // automatic $action .= "-a"; } // De-approved revisions } else { $action = FlaggedRevs::isQuality($oldDims) ? 'unapprove2' : 'unapprove'; } $ts = Revision::getTimestampFromId($title, $revId); # Param format is <rev id, old stable id, rev timestamp> $logid = $log->addEntry($action, $title, $comment, array($revId, $stableId, $ts)); # Make log easily searchable by rev_id $log->addRelations('rev_id', array($revId), $logid); }
/** * Prepare the content for the 'last edited' message, e.g. 'Last edited on 30 August * 2013, at 23:31'. This message is different for the main page since main page * content is typically transcuded rather than edited directly. * @param Title $title The Title object of the page being viewed * @return array */ protected function getHistoryLink(Title $title) { $user = $this->getUser(); $isMainPage = $title->isMainPage(); // add last modified timestamp $revId = $this->getRevisionId(); $timestamp = Revision::getTimestampFromId($this->getTitle(), $revId); // Main pages tend to include transclusions (see bug 51924) if ($isMainPage) { $lastModified = $this->msg('mobile-frontend-history')->plain(); } else { $lastModified = $this->msg('mobile-frontend-last-modified-date', $this->getLanguage()->userDate($timestamp, $user), $this->getLanguage()->userTime($timestamp, $user))->parse(); } $unixTimestamp = wfTimestamp(TS_UNIX, $timestamp); $historyUrl = $this->mobileContext->getMobileUrl($title->getFullURL('action=history')); $link = array('data-timestamp' => $isMainPage ? '' : $unixTimestamp, 'href' => $historyUrl, 'text' => $lastModified, 'data-user-name' => '', 'data-user-gender' => 'unknown'); $rev = Revision::newFromId($this->getRevisionId()); if ($rev) { $userId = $rev->getUser(); if ($userId) { $revUser = User::newFromId($userId); $revUser->load(User::READ_NORMAL); $link = array_merge($link, array('data-user-name' => $revUser->getName(), 'data-user-gender' => $revUser->getOption('gender'))); } } $link['href'] = SpecialPage::getTitleFor('History', $title)->getLocalURL(); return $link; }
public function execute() { $user = $this->getUser(); if ($user->isAnon()) { $this->dieUsage('Anonymous users cannot use watchlist change notifications', 'notloggedin'); } $params = $this->extractRequestParams(); $this->requireMaxOneParameter($params, 'timestamp', 'torevid', 'newerthanrevid'); $pageSet = new ApiPageSet($this); $args = array_merge(array($params, 'entirewatchlist'), array_keys($pageSet->getAllowedParams())); call_user_func_array(array($this, 'requireOnlyOneParameter'), $args); $dbw = $this->getDB(DB_MASTER); $timestamp = null; if (isset($params['timestamp'])) { $timestamp = $dbw->timestamp($params['timestamp']); } if (!$params['entirewatchlist']) { $pageSet->execute(); } if (isset($params['torevid'])) { if ($params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1) { $this->dieUsage('torevid may only be used with a single page', 'multpages'); } $title = reset($pageSet->getGoodTitles()); $timestamp = Revision::getTimestampFromId($title, $params['torevid']); if ($timestamp) { $timestamp = $dbw->timestamp($timestamp); } else { $timestamp = null; } } elseif (isset($params['newerthanrevid'])) { if ($params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1) { $this->dieUsage('newerthanrevid may only be used with a single page', 'multpages'); } $title = reset($pageSet->getGoodTitles()); $revid = $title->getNextRevisionID($params['newerthanrevid']); if ($revid) { $timestamp = $dbw->timestamp(Revision::getTimestampFromId($title, $revid)); } else { $timestamp = null; } } $apiResult = $this->getResult(); $result = array(); if ($params['entirewatchlist']) { // Entire watchlist mode: Just update the thing and return a success indicator $dbw->update('watchlist', array('wl_notificationtimestamp' => $timestamp), array('wl_user' => $user->getID()), __METHOD__); $result['notificationtimestamp'] = is_null($timestamp) ? '' : wfTimestamp(TS_ISO_8601, $timestamp); } else { // First, log the invalid titles foreach ($pageSet->getInvalidTitles() as $title) { $r = array(); $r['title'] = $title; $r['invalid'] = ''; $result[] = $r; } foreach ($pageSet->getMissingPageIDs() as $p) { $page = array(); $page['pageid'] = $p; $page['missing'] = ''; $page['notwatched'] = ''; $result[] = $page; } foreach ($pageSet->getMissingRevisionIDs() as $r) { $rev = array(); $rev['revid'] = $r; $rev['missing'] = ''; $rev['notwatched'] = ''; $result[] = $rev; } // Now process the valid titles $lb = new LinkBatch($pageSet->getTitles()); $dbw->update('watchlist', array('wl_notificationtimestamp' => $timestamp), array('wl_user' => $user->getID(), $lb->constructSet('wl', $dbw)), __METHOD__); // Query the results of our update $timestamps = array(); $res = $dbw->select('watchlist', array('wl_namespace', 'wl_title', 'wl_notificationtimestamp'), array('wl_user' => $user->getID(), $lb->constructSet('wl', $dbw)), __METHOD__); foreach ($res as $row) { $timestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp; } // Now, put the valid titles into the result foreach ($pageSet->getTitles() as $title) { $ns = $title->getNamespace(); $dbkey = $title->getDBkey(); $r = array('ns' => intval($ns), 'title' => $title->getPrefixedText()); if (!$title->exists()) { $r['missing'] = ''; } if (isset($timestamps[$ns]) && array_key_exists($dbkey, $timestamps[$ns])) { $r['notificationtimestamp'] = ''; if ($timestamps[$ns][$dbkey] !== null) { $r['notificationtimestamp'] = wfTimestamp(TS_ISO_8601, $timestamps[$ns][$dbkey]); } } else { $r['notwatched'] = ''; } $result[] = $r; } $apiResult->setIndexedTagName($result, 'page'); } $apiResult->addValue(null, $this->getModuleName(), $result); }
function lastModified() { global $wgLang, $wgArticle; if ($this->mRevisionId) { $timestamp = Revision::getTimestampFromId($wgArticle->getTitle(), $this->mRevisionId); } else { $timestamp = $wgArticle->getTimestamp(); } if ($timestamp) { $d = $wgLang->date($timestamp, true); $t = $wgLang->time($timestamp, true); $s = ' ' . wfMsg('lastmodifiedat', $d, $t); } else { $s = ''; } if (wfGetLB()->getLaggedSlaveMode()) { $s .= ' <strong>' . wfMsg('laggedslavemode') . '</strong>'; } return $s; }
protected function addDraftTab(array &$views, FlaggedRevision $srev, $type) { $request = $this->getRequest(); $title = $this->article->getTitle(); // convenience $tabs = array('read' => array('text' => '', 'href' => $title->getLocalUrl('stable=1'), 'class' => ''), 'draft' => array('text' => wfMsg('revreview-current'), 'href' => $title->getLocalUrl('stable=0&redirect=no'), 'class' => 'collapsible')); // Set tab selection CSS if ($this->showingStable() || $request->getVal('stableid')) { // We are looking a the stable version or an old reviewed one $tabs['read']['class'] = 'selected'; } elseif ($this->isPageViewOrDiff($request)) { $ts = null; if ($this->out->getRevisionId()) { // @TODO: avoid same query in Skin.php $ts = $this->out->getRevisionId() == $this->article->getLatest() ? $this->article->getTimestamp() : Revision::getTimestampFromId($title, $this->out->getRevisionId()); } // Are we looking at a pending revision? if ($ts > $srev->getRevTimestamp()) { // bug 15515 $tabs['draft']['class'] .= ' selected'; // Are there *just* pending template/file changes. } elseif ($this->article->onlyTemplatesOrFilesPending() && $this->out->getRevisionId() == $this->article->getStable()) { $tabs['draft']['class'] .= ' selected'; // Otherwise, fallback to regular tab behavior } else { $tabs['read']['class'] = 'selected'; } } $newViews = array(); // Rebuild tabs array. Deals with Monobook vs Vector differences. if ($type == 'nav') { // Vector et al foreach ($views as $tabAction => $data) { // The 'view' tab. Make it go to the stable version... if ($tabAction == 'view') { // 'view' for content page; make it go to the stable version $newViews[$tabAction]['text'] = $data['text']; // keep tab name $newViews[$tabAction]['href'] = $tabs['read']['href']; $newViews[$tabAction]['class'] = $tabs['read']['class']; // All other tabs... } else { // Add 'draft' tab to content page to the left of 'edit'... if ($tabAction == 'edit' || $tabAction == 'viewsource') { $newViews['current'] = $tabs['draft']; } $newViews[$tabAction] = $data; } } } elseif ($type == 'flat') { // MonoBook et al $first = true; foreach ($views as $tabAction => $data) { // The first tab ('page'). Make it go to the stable version... if ($first) { $first = false; $newViews[$tabAction]['text'] = $data['text']; // keep tab name $newViews[$tabAction]['href'] = $tabs['read']['href']; $newViews[$tabAction]['class'] = $data['class']; // keep tab class // All other tabs... } else { // Add 'draft' tab to content page to the left of 'edit'... if ($tabAction == 'edit' || $tabAction == 'viewsource') { $newViews['current'] = $tabs['draft']; } $newViews[$tabAction] = $data; } } } // Replaces old tabs with new tabs $views = $newViews; }
/** * Create revision, diff, and history links for log line entry */ public static function reviewLogLinks($action, $title, $params) { global $wgLang; $links = ''; # Show link to page with oldid=x as well as the diff to the former stable rev. # Param format is <rev id, last stable id, rev timestamp>. if (isset($params[0])) { $revId = (int) $params[0]; // the revision reviewed $oldStable = isset($params[1]) ? (int) $params[1] : 0; # Show diff to changes since the prior stable version if ($oldStable && $revId > $oldStable) { $msg = FlaggedRevsLog::isReviewDeapproval($action) ? 'review-logentry-diff2' : 'review-logentry-diff'; // reviewed $links .= '('; $links .= Linker::linkKnown($title, wfMessage($msg)->escaped(), array(), array('oldid' => $oldStable, 'diff' => $revId)); $links .= ')'; } # Show a diff link to this revision $ts = empty($params[2]) ? Revision::getTimestampFromId($title, $revId) : $params[2]; $time = $wgLang->timeanddate($ts, true); $links .= ' ('; $links .= Linker::linkKnown($title, wfMessage('review-logentry-id', $revId, $time)->escaped(), array(), array('oldid' => $revId, 'diff' => 'prev') + FlaggedRevs::diffOnlyCGI()); $links .= ')'; } return $links; }
/** * Get the timestamp of the latest revision, formatted in user language * * @return String */ protected function lastModified() { $timestamp = $this->getOutput()->getRevisionTimestamp(); # No cached timestamp, load it from the database if ($timestamp === null) { $timestamp = Revision::getTimestampFromId($this->getTitle(), $this->getRevisionId()); } if ($timestamp) { $d = $this->getLanguage()->userDate($timestamp, $this->getUser()); $t = $this->getLanguage()->userTime($timestamp, $this->getUser()); $s = ' ' . $this->msg('lastmodifiedat', $d, $t)->text(); } else { $s = ''; } if (wfGetLB()->getLaggedSlaveMode()) { $s .= ' <strong>' . $this->msg('laggedslavemode')->text() . '</strong>'; } return $s; }
/** * Get the timestamp of the latest revision, formatted in user language * * @param $article Article object. Used if we're working with the current revision * @return String */ protected function lastModified($article) { if (!$this->isRevisionCurrent()) { $timestamp = Revision::getTimestampFromId($this->getTitle(), $this->getRevisionId()); } else { $timestamp = $article->getTimestamp(); } if ($timestamp) { $d = $this->getLang()->date($timestamp, true); $t = $this->getLang()->time($timestamp, true); $s = ' ' . wfMsg('lastmodifiedat', $d, $t); } else { $s = ''; } if (wfGetLB()->getLaggedSlaveMode()) { $s .= ' <strong>' . wfMsg('laggedslavemode') . '</strong>'; } return $s; }
public function execute() { $user = $this->getUser(); if ($user->isAnon()) { $this->dieUsage('Anonymous users cannot use watchlist change notifications', 'notloggedin'); } if (!$user->isAllowed('editmywatchlist')) { $this->dieUsage('You don\'t have permission to edit your watchlist', 'permissiondenied'); } $params = $this->extractRequestParams(); $this->requireMaxOneParameter($params, 'timestamp', 'torevid', 'newerthanrevid'); $continuationManager = new ApiContinuationManager($this, array(), array()); $this->setContinuationManager($continuationManager); $pageSet = $this->getPageSet(); if ($params['entirewatchlist'] && $pageSet->getDataSource() !== null) { $this->dieUsage("Cannot use 'entirewatchlist' at the same time as '{$pageSet->getDataSource()}'", 'multisource'); } $dbw = wfGetDB(DB_MASTER, 'api'); $timestamp = null; if (isset($params['timestamp'])) { $timestamp = $dbw->timestamp($params['timestamp']); } if (!$params['entirewatchlist']) { $pageSet->execute(); } if (isset($params['torevid'])) { if ($params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1) { $this->dieUsage('torevid may only be used with a single page', 'multpages'); } $title = reset($pageSet->getGoodTitles()); if ($title) { $timestamp = Revision::getTimestampFromId($title, $params['torevid'], Revision::READ_LATEST); if ($timestamp) { $timestamp = $dbw->timestamp($timestamp); } else { $timestamp = null; } } } elseif (isset($params['newerthanrevid'])) { if ($params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1) { $this->dieUsage('newerthanrevid may only be used with a single page', 'multpages'); } $title = reset($pageSet->getGoodTitles()); if ($title) { $revid = $title->getNextRevisionID($params['newerthanrevid'], Title::GAID_FOR_UPDATE); if ($revid) { $timestamp = $dbw->timestamp(Revision::getTimestampFromId($title, $revid)); } else { $timestamp = null; } } } $apiResult = $this->getResult(); $result = array(); if ($params['entirewatchlist']) { // Entire watchlist mode: Just update the thing and return a success indicator $dbw->update('watchlist', array('wl_notificationtimestamp' => $timestamp), array('wl_user' => $user->getID()), __METHOD__); $result['notificationtimestamp'] = is_null($timestamp) ? '' : wfTimestamp(TS_ISO_8601, $timestamp); } else { // First, log the invalid titles foreach ($pageSet->getInvalidTitles() as $title) { $r = array(); $r['title'] = $title; $r['invalid'] = true; $result[] = $r; } foreach ($pageSet->getMissingPageIDs() as $p) { $page = array(); $page['pageid'] = $p; $page['missing'] = true; $page['notwatched'] = true; $result[] = $page; } foreach ($pageSet->getMissingRevisionIDs() as $r) { $rev = array(); $rev['revid'] = $r; $rev['missing'] = true; $rev['notwatched'] = true; $result[] = $rev; } if ($pageSet->getTitles()) { // Now process the valid titles $lb = new LinkBatch($pageSet->getTitles()); $dbw->update('watchlist', array('wl_notificationtimestamp' => $timestamp), array('wl_user' => $user->getID(), $lb->constructSet('wl', $dbw)), __METHOD__); // Query the results of our update $timestamps = array(); $res = $dbw->select('watchlist', array('wl_namespace', 'wl_title', 'wl_notificationtimestamp'), array('wl_user' => $user->getID(), $lb->constructSet('wl', $dbw)), __METHOD__); foreach ($res as $row) { $timestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp; } // Now, put the valid titles into the result /** @var $title Title */ foreach ($pageSet->getTitles() as $title) { $ns = $title->getNamespace(); $dbkey = $title->getDBkey(); $r = array('ns' => intval($ns), 'title' => $title->getPrefixedText()); if (!$title->exists()) { $r['missing'] = true; } if (isset($timestamps[$ns]) && array_key_exists($dbkey, $timestamps[$ns])) { $r['notificationtimestamp'] = ''; if ($timestamps[$ns][$dbkey] !== null) { $r['notificationtimestamp'] = wfTimestamp(TS_ISO_8601, $timestamps[$ns][$dbkey]); } } else { $r['notwatched'] = true; } $result[] = $r; } } ApiResult::setIndexedTagName($result, 'page'); } $apiResult->addValue(null, $this->getModuleName(), $result); $this->setContinuationManager(null); $continuationManager->setContinuationIntoResult($apiResult); }
/** * * @param Title $oTitle * @return false|\ViewStateBarTopElement */ private function makeStateBarTopLastEdited($oTitle) { wfProfileIn('BS::' . __METHOD__); $oLastEditView = new ViewStateBarTopElement(); $oArticle = Article::newFromID($oTitle->getArticleID()); $iOldId = $this->getRequest()->getInt('oldid', 0); if ($oArticle instanceof Article == false) { return false; } if ($iOldId != 0) { $sTimestamp = Revision::getTimestampFromId($oArticle->getTitle(), $iOldId); } else { $sTimestamp = $oArticle->getTimestamp(); } $sFormattedTimestamp = BsFormatConverter::mwTimestampToAgeString($sTimestamp, true); $sArticleHistoryPageLink = $oArticle->getTitle()->getLinkURL(array('diff' => 0, 'action' => 'historysubmit')); $oLastEditView->setKey('LastEdited'); $oLastEditView->setIconSrc($this->getImagePath(true) . BsConfig::get('MW::ArticleInfo::ImageLastEdited')); $oLastEditView->setIconAlt(wfMessage('bs-articleinfo-last-edited')->plain()); $oLastEditView->setText($sFormattedTimestamp); $oLastEditView->setTextLink($sArticleHistoryPageLink); $oLastEditView->setTextLinkTitle(wfMessage('bs-articleinfo-last-edited-tooltip')->plain()); $oLastEditView->setDataAttribute('timestamp', wfTimestamp(TS_UNIX, $sTimestamp)); wfRunHooks('BSArticleInfoBeforeAddLastEditView', array($this, &$oLastEditView)); wfProfileOut('BS::' . __METHOD__); return $oLastEditView; }
/** * Reset the notification timestamp of this entry * * @param bool $force Whether to force the write query to be executed even if the * page is not watched or the notification timestamp is already NULL. * @param int $oldid The revision id being viewed. If not given or 0, latest revision is assumed. * @mode int $mode WatchedItem::DEFERRED/IMMEDIATE */ public function resetNotificationTimestamp($force = '', $oldid = 0, $mode = self::IMMEDIATE) { // Only loggedin user can have a watchlist if (wfReadOnly() || $this->mUser->isAnon() || !$this->isAllowed('editmywatchlist')) { return; } if ($force != 'force') { $this->load(); if (!$this->watched || $this->timestamp === null) { return; } } $title = $this->getTitle(); if (!$oldid) { // No oldid given, assuming latest revision; clear the timestamp. $notificationTimestamp = null; } elseif (!$title->getNextRevisionID($oldid)) { // Oldid given and is the latest revision for this title; clear the timestamp. $notificationTimestamp = null; } else { // See if the version marked as read is more recent than the one we're viewing. // Call load() if it wasn't called before due to $force. $this->load(); if ($this->timestamp === null) { // This can only happen if $force is enabled. $notificationTimestamp = null; } else { // Oldid given and isn't the latest; update the timestamp. // This will result in no further notification emails being sent! $notificationTimestamp = Revision::getTimestampFromId($title, $oldid); // We need to go one second to the future because of various strict comparisons // throughout the codebase $ts = new MWTimestamp($notificationTimestamp); $ts->timestamp->add(new DateInterval('PT1S')); $notificationTimestamp = $ts->getTimestamp(TS_MW); if ($notificationTimestamp < $this->timestamp) { if ($force != 'force') { return; } else { // This is a little silly… $notificationTimestamp = $this->timestamp; } } } } // If the page is watched by the user (or may be watched), update the timestamp if ($mode === self::DEFERRED) { $job = new ActivityUpdateJob($title, array('type' => 'updateWatchlistNotification', 'userid' => $this->getUserId(), 'notifTime' => $notificationTimestamp, 'curTime' => time())); // Try to run this post-send DeferredUpdates::addCallableUpdate(function () use($job) { $job->run(); }); } else { $dbw = wfGetDB(DB_MASTER); $dbw->update('watchlist', array('wl_notificationtimestamp' => $dbw->timestampOrNull($notificationTimestamp)), $this->dbCond(), __METHOD__); } $this->timestamp = null; }
function actuallyNotifyOnPageChange($editor, $title, $timestamp, $summary, $minorEdit, $oldid = false) { # we use $wgPasswordSender as sender's address global $wgEnotifWatchlist; global $wgEnotifMinorEdits, $wgEnotifUserTalk, $wgShowUpdatedMarker; global $wgEnotifImpersonal; wfProfileIn(__METHOD__); # The following code is only run, if several conditions are met: # 1. EmailNotification for pages (other than user_talk pages) must be enabled # 2. minor edits (changes) are only regarded if the global flag indicates so $isUserTalkPage = $title->getNamespace() == NS_USER_TALK; $enotifusertalkpage = $isUserTalkPage && $wgEnotifUserTalk; $enotifwatchlistpage = $wgEnotifWatchlist; $this->title = $title; $this->timestamp = $timestamp; $this->summary = $summary; $this->minorEdit = $minorEdit; $this->oldid = $oldid; $this->editor = $editor; $this->composed_common = false; $userTalkId = false; if (!$minorEdit || $wgEnotifMinorEdits && !$editor->isAllowed('nominornewtalk')) { if ($wgEnotifUserTalk && $isUserTalkPage) { $targetUser = User::newFromName($title->getText()); if (!$targetUser || $targetUser->isAnon()) { wfDebug(__METHOD__ . ": user talk page edited, but user does not exist\n"); } elseif ($targetUser->getId() == $editor->getId()) { wfDebug(__METHOD__ . ": user edited their own talk page, no notification sent\n"); } elseif ($targetUser->getOption('enotifusertalkpages')) { if ($targetUser->isEmailConfirmed()) { wfDebug(__METHOD__ . ": sending talk page update notification\n"); $this->compose($targetUser); $userTalkId = $targetUser->getId(); } else { wfDebug(__METHOD__ . ": talk page owner doesn't have validated email\n"); } } else { wfDebug(__METHOD__ . ": talk page owner doesn't want notifications\n"); } } if ($wgEnotifWatchlist) { // Send updates to watchers other than the current editor $userCondition = 'wl_user != ' . $editor->getID(); if ($userTalkId !== false) { // Already sent an email to this person $userCondition .= ' AND wl_user != ' . intval($userTalkId); } $dbr = wfGetDB(DB_SLAVE); list($user) = $dbr->tableNamesN('user'); $res = $dbr->select(array('watchlist', 'user'), array("{$user}.*"), array('wl_user=user_id', 'wl_title' => $title->getDBkey(), 'wl_namespace' => $title->getNamespace(), $userCondition, 'wl_notificationtimestamp IS NULL'), __METHOD__); $userArray = UserArray::newFromResult($res); foreach ($userArray as $watchingUser) { if ($watchingUser->getOption('enotifwatchlistpages') && (!$minorEdit || $watchingUser->getOption('enotifminoredits')) && $watchingUser->isEmailConfirmed()) { $this->compose($watchingUser); } } } } global $wgUsersNotifiedOnAllChanges; foreach ($wgUsersNotifiedOnAllChanges as $name) { $user = User::newFromName($name); $this->compose($user); } $this->sendMails(); $latestTimestamp = Revision::getTimestampFromId($title, $title->getLatestRevID()); // Do not update watchlists if something else already did. if ($timestamp >= $latestTimestamp && ($wgShowUpdatedMarker || $wgEnotifWatchlist)) { # Mark the changed watch-listed page with a timestamp, so that the page is # listed with an "updated since your last visit" icon in the watch list. Do # not do this to users for their own edits. $dbw = wfGetDB(DB_MASTER); $dbw->update('watchlist', array('wl_notificationtimestamp' => $dbw->timestamp($timestamp)), array('wl_title' => $title->getDBkey(), 'wl_namespace' => $title->getNamespace(), 'wl_notificationtimestamp IS NULL', 'wl_user != ' . $editor->getID()), __METHOD__); } wfProfileOut(__METHOD__); }
protected static function editCheckReview(Page $article, $rev, $user, $editTimestamp) { $prevTimestamp = null; $prevRevId = $rev->getParentId(); // revision before $rev $title = $article->getTitle(); // convenience # Check wpEdittime against the former current rev for verification if ($prevRevId) { $prevTimestamp = Revision::getTimestampFromId($title, $prevRevId); } # Was $rev is an edit to an existing page? if ($prevTimestamp) { # Check wpEdittime against the former current revision's time. # If an edit was auto-merged in between, then the new revision # has content different than what the user expected. However, if # the auto-merged edit was reviewed, then assume that it's OK. if ($editTimestamp != $prevTimestamp && !FlaggedRevision::revIsFlagged($prevRevId, FR_MASTER)) { return false; // not flagged? } } $flags = null; # Review this revision of the page... return FlaggedRevs::autoReviewEdit($article, $user, $rev, $flags, false); }
/** * Retrieve the last time the page content was modified. Do not reflect null edits. * @return string timestamp representing last edited time. */ public function getLatestTimestamp() { $title = $this->getTitle(); return Revision::getTimestampFromId($title, $title->getLatestRevID()); }
/** * Template filter callback for Bootstrap skin. * Takes an associative array of data set from a SkinTemplate-based * class, and a wrapper for MediaWiki's localization database, and * outputs a formatted page. * * @access private */ public function execute() { global $wgRequest, $wgUser, $wgSitename, $wgSitenameshort, $wgCopyrightLink, $wgCopyright, $wgBootstrap, $wgArticlePath, $wgGoogleAnalyticsID, $wgSiteCSS; global $wgEnableUploads; global $wgLogo; global $wgTOCLocation; global $wgNavBarClasses; global $wgSubnavBarClasses; global $wgDisableCounters; $this->skin = $this->data['skin']; $action = $wgRequest->getText('action'); $url_prefix = str_replace('$1', '', $wgArticlePath); // Suppress warnings to prevent notices about missing indexes in $this->data wfSuppressWarnings(); $this->html('headelement'); ?> <div id="extruderTop" class="{title:'Contingut'}"></div> <div class="navbar navbar-default navbar-fixed-top <?php echo $wgNavBarClasses; ?> " role="navigation"> <div class="container-fluid"> <!-- .btn-navbar is used as the toggle for collapsed navbar content --> <div class="navbar-header"> <button class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="<?php echo $this->data['nav_urls']['mainpage']['href']; ?> " title="<?php echo $wgSitename; ?> "><?php echo isset($wgLogo) && $wgLogo ? "<img src='{$wgLogo}' alt='Logo'/> " : ''; echo $wgSitenameshort ?: $wgSitename; ?> </a> </div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <!--<li> <a href="<?php echo $this->data['nav_urls']['mainpage']['href']; ?> ">Home</a> </li> --> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown"><?php echo wfMessage('toolbox')->text(); ?> <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="<?php echo $url_prefix; ?> Special:RecentChanges" class="recent-changes"><i class="fa fa-edit"></i> <?php echo wfMessage('recentchanges')->text(); ?> </a></li> <li><a href="<?php echo $url_prefix; ?> Special:SpecialPages" class="special-pages"><i class="fa fa-star-o"></i> <?php echo wfMessage('specialpages')->text(); ?> </a></li> <?php if ($wgEnableUploads) { ?> <li><a href="<?php echo $url_prefix; ?> Special:Upload" class="upload-a-file"><i class="fa fa-upload"></i> <?php echo wfMessage('uploadbtn')->text(); ?> </a></li> <?php } ?> </ul> </li> <?php //var_export ($this->data['content_actions']); ?> <?php if ($wgUser->isLoggedIn()) { ?> <li><a href="<?php echo $this->data['content_actions']['nstab-main']['href']; ?> "><i class="fa fa-file"></i> <?php echo wfMessage('mypage')->text(); ?> </a></li> <li><a href="<?php echo $this->data['content_actions']['edit']['href']; ?> "><i class="fa fa-pencil"></i> <?php echo wfMessage('edit')->text(); ?> </a></li> <?php } ?> <?php echo $this->nav($this->get_page_links('Bootstrap:TitleBar')); ?> </ul> <?php if ($wgUser->isLoggedIn()) { if (count($this->data['personal_urls']) > 0) { $user_icon = '<span class="user-icon"><img src="https://secure.gravatar.com/avatar/' . md5(strtolower($wgUser->getEmail())) . '.jpg?s=20&r=g"/></span>'; $name = strtolower($wgUser->getName()); $user_nav = $this->get_array_links($this->data['personal_urls'], $user_icon . $name, 'user'); ?> <ul<?php $this->html('userlangattributes'); ?> class="nav navbar-nav navbar-right"> <?php echo $user_nav; ?> </ul> <?php } //end if if (count($this->data['content_actions']) > 0) { //var_export( $this->data['content_actions']); $content_nav = $this->get_array_links($this->data['content_actions'], wfMessage('nstab-main')->text(), 'page'); ?> <ul class="nav navbar-nav navbar-right content-actions"><?php echo $content_nav; ?> </ul> <?php } //end if } else { // else if is logged in ?> <ul class="nav navbar-nav navbar-right"> <li> <?php echo Linker::linkKnown(SpecialPage::getTitleFor('Userlogin'), wfMsg('login')); ?> </li> </ul> <?php } ?> <form class="navbar-search navbar-form navbar-right" action="<?php $this->text('wgScript'); ?> " id="searchform" role="search"> <div> <input class="form-control" type="search" name="search" placeholder="<?php echo $this->msg('search'); ?> " title="Search <?php echo $wgSitename; ?> [ctrl-option-f]" accesskey="f" id="searchInput" autocomplete="off"> <input type="hidden" name="title" value="Special:Search"> </div> </form> </div> </div> </div><!-- topbar --> <?php if ($subnav_links = $this->get_page_links('Bootstrap:Subnav')) { ?> <div class="subnav subnav-fixed"> <div class="container-fluid"> <?php $subnav_select = $this->nav_select($subnav_links); if (trim($subnav_select)) { ?> <select id="subnav-select"> <?php echo $subnav_select; ?> </select> <?php } //end if ?> <ul class="nav nav-pills"> <?php echo $this->nav($subnav_links); ?> </ul> </div> </div> <?php } //end if ?> <div id="wiki-outer-body"> <div id="wiki-body" class="container-fluid"> <?php if ('sidebar' == $wgTOCLocation) { ?> <script type="text/javascript"> var toc_sidebar_title = "<?php echo wfMessage('toc')->text(); ?> "; var toc_sidebar_hide = "<?php echo wfMessage('hidetoc')->text(); ?> "; var toc_sidebar_show = "<?php echo wfMessage('showtoc')->text(); ?> "; </script> <div class="row"> <section class="col-md-3 toc-sidebar"></section> <section class="col-md-9 wiki-body-section"> <?php } //end if ?> <?php if ($this->data['sitenotice']) { ?> <div id="siteNotice" class="alert-message warning"><?php $this->html('sitenotice'); ?> </div><?php } ?> <?php if ($this->data['undelete']) { ?> <!-- undelete --> <div id="contentSub2"><?php $this->html('undelete'); ?> </div> <!-- /undelete --> <?php } ?> <?php if ($this->data['newtalk']) { ?> <!-- newtalk --> <div class="usermessage"><?php $this->html('newtalk'); ?> </div> <!-- /newtalk --> <?php } ?> <div class="pagetitle page-header"> <h1><?php $this->html('title'); ?> <small><?php $this->html('subtitle'); ?> </small></h1> </div> <div class="body"> <?php $this->html('bodytext'); ?> </div> <?php if ($this->data['catlinks']) { ?> <div class="category-links"> <!-- catlinks --> <?php $this->html('catlinks'); ?> <!-- /catlinks --> </div> <?php } ?> <?php if ($this->data['dataAfterContent']) { ?> <div class="data-after-content"> <!-- dataAfterContent --> <?php $this->html('dataAfterContent'); ?> <!-- /dataAfterContent --> </div> <?php } ?> <?php if ('sidebar' == $wgTOCLocation) { ?> </section></section> <?php } //end if ?> </div><!-- container --> </div> <div class="bottom"> <div class="container-fluid" style="padding-left: 0px;padding-right:0px;"> <!--<?php //$this->includePage('Bootstrap:Footer'); ?> --> <footer class="dark-div main-color-2-bg fixed-effect"> <div class="footer-inner fixed-effect-inner"> <section id="bottom"> <div class="section-inner"> <div class="container"> <div class="row normal-sidebar"> <div id="text-2" class=" col-md-3 widget widget_text"> <div class=" widget-inner"><h2 class="widget-title maincolor1">Sobre nosaltres</h2> <div class="textwidget"><img src="/resources/assets/poweredby_mediawiki_88x31.png" alt="logo"> <br><br> <p style="font-family: Raleway,sans-serif;">Acacha Wiki és un lloc web col·laboratiu d' informàtica i telecomunicacions creat per experts en administració de sistemes, xarxes, desenvolupament multiplataforma, disseny web i altres àrees de coneixement.</p> <a class="btn btn-default footerbutton" style="margin-top:4px;" href="/mediawiki/index.php/Acacha_Wiki._Informàtica_i_telecomunicacions:Quant_a">Quant a</a> <a class="btn btn-default footerbutton" style="margin-top:5px;" href="/mediawiki/index.php/Acacha_Wiki._Informàtica_i_telecomunicacions:Política de privadesa">Política de privadesa</a> <a class="btn btn-default footerbutton" style="margin-top:5px;" href="/mediawiki/index.php/Acacha_Wiki._Informàtica_i_telecomunicacions:Avís general">Avís general</a></div> </div></div><div id="app-recent-posts-2" class=" col-md-3 widget app_recent_posts"><div class=" widget-inner"><div class="app-lastest"><h2 class="widget-title maincolor1">Blog</h2><div class="item"><div class="thumb item-thumbnail"> <a href="#" title="Android Apps on Applay"> <div class="item-thumbnail"><img width="80" height="80" src="/resources/assets/IMG_3212-1300x866-80x80.jpg" class="attachment-thumb_80x80 wp-post-image" alt="TODO" /> <div class="thumbnail-hoverlay main-color-5-bg"></div> <div class="thumbnail-hoverlay-icon"><i class="fa fa-search"></i></div> </div> </a> </div><div class="app-details item-content"> <h5><a href="#" title="Android Apps on Applay" class="main-color-5-hover">Entrada blog 1</a></h5> <span>October 9, 2014</span> </div> <div class="clearfix"></div></div> <div class="item"><div class="thumb item-thumbnail"> <a href="#" title="Apps For Work"> <div class="item-thumbnail"><img width="80" height="80" src="/resources/assets/IMG_5956-1300x866-80x80.jpg" class="attachment-thumb_80x80 wp-post-image" alt="pendent" /> <div class="thumbnail-hoverlay main-color-5-bg"></div> <div class="thumbnail-hoverlay-icon"><i class="fa fa-search"></i></div> </div> </a> </div><div class="app-details item-content"> <h5><a href="#" title="Apps For Work" class="main-color-1-hover">Entrada blog 2</a></h5> <span>October 9, 2014</span> </div> <div class="clearfix"></div></div> </div></div></div> <div id="tag_cloud-5" class=" col-md-3 widget widget_tag_cloud"><div class=" widget-inner"><h2 class="widget-title maincolor1">Pàgines populars</h2> <div> <a href='/mediawiki/index.php/Creació_de_paquets_Debian' class='btn btn-default footerbutton' style="margin-top:4px;">Creació Paquets Debian</a> <a href='/mediawiki/index.php/Apache' class='btn btn-default footerbutton' style="margin-top:4px;">Apache</a> <a href='/mediawiki/index.php/APT_i_DPKG' class='btn btn-default footerbutton' style="margin-top:4px;">APT i DPKG</a> <a href='/mediawiki/index.php/DNS' class='btn btn-default footerbutton' style="margin-top:4px;">DNS</a> <a href='/mediawiki/index.php/Ldap' class='btn btn-default footerbutton' style="margin-top:4px;">Ldap</a> <a href='/mediawiki/index.php/Android UI development' class='btn btn-default footerbutton' style="margin-top:4px;">Android UI development</a> <a href='/mediawiki/index.php/DHCP' class='btn btn-default footerbutton' style="margin-top:4px;">DHCP</a> <a href='/mediawiki/index.php/SQL' class='btn btn-default footerbutton' style="margin-top:4px;">SQL</a> <a href='/mediawiki/index.php/Especial:Pàgines_populars' class='btn btn-default footerbutton' style="margin-top:4px;">Llista completa</a> <a href='/mediawiki/index.php/AirOS' class='btn btn-default footerbutton' style="margin-top:4px;">AirOS</a> </div></div></div> <div id="zflickr-2" class="col-md-3 widget widget_flickr"><div class=" widget-inner"><h2 class="widget-title maincolor1">Segueix-nos</h2> <div class='flickr-badge-wrapper zframe-flickr-wrap-ltr'> <a class="twitter-follow-button" href="https://twitter.com/acachawiki" data-show-count="false" data-lang="en"> Follow @acachawiki </a> <script type="text/javascript"> window.twttr = (function (d, s, id) { var t, js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src= "https://platform.twitter.com/widgets.js"; fjs.parentNode.insertBefore(js, fjs); return window.twttr || (t = { _e: [], ready: function (f) { t._e.push(f) } }); }(document, "script", "twitter-wjs")); </script> </div> <div id="fb-root"></div> <div class="fb-like" data-href="http://acacha.org" data-layout="button_count" data-action="like" data-show-faces="true" data-share="true"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/ca_ES/sdk.js#xfbml=1&appId=779450832121140&version=v2.0"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk')); </script> <br/><br/> <!-- Inserta esta etiqueta en la sección "head" o justo antes de la etiqueta "body" de cierre. --> <script src="https://apis.google.com/js/platform.js" async defer> {lang: 'ca'} </script> <!-- Inserta esta etiqueta donde quieras que aparezca Botón +1. --> <div class="g-plusone" data-annotation="inline" data-width="300"></div> <br/> <h2 class="widget-title maincolor1">Pàgines Germanes</h2> <p><a href="http://acacha.org/mediawiki/index.php/Ebre-escool">Ebre-escool</a></p> </div> </div> </div> </div> </div> </section> <div id="bottom-nav"> <div class="container"> <div class="text-center back-to-top-wrap"> <a class="back-to-top main-color-10-bg" href="#top" title="Pujar" style="text-decoration:none;"><i class="fa fa-angle-double-up"></i></a> </div> <div class="row footer" style="padding-bottom: 0px;padding-top: 10px;"> <div class="col-md-12" style="font-family: Raleway,sans-serif;text-align:center;padding-bottom: 0px;padding-top: 0px;font-size:10px;"> <?php $title = RequestContext::getMain()->getTitle(); $skin = RequestContext::getMain()->getSkin(); if (RequestContext::getMain()->getOutput()->isArticle() && $title->exists()) { if ($skin->isRevisionCurrent()) { if (!$wgDisableCounters) { $viewcount = RequestContext::getMain()->getWikiPage()->getCount(); if ($viewcount) { echo wfMessage('viewcount', $viewcount)->parse(); } } } } ?> <br/> <?php $timestamp = RequestContext::getMain()->getOutput()->getRevisionTimestamp(); # No cached timestamp, load it from the database if ($timestamp === null) { $timestamp = Revision::getTimestampFromId($title, $skin->getRevisionId()); } $d = ""; $t = ""; if ($timestamp) { $d = RequestContext::getMain()->getLanguage()->userDate($timestamp, RequestContext::getMain()->getUser()); $t = RequestContext::getMain()->getLanguage()->userTime($timestamp, RequestContext::getMain()->getUser()); $s = ' ' . wfMessage('lastmodifiedat', $d, $t)->text(); } echo $s; ?> </div> </div> <div class="row footer-content" style="padding-top: 0px;"> <div class="copyright col-md-7" style="font-family: Raleway,sans-serif;"> © <?php echo date('Y'); ?> by <a href="<?php echo isset($wgCopyrightLink) ? $wgCopyrightLink : 'http://acacha.org'; ?> " style="text-decoration:none;font-family: Raleway,sans-serif;"><?php echo isset($wgCopyright) ? $wgCopyright : 'Sergi Tur Badenas i altres contribuïdors'; ?> </a> • Powered by <a href="http://mediawiki.org" style="text-decoration:none;">MediaWiki</a> & <a href="http://getbootstrap.com" style="text-decoration:none;">Bootstrap</a> </div> <nav class="col-md-5 footer-social"> <ul class="list-inline pull-right social-list"> <li><a href="https://www.facebook.com/pages/Acacha-Wiki/121532428620" target="_blank" class="btn btn-default social-icon"><i class="fa fa-facebook"></i></a></li> <li><a href="https://twitter.com/acachawiki" class="btn btn-default social-icon" target="_blank"><i class="fa fa-twitter"></i></a></li> <li><a href="https://www.linkedin.com/company/acacha-wiki?trk=company_logo" class="btn btn-default social-icon" target="_blank"><i class="fa fa-linkedin"></i></a></li> <li><a href="https://plus.google.com/b/116791959261119875735/" class="btn btn-default social-icon" target="_blank"><i class="fa fa-google-plus"></i></a></li> </ul> </nav> </div><!--/row--> </div><!--/container--> </div> </div> </footer><!--/footer-inner--> </div><!-- container --> </div><!-- bottom --> <?php $this->html('bottomscripts'); /* JS call to runBodyOnloadHook */ $this->html('reporttime'); if ($this->data['debug']) { ?> <!-- Debug output: <?php $this->text('debug'); ?> --> <?php } //end if ?> </body> </html> <?php }