protected function getReviewStatus(TMessage $message) { if (!$this->reviewMode) { return ''; } $reviewers = (array) $message->getProperty('reviewers'); $count = count($reviewers); if ($count === 0) { return ''; } $userId = $this->context->getUser()->getId(); $you = in_array($userId, $reviewers); $key = $you ? "y{$count}" : "n{$count}"; // ->text() (and ->parse()) invokes the parser. Each call takes // about 70 KiB, so it makes sense to cache these messages which // have high repetition. if (isset($this->reviewStatusCache[$key])) { return $this->reviewStatusCache[$key]; } elseif ($you) { $msg = wfMessage('translate-messagereview-reviewswithyou')->numParams($count)->text(); } else { $msg = wfMessage('translate-messagereview-reviews')->numParams($count)->text(); } $wrap = Html::rawElement('div', array('class' => 'mw-translate-messagereviewstatus'), $msg); $this->reviewStatusCache[$key] = $wrap; return $wrap; }
/** * @param $context IContextSource * @param $pageType */ public static function addNavigationLinks(IContextSource $context, $pageType) { $linkDefs = array('home' => 'Special:AbuseFilter', 'recentchanges' => 'Special:AbuseFilter/history', 'examine' => 'Special:AbuseFilter/examine', 'log' => 'Special:AbuseLog'); if ($context->getUser()->isAllowed('abusefilter-modify')) { $linkDefs = array_merge($linkDefs, array('test' => 'Special:AbuseFilter/test', 'tools' => 'Special:AbuseFilter/tools', 'import' => 'Special:AbuseFilter/import')); } // Save some translator work $msgOverrides = array('recentchanges' => 'abusefilter-filter-log'); $links = array(); foreach ($linkDefs as $name => $page) { // Give grep a chance to find the usages: // abusefilter-topnav-home, abusefilter-topnav-test, abusefilter-topnav-examine // abusefilter-topnav-log, abusefilter-topnav-tools, abusefilter-topnav-import $msgName = "abusefilter-topnav-{$name}"; if (isset($msgOverrides[$name])) { $msgName = $msgOverrides[$name]; } $msg = wfMessage($msgName)->parse(); $title = Title::newFromText($page); if ($name == $pageType) { $links[] = Xml::tags('strong', null, $msg); } else { $links[] = Linker::link($title, $msg); } } $linkStr = wfMessage('parentheses', $context->getLanguage()->pipeList($links))->text(); $linkStr = wfMessage('abusefilter-topnav')->parse() . " {$linkStr}"; $linkStr = Xml::tags('div', array('class' => 'mw-abusefilter-navigation'), $linkStr); $context->getOutput()->setSubtitle($linkStr); }
/** * Generates a brief review form for a page * @param \IContextSource|\RequestContext $context * @param FlaggableWikiPage $article * @param Revision $rev */ public function __construct(IContextSource $context, FlaggableWikiPage $article, Revision $rev) { $this->user = $context->getUser(); $this->request = $context->getRequest(); $this->article = $article; $this->rev = $rev; }
/** * Return a link to the edit page, with the text * saying "view source" if the user can't edit the page * * @param Title $titleObj * @return string */ private function buildEditLink(Title $titleObj) { if ($titleObj->quickUserCan('edit', $this->context->getUser())) { $linkMsg = 'editlink'; } else { $linkMsg = 'viewsourcelink'; } return $this->linkRenderer->makeLink($titleObj, $this->context->msg($linkMsg)->text(), [], ['action' => 'edit']); }
/** * Check whether external edit or diff should be used. * * @param $context IContextSource context to use * @param $type String can be either 'edit' or 'diff' * @return Bool */ public static function useExternalEngine(IContextSource $context, $type) { global $wgUseExternalEditor; if (!$wgUseExternalEditor) { return false; } $pref = $type == 'diff' ? 'externaldiff' : 'externaleditor'; $request = $context->getRequest(); return !$request->getVal('internaledit') && ($context->getUser()->getOption($pref) || $request->getVal('externaledit')); }
/** * Fetch an appropriate changes list class for the specified context * Some users might want to use an enhanced list format, for instance * * @param $context IContextSource to use * @return ChangesList|EnhancedChangesList|OldChangesList derivative */ public static function newFromContext( IContextSource $context ) { $user = $context->getUser(); $sk = $context->getSkin(); $list = null; if ( wfRunHooks( 'FetchChangesList', array( $user, &$sk, &$list ) ) ) { $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) ); return $new ? new EnhancedChangesList( $context ) : new OldChangesList( $context ); } else { return $list; } }
private function main() { global $wgUseFileCache, $wgTitle, $wgUseAjax; wfProfileIn(__METHOD__); $request = $this->context->getRequest(); // Send Ajax requests to the Ajax dispatcher. if ($wgUseAjax && $request->getVal('action', 'view') == 'ajax') { // Set a dummy title, because $wgTitle == null might break things $title = Title::makeTitle(NS_MAIN, 'AJAX'); $this->context->setTitle($title); $wgTitle = $title; $dispatcher = new AjaxDispatcher(); $dispatcher->performAction(); wfProfileOut(__METHOD__); return; } // Get title from request parameters, // is set on the fly by parseTitle the first time. $title = $this->getTitle(); $action = $this->getAction(); $wgTitle = $title; if ($wgUseFileCache && $title->getNamespace() >= 0) { wfProfileIn('main-try-filecache'); if (HTMLFileCache::useFileCache($this->context)) { // Try low-level file cache hit $cache = HTMLFileCache::newFromTitle($title, $action); if ($cache->isCacheGood()) { // Check incoming headers to see if client has this cached $timestamp = $cache->cacheTimestamp(); if (!$this->context->getOutput()->checkLastModified($timestamp)) { $cache->loadFromFileCache($this->context); } // Do any stats increment/watchlist stuff $this->context->getWikiPage()->doViewUpdates($this->context->getUser()); // Tell OutputPage that output is taken care of $this->context->getOutput()->disable(); wfProfileOut('main-try-filecache'); wfProfileOut(__METHOD__); return; } } wfProfileOut('main-try-filecache'); } $this->performRequest(); // Now commit any transactions, so that unreported errors after // output() don't roll back the whole DB transaction wfGetLBFactory()->commitMasterChanges(); // Output everything! $this->context->getOutput()->output(); wfProfileOut(__METHOD__); }
private function main() { global $wgUseFileCache, $wgTitle, $wgUseAjax; wfProfileIn(__METHOD__); $request = $this->context->getRequest(); // Send Ajax requests to the Ajax dispatcher. if ($wgUseAjax && $request->getVal('action', 'view') == 'ajax') { // Set a dummy title, because $wgTitle == null might break things // Wikia change - start // @author macbre, wladek $title = Wikia::createTitleFromRequest($request); // Wikia change - end $this->context->setTitle($title); $wgTitle = $title; $dispatcher = new AjaxDispatcher(); $dispatcher->performAction(); wfProfileOut(__METHOD__); return; } // Get title from request parameters, // is set on the fly by parseTitle the first time. $title = $this->getTitle(); $action = $this->getAction(); $wgTitle = $title; if ($wgUseFileCache && $title->getNamespace() >= 0) { wfProfileIn('main-try-filecache'); if (HTMLFileCache::useFileCache($this->context)) { // Try low-level file cache hit $cache = HTMLFileCache::newFromTitle($title, $action); if ($cache->isCacheGood()) { // Check incoming headers to see if client has this cached $timestamp = $cache->cacheTimestamp(); if (!$this->context->getOutput()->checkLastModified($timestamp)) { $cache->loadFromFileCache($this->context); } // Do any stats increment/watchlist stuff $this->context->getWikiPage()->doViewUpdates($this->context->getUser()); // Tell OutputPage that output is taken care of $this->context->getOutput()->disable(); wfProfileOut('main-try-filecache'); wfProfileOut(__METHOD__); return; } } wfProfileOut('main-try-filecache'); } $this->performRequest(); $this->finalCleanup(); wfProfileOut(__METHOD__); }
/** * Build protection level selector * * @param string $action Action to protect * @param string $selected Current protection level * @return string HTML fragment */ function buildSelector($action, $selected) { // If the form is disabled, display all relevant levels. Otherwise, // just show the ones this user can use. $levels = MWNamespace::getRestrictionLevels($this->mTitle->getNamespace(), $this->disabled ? null : $this->mContext->getUser()); $id = 'mwProtect-level-' . $action; $attribs = array('id' => $id, 'name' => $id, 'size' => count($levels)) + $this->disabledAttrib; $out = Xml::openElement('select', $attribs); foreach ($levels as $key) { $out .= Xml::option($this->getOptionLabel($key), $key, $key == $selected); } $out .= Xml::closeElement('select'); return $out; }
/** * Build protection level selector * * @param string $action Action to protect * @param string $selected Current protection level * @return string HTML fragment */ function buildSelector($action, $selected) { // If the form is disabled, display all relevant levels. Otherwise, // just show the ones this user can use. $levels = MWNamespace::getRestrictionLevels($this->mTitle->getNamespace(), $this->disabled ? null : $this->mContext->getUser()); $id = 'mwProtect-level-' . $action; $select = new XmlSelect($id, $id, $selected); $select->setAttribute('size', count($levels)); if ($this->disabled) { $select->setAttribute('disabled', 'disabled'); } foreach ($levels as $key) { $select->addOption($this->getOptionLabel($key), $key); } return $select->getHTML(); }
private function main() { global $wgUseFileCache, $wgTitle, $wgUseAjax; wfProfileIn(__METHOD__); # Set title from request parameters $wgTitle = $this->getTitle(); $action = $this->getAction(); $user = $this->context->getUser(); # Send Ajax requests to the Ajax dispatcher. if ($wgUseAjax && $action == 'ajax') { $dispatcher = new AjaxDispatcher(); $dispatcher->performAction(); wfProfileOut(__METHOD__); return; } if ($wgUseFileCache && $wgTitle->getNamespace() != NS_SPECIAL) { wfProfileIn('main-try-filecache'); // Raw pages should handle cache control on their own, // even when using file cache. This reduces hits from clients. if (HTMLFileCache::useFileCache()) { /* Try low-level file cache hit */ $cache = new HTMLFileCache($wgTitle, $action); if ($cache->isFileCacheGood()) { /* Check incoming headers to see if client has this cached */ $timestamp = $cache->fileCacheTime(); if (!$this->context->getOutput()->checkLastModified($timestamp)) { $cache->loadFromFileCache(); } # Do any stats increment/watchlist stuff $article = WikiPage::factory($wgTitle); $article->doViewUpdates($user); # Tell OutputPage that output is taken care of $this->context->getOutput()->disable(); wfProfileOut('main-try-filecache'); wfProfileOut(__METHOD__); return; } } wfProfileOut('main-try-filecache'); } $this->performRequest(); $this->finalCleanup(); wfProfileOut(__METHOD__); }
public static function onGetEmailAuthentication(User &$user, IContextSource $context, &$disableEmailPrefs, &$emailauthenticated) { if ($user->getEmail()) { $emailTimestamp = $user->getEmailAuthenticationTimestamp(); $optionNewEmail = $user->getGlobalAttribute('new_email'); $msgKeyPrefixEmail = empty($optionNewEmail) && !$emailTimestamp ? 'usersignup-user-pref-unconfirmed-' : 'usersignup-user-pref-'; if (empty($optionNewEmail) && $emailTimestamp) { $lang = $context->getLanguage(); $displayUser = $context->getUser(); $time = $lang->userTimeAndDate($emailTimestamp, $displayUser); $d = $lang->userDate($emailTimestamp, $displayUser); $t = $lang->userTime($emailTimestamp, $displayUser); $emailauthenticated = $context->msg($msgKeyPrefixEmail . 'emailauthenticated', $time, $d, $t)->parse() . '<br />'; $disableEmailPrefs = false; } else { $disableEmailPrefs = true; $emailauthenticated = $context->msg($msgKeyPrefixEmail . 'emailnotauthenticated', array($optionNewEmail))->parse() . '<br />' . Linker::linkKnown(SpecialPage::getTitleFor('Confirmemail'), $context->msg('usersignup-user-pref-emailconfirmlink')->escaped()) . '<br />'; } } else { $disableEmailPrefs = true; $emailauthenticated = $context->msg('usersignup-user-pref-noemailprefs')->escaped(); } return true; }
/** * Really send a mail. Permissions should have been checked using * getPermissionsError(). It is probably also a good * idea to check the edit token and ping limiter in advance. * * @param array $data * @param IContextSource $context * @return Status|string|bool Status object, or potentially a String on error * or maybe even true on success if anything uses the EmailUser hook. */ public static function submit(array $data, IContextSource $context) { $config = $context->getConfig(); $target = self::getTarget($data['Target']); if (!$target instanceof User) { // Messages used here: notargettext, noemailtext, nowikiemailtext return $context->msg($target . 'text')->parseAsBlock(); } $to = MailAddress::newFromUser($target); $from = MailAddress::newFromUser($context->getUser()); $subject = $data['Subject']; $text = $data['Text']; // Add a standard footer and trim up trailing newlines $text = rtrim($text) . "\n\n-- \n"; $text .= $context->msg('emailuserfooter', $from->name, $to->name)->inContentLanguage()->text(); $error = ''; if (!Hooks::run('EmailUser', array(&$to, &$from, &$subject, &$text, &$error))) { return $error; } if ($config->get('UserEmailUseReplyTo')) { /** * Put the generic wiki autogenerated address in the From: * header and reserve the user for Reply-To. * * This is a bit ugly, but will serve to differentiate * wiki-borne mails from direct mails and protects against * SPF and bounce problems with some mailers (see below). */ $mailFrom = new MailAddress($config->get('PasswordSender'), wfMessage('emailsender')->inContentLanguage()->text()); $replyTo = $from; } else { /** * Put the sending user's e-mail address in the From: header. * * This is clean-looking and convenient, but has issues. * One is that it doesn't as clearly differentiate the wiki mail * from "directly" sent mails. * * Another is that some mailers (like sSMTP) will use the From * address as the envelope sender as well. For open sites this * can cause mails to be flunked for SPF violations (since the * wiki server isn't an authorized sender for various users' * domains) as well as creating a privacy issue as bounces * containing the recipient's e-mail address may get sent to * the sending user. */ $mailFrom = $from; $replyTo = null; } $status = UserMailer::send($to, $mailFrom, $subject, $text, array('replyTo' => $replyTo)); if (!$status->isGood()) { return $status; } else { // if the user requested a copy of this mail, do this now, // unless they are emailing themselves, in which case one // copy of the message is sufficient. if ($data['CCMe'] && $to != $from) { $cc_subject = $context->msg('emailccsubject')->rawParams($target->getName(), $subject)->text(); // target and sender are equal, because this is the CC for the sender Hooks::run('EmailUserCC', array(&$from, &$from, &$cc_subject, &$text)); $ccStatus = UserMailer::send($from, $from, $cc_subject, $text); $status->merge($ccStatus); } Hooks::run('EmailUserComplete', array($to, $from, $subject, $text)); return $status; } }
/** * Process the form * * @param $data Array * @param $context IContextSource * @throws ErrorPageError * @return Array( Array(message key, parameters) ) on failure, True on success */ public static function processUnblock(array $data, IContextSource $context) { $performer = $context->getUser(); $target = $data['Target']; $block = Block::newFromTarget($data['Target']); if (!$block instanceof Block) { return array(array('ipb_cant_unblock', $target)); } # bug 15810: blocked admins should have limited access here. This # won't allow sysops to remove autoblocks on themselves, but they # should have ipblock-exempt anyway $status = SpecialBlock::checkUnblockSelf($target, $performer); if ($status !== true) { throw new ErrorPageError('badaccess', $status); } # If the specified IP is a single address, and the block is a range block, don't # unblock the whole range. list($target, $type) = SpecialBlock::getTargetAndType($target); if ($block->getType() == Block::TYPE_RANGE && $type == Block::TYPE_IP) { $range = $block->getTarget(); return array(array('ipb_blocked_as_range', $target, $range)); } # If the name was hidden and the blocking user cannot hide # names, then don't allow any block removals... if (!$performer->isAllowed('hideuser') && $block->mHideName) { return array('unblock-hideuser'); } # Delete block if (!$block->delete()) { return array('ipb_cant_unblock', htmlspecialchars($block->getTarget())); } # Unset _deleted fields as needed if ($block->mHideName) { # Something is deeply FUBAR if this is not a User object, but who knows? $id = $block->getTarget() instanceof User ? $block->getTarget()->getID() : User::idFromName($block->getTarget()); RevisionDeleteUser::unsuppressUserName($block->getTarget(), $id); } # Redact the name (IP address) for autoblocks if ($block->getType() == Block::TYPE_AUTO) { $page = Title::makeTitle(NS_USER, '#' . $block->getId()); } else { $page = $block->getTarget() instanceof User ? $block->getTarget()->getUserpage() : Title::makeTitle(NS_USER, $block->getTarget()); } # Make log entry $log = new LogPage('block'); $log->addEntry('unblock', $page, $data['Reason'], array(), $performer); return true; }
/** * Given the form data, actually implement a block * @param $data Array * @param $context IContextSource * @return Bool|String */ public static function processForm(array $data, IContextSource $context) { global $wgBlockAllowsUTEdit; $performer = $context->getUser(); // Handled by field validator callback // self::validateTargetField( $data['Target'] ); # This might have been a hidden field or a checkbox, so interesting data # can come from it $data['Confirm'] = !in_array($data['Confirm'], array('', '0', null, false), true); list($target, $type) = self::getTargetAndType($data['Target']); if ($type == Block::TYPE_USER) { $user = $target; $target = $user->getName(); $userId = $user->getId(); # Give admins a heads-up before they go and block themselves. Much messier # to do this for IPs, but it's pretty unlikely they'd ever get the 'block' # permission anyway, although the code does allow for it. # Note: Important to use $target instead of $data['Target'] # since both $data['PreviousTarget'] and $target are normalized # but $data['target'] gets overriden by (non-normalized) request variable # from previous request. if ($target === $performer->getName() && ($data['PreviousTarget'] !== $target || !$data['Confirm'])) { return array('ipb-blockingself'); } } elseif ($type == Block::TYPE_RANGE) { $userId = 0; } elseif ($type == Block::TYPE_IP) { $target = $target->getName(); $userId = 0; } else { # This should have been caught in the form field validation return array('badipaddress'); } if (strlen($data['Expiry']) == 0 || strlen($data['Expiry']) > 50 || !self::parseExpiryInput($data['Expiry'])) { return array('ipb_expiry_invalid'); } if (!isset($data['DisableEmail'])) { $data['DisableEmail'] = false; } # If the user has done the form 'properly', they won't even have been given the # option to suppress-block unless they have the 'hideuser' permission if (!isset($data['HideUser'])) { $data['HideUser'] = false; } if ($data['HideUser']) { if (!$performer->isAllowed('hideuser')) { # this codepath is unreachable except by a malicious user spoofing forms, # or by race conditions (user has oversight and sysop, loads block form, # and is de-oversighted before submission); so need to fail completely # rather than just silently disable hiding return array('badaccess-group0'); } # Recheck params here... if ($type != Block::TYPE_USER) { $data['HideUser'] = false; # IP users should not be hidden } elseif (!in_array($data['Expiry'], array('infinite', 'infinity', 'indefinite'))) { # Bad expiry. return array('ipb_expiry_temp'); } elseif ($user->getEditCount() > self::HIDEUSER_CONTRIBLIMIT) { # Typically, the user should have a handful of edits. # Disallow hiding users with many edits for performance. return array('ipb_hide_invalid'); } elseif (!$data['Confirm']) { return array('ipb-confirmhideuser'); } } # Create block object. $block = new Block(); $block->setTarget($target); $block->setBlocker($performer); $block->mReason = $data['Reason'][0]; $block->mExpiry = self::parseExpiryInput($data['Expiry']); $block->prevents('createaccount', $data['CreateAccount']); $block->prevents('editownusertalk', !$wgBlockAllowsUTEdit || $data['DisableUTEdit']); $block->prevents('sendemail', $data['DisableEmail']); $block->isHardblock($data['HardBlock']); $block->isAutoblocking($data['AutoBlock']); $block->mHideName = $data['HideUser']; if (!wfRunHooks('BlockIp', array(&$block, &$performer))) { return array('hookaborted'); } # Try to insert block. Is there a conflicting block? $status = $block->insert(); if (!$status) { # Show form unless the user is already aware of this... if (!$data['Confirm'] || array_key_exists('PreviousTarget', $data) && $data['PreviousTarget'] !== $target) { return array(array('ipb_already_blocked', $block->getTarget())); # Otherwise, try to update the block... } else { # This returns direct blocks before autoblocks/rangeblocks, since we should # be sure the user is blocked by now it should work for our purposes $currentBlock = Block::newFromTarget($target); if ($block->equals($currentBlock)) { return array(array('ipb_already_blocked', $block->getTarget())); } # If the name was hidden and the blocking user cannot hide # names, then don't allow any block changes... if ($currentBlock->mHideName && !$performer->isAllowed('hideuser')) { return array('cant-see-hidden-user'); } $currentBlock->delete(); $status = $block->insert(); $logaction = 'reblock'; # Unset _deleted fields if requested if ($currentBlock->mHideName && !$data['HideUser']) { RevisionDeleteUser::unsuppressUserName($target, $userId); } # If hiding/unhiding a name, this should go in the private logs if ((bool) $currentBlock->mHideName) { $data['HideUser'] = true; } } } else { $logaction = 'block'; } wfRunHooks('BlockIpComplete', array($block, $performer)); # Set *_deleted fields if requested if ($data['HideUser']) { RevisionDeleteUser::suppressUserName($target, $userId); } # Can't watch a rangeblock if ($type != Block::TYPE_RANGE && $data['Watch']) { $performer->addWatch(Title::makeTitle(NS_USER, $target)); } # Block constructor sanitizes certain block options on insert $data['BlockEmail'] = $block->prevents('sendemail'); $data['AutoBlock'] = $block->isAutoblocking(); # Prepare log parameters $logParams = array(); $logParams[] = $data['Expiry']; $logParams[] = self::blockLogFlags($data, $type); # Make log entry, if the name is hidden, put it in the oversight log $log_type = $data['HideUser'] ? 'suppress' : 'block'; $log = new LogPage($log_type); $log_id = $log->addEntry($logaction, Title::makeTitle(NS_USER, $target), $data['Reason'][0], $logParams); # Relate log ID to block IDs (bug 25763) $blockIds = array_merge(array($status['id']), $status['autoIds']); $log->addRelations('ipb_id', $blockIds, $log_id); # Report to the user return true; }
/** * Get the key and parameters for the corresponding error message. * * @since 1.22 * @param IContextSource $context * @return array */ public function getPermissionsError(IContextSource $context) { $blocker = $this->getBlocker(); if ($blocker instanceof User) { // local user $blockerUserpage = $blocker->getUserPage(); $link = "[[{$blockerUserpage->getPrefixedText()}|{$blockerUserpage->getText()}]]"; } else { // foreign user $link = $blocker; } $reason = $this->mReason; if ($reason == '') { $reason = $context->msg('blockednoreason')->text(); } /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked. * This could be a username, an IP range, or a single IP. */ $intended = $this->getTarget(); $lang = $context->getLanguage(); return array($this->mAuto ? 'autoblockedtext' : 'blockedtext', $link, $reason, $context->getRequest()->getIP(), $this->getByName(), $this->getId(), $lang->formatExpiry($this->mExpiry), (string) $intended, $lang->userTimeAndDate($this->mTimestamp, $context->getUser())); }
private function main() { global $wgTitle; $output = $this->context->getOutput(); $request = $this->context->getRequest(); // Send Ajax requests to the Ajax dispatcher. if ($this->config->get('UseAjax') && $request->getVal('action') === 'ajax') { // Set a dummy title, because $wgTitle == null might break things $title = Title::makeTitle(NS_SPECIAL, 'Badtitle/performing an AJAX call in ' . __METHOD__); $this->context->setTitle($title); $wgTitle = $title; $dispatcher = new AjaxDispatcher($this->config); $dispatcher->performAction($this->context->getUser()); return; } // Get title from request parameters, // is set on the fly by parseTitle the first time. $title = $this->getTitle(); $action = $this->getAction(); $wgTitle = $title; // Set DB query expectations for this HTTP request $trxLimits = $this->config->get('TrxProfilerLimits'); $trxProfiler = Profiler::instance()->getTransactionProfiler(); $trxProfiler->setLogger(LoggerFactory::getInstance('DBPerformance')); if ($request->hasSafeMethod()) { $trxProfiler->setExpectations($trxLimits['GET'], __METHOD__); } else { $trxProfiler->setExpectations($trxLimits['POST'], __METHOD__); } // If the user has forceHTTPS set to true, or if the user // is in a group requiring HTTPS, or if they have the HTTPS // preference set, redirect them to HTTPS. // Note: Do this after $wgTitle is setup, otherwise the hooks run from // isLoggedIn() will do all sorts of weird stuff. if ($request->getProtocol() == 'http' && preg_match('#^https://#', wfExpandUrl($request->getRequestURL(), PROTO_HTTPS)) && ($request->getSession()->shouldForceHTTPS() || $request->getCookie('forceHTTPS', '') || $request->getCookie('forceHTTPS') || $this->context->getUser()->isLoggedIn() && $this->context->getUser()->requiresHTTPS())) { $oldUrl = $request->getFullRequestURL(); $redirUrl = preg_replace('#^http://#', 'https://', $oldUrl); // ATTENTION: This hook is likely to be removed soon due to overall design of the system. if (Hooks::run('BeforeHttpsRedirect', [$this->context, &$redirUrl])) { if ($request->wasPosted()) { // This is weird and we'd hope it almost never happens. This // means that a POST came in via HTTP and policy requires us // redirecting to HTTPS. It's likely such a request is going // to fail due to post data being lost, but let's try anyway // and just log the instance. // @todo FIXME: See if we could issue a 307 or 308 here, need // to see how clients (automated & browser) behave when we do wfDebugLog('RedirectedPosts', "Redirected from HTTP to HTTPS: {$oldUrl}"); } // Setup dummy Title, otherwise OutputPage::redirect will fail $title = Title::newFromText('REDIR', NS_MAIN); $this->context->setTitle($title); // Since we only do this redir to change proto, always send a vary header $output->addVaryHeader('X-Forwarded-Proto'); $output->redirect($redirUrl); $output->output(); return; } } if ($title->canExist() && HTMLFileCache::useFileCache($this->context)) { // Try low-level file cache hit $cache = new HTMLFileCache($title, $action); if ($cache->isCacheGood()) { // Check incoming headers to see if client has this cached $timestamp = $cache->cacheTimestamp(); if (!$output->checkLastModified($timestamp)) { $cache->loadFromFileCache($this->context); } // Do any stats increment/watchlist stuff, assuming user is viewing the // latest revision (which should always be the case for file cache) $this->context->getWikiPage()->doViewUpdates($this->context->getUser()); // Tell OutputPage that output is taken care of $output->disable(); return; } } // Actually do the work of the request and build up any output $this->performRequest(); // GUI-ify and stash the page output in MediaWiki::doPreOutputCommit() while // ChronologyProtector synchronizes DB positions or slaves accross all datacenters. $buffer = null; $outputWork = function () use($output, &$buffer) { if ($buffer === null) { $buffer = $output->output(true); } return $buffer; }; // Now commit any transactions, so that unreported errors after // output() don't roll back the whole DB transaction and so that // we avoid having both success and error text in the response $this->doPreOutputCommit($outputWork); // Now send the actual output print $outputWork(); }
/** * Just like executePath() but will override global variables and execute * the page in "inclusion" mode. Returns true if the execution was * successful or false if there was no such special page, or a title object * if it was a redirect. * * Also saves the current $wgTitle, $wgOut, $wgRequest, $wgUser and $wgLang * variables so that the special page will get the context it'd expect on a * normal request, and then restores them to their previous values after. * * @param $title Title * @param $context IContextSource * * @return String: HTML fragment */ static function capturePath(Title $title, IContextSource $context) { global $wgOut, $wgTitle, $wgRequest, $wgUser, $wgLang; // Save current globals $oldTitle = $wgTitle; $oldOut = $wgOut; $oldRequest = $wgRequest; $oldUser = $wgUser; $oldLang = $wgLang; // Set the globals to the current context $wgTitle = $title; $wgOut = $context->getOutput(); $wgRequest = $context->getRequest(); $wgUser = $context->getUser(); $wgLang = $context->getLanguage(); // The useful part $ret = self::executePath($title, $context, true); // And restore the old globals $wgTitle = $oldTitle; $wgOut = $oldOut; $wgRequest = $oldRequest; $wgUser = $oldUser; $wgLang = $oldLang; return $ret; }
/** * Returns the default nav items for @see displayNavigation. * * @since 0.1 * * @return array */ public static function getDefaultNavigationItems(IContextSource $context) { $items = array(wfMsg('ep-nav-orgs') => SpecialPage::getTitleFor('Institutions'), wfMsg('ep-nav-courses') => SpecialPage::getTitleFor('Courses')); $items[wfMsg('ep-nav-students')] = SpecialPage::getTitleFor('Students'); $items[wfMsg('ep-nav-oas')] = SpecialPage::getTitleFor('OnlineAmbassadors'); $items[wfMsg('ep-nav-cas')] = SpecialPage::getTitleFor('CampusAmbassadors'); $user = $context->getUser(); if (EPStudent::has(array('user_id' => $user->getId()))) { $items[wfMsg('ep-nav-mycourses')] = SpecialPage::getTitleFor('MyCourses'); } if ($user->isAllowed('ep-online') && EPOA::newFromUser($user)->hasCourse()) { $items[wfMsg('ep-nav-oaprofile')] = SpecialPage::getTitleFor('OnlineAmbassadorProfile'); } if ($user->isAllowed('ep-campus') && EPCA::newFromUser($user)->hasCourse()) { $items[wfMsg('ep-nav-caprofile')] = SpecialPage::getTitleFor('CampusAmbassadorProfile'); } return $items; }
/** * Check if pages can be cached for this request/user * @param $context IContextSource * @return bool */ public static function useFileCache(IContextSource $context) { global $wgUseFileCache, $wgShowIPinHeader, $wgDebugToolbar, $wgContLang; if (!$wgUseFileCache) { return false; } if ($wgShowIPinHeader || $wgDebugToolbar) { wfDebug("HTML file cache skipped. Either \$wgShowIPinHeader and/or \$wgDebugToolbar on\n"); return false; } // Get all query values $queryVals = $context->getRequest()->getValues(); foreach ($queryVals as $query => $val) { if ($query === 'title' || $query === 'curid') { continue; // note: curid sets title // Normal page view in query form can have action=view. } elseif ($query === 'action' && in_array($val, self::cacheablePageActions())) { continue; // Below are header setting params } elseif ($query === 'maxage' || $query === 'smaxage') { continue; } return false; } $user = $context->getUser(); // Check for non-standard user language; this covers uselang, // and extensions for auto-detecting user language. $ulang = $context->getLanguage()->getCode(); $clang = $wgContLang->getCode(); // Check that there are no other sources of variation return !$user->getId() && !$user->getNewtalk() && $ulang == $clang; }
/** * @param $user User * @param $context IContextSource * @param $defaultPreferences * @return void */ static function profilePreferences($user, IContextSource $context, &$defaultPreferences) { global $wgAuth, $wgContLang, $wgParser, $wgCookieExpiration, $wgLanguageCode, $wgDisableTitleConversion, $wgDisableLangConversion, $wgMaxSigChars, $wgEnableEmail, $wgEmailConfirmToEdit, $wgEnableUserEmail, $wgEmailAuthentication, $wgEnotifWatchlist, $wgEnotifUserTalk, $wgEnotifRevealEditorAddress; ## User info ##################################### // Information panel $defaultPreferences['username'] = array('type' => 'info', 'label-message' => 'username', 'default' => $user->getName(), 'section' => 'personal/info'); $defaultPreferences['userid'] = array('type' => 'info', 'label-message' => 'uid', 'default' => $user->getId(), 'section' => 'personal/info'); # Get groups to which the user belongs $userEffectiveGroups = $user->getEffectiveGroups(); $userGroups = $userMembers = array(); foreach ($userEffectiveGroups as $ueg) { if ($ueg == '*') { // Skip the default * group, seems useless here continue; } $groupName = User::getGroupName($ueg); $userGroups[] = User::makeGroupLinkHTML($ueg, $groupName); $memberName = User::getGroupMember($ueg, $user->getName()); $userMembers[] = User::makeGroupLinkHTML($ueg, $memberName); } asort($userGroups); asort($userMembers); $lang = $context->getLanguage(); $defaultPreferences['usergroups'] = array('type' => 'info', 'label' => $context->msg('prefs-memberingroups')->numParams(count($userGroups))->parse(), 'default' => $context->msg('prefs-memberingroups-type', $lang->commaList($userGroups), $lang->commaList($userMembers))->plain(), 'raw' => true, 'section' => 'personal/info'); $defaultPreferences['editcount'] = array('type' => 'info', 'label-message' => 'prefs-edits', 'default' => $lang->formatNum($user->getEditCount()), 'section' => 'personal/info'); if ($user->getRegistration()) { $displayUser = $context->getUser(); $userRegistration = $user->getRegistration(); $defaultPreferences['registrationdate'] = array('type' => 'info', 'label-message' => 'prefs-registration', 'default' => $context->msg('prefs-registration-date-time', $lang->userTimeAndDate($userRegistration, $displayUser), $lang->userDate($userRegistration, $displayUser), $lang->userTime($userRegistration, $displayUser))->parse(), 'section' => 'personal/info'); } // Actually changeable stuff $defaultPreferences['realname'] = array('type' => $wgAuth->allowPropChange('realname') ? 'text' : 'info', 'default' => $user->getRealName(), 'section' => 'personal/info', 'label-message' => 'yourrealname', 'help-message' => 'prefs-help-realname'); $defaultPreferences['gender'] = array('type' => 'select', 'section' => 'personal/info', 'options' => array($context->msg('gender-male')->text() => 'male', $context->msg('gender-female')->text() => 'female', $context->msg('gender-unknown')->text() => 'unknown'), 'label-message' => 'yourgender', 'help-message' => 'prefs-help-gender'); if ($wgAuth->allowPasswordChange()) { $link = Linker::link(SpecialPage::getTitleFor('ChangePassword'), $context->msg('prefs-resetpass')->escaped(), array(), array('returnto' => SpecialPage::getTitleFor('Preferences'))); $defaultPreferences['password'] = array('type' => 'info', 'raw' => true, 'default' => $link, 'label-message' => 'yourpassword', 'section' => 'personal/info'); } if ($wgCookieExpiration > 0) { $defaultPreferences['rememberpassword'] = array('type' => 'toggle', 'label' => $context->msg('tog-rememberpassword')->numParams(ceil($wgCookieExpiration / (3600 * 24)))->text(), 'section' => 'personal/info'); } // Language $languages = Language::getLanguageNames(false); if (!array_key_exists($wgLanguageCode, $languages)) { $languages[$wgLanguageCode] = $wgLanguageCode; } ksort($languages); $options = array(); foreach ($languages as $code => $name) { $display = wfBCP47($code) . ' - ' . $name; $options[$display] = $code; } $defaultPreferences['language'] = array('type' => 'select', 'section' => 'personal/i18n', 'options' => $options, 'label-message' => 'yourlanguage'); /* see if there are multiple language variants to choose from*/ $variantArray = array(); if (!$wgDisableLangConversion) { $variants = $wgContLang->getVariants(); foreach ($variants as $v) { $v = str_replace('_', '-', strtolower($v)); $variantArray[$v] = $wgContLang->getVariantname($v, false); } $options = array(); foreach ($variantArray as $code => $name) { $display = wfBCP47($code) . ' - ' . $name; $options[$display] = $code; } if (count($variantArray) > 1) { $defaultPreferences['variant'] = array('label-message' => 'yourvariant', 'type' => 'select', 'options' => $options, 'section' => 'personal/i18n', 'help-message' => 'prefs-help-variant'); } } if (count($variantArray) > 1 && !$wgDisableLangConversion && !$wgDisableTitleConversion) { $defaultPreferences['noconvertlink'] = array('type' => 'toggle', 'section' => 'personal/i18n', 'label-message' => 'tog-noconvertlink'); } // show a preview of the old signature first $oldsigWikiText = $wgParser->preSaveTransform("~~~", $context->getTitle(), $user, ParserOptions::newFromContext($context)); $oldsigHTML = $context->getOutput()->parseInline($oldsigWikiText, true, true); $defaultPreferences['oldsig'] = array('type' => 'info', 'raw' => true, 'label-message' => 'tog-oldsig', 'default' => $oldsigHTML, 'section' => 'personal/signature'); $defaultPreferences['nickname'] = array('type' => $wgAuth->allowPropChange('nickname') ? 'text' : 'info', 'maxlength' => $wgMaxSigChars, 'label-message' => 'yournick', 'validation-callback' => array('Preferences', 'validateSignature'), 'section' => 'personal/signature', 'filter-callback' => array('Preferences', 'cleanSignature')); $defaultPreferences['fancysig'] = array('type' => 'toggle', 'label-message' => 'tog-fancysig', 'help-message' => 'prefs-help-signature', 'section' => 'personal/signature'); ## Email stuff if ($wgEnableEmail) { $helpMessages[] = $wgEmailConfirmToEdit ? 'prefs-help-email-required' : 'prefs-help-email'; if ($wgEnableUserEmail) { // additional messages when users can send email to each other $helpMessages[] = 'prefs-help-email-others'; } $link = Linker::link(SpecialPage::getTitleFor('ChangeEmail'), $context->msg($user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail')->escaped(), array(), array('returnto' => SpecialPage::getTitleFor('Preferences'))); $emailAddress = $user->getEmail() ? htmlspecialchars($user->getEmail()) : ''; if ($wgAuth->allowPropChange('emailaddress')) { $emailAddress .= $emailAddress == '' ? $link : " ({$link})"; } $defaultPreferences['emailaddress'] = array('type' => 'info', 'raw' => true, 'default' => $emailAddress, 'label-message' => 'youremail', 'section' => 'personal/email', 'help-messages' => $helpMessages); $disableEmailPrefs = false; if ($wgEmailAuthentication) { if ($user->getEmail()) { if ($user->getEmailAuthenticationTimestamp()) { // date and time are separate parameters to facilitate localisation. // $time is kept for backward compat reasons. // 'emailauthenticated' is also used in SpecialConfirmemail.php $displayUser = $context->getUser(); $emailTimestamp = $user->getEmailAuthenticationTimestamp(); $time = $lang->userTimeAndDate($emailTimestamp, $displayUser); $d = $lang->userDate($emailTimestamp, $displayUser); $t = $lang->userTime($emailTimestamp, $displayUser); $emailauthenticated = $context->msg('emailauthenticated', $time, $d, $t)->parse() . '<br />'; $disableEmailPrefs = false; } else { $disableEmailPrefs = true; $emailauthenticated = $context->msg('emailnotauthenticated')->parse() . '<br />' . Linker::linkKnown(SpecialPage::getTitleFor('Confirmemail'), $context->msg('emailconfirmlink')->escaped()) . '<br />'; } } else { $disableEmailPrefs = true; $emailauthenticated = $context->msg('noemailprefs')->escaped(); } $defaultPreferences['emailauthentication'] = array('type' => 'info', 'raw' => true, 'section' => 'personal/email', 'label-message' => 'prefs-emailconfirm-label', 'default' => $emailauthenticated); } if ($wgEnableUserEmail && $user->isAllowed('sendemail')) { $defaultPreferences['disablemail'] = array('type' => 'toggle', 'invert' => true, 'section' => 'personal/email', 'label-message' => 'allowemail', 'disabled' => $disableEmailPrefs); $defaultPreferences['ccmeonemails'] = array('type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-ccmeonemails', 'disabled' => $disableEmailPrefs); } if ($wgEnotifWatchlist) { $defaultPreferences['enotifwatchlistpages'] = array('type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifwatchlistpages', 'disabled' => $disableEmailPrefs); } if ($wgEnotifUserTalk) { $defaultPreferences['enotifusertalkpages'] = array('type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifusertalkpages', 'disabled' => $disableEmailPrefs); } if ($wgEnotifUserTalk || $wgEnotifWatchlist) { $defaultPreferences['enotifminoredits'] = array('type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifminoredits', 'disabled' => $disableEmailPrefs); if ($wgEnotifRevealEditorAddress) { $defaultPreferences['enotifrevealaddr'] = array('type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifrevealaddr', 'disabled' => $disableEmailPrefs); } } } }
/** * Common implementation for the APIEditBeforeSave, EditFilterMerged * and EditFilterMergedContent hooks. * * @param IContextSource $context the context of the edit * @param Content|null $content the new Content generated by the edit * @param string $text new page content (subject of filtering) * @param Status $status Error message to return * @param string $summary Edit summary for page * @param bool $minoredit whether this is a minor edit according to the user. * * @return bool */ public static function filterEdit(IContextSource $context, $content, $text, Status $status, $summary, $minoredit) { // Load vars $vars = new AbuseFilterVariableHolder(); $title = $context->getTitle(); // Some edits are running through multiple hooks, but we only want to filter them once if (isset($title->editAlreadyFiltered)) { return true; } elseif ($title) { $title->editAlreadyFiltered = true; } self::$successful_action_vars = false; self::$last_edit_page = false; $user = $context->getUser(); // Check for null edits. $oldtext = ''; $oldcontent = null; if ($title instanceof Title && $title->canExist() && $title->exists()) { // Make sure we load the latest text saved in database (bug 31656) $page = $context->getWikiPage(); $revision = $page->getRevision(); if (!$revision) { return true; } if (defined('MW_SUPPORTS_CONTENTHANDLER')) { $oldcontent = $revision->getContent(Revision::RAW); $oldtext = AbuseFilter::contentToString($oldcontent); } else { $oldtext = AbuseFilter::revisionToString($revision, Revision::RAW); } // Cache article object so we can share a parse operation $articleCacheKey = $title->getNamespace() . ':' . $title->getText(); AFComputedVariable::$articleCache[$articleCacheKey] = $page; } else { $page = null; } // Don't trigger for null edits. if ($content && $oldcontent && $oldcontent->equals($content)) { // Compare Content objects if available return true; } else { if (strcmp($oldtext, $text) == 0) { // Otherwise, compare strings return true; } } $vars->addHolders(AbuseFilter::generateUserVars($user), AbuseFilter::generateTitleVars($title, 'ARTICLE')); $vars->setVar('action', 'edit'); $vars->setVar('summary', $summary); $vars->setVar('minor_edit', $minoredit); $vars->setVar('old_wikitext', $oldtext); $vars->setVar('new_wikitext', $text); // TODO: set old_content and new_content vars, use them $vars->addHolders(AbuseFilter::getEditVars($title, $page)); $filter_result = AbuseFilter::filterAction($vars, $title); if (!$filter_result->isOK()) { $status->merge($filter_result); return true; // re-show edit form } self::$successful_action_vars = $vars; self::$last_edit_page = $page; return true; }
/** * Get a ParserOptions object from a IContextSource object * * @param IContextSource $context * @return ParserOptions */ public static function newFromContext(IContextSource $context) { return new ParserOptions($context->getUser(), $context->getLanguage()); }
/** * Get the User being used for this instance. * IndexPager extends ContextSource as of 1.19. * * @since 0.1 * * @return User */ public function getUser() { return $this->context->getUser(); }
/** * Just like executePath() but will override global variables and execute * the page in "inclusion" mode. Returns true if the execution was * successful or false if there was no such special page, or a title object * if it was a redirect. * * Also saves the current $wgTitle, $wgOut, $wgRequest, $wgUser and $wgLang * variables so that the special page will get the context it'd expect on a * normal request, and then restores them to their previous values after. * * @param Title $title * @param IContextSource $context * @return string HTML fragment */ public static function capturePath(Title $title, IContextSource $context) { global $wgTitle, $wgOut, $wgRequest, $wgUser, $wgLang; $main = RequestContext::getMain(); // Save current globals and main context $glob = ['title' => $wgTitle, 'output' => $wgOut, 'request' => $wgRequest, 'user' => $wgUser, 'language' => $wgLang]; $ctx = ['title' => $main->getTitle(), 'output' => $main->getOutput(), 'request' => $main->getRequest(), 'user' => $main->getUser(), 'language' => $main->getLanguage()]; // Override $wgTitle = $title; $wgOut = $context->getOutput(); $wgRequest = $context->getRequest(); $wgUser = $context->getUser(); $wgLang = $context->getLanguage(); $main->setTitle($title); $main->setOutput($context->getOutput()); $main->setRequest($context->getRequest()); $main->setUser($context->getUser()); $main->setLanguage($context->getLanguage()); // The useful part $ret = self::executePath($title, $context, true); // Restore old globals and context $wgTitle = $glob['title']; $wgOut = $glob['output']; $wgRequest = $glob['request']; $wgUser = $glob['user']; $wgLang = $glob['language']; $main->setTitle($ctx['title']); $main->setOutput($ctx['output']); $main->setRequest($ctx['request']); $main->setUser($ctx['user']); $main->setLanguage($ctx['language']); return $ret; }
/** * Given the form data, actually implement a block. This is also called from ApiBlock. * * @param array $data * @param IContextSource $context * @return bool|string */ public static function processForm(array $data, IContextSource $context) { global $wgBlockAllowsUTEdit, $wgHideUserContribLimit, $wgContLang; $performer = $context->getUser(); // Handled by field validator callback // self::validateTargetField( $data['Target'] ); # This might have been a hidden field or a checkbox, so interesting data # can come from it $data['Confirm'] = !in_array($data['Confirm'], ['', '0', null, false], true); /** @var User $target */ list($target, $type) = self::getTargetAndType($data['Target']); if ($type == Block::TYPE_USER) { $user = $target; $target = $user->getName(); $userId = $user->getId(); # Give admins a heads-up before they go and block themselves. Much messier # to do this for IPs, but it's pretty unlikely they'd ever get the 'block' # permission anyway, although the code does allow for it. # Note: Important to use $target instead of $data['Target'] # since both $data['PreviousTarget'] and $target are normalized # but $data['target'] gets overridden by (non-normalized) request variable # from previous request. if ($target === $performer->getName() && ($data['PreviousTarget'] !== $target || !$data['Confirm'])) { return ['ipb-blockingself', 'ipb-confirmaction']; } } elseif ($type == Block::TYPE_RANGE) { $userId = 0; } elseif ($type == Block::TYPE_IP) { $target = $target->getName(); $userId = 0; } else { # This should have been caught in the form field validation return ['badipaddress']; } $expiryTime = self::parseExpiryInput($data['Expiry']); if (strlen($data['Expiry']) == 0 || strlen($data['Expiry']) > 50 || !$expiryTime) { return ['ipb_expiry_invalid']; } // an expiry time should be in the future, not in the // past (wouldn't make any sense) - bug T123069 if ($expiryTime < wfTimestampNow()) { return ['ipb_expiry_old']; } if (!isset($data['DisableEmail'])) { $data['DisableEmail'] = false; } # If the user has done the form 'properly', they won't even have been given the # option to suppress-block unless they have the 'hideuser' permission if (!isset($data['HideUser'])) { $data['HideUser'] = false; } if ($data['HideUser']) { if (!$performer->isAllowed('hideuser')) { # this codepath is unreachable except by a malicious user spoofing forms, # or by race conditions (user has hideuser and block rights, loads block form, # and loses hideuser rights before submission); so need to fail completely # rather than just silently disable hiding return ['badaccess-group0']; } # Recheck params here... if ($type != Block::TYPE_USER) { $data['HideUser'] = false; # IP users should not be hidden } elseif (!wfIsInfinity($data['Expiry'])) { # Bad expiry. return ['ipb_expiry_temp']; } elseif ($wgHideUserContribLimit !== false && $user->getEditCount() > $wgHideUserContribLimit) { # Typically, the user should have a handful of edits. # Disallow hiding users with many edits for performance. return [['ipb_hide_invalid', Message::numParam($wgHideUserContribLimit)]]; } elseif (!$data['Confirm']) { return ['ipb-confirmhideuser', 'ipb-confirmaction']; } } # Create block object. $block = new Block(); $block->setTarget($target); $block->setBlocker($performer); # Truncate reason for whole multibyte characters $block->mReason = $wgContLang->truncate($data['Reason'][0], 255); $block->mExpiry = $expiryTime; $block->prevents('createaccount', $data['CreateAccount']); $block->prevents('editownusertalk', !$wgBlockAllowsUTEdit || $data['DisableUTEdit']); $block->prevents('sendemail', $data['DisableEmail']); $block->isHardblock($data['HardBlock']); $block->isAutoblocking($data['AutoBlock']); $block->mHideName = $data['HideUser']; $reason = ['hookaborted']; if (!Hooks::run('BlockIp', [&$block, &$performer, &$reason])) { return $reason; } # Try to insert block. Is there a conflicting block? $status = $block->insert(); if (!$status) { # Indicates whether the user is confirming the block and is aware of # the conflict (did not change the block target in the meantime) $blockNotConfirmed = !$data['Confirm'] || array_key_exists('PreviousTarget', $data) && $data['PreviousTarget'] !== $target; # Special case for API - bug 32434 $reblockNotAllowed = array_key_exists('Reblock', $data) && !$data['Reblock']; # Show form unless the user is already aware of this... if ($blockNotConfirmed || $reblockNotAllowed) { return [['ipb_already_blocked', $block->getTarget()]]; # Otherwise, try to update the block... } else { # This returns direct blocks before autoblocks/rangeblocks, since we should # be sure the user is blocked by now it should work for our purposes $currentBlock = Block::newFromTarget($target); if ($block->equals($currentBlock)) { return [['ipb_already_blocked', $block->getTarget()]]; } # If the name was hidden and the blocking user cannot hide # names, then don't allow any block changes... if ($currentBlock->mHideName && !$performer->isAllowed('hideuser')) { return ['cant-see-hidden-user']; } $currentBlock->isHardblock($block->isHardblock()); $currentBlock->prevents('createaccount', $block->prevents('createaccount')); $currentBlock->mExpiry = $block->mExpiry; $currentBlock->isAutoblocking($block->isAutoblocking()); $currentBlock->mHideName = $block->mHideName; $currentBlock->prevents('sendemail', $block->prevents('sendemail')); $currentBlock->prevents('editownusertalk', $block->prevents('editownusertalk')); $currentBlock->mReason = $block->mReason; $status = $currentBlock->update(); $logaction = 'reblock'; # Unset _deleted fields if requested if ($currentBlock->mHideName && !$data['HideUser']) { RevisionDeleteUser::unsuppressUserName($target, $userId); } # If hiding/unhiding a name, this should go in the private logs if ((bool) $currentBlock->mHideName) { $data['HideUser'] = true; } } } else { $logaction = 'block'; } Hooks::run('BlockIpComplete', [$block, $performer]); # Set *_deleted fields if requested if ($data['HideUser']) { RevisionDeleteUser::suppressUserName($target, $userId); } # Can't watch a rangeblock if ($type != Block::TYPE_RANGE && $data['Watch']) { WatchAction::doWatch(Title::makeTitle(NS_USER, $target), $performer, User::IGNORE_USER_RIGHTS); } # Block constructor sanitizes certain block options on insert $data['BlockEmail'] = $block->prevents('sendemail'); $data['AutoBlock'] = $block->isAutoblocking(); # Prepare log parameters $logParams = []; $logParams['5::duration'] = $data['Expiry']; $logParams['6::flags'] = self::blockLogFlags($data, $type); # Make log entry, if the name is hidden, put it in the suppression log $log_type = $data['HideUser'] ? 'suppress' : 'block'; $logEntry = new ManualLogEntry($log_type, $logaction); $logEntry->setTarget(Title::makeTitle(NS_USER, $target)); $logEntry->setComment($data['Reason'][0]); $logEntry->setPerformer($performer); $logEntry->setParameters($logParams); # Relate log ID to block IDs (bug 25763) $blockIds = array_merge([$status['id']], $status['autoIds']); $logEntry->setRelations(['ipb_id' => $blockIds]); $logId = $logEntry->insert(); $logEntry->publish($logId); # Report to the user return true; }
/** * Handler for non-standard (edit/log) entries that need IP data * * @param $context IContextSource * @param $data Array * @return bool */ protected static function onLoggableUserIPData(IContextSource $context, array $data) { $user = $context->getUser(); $request = $context->getRequest(); // Get IP address $ip = $request->getIP(); // Get XFF header $xff = $request->getHeader('X-Forwarded-For'); list($xff_ip, $isSquidOnly) = IP::getClientIPfromXFF($xff); // Get agent $agent = $request->getHeader('User-Agent'); $dbw = wfGetDB(DB_MASTER); $cuc_id = $dbw->nextSequenceValue('cu_changes_cu_id_seq'); $rcRow = array('cuc_id' => $cuc_id, 'cuc_page_id' => $data['pageid'], 'cuc_namespace' => $data['namespace'], 'cuc_title' => $data['title'], 'cuc_minor' => 0, 'cuc_user' => $user->getId(), 'cuc_user_text' => $user->getName(), 'cuc_actiontext' => $data['action'], 'cuc_comment' => $data['comment'], 'cuc_this_oldid' => 0, 'cuc_last_oldid' => 0, 'cuc_type' => RC_LOG, 'cuc_timestamp' => $dbw->timestamp($data['timestamp']), 'cuc_ip' => IP::sanitizeIP($ip), 'cuc_ip_hex' => $ip ? IP::toHex($ip) : null, 'cuc_xff' => !$isSquidOnly ? $xff : '', 'cuc_xff_hex' => $xff_ip && !$isSquidOnly ? IP::toHex($xff_ip) : null, 'cuc_agent' => $agent); $dbw->insert('cu_changes', $rcRow, __METHOD__); return true; }
/** * Add the video into the database * * @param $url String: URL to the video on the provider service * @param $type String: (internal) provider name in lowercase * @param $categories String: pipe-separated list of categories * @param $watch Boolean: add the new video page to the user's watchlist? */ public function addVideo($url, $type, $categories, $watch = false) { $user = $this->context->getUser(); $dbw = wfGetDB(DB_MASTER); $now = $dbw->timestamp(); $desc = wfMsgForContent('video-log-added-entry', Title::makeTitle(NS_VIDEO, $this->getName())->getPrefixedText()); // Test to see if the row exists using INSERT IGNORE // This avoids race conditions by locking the row until the commit, and also // doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition. $dbw->insert('video', array('video_name' => $this->getName(), 'video_url' => $url, 'video_type' => $type, 'video_user_id' => $user->getID(), 'video_user_name' => $user->getName(), 'video_timestamp' => $now), __METHOD__, 'IGNORE'); $categoryWikiText = ''; if ($dbw->affectedRows() == 0) { $desc = wfMsgForContent('video-log-updated-entry', Title::makeTitle(NS_VIDEO, $this->getName())->getPrefixedText()); // Clear cache global $wgMemc; $key = $this->getCacheKey(); $wgMemc->delete($key); // Collision, this is an update of a video // Insert previous contents into oldvideo $dbw->insertSelect('oldvideo', 'video', array('ov_name' => 'video_name', 'ov_archive_name' => $dbw->addQuotes(gmdate('YmdHis') . "!{$this->getName()}"), 'ov_url' => 'video_url', 'ov_type' => 'video_type', 'ov_user_id' => 'video_user_id', 'ov_user_name' => 'video_user_name', 'ov_timestamp' => 'video_timestamp'), array('video_name' => $this->getName()), __METHOD__); // Update the current video row $dbw->update('video', array('video_url' => $url, 'video_type' => $type, 'video_user_id' => $user->getID(), 'video_user_name' => $user->getName(), 'video_timestamp' => $now), array('video_name' => $this->getName()), __METHOD__); } $descTitle = $this->getTitle(); $article = new Article($descTitle); $watch = $watch || $user->isWatched($descTitle); // Get the localized category name $videoCategoryName = wfMsgForContent('video-category-name'); if ($categories) { $categories .= "|{$videoCategoryName}"; } else { $categories = "{$videoCategoryName}"; } // Loop through category variable and individually build Category Tab for Wiki text if ($categories) { $categories_array = explode('|', $categories); foreach ($categories_array as $ctg) { $ctg = trim($ctg); if ($ctg) { $catName = $this->context->getLang()->getNsText(NS_CATEGORY); $tag = "[[{$catName}:{$ctg}]]"; if (strpos($categoryWikiText, $tag) === false) { $categoryWikiText .= "\n{$tag}"; } } } } if ($descTitle->exists()) { # Invalidate the cache for the description page $descTitle->invalidateCache(); $descTitle->purgeSquid(); } else { // New video; create the description page. // Supress the recent changes bc it will appear in the log/video $article->doEdit($categoryWikiText, $desc, EDIT_SUPPRESS_RC); } if ($watch) { $user->addWatch($descTitle); } // Add the log entry $log = new LogPage('video'); $log->addEntry('video', $descTitle, $desc); // Commit the transaction now, in case something goes wrong later // The most important thing is that videos don't get lost, especially archives $dbw->commit(); }
/** * @param $user User * @param $context IContextSource * @param $defaultPreferences * @return void */ static function profilePreferences( $user, IContextSource $context, &$defaultPreferences ) { global $wgAuth, $wgContLang, $wgParser, $wgCookieExpiration, $wgLanguageCode, $wgDisableTitleConversion, $wgDisableLangConversion, $wgMaxSigChars, $wgEnableEmail, $wgEmailConfirmToEdit, $wgEnableUserEmail, $wgEmailAuthentication, $wgEnotifWatchlist, $wgEnotifUserTalk, $wgEnotifRevealEditorAddress, $wgSecureLogin; // retrieving user name for GENDER and misc. $userName = $user->getName(); ## User info ##################################### // Information panel $defaultPreferences['username'] = array( 'type' => 'info', 'label-message' => array( 'username', $userName ), 'default' => $userName, 'section' => 'personal/info', ); $defaultPreferences['userid'] = array( 'type' => 'info', 'label-message' => array( 'uid', $userName ), 'default' => $user->getId(), 'section' => 'personal/info', ); # Get groups to which the user belongs $userEffectiveGroups = $user->getEffectiveGroups(); $userGroups = $userMembers = array(); foreach ( $userEffectiveGroups as $ueg ) { if ( $ueg == '*' ) { // Skip the default * group, seems useless here continue; } $groupName = User::getGroupName( $ueg ); $userGroups[] = User::makeGroupLinkHTML( $ueg, $groupName ); $memberName = User::getGroupMember( $ueg, $userName ); $userMembers[] = User::makeGroupLinkHTML( $ueg, $memberName ); } asort( $userGroups ); asort( $userMembers ); $lang = $context->getLanguage(); $defaultPreferences['usergroups'] = array( 'type' => 'info', 'label' => $context->msg( 'prefs-memberingroups' )->numParams( count( $userGroups ) )->params( $userName )->parse(), 'default' => $context->msg( 'prefs-memberingroups-type', $lang->commaList( $userGroups ), $lang->commaList( $userMembers ) )->plain(), 'raw' => true, 'section' => 'personal/info', ); $editCount = Linker::link( SpecialPage::getTitleFor( "Contributions", $userName ), $lang->formatNum( $user->getEditCount() ) ); $defaultPreferences['editcount'] = array( 'type' => 'info', 'raw' => true, 'label-message' => 'prefs-edits', 'default' => $editCount, 'section' => 'personal/info', ); if ( $user->getRegistration() ) { $displayUser = $context->getUser(); $userRegistration = $user->getRegistration(); $defaultPreferences['registrationdate'] = array( 'type' => 'info', 'label-message' => 'prefs-registration', 'default' => $context->msg( 'prefs-registration-date-time', $lang->userTimeAndDate( $userRegistration, $displayUser ), $lang->userDate( $userRegistration, $displayUser ), $lang->userTime( $userRegistration, $displayUser ) )->parse(), 'section' => 'personal/info', ); } $canViewPrivateInfo = $user->isAllowed( 'viewmyprivateinfo' ); $canEditPrivateInfo = $user->isAllowed( 'editmyprivateinfo' ); // Actually changeable stuff $defaultPreferences['realname'] = array( // (not really "private", but still shouldn't be edited without permission) 'type' => $canEditPrivateInfo && $wgAuth->allowPropChange( 'realname' ) ? 'text' : 'info', 'default' => $user->getRealName(), 'section' => 'personal/info', 'label-message' => 'yourrealname', 'help-message' => 'prefs-help-realname', ); if ( $canEditPrivateInfo && $wgAuth->allowPasswordChange() ) { $link = Linker::link( SpecialPage::getTitleFor( 'ChangePassword' ), $context->msg( 'prefs-resetpass' )->escaped(), array(), array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) ); $defaultPreferences['password'] = array( 'type' => 'info', 'raw' => true, 'default' => $link, 'label-message' => 'yourpassword', 'section' => 'personal/info', ); } if ( $wgCookieExpiration > 0 ) { $defaultPreferences['rememberpassword'] = array( 'type' => 'toggle', 'label' => $context->msg( 'tog-rememberpassword' )->numParams( ceil( $wgCookieExpiration / ( 3600 * 24 ) ) )->text(), 'section' => 'personal/info', ); } // Only show preferhttps if secure login is turned on if ( $wgSecureLogin && wfCanIPUseHTTPS( $context->getRequest()->getIP() ) ) { $defaultPreferences['prefershttps'] = array( 'type' => 'toggle', 'label-message' => 'tog-prefershttps', 'help-message' => 'prefs-help-prefershttps', 'section' => 'personal/info' ); } // Language $languages = Language::fetchLanguageNames( null, 'mw' ); if ( !array_key_exists( $wgLanguageCode, $languages ) ) { $languages[$wgLanguageCode] = $wgLanguageCode; } ksort( $languages ); $options = array(); foreach ( $languages as $code => $name ) { $display = wfBCP47( $code ) . ' - ' . $name; $options[$display] = $code; } $defaultPreferences['language'] = array( 'type' => 'select', 'section' => 'personal/i18n', 'options' => $options, 'label-message' => 'yourlanguage', ); $defaultPreferences['gender'] = array( 'type' => 'radio', 'section' => 'personal/i18n', 'options' => array( $context->msg( 'parentheses', $context->msg( 'gender-unknown' )->text() )->text() => 'unknown', $context->msg( 'gender-female' )->text() => 'female', $context->msg( 'gender-male' )->text() => 'male', ), 'label-message' => 'yourgender', 'help-message' => 'prefs-help-gender', ); // see if there are multiple language variants to choose from if ( !$wgDisableLangConversion ) { foreach ( LanguageConverter::$languagesWithVariants as $langCode ) { if ( $langCode == $wgContLang->getCode() ) { $variants = $wgContLang->getVariants(); if ( count( $variants ) <= 1 ) { continue; } $variantArray = array(); foreach ( $variants as $v ) { $v = str_replace( '_', '-', strtolower( $v ) ); $variantArray[$v] = $lang->getVariantname( $v, false ); } $options = array(); foreach ( $variantArray as $code => $name ) { $display = wfBCP47( $code ) . ' - ' . $name; $options[$display] = $code; } $defaultPreferences['variant'] = array( 'label-message' => 'yourvariant', 'type' => 'select', 'options' => $options, 'section' => 'personal/i18n', 'help-message' => 'prefs-help-variant', ); if ( !$wgDisableTitleConversion ) { $defaultPreferences['noconvertlink'] = array( 'type' => 'toggle', 'section' => 'personal/i18n', 'label-message' => 'tog-noconvertlink', ); } } else { $defaultPreferences["variant-$langCode"] = array( 'type' => 'api', ); } } } // Stuff from Language::getExtraUserToggles() // FIXME is this dead code? $extraUserToggles doesn't seem to be defined for any language $toggles = $wgContLang->getExtraUserToggles(); foreach ( $toggles as $toggle ) { $defaultPreferences[$toggle] = array( 'type' => 'toggle', 'section' => 'personal/i18n', 'label-message' => "tog-$toggle", ); } // show a preview of the old signature first $oldsigWikiText = $wgParser->preSaveTransform( "~~~", $context->getTitle(), $user, ParserOptions::newFromContext( $context ) ); $oldsigHTML = $context->getOutput()->parseInline( $oldsigWikiText, true, true ); $defaultPreferences['oldsig'] = array( 'type' => 'info', 'raw' => true, 'label-message' => 'tog-oldsig', 'default' => $oldsigHTML, 'section' => 'personal/signature', ); $defaultPreferences['nickname'] = array( 'type' => $wgAuth->allowPropChange( 'nickname' ) ? 'text' : 'info', 'maxlength' => $wgMaxSigChars, 'label-message' => 'yournick', 'validation-callback' => array( 'Preferences', 'validateSignature' ), 'section' => 'personal/signature', 'filter-callback' => array( 'Preferences', 'cleanSignature' ), ); $defaultPreferences['fancysig'] = array( 'type' => 'toggle', 'label-message' => 'tog-fancysig', 'help-message' => 'prefs-help-signature', // show general help about signature at the bottom of the section 'section' => 'personal/signature' ); ## Email stuff if ( $wgEnableEmail ) { if ( $canViewPrivateInfo ) { $helpMessages[] = $wgEmailConfirmToEdit ? 'prefs-help-email-required' : 'prefs-help-email'; if ( $wgEnableUserEmail ) { // additional messages when users can send email to each other $helpMessages[] = 'prefs-help-email-others'; } $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : ''; if ( $canEditPrivateInfo && $wgAuth->allowPropChange( 'emailaddress' ) ) { $link = Linker::link( SpecialPage::getTitleFor( 'ChangeEmail' ), $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->escaped(), array(), array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) ); $emailAddress .= $emailAddress == '' ? $link : ( $context->msg( 'word-separator' )->plain() . $context->msg( 'parentheses' )->rawParams( $link )->plain() ); } $defaultPreferences['emailaddress'] = array( 'type' => 'info', 'raw' => true, 'default' => $emailAddress, 'label-message' => 'youremail', 'section' => 'personal/email', 'help-messages' => $helpMessages, # 'cssclass' chosen below ); } $disableEmailPrefs = false; if ( $wgEmailAuthentication ) { $emailauthenticationclass = 'mw-email-not-authenticated'; if ( $user->getEmail() ) { if ( $user->getEmailAuthenticationTimestamp() ) { // date and time are separate parameters to facilitate localisation. // $time is kept for backward compat reasons. // 'emailauthenticated' is also used in SpecialConfirmemail.php $displayUser = $context->getUser(); $emailTimestamp = $user->getEmailAuthenticationTimestamp(); $time = $lang->userTimeAndDate( $emailTimestamp, $displayUser ); $d = $lang->userDate( $emailTimestamp, $displayUser ); $t = $lang->userTime( $emailTimestamp, $displayUser ); $emailauthenticated = $context->msg( 'emailauthenticated', $time, $d, $t )->parse() . '<br />'; $disableEmailPrefs = false; $emailauthenticationclass = 'mw-email-authenticated'; } else { $disableEmailPrefs = true; $emailauthenticated = $context->msg( 'emailnotauthenticated' )->parse() . '<br />' . Linker::linkKnown( SpecialPage::getTitleFor( 'Confirmemail' ), $context->msg( 'emailconfirmlink' )->escaped() ) . '<br />'; $emailauthenticationclass = "mw-email-not-authenticated"; } } else { $disableEmailPrefs = true; $emailauthenticated = $context->msg( 'noemailprefs' )->escaped(); $emailauthenticationclass = 'mw-email-none'; } if ( $canViewPrivateInfo ) { $defaultPreferences['emailauthentication'] = array( 'type' => 'info', 'raw' => true, 'section' => 'personal/email', 'label-message' => 'prefs-emailconfirm-label', 'default' => $emailauthenticated, # Apply the same CSS class used on the input to the message: 'cssclass' => $emailauthenticationclass, ); $defaultPreferences['emailaddress']['cssclass'] = $emailauthenticationclass; } } if ( $wgEnableUserEmail && $user->isAllowed( 'sendemail' ) ) { $defaultPreferences['disablemail'] = array( 'type' => 'toggle', 'invert' => true, 'section' => 'personal/email', 'label-message' => 'allowemail', 'disabled' => $disableEmailPrefs, ); $defaultPreferences['ccmeonemails'] = array( 'type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-ccmeonemails', 'disabled' => $disableEmailPrefs, ); } if ( $wgEnotifWatchlist ) { $defaultPreferences['enotifwatchlistpages'] = array( 'type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifwatchlistpages', 'disabled' => $disableEmailPrefs, ); } if ( $wgEnotifUserTalk ) { $defaultPreferences['enotifusertalkpages'] = array( 'type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifusertalkpages', 'disabled' => $disableEmailPrefs, ); } if ( $wgEnotifUserTalk || $wgEnotifWatchlist ) { $defaultPreferences['enotifminoredits'] = array( 'type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifminoredits', 'disabled' => $disableEmailPrefs, ); if ( $wgEnotifRevealEditorAddress ) { $defaultPreferences['enotifrevealaddr'] = array( 'type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifrevealaddr', 'disabled' => $disableEmailPrefs, ); } } } }
private function main() { global $wgUseFileCache, $wgTitle, $wgUseAjax; wfProfileIn(__METHOD__); $request = $this->context->getRequest(); // Send Ajax requests to the Ajax dispatcher. if ($wgUseAjax && $request->getVal('action', 'view') == 'ajax') { // Set a dummy title, because $wgTitle == null might break things $title = Title::makeTitle(NS_MAIN, 'AJAX'); $this->context->setTitle($title); $wgTitle = $title; $dispatcher = new AjaxDispatcher(); $dispatcher->performAction(); wfProfileOut(__METHOD__); return; } // Get title from request parameters, // is set on the fly by parseTitle the first time. $title = $this->getTitle(); $action = $this->getAction(); $wgTitle = $title; // If the user has forceHTTPS set to true, or if the user // is in a group requiring HTTPS, or if they have the HTTPS // preference set, redirect them to HTTPS. // Note: Do this after $wgTitle is setup, otherwise the hooks run from // isLoggedIn() will do all sorts of weird stuff. if (($request->getCookie('forceHTTPS', '') || $request->getCookie('forceHTTPS') || $this->context->getUser()->isLoggedIn() && $this->context->getUser()->requiresHTTPS()) && $request->getProtocol() == 'http') { $oldUrl = $request->getFullRequestURL(); $redirUrl = str_replace('http://', 'https://', $oldUrl); if ($request->wasPosted()) { // This is weird and we'd hope it almost never happens. This // means that a POST came in via HTTP and policy requires us // redirecting to HTTPS. It's likely such a request is going // to fail due to post data being lost, but let's try anyway // and just log the instance. // // @todo @fixme See if we could issue a 307 or 308 here, need // to see how clients (automated & browser) behave when we do wfDebugLog('RedirectedPosts', "Redirected from HTTP to HTTPS: {$oldUrl}"); } // Setup dummy Title, otherwise OutputPage::redirect will fail $title = Title::newFromText(NS_MAIN, 'REDIR'); $this->context->setTitle($title); $output = $this->context->getOutput(); // Since we only do this redir to change proto, always send a vary header $output->addVaryHeader('X-Forwarded-Proto'); $output->redirect($redirUrl); $output->output(); wfProfileOut(__METHOD__); return; } if ($wgUseFileCache && $title->getNamespace() >= 0) { wfProfileIn('main-try-filecache'); if (HTMLFileCache::useFileCache($this->context)) { // Try low-level file cache hit $cache = HTMLFileCache::newFromTitle($title, $action); if ($cache->isCacheGood()) { // Check incoming headers to see if client has this cached $timestamp = $cache->cacheTimestamp(); if (!$this->context->getOutput()->checkLastModified($timestamp)) { $cache->loadFromFileCache($this->context); } // Do any stats increment/watchlist stuff // Assume we're viewing the latest revision (this should always be the case with file cache) $this->context->getWikiPage()->doViewUpdates($this->context->getUser()); // Tell OutputPage that output is taken care of $this->context->getOutput()->disable(); wfProfileOut('main-try-filecache'); wfProfileOut(__METHOD__); return; } } wfProfileOut('main-try-filecache'); } // Actually do the work of the request and build up any output $this->performRequest(); // Either all DB and deferred updates should happen or none. // The later should not be cancelled due to client disconnect. ignore_user_abort(true); // Now commit any transactions, so that unreported errors after // output() don't roll back the whole DB transaction wfGetLBFactory()->commitMasterChanges(); // Output everything! $this->context->getOutput()->output(); wfProfileOut(__METHOD__); }