public function executeRejectAll() { $out = $this->mSpecial->getOutput(); $userpage = $this->mSpecial->getUserpageByModId($this->id); if (!$userpage) { throw new ModerationError('moderation-edit-not-found'); } $dbw = wfGetDB(DB_MASTER); # Need latest data without lag $res = $dbw->select('moderation', array('mod_id AS id'), array('mod_user_text' => $userpage->getText(), 'mod_rejected' => 0, 'mod_merged_revid' => 0), __METHOD__, array('USE INDEX' => 'moderation_rejectall')); if (!$res || $res->numRows() == 0) { throw new ModerationError('moderation-nothing-to-rejectall'); } $ids = array(); foreach ($res as $row) { $ids[] = $row->id; } $dbw->update('moderation', array('mod_rejected' => 1, 'mod_rejected_by_user' => $this->moderator->getId(), 'mod_rejected_by_user_text' => $this->moderator->getName(), 'mod_rejected_batch' => 1, 'mod_preloadable' => 0), array('mod_id' => $ids), __METHOD__); $nrows = $dbw->affectedRows(); if ($nrows) { $logEntry = new ManualLogEntry('moderation', 'rejectall'); $logEntry->setPerformer($this->moderator); $logEntry->setTarget($userpage); $logEntry->setParameters(array('4::count' => $nrows)); $logid = $logEntry->insert(); $logEntry->publish($logid); } $out->addWikiMsg('moderation-rejected-ok', $nrows); }
/** * Record a log event for a change being patrolled * * @param mixed $rc Change identifier or RecentChange object * @param bool $auto Was this patrol event automatic? * @param User $user User performing the action or null to use $wgUser * * @return bool */ public static function record($rc, $auto = false, User $user = null) { global $wgLogAutopatrol; // do not log autopatrolled edits if setting disables it if ($auto && !$wgLogAutopatrol) { return false; } if (!$rc instanceof RecentChange) { $rc = RecentChange::newFromId($rc); if (!is_object($rc)) { return false; } } if (!$user) { global $wgUser; $user = $wgUser; } $entry = new ManualLogEntry('patrol', 'patrol'); $entry->setTarget($rc->getTitle()); $entry->setParameters(self::buildParams($rc, $auto)); $entry->setPerformer($user); $logid = $entry->insert(); if (!$auto) { $entry->publish($logid, 'udp'); } return true; }
protected function logTagAdded($tags, $revId, $user, $reason) { // log it $logEntry = new ManualLogEntry('tag', 'update'); $logEntry->setPerformer($user); $logEntry->setComment($reason); // find the appropriate target page if ($revId) { $rev = Revision::newFromId($revId); if ($rev) { $logEntry->setTarget($rev->getTitle()); } } if (!$logEntry->getTarget()) { // target is required, so we have to set something $logEntry->setTarget(SpecialPage::getTitleFor('Tags')); } $logParams = array('4::revid' => $revId, '6:list:tagsAdded' => $tags, '7:number:tagsAddedCount' => count($tags)); $logEntry->setParameters($logParams); $logEntry->setRelations(array('Tag' => $tags)); $dbw = wfGetDB(DB_MASTER); $logId = $logEntry->insert($dbw); // Only send this to UDP, not RC, similar to patrol events $logEntry->publish($logId, 'udp'); //$logEntry->publish( $logId ); }
public function onSubmit(array $formData) { global $IP, $wgCreateWikiSQLfiles; $DBname = $formData['dbname']; $founderName = $formData['founder']; $siteName = $formData['sitename']; $language = $formData['language']; $private = $formData['private']; $reason = $formData['reason']; $dbw = wfGetDB(DB_MASTER); $farmerLogEntry = new ManualLogEntry('farmer', 'createwiki'); $farmerLogEntry->setPerformer($this->getUser()); $farmerLogEntry->setTarget($this->getTitle()); $farmerLogEntry->setComment($reason); $farmerLogEntry->setParameters(array('4::wiki' => $DBname)); $farmerLogID = $farmerLogEntry->insert(); $farmerLogEntry->publish($farmerLogID); $dbw->query('SET storage_engine=InnoDB;'); $dbw->query('CREATE DATABASE ' . $dbw->addIdentifierQuotes($DBname) . ';'); $dbw->selectDB($DBname); foreach ($wgCreateWikiSQLfiles as $sqlfile) { $dbw->sourceFile($sqlfile); } $this->writeToDBlist($DBname, $siteName, $language, $private); $this->createMainPage($language); $shcreateaccount = exec("/usr/bin/php " . "{$IP}/extensions/CentralAuth/maintenance/createLocalAccount.php " . wfEscapeShellArg($founderName) . " --wiki " . wfEscapeShellArg($DBname)); if (!strpos($shcreateaccount, 'created')) { wfDebugLog('CreateWiki', 'Failed to create local account for founder. - error: ' . $shcreateaccount); return wfMessage('createwiki-error-usernotcreated')->escaped(); } $shpromoteaccount = exec("/usr/bin/php " . "{$IP}/maintenance/createAndPromote.php " . wfEscapeShellArg($founderName) . " --bureaucrat --sysop --force --wiki " . wfEscapeShellArg($DBname)); $this->getOutput()->addHTML('<div class="successbox">' . wfMessage('createwiki-success')->escaped() . '</div>'); return true; }
public function newLogEntry($action, $params) { $logEntry = new ManualLogEntry('phpunit', $action); $logEntry->setPerformer($this->user); $logEntry->setTarget($this->title); $logEntry->setComment('A very good reason'); $logEntry->setParameters($params); return $logEntry; }
/** * Log the promotion of a local unattached to a global * * @param string $oldName * @param string $wiki * @param string $newName * @param string $reason */ public function logPromotion($oldName, $wiki, $newName, $reason) { $logEntry = new ManualLogEntry('gblrename', 'promote'); $logEntry->setPerformer($this->performingUser); $logEntry->setTarget(Title::makeTitleSafe(NS_SPECIAL, 'CentralAuth/' . $newName)); $logEntry->setComment($reason); $logEntry->setParameters(array('4::olduser' => $oldName, '5::newuser' => $newName, '6::oldwiki' => $wiki)); $logEntry->setRelations(array('oldname' => $oldName)); $logid = $logEntry->insert(); $logEntry->publish($logid); }
/** * @param CentralAuthUser[] $oldNames * @param string $newName * @param string $reason */ public function log(array $oldNames, $newName, $reason) { $logEntry = new ManualLogEntry('gblrename', 'merge'); $logEntry->setPerformer($this->performingUser); $logEntry->setTarget(Title::makeTitleSafe(NS_SPECIAL, 'CentralAuth/' . $newName)); $imploded = implode('|', array_map(function (CentralAuthUser $user) { return $user->getName(); }, $oldNames)); $logEntry->setComment($reason); $logEntry->setParameters(array('4::olduser' => $imploded, '5::newuser' => $newName)); $logid = $logEntry->insert(); $logEntry->publish($logid); }
/** * adds a log entry to the database. * * @param $type string: type of the log entry * @param $subtype string: subtype of the log entry * @param $user User: user that performs the logged operation * @param $ns int: number of the namespace for the entry's target's title * @param $title string: title of the entry's target * @param $comment string: comment of the log entry * @param $parameters Array: (optional) accompanying data that is attached * to the entry * * @return int id of the added log entry */ private function addLogEntry($type, $subtype, User $user, $ns, $title, $comment = null, $parameters = null) { $logEntry = new ManualLogEntry($type, $subtype); $logEntry->setPerformer($user); $logEntry->setTarget(Title::newFromText($title, $ns)); if ($comment !== null) { $logEntry->setComment($comment); } if ($parameters !== null) { $logEntry->setParameters($parameters); } return $logEntry->insert(); }
function run() { // Unfortunately the global is needed until bug is fixed: // https://phabricator.wikimedia.org/T51086 // Once MW >= 1.24 is supported, can use MovePage class. global $wgUser; // Initialization $title = $this->title; // Other stuff $user = $this->getUser(); $summary = $this->getSummary(); $target = $this->getTarget(); $base = $this->params['base-source']; $doer = User::newFromName($this->getPerformer()); PageTranslationHooks::$allowTargetEdit = true; PageTranslationHooks::$jobQueueRunning = true; $oldUser = $wgUser; $wgUser = $user; self::forceRedirects(false); // Don't check perms, don't leave a redirect $ok = $title->moveTo($target, false, $summary, false); if (!$ok) { $params = array('target' => $target->getPrefixedText(), 'error' => $ok); $entry = new ManualLogEntry('pagetranslation', 'movenok'); $entry->setPerformer($doer); $entry->setTarget($title); $entry->setParameters($params); $logid = $entry->insert(); $entry->publish($logid); } self::forceRedirects(true); PageTranslationHooks::$allowTargetEdit = false; $this->unlock(); $cache = wfGetCache(CACHE_ANYTHING); $key = wfMemcKey('translate-pt-move', $base); $count = $cache->decr($key); $last = strval($count) === '0'; if ($last) { $cache->delete($key); $params = array('target' => $this->params['base-target']); $entry = new ManualLogEntry('pagetranslation', 'moveok'); $entry->setPerformer($doer); $entry->setParameters($params); $entry->setTarget(Title::newFromText($base)); $logid = $entry->insert(); $entry->publish($logid); PageTranslationHooks::$jobQueueRunning = false; } $wgUser = $oldUser; return true; }
/** * Create new Log entry * * @param string $action Action name as defined above * @param integer $mapId Map id * @param string $comment Comment * @param array $params Additional params * @return ManualLogEntry */ public static function newLogEntry($action, $user, $mapId, $comment, $params = []) { $logEntry = new ManualLogEntry(self::LOG_TYPE_NAME, $action); $logEntry->setPerformer($user); $logEntry->setTarget(SpecialPage::getTitleFor(WikiaMapsSpecialController::PAGE_NAME, $mapId)); $logEntry->setComment($comment); if (!empty($params)) { // we can't allow to pass those elements // more info: https://www.mediawiki.org/wiki/Manual:Logging_to_Special:Log#1.19_and_later unset($params[1], $params[2], $params[3]); $logEntry->setParameters($params); } return $logEntry; }
/** * Create a log entry using the provided info. * Takes care about the logging interface changes in MediaWiki 1.19. * * @since 0.1 * * @param array $info */ public static function log(array $info) { $user = array_key_exists('user', $info) ? $info['user'] : $GLOBALS['wgUser']; $logEntry = new ManualLogEntry($info['type'], $info['subtype']); $logEntry->setPerformer($user); $logEntry->setTarget($info['title']); if (array_key_exists('comment', $info)) { $logEntry->setComment($info['comment']); } if (array_key_exists('parameters', $info)) { $logEntry->setParameters($info['parameters']); } $logid = $logEntry->insert(); $logEntry->publish($logid); }
function run() { // Initialization $title = $this->title; // Other stuff $user = $this->getUser(); $summary = $this->getSummary(); $base = $this->getBase(); $doer = User::newFromName($this->getPerformer()); PageTranslationHooks::$allowTargetEdit = true; $error = ''; $wikipage = new WikiPage($title); $ok = $wikipage->doDeleteArticle($summary, false, 0, true, $error, $user); if (!$ok) { $params = array('target' => $base, 'error' => $ok); $type = $this->getFull() ? 'deletefnok' : 'deletelnok'; $entry = new ManualLogEntry('pagetranslation', $type); $entry->setPerformer($doer); $entry->setTarget($title); $entry->setParameters($params); $logid = $entry->insert(); $entry->publish($logid); } PageTranslationHooks::$allowTargetEdit = false; $cache = wfGetCache(CACHE_DB); $pages = (array) $cache->get(wfMemcKey('pt-base', $base)); $lastitem = array_pop($pages); if ($title->getPrefixedText() === $lastitem) { $cache->delete(wfMemcKey('pt-base', $base)); $type = $this->getFull() ? 'deletefok' : 'deletelok'; $entry = new ManualLogEntry('pagetranslation', $type); $entry->setPerformer($doer); $entry->setTarget(Title::newFromText($base)); $logid = $entry->insert(); $entry->publish($logid); $tpage = TranslatablePage::newFromTitle($title); $tpage->getTranslationPercentages(true); foreach ($tpage->getTranslationPages() as $page) { $page->invalidateCache(); } $title->invalidateCache(); } return true; }
function onSubmitInput(array $params) { if (!$this->getUser()->isAllowed('managewiki')) { throw new MWException("User '{$this->getUser()->getName()}' without managewiki right tried to change wiki settings!"); } $values = array('wiki_sitename' => $params['sitename'], 'wiki_language' => $params['language'], 'wiki_closed' => $params['closed'] == true ? 1 : 0); if ($this->getUser()->isAllowed('managewiki-restricted')) { $values['wiki_private'] = $params['private'] == true ? 1 : 0; } $dbw = wfGetDB(DB_MASTER); $dbw->update('cw_wikis', $values, array('wiki_dbname' => $params['dbname']), __METHOD__); $farmerLogEntry = new ManualLogEntry('farmer', 'managewiki'); $farmerLogEntry->setPerformer($this->getUser()); $farmerLogEntry->setTarget($this->getTitle()); $farmerLogEntry->setComment($params['reason']); $farmerLogEntry->setParameters(array('4::wiki' => $params['dbname'])); $farmerLogID = $farmerLogEntry->insert(); $farmerLogEntry->publish($farmerLogID); $this->getOutput()->addHTML('<div class="successbox">' . wfMessage('managewiki-success')->escaped() . '</div>'); return true; }
public static function changeState(MessageGroup $group, $code, $newState, User $user) { $currentState = self::getState($group, $code); if ($currentState === $newState) { return false; } $table = 'translate_groupreviews'; $index = array('tgr_group', 'tgr_language'); $row = array('tgr_group' => $group->getId(), 'tgr_lang' => $code, 'tgr_state' => $newState); $dbw = wfGetDB(DB_MASTER); $dbw->replace($table, array($index), $row, __METHOD__); $entry = new ManualLogEntry('translationreview', 'group'); $entry->setPerformer($user); $entry->setTarget(SpecialPage::getTitleFor('Translate', $group->getId())); // @todo // $entry->setComment( $comment ); $entry->setParameters(array('4::language' => $code, '5::group-label' => $group->getLabel(), '6::old-state' => $currentState, '7::new-state' => $newState)); $logid = $entry->insert(); $entry->publish($logid); Hooks::run('TranslateEventMessageGroupStateChange', array($group, $code, $currentState, $newState)); return true; }
private function doImport(array $json) { global $wgTriggerFlowThreadHooks; $wgTriggerFlowThreadHooks = false; $output = $this->getOutput(); foreach ($json as $articles) { $title = \Title::newFromText($articles->title); $count = count($articles->posts); $skipped = 0; // Skip non-existent title if ($title === null || !$title->exists()) { $output->addWikiMsg('flowthreadimport-failed', $articles->title, $count); continue; } $titleId = $title->getArticleID(); foreach ($articles->posts as $postJson) { $data = array('id' => UUID::fromHex($postJson->id), 'pageid' => $titleId, 'userid' => $postJson->userid, 'username' => $postJson->username, 'text' => $postJson->text, 'parentid' => $postJson->parentid ? UUID::fromHex($postJson->parentid) : null, 'status' => $postJson->status, 'like' => 0, 'report' => 0); $postObject = new Post($data); try { $postObject->post(); } catch (\Exception $ex) { $count--; $skipped++; } } if ($count) { $logEntry = new \ManualLogEntry('comments', 'import'); $logEntry->setPerformer($this->getUser()); $logEntry->setTarget($title); $logEntry->setParameters(array('4::count' => $count)); $logId = $logEntry->insert(); $logEntry->publish($logId, 'udp'); $output->addWikiMsg('flowthreadimport-success', $articles->title, $count); } if ($skipped) { $output->addWikiMsg('flowthreadimport-skipped', $articles->title, $skipped); } } }
/** * Record a log event for a change being patrolled * * @param $rc Mixed: change identifier or RecentChange object * @param $auto Boolean: was this patrol event automatic? * * @return bool */ public static function record($rc, $auto = false) { if (!$rc instanceof RecentChange) { $rc = RecentChange::newFromId($rc); if (!is_object($rc)) { return false; } } $title = Title::makeTitleSafe($rc->getAttribute('rc_namespace'), $rc->getAttribute('rc_title')); if ($title) { $entry = new ManualLogEntry('patrol', 'patrol'); $entry->setTarget($title); $entry->setParameters(self::buildParams($rc, $auto)); $entry->setPerformer(User::newFromName($rc->getAttribute('rc_user_text'), false)); $logid = $entry->insert(); if (!$auto) { $entry->publish($logid, 'udp'); } return true; } return false; }
/** * Create a log entry using the provided info. * Takes care about the logging interface changes in MediaWiki 1.19. * * @since 0.1 * * @param array $info */ public static function log(array $info) { $user = array_key_exists('user', $info) ? $info['user'] : $GLOBALS['wgUser']; if ($info !== false) { if (class_exists('ManualLogEntry')) { $logEntry = new ManualLogEntry($info['type'], $info['subtype']); $logEntry->setPerformer($user); $logEntry->setTarget($info['title']); if (array_key_exists('comment', $info)) { $logEntry->setComment($info['comment']); } if (array_key_exists('parameters', $info)) { $logEntry->setParameters($info['parameters']); } $logid = $logEntry->insert(); $logEntry->publish($logid); } else { // Compatibility with MediaWiki 1.18. $log = new LogPage($info['type']); $log->addEntry($info['subtype'], $info['title'], array_key_exists('comment', $info) ? $info['comment'] : '', array_key_exists('parameters', $info) ? $info['parameters'] : array(), $user); } } }
/** * @param $actions_taken * @param $log_template * @param $action * @param $vars AbuseFilterVariableHolder * @param string $group * @return mixed */ public static function addLogEntries($actions_taken, $log_template, $action, $vars, $group = 'default') { wfProfileIn(__METHOD__); $dbw = wfGetDB(DB_MASTER); $central_log_template = array('afl_wiki' => wfWikiID()); $log_rows = array(); $central_log_rows = array(); $logged_local_filters = array(); $logged_global_filters = array(); foreach ($actions_taken as $filter => $actions) { $globalIndex = self::decodeGlobalName($filter); $thisLog = $log_template; $thisLog['afl_filter'] = $filter; $thisLog['afl_action'] = $action; $thisLog['afl_actions'] = implode(',', $actions); // Don't log if we were only throttling. if ($thisLog['afl_actions'] != 'throttle') { $log_rows[] = $thisLog; if (!$globalIndex) { $logged_local_filters[] = $filter; } // Global logging if ($globalIndex) { $title = Title::makeTitle($thisLog['afl_namespace'], $thisLog['afl_title']); $centralLog = $thisLog + $central_log_template; $centralLog['afl_filter'] = $globalIndex; $centralLog['afl_title'] = $title->getPrefixedText(); $centralLog['afl_namespace'] = 0; $central_log_rows[] = $centralLog; $logged_global_filters[] = $globalIndex; } } } if (!count($log_rows)) { wfProfileOut(__METHOD__); return; } // Only store the var dump if we're actually going to add log rows. $var_dump = self::storeVarDump($vars); $var_dump = "stored-text:{$var_dump}"; // To distinguish from stuff stored directly wfProfileIn(__METHOD__ . '-hitstats'); global $wgMemc; // Increment trigger counter $wgMemc->incr(self::filterMatchesKey()); $local_log_ids = array(); global $wgAbuseFilterNotifications, $wgAbuseFilterNotificationsPrivate; foreach ($log_rows as $data) { $data['afl_var_dump'] = $var_dump; $data['afl_id'] = $dbw->nextSequenceValue('abuse_filter_log_afl_id_seq'); $dbw->insert('abuse_filter_log', $data, __METHOD__); $local_log_ids[] = $dbw->insertId(); if ($data['afl_id'] === null) { $data['afl_id'] = $dbw->insertId(); } $entry = new ManualLogEntry('abusefilter', 'hit'); // Construct a user object $user = User::newFromId($data['afl_user']); $user->setName($data['afl_user_text']); $entry->setPerformer($user); // Set action target $entry->setTarget(Title::makeTitle($data['afl_namespace'], $data['afl_title'])); // Additional info $entry->setParameters(array('action' => $data['afl_action'], 'filter' => $data['afl_filter'], 'actions' => $data['afl_actions'], 'log' => $data['afl_id'])); // Send data to CheckUser if installed and we // aren't already sending a notification to recentchanges // Requires MW 1.23+ if (is_callable('CheckUserHooks::updateCheckUserData') && is_callable('ManualLogEntry::getRecentChange') && strpos($wgAbuseFilterNotifications, 'rc') === false) { $rc = $entry->getRecentChange(); CheckUserHooks::updateCheckUserData($rc); } if ($wgAbuseFilterNotifications !== false) { if (self::filterHidden($data['afl_filter']) && !$wgAbuseFilterNotificationsPrivate) { continue; } $entry->publish(0, $wgAbuseFilterNotifications); } } $method = __METHOD__; if (count($logged_local_filters)) { // Update hit-counter. $dbw->onTransactionPreCommitOrIdle(function () use($dbw, $logged_local_filters, $method) { $dbw->update('abuse_filter', array('af_hit_count=af_hit_count+1'), array('af_id' => $logged_local_filters), $method); }); } $global_log_ids = array(); // Global stuff if (count($logged_global_filters)) { $vars->computeDBVars(); $global_var_dump = self::storeVarDump($vars, true); $global_var_dump = "stored-text:{$global_var_dump}"; foreach ($central_log_rows as $index => $data) { $central_log_rows[$index]['afl_var_dump'] = $global_var_dump; } global $wgAbuseFilterCentralDB; $fdb = wfGetDB(DB_MASTER, array(), $wgAbuseFilterCentralDB); foreach ($central_log_rows as $row) { $fdb->insert('abuse_filter_log', $row, __METHOD__); $global_log_ids[] = $dbw->insertId(); } $fdb->onTransactionPreCommitOrIdle(function () use($fdb, $logged_global_filters, $method) { $fdb->update('abuse_filter', array('af_hit_count=af_hit_count+1'), array('af_id' => $logged_global_filters), $method); }); } $vars->setVar('global_log_ids', $global_log_ids); $vars->setVar('local_log_ids', $local_log_ids); // Check for emergency disabling. $total = $wgMemc->get(AbuseFilter::filterUsedKey($group)); self::checkEmergencyDisable($group, $logged_local_filters, $total); wfProfileOut(__METHOD__ . '-hitstats'); wfProfileOut(__METHOD__); }
/** * @param array $formData * @param HtmlForm $form * * @return bool|string * @throws DBUnexpectedError * @throws Exception * @throws MWException */ public static function processInput(array $formData, HtmlForm $form) { error_reporting(0); global $wgCreateWikiSQLfiles, $IP; $DBname = $formData['dbname']; $founderName = $formData['founder']; $dbw = wfGetDB(DB_MASTER); $dbTest = $dbw->query('SHOW DATABASES LIKE ' . $dbw->addQuotes($DBname) . ';'); $rows = $dbTest->numRows(); $dbTest->free(); if ($rows !== 0) { return wfMessage('createwiki-error-dbexists')->plain(); } $farmerLogEntry = new ManualLogEntry('farmer', 'createandpromote'); $farmerLogEntry->setPerformer($form->getUser()); $farmerLogEntry->setTarget($form->getTitle()); $farmerLogEntry->setComment($formData['comment']); $farmerLogEntry->setParameters(array('4::wiki' => $DBname, '5::founder' => $founderName)); $farmerLogID = $farmerLogEntry->insert(); $farmerLogEntry->publish($farmerLogID); $dbw->query('SET storage_engine=InnoDB;'); $dbw->query('CREATE DATABASE ' . $dbw->addIdentifierQuotes($DBname) . ';'); $dbw->selectDB($DBname); foreach ($wgCreateWikiSQLfiles as $file) { $dbw->sourceFile($file); } $dbw->insert('site_stats', array('ss_row_id' => 1)); $dbw->close(); // Add DNS record to cloudflare global $wgCreateWikiUseCloudFlare, $wgCloudFlareUser, $wgCloudFlareKey; if ($wgCreateWikiUseCloudFlare) { $domainPrefix = substr($DBname, 0, -4); $cloudFlare = new cloudflare_api($wgCloudFlareUser, $wgCloudFlareKey); $cloudFlareResult = $cloudFlare->rec_new('orain.org', 'CNAME', $domainPrefix, 'lb.orain.org'); if (!is_object($cloudFlareResult) || $cloudFlareResult->result !== 'success') { wfDebugLog('CreateWiki', 'CloudFlare FAILED to add CNAME for ' . $domainPrefix . '.orain.org'); } else { wfDebugLog('CreateWiki', 'CloudFlare CNAME added for ' . $domainPrefix . '.orain.org'); } } // Create local account for founder (hack) $out = exec("php5 {$IP}/extensions/CentralAuth/maintenance/createLocalAccount.php " . escapeshellarg($founderName) . ' --wiki ' . escapeshellarg($DBname)); if (!strpos($out, 'created')) { return wfMessage('createwiki-error-usernotcreated')->plain(); } require_once "{$IP}/includes/UserRightsProxy.php"; // Grant founder sysop and bureaucrat rights $founderUser = UserRightsProxy::newFromName($DBname, $founderName); $newGroups = array('sysop', 'bureaucrat'); array_map(array($founderUser, 'addGroup'), $newGroups); $founderUser->invalidateCache(); $form->getOutput()->addWikiMsg('createwiki-success', $DBname); return true; }
/** * Writes a tag action into the tag management log. * * @param string $action * @param string $tag * @param string $reason * @param User $user Who to attribute the action to * @param int $tagCount For deletion only, how many usages the tag had before * it was deleted. * @return int ID of the inserted log entry * @since 1.25 */ protected static function logTagManagementAction($action, $tag, $reason, User $user, $tagCount = null) { $dbw = wfGetDB(DB_MASTER); $logEntry = new ManualLogEntry('managetags', $action); $logEntry->setPerformer($user); // target page is not relevant, but it has to be set, so we just put in // the title of Special:Tags $logEntry->setTarget(Title::newFromText('Special:Tags')); $logEntry->setComment($reason); $params = array('4::tag' => $tag); if (!is_null($tagCount)) { $params['5:number:count'] = $tagCount; } $logEntry->setParameters($params); $logEntry->setRelations(array('Tag' => $tag)); $logId = $logEntry->insert($dbw); $logEntry->publish($logId); return $logId; }
/** * Record a file upload in the upload log and the image table * @param string $oldver * @param string $comment * @param string $pageText * @param bool|array $props * @param string|bool $timestamp * @param null|User $user * @return bool */ function recordUpload2($oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null) { wfProfileIn(__METHOD__); if (is_null($user)) { global $wgUser; $user = $wgUser; } $dbw = $this->repo->getMasterDB(); $dbw->begin(__METHOD__); if (!$props) { wfProfileIn(__METHOD__ . '-getProps'); $props = $this->repo->getFileProps($this->getVirtualUrl()); wfProfileOut(__METHOD__ . '-getProps'); } if ($timestamp === false) { $timestamp = $dbw->timestamp(); } $props['description'] = $comment; $props['user'] = $user->getId(); $props['user_text'] = $user->getName(); $props['timestamp'] = wfTimestamp(TS_MW, $timestamp); // DB -> TS_MW $this->setProps($props); # Fail now if the file isn't there if (!$this->fileExists) { wfDebug(__METHOD__ . ": File " . $this->getRel() . " went missing!\n"); wfProfileOut(__METHOD__); return false; } $reupload = false; # 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('image', array('img_name' => $this->getName(), 'img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $dbw->encodeBlob($this->metadata), 'img_sha1' => $this->sha1), __METHOD__, 'IGNORE'); if ($dbw->affectedRows() == 0) { # (bug 34993) Note: $oldver can be empty here, if the previous # version of the file was broken. Allow registration of the new # version to continue anyway, because that's better than having # an image that's not fixable by user operations. $reupload = true; # Collision, this is an update of a file # Insert previous contents into oldimage $dbw->insertSelect('oldimage', 'image', array('oi_name' => 'img_name', 'oi_archive_name' => $dbw->addQuotes($oldver), 'oi_size' => 'img_size', 'oi_width' => 'img_width', 'oi_height' => 'img_height', 'oi_bits' => 'img_bits', 'oi_timestamp' => 'img_timestamp', 'oi_description' => 'img_description', 'oi_user' => 'img_user', 'oi_user_text' => 'img_user_text', 'oi_metadata' => 'img_metadata', 'oi_media_type' => 'img_media_type', 'oi_major_mime' => 'img_major_mime', 'oi_minor_mime' => 'img_minor_mime', 'oi_sha1' => 'img_sha1'), array('img_name' => $this->getName()), __METHOD__); # Update the current image row $dbw->update('image', array('img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $dbw->encodeBlob($this->metadata), 'img_sha1' => $this->sha1), array('img_name' => $this->getName()), __METHOD__); } else { # This is a new file, so update the image count DeferredUpdates::addUpdate(SiteStatsUpdate::factory(array('images' => 1))); } $descTitle = $this->getTitle(); $wikiPage = new WikiFilePage($descTitle); $wikiPage->setFile($this); # Add the log entry $action = $reupload ? 'overwrite' : 'upload'; $logEntry = new ManualLogEntry('upload', $action); $logEntry->setPerformer($user); $logEntry->setComment($comment); $logEntry->setTarget($descTitle); // Allow people using the api to associate log entries with the upload. // Log has a timestamp, but sometimes different from upload timestamp. $logEntry->setParameters(array('img_sha1' => $this->sha1, 'img_timestamp' => $timestamp)); // Note we keep $logId around since during new image // creation, page doesn't exist yet, so log_page = 0 // but we want it to point to the page we're making, // so we later modify the log entry. // For a similar reason, we avoid making an RC entry // now and wait until the page exists. $logId = $logEntry->insert(); $exists = $descTitle->exists(); if ($exists) { // Page exists, do RC entry now (otherwise we wait for later). $logEntry->publish($logId); } wfProfileIn(__METHOD__ . '-edit'); if ($exists) { # Create a null revision $latest = $descTitle->getLatestRevID(); $editSummary = LogFormatter::newFromEntry($logEntry)->getPlainActionText(); $nullRevision = Revision::newNullRevision($dbw, $descTitle->getArticleID(), $editSummary, false); if (!is_null($nullRevision)) { $nullRevision->insertOn($dbw); wfRunHooks('NewRevisionFromEditComplete', array($wikiPage, $nullRevision, $latest, $user)); $wikiPage->updateRevisionOn($dbw, $nullRevision); } } # Commit the transaction now, in case something goes wrong later # The most important thing is that files don't get lost, especially archives # NOTE: once we have support for nested transactions, the commit may be moved # to after $wikiPage->doEdit has been called. $dbw->commit(__METHOD__); # Save to memcache. # We shall not saveToCache before the commit since otherwise # in case of a rollback there is an usable file from memcached # which in fact doesn't really exist (bug 24978) $this->saveToCache(); if ($exists) { # Invalidate the cache for the description page $descTitle->invalidateCache(); $descTitle->purgeSquid(); } else { # New file; create the description page. # There's already a log entry, so don't make a second RC entry # Squid and file cache for the description page are purged by doEditContent. $content = ContentHandler::makeContent($pageText, $descTitle); $status = $wikiPage->doEditContent($content, $comment, EDIT_NEW | EDIT_SUPPRESS_RC, false, $user); $dbw->begin(__METHOD__); // XXX; doEdit() uses a transaction // Now that the page exists, make an RC entry. $logEntry->publish($logId); if (isset($status->value['revision'])) { $dbw->update('logging', array('log_page' => $status->value['revision']->getPage()), array('log_id' => $logId), __METHOD__); } $dbw->commit(__METHOD__); // commit before anything bad can happen } wfProfileOut(__METHOD__ . '-edit'); if ($reupload) { # Delete old thumbnails wfProfileIn(__METHOD__ . '-purge'); $this->purgeThumbnails(); wfProfileOut(__METHOD__ . '-purge'); # Remove the old file from the squid cache SquidUpdate::purge(array($this->getURL())); } # Hooks, hooks, the magic of hooks... wfProfileIn(__METHOD__ . '-hooks'); wfRunHooks('FileUpload', array($this, $reupload, $descTitle->exists())); wfProfileOut(__METHOD__ . '-hooks'); # Invalidate cache for all pages using this file $update = new HTMLCacheUpdate($this->getTitle(), 'imagelinks'); $update->doUpdate(); if (!$reupload) { LinksUpdate::queueRecursiveJobsForTable($this->getTitle(), 'imagelinks'); } wfProfileOut(__METHOD__); return true; }
/** * Add a newuser log entry for this user. * Before 1.19 the return value was always true. * * @param string|bool $action Account creation type. * - String, one of the following values: * - 'create' for an anonymous user creating an account for himself. * This will force the action's performer to be the created user itself, * no matter the value of $wgUser * - 'create2' for a logged in user creating an account for someone else * - 'byemail' when the created user will receive its password by e-mail * - 'autocreate' when the user is automatically created (such as by CentralAuth). * - Boolean means whether the account was created by e-mail (deprecated): * - true will be converted to 'byemail' * - false will be converted to 'create' if this object is the same as * $wgUser and to 'create2' otherwise * * @param string $reason User supplied reason * * @return int|bool True if not $wgNewUserLog; otherwise ID of log item or 0 on failure */ public function addNewUserLogEntry($action = false, $reason = '') { global $wgUser, $wgNewUserLog; if (empty($wgNewUserLog)) { return true; // disabled } if ($action === true) { $action = 'byemail'; } elseif ($action === false) { if ($this->equals($wgUser)) { $action = 'create'; } else { $action = 'create2'; } } if ($action === 'create' || $action === 'autocreate') { $performer = $this; } else { $performer = $wgUser; } $logEntry = new ManualLogEntry('newusers', $action); $logEntry->setPerformer($performer); $logEntry->setTarget($this->getUserPage()); $logEntry->setComment($reason); $logEntry->setParameters(array('4::userid' => $this->getId())); $logid = $logEntry->insert(); if ($action !== 'autocreate') { $logEntry->publish($logid); } return (int) $logid; }
/** * @deprecated since 1.25 Use LogFormatter::formatParametersForApi instead * @param ApiResult $result * @param array $vals * @param string $params * @param string $type * @param string $action * @param string $ts * @param bool $legacy * @return array */ public static function addLogParams($result, &$vals, $params, $type, $action, $ts, $legacy = false) { wfDeprecated(__METHOD__, '1.25'); $entry = new ManualLogEntry($type, $action); $entry->setParameters($params); $entry->setTimestamp($ts); $entry->setLegacy($legacy); $formatter = LogFormatter::newFromEntry($entry); $vals['params'] = $formatter->formatParametersForApi(); return $vals; }
public function onSubmit(array $data) { global $wgContLang; if ($data['pagetitle'] === '') { // Initial form view of special page, pass return false; } // At this point, it has to be a POST request. This is enforced by HTMLForm, // but lets be safe verify that. if (!$this->getRequest()->wasPosted()) { throw new RuntimeException("Form submission was not POSTed"); } $this->title = Title::newFromText($data['pagetitle']); $user = $this->getUser(); // Check permissions and make sure the user has permission to edit the specific page $errors = $this->title->getUserPermissionsErrors('editcontentmodel', $user); $errors = wfMergeErrorArrays($errors, $this->title->getUserPermissionsErrors('edit', $user)); if ($errors) { $out = $this->getOutput(); $wikitext = $out->formatPermissionsErrorMessage($errors); // Hack to get our wikitext parsed return Status::newFatal(new RawMessage('$1', array($wikitext))); } $page = WikiPage::factory($this->title); if ($this->oldRevision === null) { $this->oldRevision = $page->getRevision() ?: false; } $oldModel = $this->title->getContentModel(); if ($this->oldRevision) { $oldContent = $this->oldRevision->getContent(); try { $newContent = ContentHandler::makeContent($oldContent->getNativeData(), $this->title, $data['model']); } catch (MWException $e) { return Status::newFatal($this->msg('changecontentmodel-cannot-convert')->params($this->title->getPrefixedText(), ContentHandler::getLocalizedName($data['model']))); } } else { // Page doesn't exist, create an empty content object $newContent = ContentHandler::getForModelID($data['model'])->makeEmptyContent(); } $flags = $this->oldRevision ? EDIT_UPDATE : EDIT_NEW; if ($user->isAllowed('bot')) { $flags |= EDIT_FORCE_BOT; } $log = new ManualLogEntry('contentmodel', 'change'); $log->setPerformer($user); $log->setTarget($this->title); $log->setComment($data['reason']); $log->setParameters(array('4::oldmodel' => $oldModel, '5::newmodel' => $data['model'])); $formatter = LogFormatter::newFromEntry($log); $formatter->setContext(RequestContext::newExtraneousContext($this->title)); $reason = $formatter->getPlainActionText(); if ($data['reason'] !== '') { $reason .= $this->msg('colon-separator')->inContentLanguage()->text() . $data['reason']; } # Truncate for whole multibyte characters. $reason = $wgContLang->truncate($reason, 255); $status = $page->doEditContent($newContent, $reason, $flags, $this->oldRevision ? $this->oldRevision->getId() : false, $user); if (!$status->isOK()) { return $status; } $logid = $log->insert(); $log->publish($logid); return $status; }
/** * Update the article's restriction field, and leave a log entry. * This works for protection both existing and non-existing pages. * * @param array $limit Set of restriction keys * @param array $expiry Per restriction type expiration * @param int &$cascade Set to false if cascading protection isn't allowed. * @param string $reason * @param User $user The user updating the restrictions * @return Status */ public function doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user) { global $wgCascadingRestrictionLevels, $wgContLang; if (wfReadOnly()) { return Status::newFatal('readonlytext', wfReadOnlyReason()); } $this->loadPageData('fromdbmaster'); $restrictionTypes = $this->mTitle->getRestrictionTypes(); $id = $this->getId(); if (!$cascade) { $cascade = false; } // Take this opportunity to purge out expired restrictions Title::purgeExpiredRestrictions(); // @todo FIXME: Same limitations as described in ProtectionForm.php (line 37); // we expect a single selection, but the schema allows otherwise. $isProtected = false; $protect = false; $changed = false; $dbw = wfGetDB(DB_MASTER); foreach ($restrictionTypes as $action) { if (!isset($expiry[$action]) || $expiry[$action] === $dbw->getInfinity()) { $expiry[$action] = 'infinity'; } if (!isset($limit[$action])) { $limit[$action] = ''; } elseif ($limit[$action] != '') { $protect = true; } // Get current restrictions on $action $current = implode('', $this->mTitle->getRestrictions($action)); if ($current != '') { $isProtected = true; } if ($limit[$action] != $current) { $changed = true; } elseif ($limit[$action] != '') { // Only check expiry change if the action is actually being // protected, since expiry does nothing on an not-protected // action. if ($this->mTitle->getRestrictionExpiry($action) != $expiry[$action]) { $changed = true; } } } if (!$changed && $protect && $this->mTitle->areRestrictionsCascading() != $cascade) { $changed = true; } // If nothing has changed, do nothing if (!$changed) { return Status::newGood(); } if (!$protect) { // No protection at all means unprotection $revCommentMsg = 'unprotectedarticle'; $logAction = 'unprotect'; } elseif ($isProtected) { $revCommentMsg = 'modifiedarticleprotection'; $logAction = 'modify'; } else { $revCommentMsg = 'protectedarticle'; $logAction = 'protect'; } // Truncate for whole multibyte characters $reason = $wgContLang->truncate($reason, 255); $logRelationsValues = array(); $logRelationsField = null; $logParamsDetails = array(); if ($id) { // Protection of existing page if (!Hooks::run('ArticleProtect', array(&$this, &$user, $limit, $reason))) { return Status::newGood(); } // Only certain restrictions can cascade... $editrestriction = isset($limit['edit']) ? array($limit['edit']) : $this->mTitle->getRestrictions('edit'); foreach (array_keys($editrestriction, 'sysop') as $key) { $editrestriction[$key] = 'editprotected'; // backwards compatibility } foreach (array_keys($editrestriction, 'autoconfirmed') as $key) { $editrestriction[$key] = 'editsemiprotected'; // backwards compatibility } $cascadingRestrictionLevels = $wgCascadingRestrictionLevels; foreach (array_keys($cascadingRestrictionLevels, 'sysop') as $key) { $cascadingRestrictionLevels[$key] = 'editprotected'; // backwards compatibility } foreach (array_keys($cascadingRestrictionLevels, 'autoconfirmed') as $key) { $cascadingRestrictionLevels[$key] = 'editsemiprotected'; // backwards compatibility } // The schema allows multiple restrictions if (!array_intersect($editrestriction, $cascadingRestrictionLevels)) { $cascade = false; } // insert null revision to identify the page protection change as edit summary $latest = $this->getLatest(); $nullRevision = $this->insertProtectNullRevision($revCommentMsg, $limit, $expiry, $cascade, $reason, $user); if ($nullRevision === null) { return Status::newFatal('no-null-revision', $this->mTitle->getPrefixedText()); } $logRelationsField = 'pr_id'; // Update restrictions table foreach ($limit as $action => $restrictions) { $dbw->delete('page_restrictions', array('pr_page' => $id, 'pr_type' => $action), __METHOD__); if ($restrictions != '') { $cascadeValue = $cascade && $action == 'edit' ? 1 : 0; $dbw->insert('page_restrictions', array('pr_id' => $dbw->nextSequenceValue('page_restrictions_pr_id_seq'), 'pr_page' => $id, 'pr_type' => $action, 'pr_level' => $restrictions, 'pr_cascade' => $cascadeValue, 'pr_expiry' => $dbw->encodeExpiry($expiry[$action])), __METHOD__); $logRelationsValues[] = $dbw->insertId(); $logParamsDetails[] = array('type' => $action, 'level' => $restrictions, 'expiry' => $expiry[$action], 'cascade' => (bool) $cascadeValue); } } // Clear out legacy restriction fields $dbw->update('page', array('page_restrictions' => ''), array('page_id' => $id), __METHOD__); Hooks::run('NewRevisionFromEditComplete', array($this, $nullRevision, $latest, $user)); Hooks::run('ArticleProtectComplete', array(&$this, &$user, $limit, $reason)); } else { // Protection of non-existing page (also known as "title protection") // Cascade protection is meaningless in this case $cascade = false; if ($limit['create'] != '') { $dbw->replace('protected_titles', array(array('pt_namespace', 'pt_title')), array('pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey(), 'pt_create_perm' => $limit['create'], 'pt_timestamp' => $dbw->timestamp(), 'pt_expiry' => $dbw->encodeExpiry($expiry['create']), 'pt_user' => $user->getId(), 'pt_reason' => $reason), __METHOD__); $logParamsDetails[] = array('type' => 'create', 'level' => $limit['create'], 'expiry' => $expiry['create']); } else { $dbw->delete('protected_titles', array('pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey()), __METHOD__); } } $this->mTitle->flushRestrictions(); InfoAction::invalidateCache($this->mTitle); if ($logAction == 'unprotect') { $params = array(); } else { $protectDescriptionLog = $this->protectDescriptionLog($limit, $expiry); $params = array('4::description' => $protectDescriptionLog, '5:bool:cascade' => $cascade, 'details' => $logParamsDetails); } // Update the protection log $logEntry = new ManualLogEntry('protect', $logAction); $logEntry->setTarget($this->mTitle); $logEntry->setComment($reason); $logEntry->setPerformer($user); $logEntry->setParameters($params); if ($logRelationsField !== null && count($logRelationsValues)) { $logEntry->setRelations(array($logRelationsField => $logRelationsValues)); } $logId = $logEntry->insert(); $logEntry->publish($logId); return Status::newGood(); }
/** * Record a file upload in the upload log and the image table * @param string $oldver * @param string $comment * @param string $pageText * @param bool|array $props * @param string|bool $timestamp * @param null|User $user * @param string[] $tags * @return bool */ function recordUpload2($oldver, $comment, $pageText, $props = false, $timestamp = false, $user = null, $tags = array()) { if (is_null($user)) { global $wgUser; $user = $wgUser; } $dbw = $this->repo->getMasterDB(); # Imports or such might force a certain timestamp; otherwise we generate # it and can fudge it slightly to keep (name,timestamp) unique on re-upload. if ($timestamp === false) { $timestamp = $dbw->timestamp(); $allowTimeKludge = true; } else { $allowTimeKludge = false; } $props = $props ?: $this->repo->getFileProps($this->getVirtualUrl()); $props['description'] = $comment; $props['user'] = $user->getId(); $props['user_text'] = $user->getName(); $props['timestamp'] = wfTimestamp(TS_MW, $timestamp); // DB -> TS_MW $this->setProps($props); # Fail now if the file isn't there if (!$this->fileExists) { wfDebug(__METHOD__ . ": File " . $this->getRel() . " went missing!\n"); return false; } $dbw->startAtomic(__METHOD__); # 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('image', array('img_name' => $this->getName(), 'img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $dbw->encodeBlob($this->metadata), 'img_sha1' => $this->sha1), __METHOD__, 'IGNORE'); $reupload = $dbw->affectedRows() == 0; if ($reupload) { if ($allowTimeKludge) { # Use LOCK IN SHARE MODE to ignore any transaction snapshotting $ltimestamp = $dbw->selectField('image', 'img_timestamp', array('img_name' => $this->getName()), __METHOD__, array('LOCK IN SHARE MODE')); $lUnixtime = $ltimestamp ? wfTimestamp(TS_UNIX, $ltimestamp) : false; # Avoid a timestamp that is not newer than the last version # TODO: the image/oldimage tables should be like page/revision with an ID field if ($lUnixtime && wfTimestamp(TS_UNIX, $timestamp) <= $lUnixtime) { sleep(1); // fast enough re-uploads would go far in the future otherwise $timestamp = $dbw->timestamp($lUnixtime + 1); $this->timestamp = wfTimestamp(TS_MW, $timestamp); // DB -> TS_MW } } # (bug 34993) Note: $oldver can be empty here, if the previous # version of the file was broken. Allow registration of the new # version to continue anyway, because that's better than having # an image that's not fixable by user operations. # Collision, this is an update of a file # Insert previous contents into oldimage $dbw->insertSelect('oldimage', 'image', array('oi_name' => 'img_name', 'oi_archive_name' => $dbw->addQuotes($oldver), 'oi_size' => 'img_size', 'oi_width' => 'img_width', 'oi_height' => 'img_height', 'oi_bits' => 'img_bits', 'oi_timestamp' => 'img_timestamp', 'oi_description' => 'img_description', 'oi_user' => 'img_user', 'oi_user_text' => 'img_user_text', 'oi_metadata' => 'img_metadata', 'oi_media_type' => 'img_media_type', 'oi_major_mime' => 'img_major_mime', 'oi_minor_mime' => 'img_minor_mime', 'oi_sha1' => 'img_sha1'), array('img_name' => $this->getName()), __METHOD__); # Update the current image row $dbw->update('image', array('img_size' => $this->size, 'img_width' => intval($this->width), 'img_height' => intval($this->height), 'img_bits' => $this->bits, 'img_media_type' => $this->media_type, 'img_major_mime' => $this->major_mime, 'img_minor_mime' => $this->minor_mime, 'img_timestamp' => $timestamp, 'img_description' => $comment, 'img_user' => $user->getId(), 'img_user_text' => $user->getName(), 'img_metadata' => $dbw->encodeBlob($this->metadata), 'img_sha1' => $this->sha1), array('img_name' => $this->getName()), __METHOD__); } $descTitle = $this->getTitle(); $descId = $descTitle->getArticleID(); $wikiPage = new WikiFilePage($descTitle); $wikiPage->setFile($this); // Add the log entry... $logEntry = new ManualLogEntry('upload', $reupload ? 'overwrite' : 'upload'); $logEntry->setTimestamp($this->timestamp); $logEntry->setPerformer($user); $logEntry->setComment($comment); $logEntry->setTarget($descTitle); // Allow people using the api to associate log entries with the upload. // Log has a timestamp, but sometimes different from upload timestamp. $logEntry->setParameters(array('img_sha1' => $this->sha1, 'img_timestamp' => $timestamp)); // Note we keep $logId around since during new image // creation, page doesn't exist yet, so log_page = 0 // but we want it to point to the page we're making, // so we later modify the log entry. // For a similar reason, we avoid making an RC entry // now and wait until the page exists. $logId = $logEntry->insert(); if ($descTitle->exists()) { // Use own context to get the action text in content language $formatter = LogFormatter::newFromEntry($logEntry); $formatter->setContext(RequestContext::newExtraneousContext($descTitle)); $editSummary = $formatter->getPlainActionText(); $nullRevision = Revision::newNullRevision($dbw, $descId, $editSummary, false, $user); if ($nullRevision) { $nullRevision->insertOn($dbw); Hooks::run('NewRevisionFromEditComplete', array($wikiPage, $nullRevision, $nullRevision->getParentId(), $user)); $wikiPage->updateRevisionOn($dbw, $nullRevision); // Associate null revision id $logEntry->setAssociatedRevId($nullRevision->getId()); } $newPageContent = null; } else { // Make the description page and RC log entry post-commit $newPageContent = ContentHandler::makeContent($pageText, $descTitle); } # Defer purges, page creation, and link updates in case they error out. # The most important thing is that files and the DB registry stay synced. $dbw->endAtomic(__METHOD__); # Do some cache purges after final commit so that: # a) Changes are more likely to be seen post-purge # b) They won't cause rollback of the log publish/update above $that = $this; $dbw->onTransactionIdle(function () use($that, $reupload, $wikiPage, $newPageContent, $comment, $user, $logEntry, $logId, $descId, $tags) { # Update memcache after the commit $that->invalidateCache(); $updateLogPage = false; if ($newPageContent) { # New file page; create the description page. # There's already a log entry, so don't make a second RC entry # CDN and file cache for the description page are purged by doEditContent. $status = $wikiPage->doEditContent($newPageContent, $comment, EDIT_NEW | EDIT_SUPPRESS_RC, false, $user); if (isset($status->value['revision'])) { // Associate new page revision id $logEntry->setAssociatedRevId($status->value['revision']->getId()); } // This relies on the resetArticleID() call in WikiPage::insertOn(), // which is triggered on $descTitle by doEditContent() above. if (isset($status->value['revision'])) { /** @var $rev Revision */ $rev = $status->value['revision']; $updateLogPage = $rev->getPage(); } } else { # Existing file page: invalidate description page cache $wikiPage->getTitle()->invalidateCache(); $wikiPage->getTitle()->purgeSquid(); # Allow the new file version to be patrolled from the page footer Article::purgePatrolFooterCache($descId); } # Update associated rev id. This should be done by $logEntry->insert() earlier, # but setAssociatedRevId() wasn't called at that point yet... $logParams = $logEntry->getParameters(); $logParams['associated_rev_id'] = $logEntry->getAssociatedRevId(); $update = array('log_params' => LogEntryBase::makeParamBlob($logParams)); if ($updateLogPage) { # Also log page, in case where we just created it above $update['log_page'] = $updateLogPage; } $that->getRepo()->getMasterDB()->update('logging', $update, array('log_id' => $logId), __METHOD__); $that->getRepo()->getMasterDB()->insert('log_search', array('ls_field' => 'associated_rev_id', 'ls_value' => $logEntry->getAssociatedRevId(), 'ls_log_id' => $logId), __METHOD__); # Now that the log entry is up-to-date, make an RC entry. $recentChange = $logEntry->publish($logId); if ($tags) { ChangeTags::addTags($tags, $recentChange ? $recentChange->getAttribute('rc_id') : null, $logEntry->getAssociatedRevId(), $logId); } # Run hook for other updates (typically more cache purging) Hooks::run('FileUpload', array($that, $reupload, !$newPageContent)); if ($reupload) { # Delete old thumbnails $that->purgeThumbnails(); # Remove the old file from the CDN cache DeferredUpdates::addUpdate(new CdnCacheUpdate(array($that->getUrl())), DeferredUpdates::PRESEND); } else { # Update backlink pages pointing to this title if created LinksUpdate::queueRecursiveJobsForTable($that->getTitle(), 'imagelinks'); } }); if (!$reupload) { # This is a new file, so update the image count DeferredUpdates::addUpdate(SiteStatsUpdate::factory(array('images' => 1))); } # Invalidate cache for all pages using this file DeferredUpdates::addUpdate(new HTMLCacheUpdate($this->getTitle(), 'imagelinks')); return true; }
/** * Move page to a title which is either a redirect to the * source page or nonexistent * * @param Title $nt The page to move to, which should be a redirect or nonexistent * @param string $reason The reason for the move * @param bool $createRedirect Whether to leave a redirect at the old title. Does not check * if the user has the suppressredirect right * @throws MWException */ private function moveToInternal(&$nt, $reason = '', $createRedirect = true) { global $wgUser, $wgContLang; if ($nt->exists()) { $moveOverRedirect = true; $logType = 'move_redir'; } else { $moveOverRedirect = false; $logType = 'move'; } if ($createRedirect) { if ($this->getNamespace() == NS_CATEGORY && !wfMessage('category-move-redirect-override')->inContentLanguage()->isDisabled()) { $redirectContent = new WikitextContent(wfMessage('category-move-redirect-override')->params($nt->getPrefixedText())->inContentLanguage()->plain()); } else { $contentHandler = ContentHandler::getForTitle($this); $redirectContent = $contentHandler->makeRedirectContent($nt, wfMessage('move-redirect-text')->inContentLanguage()->plain()); } // NOTE: If this page's content model does not support redirects, $redirectContent will be null. } else { $redirectContent = null; } // bug 57084: log_page should be the ID of the *moved* page $oldid = $this->getArticleID(); $logTitle = clone $this; $logEntry = new ManualLogEntry('move', $logType); $logEntry->setPerformer($wgUser); $logEntry->setTarget($logTitle); $logEntry->setComment($reason); $logEntry->setParameters(array('4::target' => $nt->getPrefixedText(), '5::noredir' => $redirectContent ? '0' : '1')); $formatter = LogFormatter::newFromEntry($logEntry); $formatter->setContext(RequestContext::newExtraneousContext($this)); $comment = $formatter->getPlainActionText(); if ($reason) { $comment .= wfMessage('colon-separator')->inContentLanguage()->text() . $reason; } # Truncate for whole multibyte characters. $comment = $wgContLang->truncate($comment, 255); $dbw = wfGetDB(DB_MASTER); $newpage = WikiPage::factory($nt); if ($moveOverRedirect) { $newid = $nt->getArticleID(); $newcontent = $newpage->getContent(); # Delete the old redirect. We don't save it to history since # by definition if we've got here it's rather uninteresting. # We have to remove it so that the next step doesn't trigger # a conflict on the unique namespace+title index... $dbw->delete('page', array('page_id' => $newid), __METHOD__); $newpage->doDeleteUpdates($newid, $newcontent); } # Save a null revision in the page's history notifying of the move $nullRevision = Revision::newNullRevision($dbw, $oldid, $comment, true, $wgUser); if (!is_object($nullRevision)) { throw new MWException('No valid null revision produced in ' . __METHOD__); } $nullRevision->insertOn($dbw); # Change the name of the target page: $dbw->update('page', array('page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey()), array('page_id' => $oldid), __METHOD__); // clean up the old title before reset article id - bug 45348 if (!$redirectContent) { WikiPage::onArticleDelete($this); } $this->resetArticleID(0); // 0 == non existing $nt->resetArticleID($oldid); $newpage->loadPageData(WikiPage::READ_LOCKING); // bug 46397 $newpage->updateRevisionOn($dbw, $nullRevision); wfRunHooks('NewRevisionFromEditComplete', array($newpage, $nullRevision, $nullRevision->getParentId(), $wgUser)); $newpage->doEditUpdates($nullRevision, $wgUser, array('changed' => false)); if (!$moveOverRedirect) { WikiPage::onArticleCreate($nt); } # Recreate the redirect, this time in the other direction. if ($redirectContent) { $redirectArticle = WikiPage::factory($this); $redirectArticle->loadFromRow(false, WikiPage::READ_LOCKING); // bug 46397 $newid = $redirectArticle->insertOn($dbw); if ($newid) { // sanity $this->resetArticleID($newid); $redirectRevision = new Revision(array('title' => $this, 'page' => $newid, 'user_text' => $wgUser->getName(), 'user' => $wgUser->getId(), 'comment' => $comment, 'content' => $redirectContent)); $redirectRevision->insertOn($dbw); $redirectArticle->updateRevisionOn($dbw, $redirectRevision, 0); wfRunHooks('NewRevisionFromEditComplete', array($redirectArticle, $redirectRevision, false, $wgUser)); $redirectArticle->doEditUpdates($redirectRevision, $wgUser, array('created' => true)); } } # Log the move $logid = $logEntry->insert(); $logEntry->publish($logid); }
/** * Backend implementation of doRollback(), please refer there for parameter * and return value documentation * * NOTE: This function does NOT check ANY permissions, it just commits the * rollback to the DB. Therefore, you should only call this function direct- * ly if you want to use custom permissions checks. If you don't, use * doRollback() instead. * @param string $fromP Name of the user whose edits to rollback. * @param string $summary Custom summary. Set to default summary if empty. * @param bool $bot If true, mark all reverted edits as bot. * * @param array $resultDetails Contains result-specific array of additional values * @param User $guser The user performing the rollback * @param array|null $tags Change tags to apply to the rollback * Callers are responsible for permission checks * (with ChangeTags::canAddTagsAccompanyingChange) * * @return array */ public function commitRollback($fromP, $summary, $bot, &$resultDetails, User $guser, $tags = null) { global $wgUseRCPatrol, $wgContLang; $dbw = wfGetDB(DB_MASTER); if (wfReadOnly()) { return [['readonlytext']]; } // Get the last editor $current = $this->getRevision(); if (is_null($current)) { // Something wrong... no page? return [['notanarticle']]; } $from = str_replace('_', ' ', $fromP); // User name given should match up with the top revision. // If the user was deleted then $from should be empty. if ($from != $current->getUserText()) { $resultDetails = ['current' => $current]; return [['alreadyrolled', htmlspecialchars($this->mTitle->getPrefixedText()), htmlspecialchars($fromP), htmlspecialchars($current->getUserText())]]; } // Get the last edit not by this person... // Note: these may not be public values $user = intval($current->getUser(Revision::RAW)); $user_text = $dbw->addQuotes($current->getUserText(Revision::RAW)); $s = $dbw->selectRow('revision', ['rev_id', 'rev_timestamp', 'rev_deleted'], ['rev_page' => $current->getPage(), "rev_user != {$user} OR rev_user_text != {$user_text}"], __METHOD__, ['USE INDEX' => 'page_timestamp', 'ORDER BY' => 'rev_timestamp DESC']); if ($s === false) { // No one else ever edited this page return [['cantrollback']]; } elseif ($s->rev_deleted & Revision::DELETED_TEXT || $s->rev_deleted & Revision::DELETED_USER) { // Only admins can see this text return [['notvisiblerev']]; } // Generate the edit summary if necessary $target = Revision::newFromId($s->rev_id, Revision::READ_LATEST); if (empty($summary)) { if ($from == '') { // no public user name $summary = wfMessage('revertpage-nouser'); } else { $summary = wfMessage('revertpage'); } } // Allow the custom summary to use the same args as the default message $args = [$target->getUserText(), $from, $s->rev_id, $wgContLang->timeanddate(wfTimestamp(TS_MW, $s->rev_timestamp)), $current->getId(), $wgContLang->timeanddate($current->getTimestamp())]; if ($summary instanceof Message) { $summary = $summary->params($args)->inContentLanguage()->text(); } else { $summary = wfMsgReplaceArgs($summary, $args); } // Trim spaces on user supplied text $summary = trim($summary); // Truncate for whole multibyte characters. $summary = $wgContLang->truncate($summary, 255); // Save $flags = EDIT_UPDATE | EDIT_INTERNAL; if ($guser->isAllowed('minoredit')) { $flags |= EDIT_MINOR; } if ($bot && $guser->isAllowedAny('markbotedits', 'bot')) { $flags |= EDIT_FORCE_BOT; } $targetContent = $target->getContent(); $changingContentModel = $targetContent->getModel() !== $current->getContentModel(); // Actually store the edit $status = $this->doEditContent($targetContent, $summary, $flags, $target->getId(), $guser, null, $tags); // Set patrolling and bot flag on the edits, which gets rollbacked. // This is done even on edit failure to have patrolling in that case (bug 62157). $set = []; if ($bot && $guser->isAllowed('markbotedits')) { // Mark all reverted edits as bot $set['rc_bot'] = 1; } if ($wgUseRCPatrol) { // Mark all reverted edits as patrolled $set['rc_patrolled'] = 1; } if (count($set)) { $dbw->update('recentchanges', $set, ['rc_cur_id' => $current->getPage(), 'rc_user_text' => $current->getUserText(), 'rc_timestamp > ' . $dbw->addQuotes($s->rev_timestamp)], __METHOD__); } if (!$status->isOK()) { return $status->getErrorsArray(); } // raise error, when the edit is an edit without a new version $statusRev = isset($status->value['revision']) ? $status->value['revision'] : null; if (!$statusRev instanceof Revision) { $resultDetails = ['current' => $current]; return [['alreadyrolled', htmlspecialchars($this->mTitle->getPrefixedText()), htmlspecialchars($fromP), htmlspecialchars($current->getUserText())]]; } if ($changingContentModel) { // If the content model changed during the rollback, // make sure it gets logged to Special:Log/contentmodel $log = new ManualLogEntry('contentmodel', 'change'); $log->setPerformer($guser); $log->setTarget($this->mTitle); $log->setComment($summary); $log->setParameters(['4::oldmodel' => $current->getContentModel(), '5::newmodel' => $targetContent->getModel()]); $logId = $log->insert($dbw); $log->publish($logId); } $revId = $statusRev->getId(); Hooks::run('ArticleRollbackComplete', [$this, $guser, $target, $current]); $resultDetails = ['summary' => $summary, 'current' => $current, 'target' => $target, 'newid' => $revId]; return []; }
/** * Add a log entry * * @param string $action One of '', 'block', 'protect', 'rights', 'delete', * 'upload', 'move', 'move_redir' * @param Title $target Title object * @param string $comment Description associated * @param array $params Parameters passed later to wfMessage function * @param null|int|User $doer The user doing the action. null for $wgUser * * @return int The log_id of the inserted log entry */ public function addEntry($action, $target, $comment, $params = array(), $doer = null) { global $wgContLang; if (!is_array($params)) { $params = array($params); } if ($comment === null) { $comment = ''; } # Trim spaces on user supplied text $comment = trim($comment); # Truncate for whole multibyte characters. $comment = $wgContLang->truncate($comment, 255); $this->action = $action; $this->target = $target; $this->comment = $comment; $this->params = LogPage::makeParamBlob($params); if ($doer === null) { global $wgUser; $doer = $wgUser; } elseif (!is_object($doer)) { $doer = User::newFromId($doer); } $this->doer = $doer; $logEntry = new ManualLogEntry($this->type, $action); $logEntry->setTarget($target); $logEntry->setPerformer($doer); $logEntry->setParameters($params); // All log entries using the LogPage to insert into the logging table // are using the old logging system and therefore the legacy flag is // needed to say the LogFormatter the parameters have numeric keys $logEntry->setLegacy(true); $formatter = LogFormatter::newFromEntry($logEntry); $context = RequestContext::newExtraneousContext($target); $formatter->setContext($context); $this->actionText = $formatter->getPlainActionText(); $this->ircActionText = $formatter->getIRCActionText(); return $this->saveContent(); }
/** * * @param array $data * @return bool */ public function onSubmit(array $data) { $title = Title::newFromText($data['pagename']); // Check if title is valid if (!$title) { return false; } // Get the default language for the wiki // Returns the default since the page is not loaded from DB $defLang = $title->getPageLanguage()->getCode(); $pageId = $title->getArticleID(); // Check if article exists if (!$pageId) { return false; } // Load the page language from DB $dbw = wfGetDB(DB_MASTER); $langOld = $dbw->selectField('page', 'page_lang', array('page_id' => $pageId), __METHOD__); // Url to redirect to after the operation $this->goToUrl = $title->getFullURL(); // Check if user wants to use default language if ($data['selectoptions'] == 1) { $langNew = null; } else { $langNew = $data['language']; } // No change in language if ($langNew === $langOld) { return false; } // Hardcoded [def] if the language is set to null $logOld = $langOld ? $langOld : $defLang . '[def]'; $logNew = $langNew ? $langNew : $defLang . '[def]'; // Writing new page language to database $dbw = wfGetDB(DB_MASTER); $dbw->update('page', array('page_lang' => $langNew), array('page_id' => $pageId, 'page_lang' => $langOld), __METHOD__); if (!$dbw->affectedRows()) { return false; } // Logging change of language $logParams = array('4::oldlanguage' => $logOld, '5::newlanguage' => $logNew); $entry = new ManualLogEntry('pagelang', 'pagelang'); $entry->setPerformer($this->getUser()); $entry->setTarget($title); $entry->setParameters($logParams); $logid = $entry->insert(); $entry->publish($logid); return true; }