function __construct() { global $wgModerationTimeToOverrideRejection; $mw_ts = new MWTimestamp(time()); $mw_ts->timestamp->modify('-' . intval($wgModerationTimeToOverrideRejection) . ' seconds'); $this->earliestReapprovableTimestamp = $mw_ts->getTimestamp(TS_MW); $this->mblockCheck = new ModerationBlockCheck(); parent::__construct('Moderation', 'moderation'); }
public function execute() { $params = $this->extractRequestParams(); $token = $params['token']; $maxage = $params['maxtokenage']; $salts = ApiQueryTokens::getTokenTypeSalts(); $res = array(); $tokenObj = ApiQueryTokens::getToken($this->getUser(), $this->getRequest()->getSession(), $salts[$params['type']]); if ($tokenObj->match($token, $maxage)) { $res['result'] = 'valid'; } elseif ($maxage !== null && $tokenObj->match($token)) { $res['result'] = 'expired'; } else { $res['result'] = 'invalid'; } $ts = MediaWiki\Session\Token::getTimestamp($token); if ($ts !== null) { $mwts = new MWTimestamp(); $mwts->timestamp->setTimestamp($ts); $res['generated'] = $mwts->getTimestamp(TS_ISO_8601); } $this->getResult()->addValue(null, $this->getModuleName(), $res); }
/** * Convert dates like "Tue, 03 Jan 2012 22:01:04 GMT"/"2013-05-11T07:37:27.678360Z". * Dates might also come in like "2013-05-11T07:37:27.678360" from Swift listings, * missing the timezone suffix (though Ceph RGW does not appear to have this bug). * * @param string $ts * @param int $format Output format (TS_* constant) * @return string * @throws FileBackendError */ protected function convertSwiftDate($ts, $format = TS_MW) { try { $timestamp = new MWTimestamp($ts); return $timestamp->getTimestamp($format); } catch (Exception $e) { throw new FileBackendError($e->getMessage()); } }
/** * Get a timestamp string in one of various formats * * @param mixed $outputtype A timestamp in one of the supported formats, the * function will autodetect which format is supplied and act accordingly. * @param mixed $ts Optional timestamp to convert, default 0 for the current time * @return string|bool String / false The same date in the format specified in $outputtype or false */ function wfTimestamp($outputtype = TS_UNIX, $ts = 0) { try { $timestamp = new MWTimestamp($ts); return $timestamp->getTimestamp($outputtype); } catch (TimestampException $e) { wfDebug("wfTimestamp() fed bogus time value: TYPE={$outputtype}; VALUE={$ts}\n"); return false; } }
/** * Reset the notification timestamp of this entry * * @param $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. */ public function resetNotificationTimestamp($force = '', $oldid = 0) { // 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! $dbr = wfGetDB(DB_SLAVE); $notificationTimestamp = $dbr->selectField('revision', 'rev_timestamp', array('rev_page' => $title->getArticleID(), 'rev_id' => $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 on any // any matching rows $dbw = wfGetDB(DB_MASTER); $dbw->update('watchlist', array('wl_notificationtimestamp' => $notificationTimestamp), $this->dbCond(), __METHOD__); $this->timestamp = null; }
public function execute() { $user = $this->getUser(); $errors = $this->getTitle()->getUserPermissionsErrors('abusefilter-log', $user); if (count($errors)) { $this->dieUsageMsg($errors[0]); return; } $params = $this->extractRequestParams(); $prop = array_flip($params['prop']); $fld_ids = isset($prop['ids']); $fld_filter = isset($prop['filter']); $fld_user = isset($prop['user']); $fld_ip = isset($prop['ip']); $fld_title = isset($prop['title']); $fld_action = isset($prop['action']); $fld_details = isset($prop['details']); $fld_result = isset($prop['result']); $fld_timestamp = isset($prop['timestamp']); $fld_hidden = isset($prop['hidden']); $fld_revid = isset($prop['revid']); if ($fld_ip && !$user->isAllowed('abusefilter-private')) { $this->dieUsage('You don\'t have permission to view IP addresses', 'permissiondenied'); } if ($fld_details && !$user->isAllowed('abusefilter-log-detail')) { $this->dieUsage('You don\'t have permission to view detailed abuse log entries', 'permissiondenied'); } // Match permissions for viewing events on private filters to SpecialAbuseLog (bug 42814) if ($params['filter'] && !(AbuseFilterView::canViewPrivate() || $user->isAllowed('abusefilter-log-private'))) { // A specific filter parameter is set but the user isn't allowed to view all filters if (!is_array($params['filter'])) { $params['filter'] = array($params['filter']); } foreach ($params['filter'] as $filter) { if (AbuseFilter::filterHidden($filter)) { $this->dieUsage('You don\'t have permission to view log entries for private filters', 'permissiondenied'); } } } $result = $this->getResult(); $this->addTables('abuse_filter_log'); $this->addFields('afl_timestamp'); $this->addFields('afl_rev_id'); $this->addFields('afl_deleted'); $this->addFields('afl_filter'); $this->addFieldsIf('afl_id', $fld_ids); $this->addFieldsIf('afl_user_text', $fld_user); $this->addFieldsIf('afl_ip', $fld_ip); $this->addFieldsIf(array('afl_namespace', 'afl_title'), $fld_title); $this->addFieldsIf('afl_action', $fld_action); $this->addFieldsIf('afl_var_dump', $fld_details); $this->addFieldsIf('afl_actions', $fld_result); if ($fld_filter) { $this->addTables('abuse_filter'); $this->addFields('af_public_comments'); $this->addJoinConds(array('abuse_filter' => array('LEFT JOIN', 'af_id=afl_filter'))); } $this->addOption('LIMIT', $params['limit'] + 1); $this->addWhereRange('afl_timestamp', $params['dir'], $params['start'], $params['end']); $db = $this->getDB(); $notDeletedCond = SpecialAbuseLog::getNotDeletedCond($db); if (isset($params['user'])) { $u = User::newFromName($params['user']); if ($u) { // Username normalisation $params['user'] = $u->getName(); $userId = $u->getId(); } elseif (IP::isIPAddress($params['user'])) { // It's an IP, sanitize it $params['user'] = IP::sanitizeIP($params['user']); $userId = 0; } if (isset($userId)) { // Only add the WHERE for user in case it's either a valid user (but not necessary an existing one) or an IP $this->addWhere(array('afl_user' => $userId, 'afl_user_text' => $params['user'])); } } $this->addWhereIf(array('afl_filter' => $params['filter']), isset($params['filter'])); $this->addWhereIf($notDeletedCond, !SpecialAbuseLog::canSeeHidden($user)); $title = $params['title']; if (!is_null($title)) { $titleObj = Title::newFromText($title); if (is_null($titleObj)) { $this->dieUsageMsg(array('invalidtitle', $title)); } $this->addWhereFld('afl_namespace', $titleObj->getNamespace()); $this->addWhereFld('afl_title', $titleObj->getDBkey()); } $res = $this->select(__METHOD__); $count = 0; foreach ($res as $row) { if (++$count > $params['limit']) { // We've had enough $ts = new MWTimestamp($row->afl_timestamp); $this->setContinueEnumParameter('start', $ts->getTimestamp(TS_ISO_8601)); break; } if (SpecialAbuseLog::isHidden($row) && !SpecialAbuseLog::canSeeHidden($user)) { continue; } $canSeeDetails = SpecialAbuseLog::canSeeDetails($row->afl_filter); $entry = array(); if ($fld_ids) { $entry['id'] = intval($row->afl_id); $entry['filter_id'] = ''; if ($canSeeDetails) { $entry['filter_id'] = $row->afl_filter; } } if ($fld_filter) { $entry['filter'] = $row->af_public_comments; } if ($fld_user) { $entry['user'] = $row->afl_user_text; } if ($fld_ip) { $entry['ip'] = $row->afl_ip; } if ($fld_title) { $title = Title::makeTitle($row->afl_namespace, $row->afl_title); ApiQueryBase::addTitleInfo($entry, $title); } if ($fld_action) { $entry['action'] = $row->afl_action; } if ($fld_result) { $entry['result'] = $row->afl_actions; } if ($fld_revid && !is_null($row->afl_rev_id)) { $entry['revid'] = ''; if ($canSeeDetails) { $entry['revid'] = $row->afl_rev_id; } } if ($fld_timestamp) { $ts = new MWTimestamp($row->afl_timestamp); $entry['timestamp'] = $ts->getTimestamp(TS_ISO_8601); } if ($fld_details) { $entry['details'] = array(); if ($canSeeDetails) { $vars = AbuseFilter::loadVarDump($row->afl_var_dump); if ($vars instanceof AbuseFilterVariableHolder) { $entry['details'] = $vars->exportAllVars(); } else { $entry['details'] = array_change_key_case($vars, CASE_LOWER); } } } if ($fld_hidden) { $val = SpecialAbuseLog::isHidden($row); if ($val) { $entry['hidden'] = $val; } } if ($entry) { $fit = $result->addValue(array('query', $this->getModuleName()), null, $entry); if (!$fit) { $ts = new MWTimestamp($row->afl_timestamp); $this->setContinueEnumParameter('start', $ts->getTimestamp(TS_ISO_8601)); break; } } } $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'item'); }
public function execute() { $user = $this->getUser(); if (!$user->isAllowed('abusefilter-view')) { $this->dieUsage('You don\'t have permission to view abuse filters', 'permissiondenied'); } $params = $this->extractRequestParams(); $prop = array_flip($params['prop']); $fld_id = isset($prop['id']); $fld_desc = isset($prop['description']); $fld_pattern = isset($prop['pattern']); $fld_actions = isset($prop['actions']); $fld_hits = isset($prop['hits']); $fld_comments = isset($prop['comments']); $fld_user = isset($prop['lasteditor']); $fld_time = isset($prop['lastedittime']); $fld_status = isset($prop['status']); $fld_private = isset($prop['private']); $result = $this->getResult(); $this->addTables('abuse_filter'); $this->addFields('af_id'); $this->addFields('af_hidden'); $this->addFieldsIf('af_hit_count', $fld_hits); $this->addFieldsIf('af_enabled', $fld_status); $this->addFieldsIf('af_deleted', $fld_status); $this->addFieldsIf('af_public_comments', $fld_desc); $this->addFieldsIf('af_pattern', $fld_pattern); $this->addFieldsIf('af_actions', $fld_actions); $this->addFieldsIf('af_comments', $fld_comments); $this->addFieldsIf('af_user_text', $fld_user); $this->addFieldsIf('af_timestamp', $fld_time); $this->addOption('LIMIT', $params['limit'] + 1); $this->addWhereRange('af_id', $params['dir'], $params['startid'], $params['endid']); if (!is_null($params['show'])) { $show = array_flip($params['show']); /* Check for conflicting parameters. */ if (isset($show['enabled']) && isset($show['!enabled']) || isset($show['deleted']) && isset($show['!deleted']) || isset($show['private']) && isset($show['!private'])) { $this->dieUsage('Incorrect parameter - mutually exclusive values may not be supplied', 'show'); } $this->addWhereIf('af_enabled = 0', isset($show['!enabled'])); $this->addWhereIf('af_enabled != 0', isset($show['enabled'])); $this->addWhereIf('af_deleted = 0', isset($show['!deleted'])); $this->addWhereIf('af_deleted != 0', isset($show['deleted'])); $this->addWhereIf('af_hidden = 0', isset($show['!private'])); $this->addWhereIf('af_hidden != 0', isset($show['private'])); } $res = $this->select(__METHOD__); $showhidden = $user->isAllowed('abusefilter-modify'); $count = 0; foreach ($res as $row) { if (++$count > $params['limit']) { // We've had enough $this->setContinueEnumParameter('startid', $row->af_id); break; } $entry = array(); if ($fld_id) { $entry['id'] = intval($row->af_id); } if ($fld_desc) { $entry['description'] = $row->af_public_comments; } if ($fld_pattern && (!$row->af_hidden || $showhidden)) { $entry['pattern'] = $row->af_pattern; } if ($fld_actions) { $entry['actions'] = $row->af_actions; } if ($fld_hits) { $entry['hits'] = intval($row->af_hit_count); } if ($fld_comments && (!$row->af_hidden || $showhidden)) { $entry['comments'] = $row->af_comments; } if ($fld_user) { $entry['lasteditor'] = $row->af_user_text; } if ($fld_time) { $ts = new MWTimestamp($row->af_timestamp); $entry['lastedittime'] = $ts->getTimestamp(TS_ISO_8601); } if ($fld_private && $row->af_hidden) { $entry['private'] = ''; } if ($fld_status) { if ($row->af_enabled) { $entry['enabled'] = ''; } if ($row->af_deleted) { $entry['deleted'] = ''; } } if ($entry) { $fit = $result->addValue(array('query', $this->getModuleName()), null, $entry); if (!$fit) { $this->setContinueEnumParameter('startid', $row->af_id); break; } } } $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'filter'); }
function formatValue($name, $value) { static $msg = null; if ($msg === null) { $keys = ['anononlyblock', 'createaccountblock', 'noautoblockblock', 'emailblock', 'blocklist-nousertalk', 'unblocklink', 'change-blocklink']; foreach ($keys as $key) { $msg[$key] = $this->msg($key)->escaped(); } } /** @var $row object */ $row = $this->mCurrentRow; $language = $this->getLanguage(); $formatted = ''; switch ($name) { case 'ipb_timestamp': $formatted = htmlspecialchars($language->userTimeAndDate($value, $this->getUser())); break; case 'ipb_target': if ($row->ipb_auto) { $formatted = $this->msg('autoblockid', $row->ipb_id)->parse(); } else { list($target, $type) = Block::parseTarget($row->ipb_address); switch ($type) { case Block::TYPE_USER: case Block::TYPE_IP: $formatted = Linker::userLink($target->getId(), $target); $formatted .= Linker::userToolLinks($target->getId(), $target, false, Linker::TOOL_LINKS_NOBLOCK); break; case Block::TYPE_RANGE: $formatted = htmlspecialchars($target); } } break; case 'ipb_expiry': $formatted = htmlspecialchars($language->formatExpiry($value, true)); if ($this->getUser()->isAllowed('block')) { if ($row->ipb_auto) { $links[] = Linker::linkKnown(SpecialPage::getTitleFor('Unblock'), $msg['unblocklink'], [], ['wpTarget' => "#{$row->ipb_id}"]); } else { $links[] = Linker::linkKnown(SpecialPage::getTitleFor('Unblock', $row->ipb_address), $msg['unblocklink']); $links[] = Linker::linkKnown(SpecialPage::getTitleFor('Block', $row->ipb_address), $msg['change-blocklink']); } $formatted .= ' ' . Html::rawElement('span', ['class' => 'mw-blocklist-actions'], $this->msg('parentheses')->rawParams($language->pipeList($links))->escaped()); } if ($value !== 'infinity') { $timestamp = new MWTimestamp($value); $formatted .= '<br />' . $this->msg('ipb-blocklist-duration-left', $language->formatDuration($timestamp->getTimestamp() - time(), ['minutes', 'hours', 'days', 'years']))->escaped(); } break; case 'ipb_by': if (isset($row->by_user_name)) { $formatted = Linker::userLink($value, $row->by_user_name); $formatted .= Linker::userToolLinks($value, $row->by_user_name); } else { $formatted = htmlspecialchars($row->ipb_by_text); // foreign user? } break; case 'ipb_reason': $formatted = Linker::formatComment($value); break; case 'ipb_params': $properties = []; if ($row->ipb_anon_only) { $properties[] = $msg['anononlyblock']; } if ($row->ipb_create_account) { $properties[] = $msg['createaccountblock']; } if ($row->ipb_user && !$row->ipb_enable_autoblock) { $properties[] = $msg['noautoblockblock']; } if ($row->ipb_block_email) { $properties[] = $msg['emailblock']; } if (!$row->ipb_allow_usertalk) { $properties[] = $msg['blocklist-nousertalk']; } $formatted = $language->commaList($properties); break; default: $formatted = "Unable to format {$name}"; break; } return $formatted; }
/** * Outputs a table of existing drafts * * @param $title Title [optional] Title of article, defaults to all articles * @param $userID Integer: [optional] ID of user, defaults to current user * @return int Number of drafts in the table */ public static function display($title = null, $userID = null) { global $wgOut, $wgRequest, $wgUser, $wgLang; // Gets draftID $currentDraft = Draft::newFromID($wgRequest->getIntOrNull('draft')); // Output HTML for list of drafts $drafts = Drafts::get($title, $userID); if (count($drafts) > 0) { global $egDraftsLifeSpan; // Internationalization // Add a summary, on Special:Drafts only if (!$title || $title->getNamespace() === NS_SPECIAL) { $wgOut->wrapWikiMsg('<div class="mw-drafts-summary">$1</div>', array('drafts-view-summary', $wgLang->formatNum($egDraftsLifeSpan))); } // Build XML $wgOut->addHTML(Xml::openElement('table', array('cellpadding' => 5, 'cellspacing' => 0, 'width' => '100%', 'border' => 0, 'id' => 'drafts-list-table', 'class' => 'section_text'))); $wgOut->addHTML(Xml::openElement('tr')); $wgOut->addHTML(Xml::element('th', array(), wfMessage('drafts-view-article')->text())); $wgOut->addHTML(Xml::element('th', null, wfMessage('drafts-view-saved')->text())); $wgOut->addHTML(Xml::element('th', array())); $wgOut->addHTML(Xml::element('th', array())); $wgOut->addHTML(Xml::closeElement('tr')); // Add existing drafts for this page and user /** * @var $draft Draft */ foreach ($drafts as $draft) { // Get article title text $htmlTitle = $draft->getTitle()->getEscapedText(); // Build Article Load link $urlLoad = $draft->getTitle()->getFullURL('action=edit&draft=' . urlencode($draft->getID())); // Build discard link $urlDiscard = SpecialPage::getTitleFor('Drafts')->getFullURL(sprintf('discard=%s&token=%s', urlencode($draft->getID()), urlencode($wgUser->getEditToken()))); // If in edit mode, return to editor if ($wgRequest->getText('action') == 'edit' || $wgRequest->getText('action') == 'submit') { $urlDiscard .= '&returnto=' . urlencode('edit'); } // Append section to titles and links if ($draft->getSection() !== null) { // Detect section name $lines = explode("\n", $draft->getText()); // If there is any content in the section if (count($lines) > 0) { $htmlTitle .= '#' . htmlspecialchars(trim(trim(substr($lines[0], 0, 255), '='))); } // Modify article link and title $urlLoad .= '§ion=' . urlencode($draft->getSection()); $urlDiscard .= '§ion=' . urlencode($draft->getSection()); } // Build XML $wgOut->addHTML(Xml::openElement('tr')); $wgOut->addHTML(Xml::openElement('td')); $wgOut->addHTML(Xml::element('a', array('href' => $urlLoad, 'style' => 'font-weight:' . ($currentDraft->getID() == $draft->getID() ? 'bold' : 'normal')), $htmlTitle)); $wgOut->addHTML(Xml::closeElement('td')); $saveTime = new MWTimestamp($draft->getSaveTime()); $wgOut->addHTML(Xml::element('td', null, $wgLang->timeanddate($saveTime->getTimestamp(), true))); $wgOut->addHTML(Xml::openElement('td')); $wgOut->addHTML(Xml::element('a', array('href' => $urlLoad), wfMessage('edit')->text())); $wgOut->addHTML(Xml::closeElement('td')); $wgOut->addHTML(Xml::openElement('td')); $jsClick = "if( wgDraft.getState() !== 'unchanged' )" . "return confirm('" . Xml::escapeJsString(wfMessage('drafts-view-warn')->escaped()) . "')"; $wgOut->addHTML(Xml::element('a', array('href' => $urlDiscard, 'onclick' => $jsClick), wfMessage('drafts-view-discard')->text())); $wgOut->addHTML(Xml::closeElement('td')); $wgOut->addHTML(Xml::closeElement('tr')); } $wgOut->addHTML(Xml::closeElement('table')); // Return number of drafts return count($drafts); } return 0; }
/** * Test requesting an invalid output format. * @expectedException TimestampException */ function testInvalidOutput() { $timestamp = new MWTimestamp('1343761268'); $timestamp->getTimestamp(98); }
/** * formatDate handler */ function formatDate($lang, $args) { $this->checkType('formatDate', 1, $args[0], 'string'); $this->checkTypeOptional('formatDate', 2, $args[1], 'string', ''); $this->checkTypeOptional('formatDate', 3, $args[2], 'boolean', false); list($format, $date, $local) = $args; $langcode = $lang->getCode(); if ($date === '') { $cacheKey = $this->getParserOptions()->getTimestamp(); $timestamp = new MWTimestamp($cacheKey); $date = $timestamp->getTimestamp(TS_ISO_8601); $useTTL = true; } else { # Correct for DateTime interpreting 'XXXX' as XX:XX o'clock if (preg_match('/^[0-9]{4}$/', $date)) { $date = '00:00 ' . $date; } $cacheKey = $date; $useTTL = false; } if (isset($this->timeCache[$format][$cacheKey][$langcode][$local])) { $ttl = $this->timeCache[$format][$cacheKey][$langcode][$local][1]; if ($useTTL && $ttl !== null) { $this->getEngine()->setTTL($ttl); } return array($this->timeCache[$format][$cacheKey][$langcode][$local][0]); } # Default input timezone is UTC. try { $utc = new DateTimeZone('UTC'); $dateObject = new DateTime($date, $utc); } catch (Exception $ex) { throw new Scribunto_LuaError("bad argument #2 to 'formatDate' (not a valid timestamp)"); } # Set output timezone. if ($local) { global $wgLocaltimezone; if (isset($wgLocaltimezone)) { $tz = new DateTimeZone($wgLocaltimezone); } else { $tz = new DateTimeZone(date_default_timezone_get()); } } else { $tz = $utc; } $dateObject->setTimezone($tz); # Generate timestamp $ts = $dateObject->format('YmdHis'); if ($ts < 0) { throw new Scribunto_LuaError("mw.language:formatDate() only supports years from 0"); } elseif ($ts >= 100000000000000.0) { throw new Scribunto_LuaError("mw.language:formatDate() only supports years up to 9999"); } $ttl = null; $ret = $lang->sprintfDate($format, $ts, $tz, $ttl); $this->timeCache[$format][$cacheKey][$langcode][$local] = array($ret, $ttl); if ($useTTL && $ttl !== null) { $this->getEngine()->setTTL($ttl); } return array($ret); }
/** * 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; }
/** * Internal helper function for converting UTC timezone to a user's timezone * @param $user User * @param $ts string * @param $format output format */ private static function getUserLocalTime($user, $ts, $format = TS_MW) { $timestamp = new MWTimestamp($ts); $timestamp->offsetForUser($user); return $timestamp->getTimestamp($format); }
private function getNotificationTimestamp(User $user, Title $title, $item, $force, $oldid) { if (!$oldid) { // No oldid given, assuming latest revision; clear the timestamp. return null; } if (!$title->getNextRevisionID($oldid)) { // Oldid given and is the latest revision for this title; clear the timestamp. return null; } if ($item === null) { $item = $this->loadWatchedItem($user, $title); } if (!$item) { // This can only happen if $force is enabled. return null; } // Oldid given and isn't the latest; update the timestamp. // This will result in no further notification emails being sent! // Calls Revision::getTimestampFromId in normal operation $notificationTimestamp = call_user_func($this->revisionGetTimestampFromIdCallback, $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 < $item->getNotificationTimestamp()) { if ($force != 'force') { return false; } else { // This is a little silly… return $item->getNotificationTimestamp(); } } return $notificationTimestamp; }
function showImage() { global $wgOut; $wgOut->disable(); $info = $this->retrieveCaptcha(); if ($info) { $timestamp = new MWTimestamp(); $info['viewed'] = $timestamp->getTimestamp(); $this->storeCaptcha($info); $salt = $info['salt']; $hash = $info['hash']; return $this->getBackend()->streamFile(array('src' => $this->imagePath($salt, $hash), 'headers' => array("Cache-Control: private, s-maxage=0, max-age=3600")))->isOK(); } wfHttpError(500, 'Internal Error', 'Requested bogus captcha image'); return false; }
/** * @param $parser Parser * @param $frame PPFrame * @param $format string * @param $date string * @param $language string * @param $local string|bool * @return string */ public static function timeCommon($parser, $frame = null, $format = '', $date = '', $language = '', $local = false) { global $wgLocaltimezone; self::registerClearHook(); if ($date === '') { $cacheKey = $parser->getOptions()->getTimestamp(); $timestamp = new MWTimestamp($cacheKey); $date = $timestamp->getTimestamp(TS_ISO_8601); $useTTL = true; } else { $cacheKey = $date; $useTTL = false; } if (isset(self::$mTimeCache[$format][$cacheKey][$language][$local])) { $cachedVal = self::$mTimeCache[$format][$cacheKey][$language][$local]; if ($useTTL && $cachedVal[1] !== null && $frame && is_callable(array($frame, 'setTTL'))) { $frame->setTTL($cachedVal[1]); } return $cachedVal[0]; } # compute the timestamp string $ts # PHP >= 5.2 can handle dates before 1970 or after 2038 using the DateTime object $invalidTime = false; # the DateTime constructor must be used because it throws exceptions # when errors occur, whereas date_create appears to just output a warning # that can't really be detected from within the code try { # Default input timezone is UTC. $utc = new DateTimeZone('UTC'); # Correct for DateTime interpreting 'XXXX' as XX:XX o'clock if (preg_match('/^[0-9]{4}$/', $date)) { $date = '00:00 ' . $date; } # Parse date # UTC is a default input timezone. $dateObject = new DateTime($date, $utc); # Set output timezone. if ($local) { if (isset($wgLocaltimezone)) { $tz = new DateTimeZone($wgLocaltimezone); } else { $tz = new DateTimeZone(date_default_timezone_get()); } } else { $tz = $utc; } $dateObject->setTimezone($tz); # Generate timestamp $ts = $dateObject->format('YmdHis'); } catch (Exception $ex) { $invalidTime = true; } $ttl = null; # format the timestamp and return the result if ($invalidTime) { $result = '<strong class="error">' . wfMessage('pfunc_time_error')->inContentLanguage()->escaped() . '</strong>'; } else { self::$mTimeChars += strlen($format); if (self::$mTimeChars > self::$mMaxTimeChars) { return '<strong class="error">' . wfMessage('pfunc_time_too_long')->inContentLanguage()->escaped() . '</strong>'; } else { if ($ts < 0) { // Language can't deal with BC years return '<strong class="error">' . wfMessage('pfunc_time_too_small')->inContentLanguage()->escaped() . '</strong>'; } elseif ($ts < 100000000000000) { // Language can't deal with years after 9999 if ($language !== '' && Language::isValidBuiltInCode($language)) { // use whatever language is passed as a parameter $langObject = Language::factory($language); } else { // use wiki's content language $langObject = $parser->getFunctionLang(); StubObject::unstub($langObject); // $ttl is passed by reference, which doesn't work right on stub objects } $result = $langObject->sprintfDate($format, $ts, $tz, $ttl); } else { return '<strong class="error">' . wfMessage('pfunc_time_too_big')->inContentLanguage()->escaped() . '</strong>'; } } } self::$mTimeCache[$format][$cacheKey][$language][$local] = array($result, $ttl); if ($useTTL && $ttl !== null && $frame && is_callable(array($frame, 'setTTL'))) { $frame->setTTL($ttl); } return $result; }
/** * Check selected RFC 7232 precondition headers * * RFC 7232 envisions a particular model where you send your request to "a * resource", and for write requests that you can read "the resource" by * changing the method to GET. When the API receives a GET request, it * works out even though "the resource" from RFC 7232's perspective might * be many resources from MediaWiki's perspective. But it totally fails for * a POST, since what HTTP sees as "the resource" is probably just * "/api.php" with all the interesting bits in the body. * * Therefore, we only support RFC 7232 precondition headers for GET (and * HEAD). That means we don't need to bother with If-Match and * If-Unmodified-Since since they only apply to modification requests. * * And since we don't support Range, If-Range is ignored too. * * @since 1.26 * @param ApiBase $module Api module being used * @return bool True on success, false should exit immediately */ protected function checkConditionalRequestHeaders($module) { if ($this->mInternalMode) { // No headers to check in internal mode return true; } if ($this->getRequest()->getMethod() !== 'GET' && $this->getRequest()->getMethod() !== 'HEAD') { // Don't check POSTs return true; } $return304 = false; $ifNoneMatch = array_diff($this->getRequest()->getHeader('If-None-Match', WebRequest::GETHEADER_LIST) ?: array(), array('')); if ($ifNoneMatch) { if ($ifNoneMatch === array('*')) { // API responses always "exist" $etag = '*'; } else { $etag = $module->getConditionalRequestData('etag'); } } if ($ifNoneMatch && $etag !== null) { $test = substr($etag, 0, 2) === 'W/' ? substr($etag, 2) : $etag; $match = array_map(function ($s) { return substr($s, 0, 2) === 'W/' ? substr($s, 2) : $s; }, $ifNoneMatch); $return304 = in_array($test, $match, true); } else { $value = trim($this->getRequest()->getHeader('If-Modified-Since')); // Some old browsers sends sizes after the date, like this: // Wed, 20 Aug 2003 06:51:19 GMT; length=5202 // Ignore that. $i = strpos($value, ';'); if ($i !== false) { $value = trim(substr($value, 0, $i)); } if ($value !== '') { try { $ts = new MWTimestamp($value); if ($ts->getTimestamp(TS_RFC2822) === $value || $ts->format('l, d-M-y H:i:s') . ' GMT' === $value || $ts->format('D M j H:i:s Y') === $value || $ts->format('D M j H:i:s Y') === $value) { $lastMod = $module->getConditionalRequestData('last-modified'); if ($lastMod !== null) { // Mix in some MediaWiki modification times $modifiedTimes = array('page' => $lastMod, 'user' => $this->getUser()->getTouched(), 'epoch' => $this->getConfig()->get('CacheEpoch')); if ($this->getConfig()->get('UseSquid')) { // T46570: the core page itself may not change, but resources might $modifiedTimes['sepoch'] = wfTimestamp(TS_MW, time() - $this->getConfig()->get('SquidMaxage')); } Hooks::run('OutputPageCheckLastModified', array(&$modifiedTimes)); $lastMod = max($modifiedTimes); $return304 = wfTimestamp(TS_MW, $lastMod) <= $ts->getTimestamp(TS_MW); } } } catch (TimestampException $e) { // Invalid timestamp, ignore it } } } if ($return304) { $this->getRequest()->response()->statusHeader(304); // Avoid outputting the compressed representation of a zero-length body MediaWiki\suppressWarnings(); ini_set('zlib.output_compression', 0); MediaWiki\restoreWarnings(); wfClearOutputBuffers(); return false; } return true; }
public function testApproveTimestamp() { $t = new ModerationTestsuite(); $entry = $t->getSampleEntry(); $TEST_TIME_CHANGE = '6 hours'; $ACCEPTABLE_DIFFERENCE = 300; # in seconds $ts = new MWTimestamp(time()); $ts->timestamp->modify('-' . $TEST_TIME_CHANGE); $dbw = wfGetDB(DB_MASTER); $dbw->update('moderation', array('mod_timestamp' => $ts->getTimestamp(TS_MW)), array('mod_id' => $entry->id), __METHOD__); $rev = $this->tryToApprove($t, $entry); # Page history should mention the time when edit was made, # not when it was approved. $expected = $ts->getTimestamp(TS_ISO_8601); $this->assertEquals($expected, $rev['timestamp'], "testApproveTimestamp(): approved edit has incorrect timestamp in the page history"); # RecentChanges should mention the time when the edit was # approved, so that it won't "appear in the past", confusing # those who read RecentChanges. $ret = $t->query(array('action' => 'query', 'list' => 'recentchanges', 'rcprop' => 'timestamp', 'rclimit' => 1, 'rcuser' => $t->lastEdit['User'])); $rc_timestamp = $ret['query']['recentchanges'][0]['timestamp']; $this->assertNotEquals($expected, $rc_timestamp, "testApproveTimestamp(): approved edit has \"appeared in the past\" in the RecentChanges"); # Does the time in RecentChanges match the time of approval? # # NOTE: we don't know the time of approval to the second, so # string comparison can't be used. Difference can be seconds # or even minutes (if system time is off). $ts->timestamp->modify('+' . $TEST_TIME_CHANGE); $expected = $ts->getTimestamp(TS_UNIX); $ts_actual = new MWTimestamp($rc_timestamp); $actual = $ts_actual->getTimestamp(TS_UNIX); $this->assertLessThan($ACCEPTABLE_DIFFERENCE, abs($expected - $actual), "testApproveTimestamp(): timestamp of approved edit in RecentChanges is too different from the time of approval"); }