/** * @param Title $Title * @param User $User * @param int $articleId * @param string $text */ public function __construct($Title, $User, $articleId = 0, $text = '') { global $wgEnableBlogArticles; /** * initialization */ $this->mPageNs = $Title->getNamespace(); if (empty($articleId)) { $this->mPageId = $Title->getArticleID(); if (empty($this->mPageId)) { $Title->getArticleID(Title::GAID_FOR_UPDATE); } } else { $this->setPageId($articleId); } if (is_object($User)) { $this->mUserId = intval($User->getID()); } else { $this->mUserId = intval($User); } $this->mIsContent = $Title->isContentPage() && ($wgEnableBlogArticles && !in_array($this->mPageNs, array(NS_BLOG_ARTICLE, NS_BLOG_ARTICLE_TALK, NS_BLOG_LISTING, NS_BLOG_LISTING_TALK))); $this->mDate = date('Y-m-d'); if ($text) { $this->mText = preg_replace('/\\[\\[[^\\:\\]]+\\:[^\\]]*\\]\\]/', '', $text); } }
/** * @return int log_id of the inserted log entry */ protected function saveContent() { global $wgLogRestrictions; $dbw = wfGetDB(DB_MASTER); $log_id = $dbw->nextSequenceValue('logging_log_id_seq'); $this->timestamp = $now = wfTimestampNow(); $data = array('log_id' => $log_id, 'log_type' => $this->type, 'log_action' => $this->action, 'log_timestamp' => $dbw->timestamp($now), 'log_user' => $this->doer->getId(), 'log_user_text' => $this->doer->getName(), 'log_namespace' => $this->target->getNamespace(), 'log_title' => $this->target->getDBkey(), 'log_page' => $this->target->getArticleID(), 'log_comment' => $this->comment, 'log_params' => $this->params); $dbw->insert('logging', $data, __METHOD__); $newId = !is_null($log_id) ? $log_id : $dbw->insertId(); # And update recentchanges if ($this->updateRecentChanges) { $titleObj = SpecialPage::getTitleFor('Log', $this->type); RecentChange::notifyLog($now, $titleObj, $this->doer, $this->getRcComment(), '', $this->type, $this->action, $this->target, $this->comment, $this->params, $newId, $this->getRcCommentIRC()); } elseif ($this->sendToUDP) { # Don't send private logs to UDP if (isset($wgLogRestrictions[$this->type]) && $wgLogRestrictions[$this->type] != '*') { return $newId; } # Notify external application via UDP. # We send this to IRC but do not want to add it the RC table. $titleObj = SpecialPage::getTitleFor('Log', $this->type); $rc = RecentChange::newLogEntry($now, $titleObj, $this->doer, $this->getRcComment(), '', $this->type, $this->action, $this->target, $this->comment, $this->params, $newId, $this->getRcCommentIRC()); $rc->notifyRC2UDP(); } return $newId; }
/** * Returns a list of query conditions that should be run against the revision table * * @return array List of conditions */ protected function getQueryConditions() { $conds = array(); if ($this->title) { $conds['rev_page'] = $this->title->getArticleID(); } if ($this->offset) { $dbr = wfGetDB(DB_SLAVE, self::DB_REVISIONS_TABLE); $conds[] = 'rev_timestamp <= ' . $dbr->addQuotes($this->offset); } return $conds; }
/** * @throws \Email\Check */ protected function assertValidPageAddedToCategory() { if (!$this->pageAddedToCategory instanceof \Title) { throw new Check("Invalid value passed for pageAddedToCategory (param: pageTitle)"); } if (!$this->pageAddedToCategory->exists()) { // Check master DB just in case the page was just created and it // hasn't been replicated to the slave yet if ($this->pageAddedToCategory->getArticleID(\Title::GAID_FOR_UPDATE) == 0) { throw new Check("pageAddedToCategory doesn't exist."); } } }
/** * Checks if a given article has been fixed by a user * inside a productivity loop. * @param Title $title * @return bool */ public function isItemFixed(Title $title) { if ($title->getArticleID() !== 0) { return $this->removeFixedItem(ucfirst(self::INSIGHT_TYPE), $title); } return false; }
/** * Get MediaWiki's ID for this value or 0 if not available. * * @return integer */ public function getArticleID() { if ($this->m_id === false) { $this->m_id = !is_null($this->getTitle()) ? $this->m_title->getArticleID() : 0; } return $this->m_id; }
/** * Performs all actions required to log all information required to have a given template * classified and have them stored in an accessible way. * @param string $type A type that you want to classify the template as. * @param bool $value Value of the classification. If false, the type will be set to unclassified. * @param int $actor Specifies if the recognition was made by a machine or a human * @return bool * @throws MWException */ public function classifyTemplate($type, $value, $actor = self::CLASSIFICATION_ACTOR_HUMAN) { /** * Check if the fetched $type is valid * and if the user is permitted to perform templatedraft related actions. */ $prop = self::getClassificationProp($type); if (!$prop || !$this->title->userCan('templatedraft')) { return false; } /** * SET THE PRIMARY PAGE PROPERTY * * If the $value equals false it means somebody has just made a negative recognition * (e.g. the template is NOT an infobox). In this case, we set the primary property to * unclassified to mark that the template has been reviewed, but we do not have a definite * information on its type. */ if (!$value) { $type = self::TEMPLATE_UNCLASSIFIED; } Wikia::setProps($this->title->getArticleID(), [self::TEMPLATE_CLASSIFICATION_MAIN_PROP => $type]); /** * SET THE SECONDARY PAGE PROPERTY * * This property is used to log more detailed information on the performed action. */ $data = ['value' => (bool) $value, 'actor' => $actor, 'actor-id' => $this->wg->User->getId(), 'timestamp' => wfTimestamp()]; Wikia::setProps($this->title->getArticleID(), [$prop => json_encode($data)]); /** * Since Wikia::setProps fails silently we can return true at this point. */ return true; }
/** * getProps -- get props for comment article * */ public function getProps() { if ((!$this->mProps || !is_array($this->mProps)) && class_exists('BlogArticle')) { $this->mProps = BlogArticle::getProps($this->mTitle->getArticleID()); } return $this->mProps; }
/** * Updates cascading protections * * @param $parserOutput ParserOutput object for the current version */ public function doCascadeProtectionUpdates(ParserOutput $parserOutput) { if (wfReadOnly() || !$this->mTitle->areRestrictionsCascading()) { return; } // templatelinks table may have become out of sync, // especially if using variable-based transclusions. // For paranoia, check if things have changed and if // so apply updates to the database. This will ensure // that cascaded protections apply as soon as the changes // are visible. # Get templates from templatelinks $id = $this->mTitle->getArticleID(); $tlTemplates = array(); $dbr = wfGetDB(DB_SLAVE); $res = $dbr->select(array('templatelinks'), array('tl_namespace', 'tl_title'), array('tl_from' => $id), __METHOD__); foreach ($res as $row) { $tlTemplates["{$row->tl_namespace}:{$row->tl_title}"] = true; } # Get templates from parser output. $poTemplates = array(); foreach ($parserOutput->getTemplates() as $ns => $templates) { foreach ($templates as $dbk => $id) { $poTemplates["{$ns}:{$dbk}"] = true; } } # Get the diff $templates_diff = array_diff_key($poTemplates, $tlTemplates); if (count($templates_diff) > 0) { # Whee, link updates time. # Note: we are only interested in links here. We don't need to get other DataUpdate items from the parser output. $u = new LinksUpdate($this->mTitle, $parserOutput, false); $u->doUpdate(); } }
/** * Returns primary coordinates of the given page, if any * @param Title $title * @return Coord|false: Coordinates or false */ public static function getPageCoordinates(Title $title) { $coords = self::getAllCoordinates($title->getArticleID(), array('gt_primary' => 1)); if ($coords) { return $coords[0]; } return false; }
/** * Checks if a given article has been fixed by a user * inside a productivity loop. * @param Title $title * @return bool */ public function isItemFixed(Title $title) { $dbr = wfGetDB(DB_MASTER); $row = $dbr->selectRow('pagelinks', '*', ['pl_from' => $title->getArticleID()]); if ($row) { return $this->removeFixedItem(ucfirst(self::INSIGHT_TYPE), $title); } return false; }
function __construct(SpecialMergeHistory $form, $conds, Title $source, Title $dest) { $this->mForm = $form; $this->mConds = $conds; $this->title = $source; $this->articleID = $source->getArticleID(); $dbr = wfGetDB(DB_REPLICA); $maxtimestamp = $dbr->selectField('revision', 'MIN(rev_timestamp)', ['rev_page' => $dest->getArticleID()], __METHOD__); $this->maxTimestamp = $maxtimestamp; parent::__construct($form->getContext()); }
/** * Test that getting all properties clears the single properties * that have been cached by getting a property, saving a new value for * the property, getting all properties (which clears the cached single * properties), then getting the property again. The new value for the * property rather than the cached value of the property should be * returned. */ public function testClearCache() { $pageProps = PageProps::getInstance(); $page1ID = $this->title1->getArticleID(); $pageProps->getProperty($this->title1, "property1"); $new_value = "another value"; $this->setProperty($page1ID, "property1", $new_value); $pageProps->getProperties($this->title1); $result = $pageProps->getProperty($this->title1, "property1"); $this->assertArrayHasKey($page1ID, $result, "Found property"); $this->assertEquals($result[$page1ID], "another value", "Clear cache"); }
/** * Driver function that handles updating assessment data in database * @param Title $titleObj Title object of the subject page * @param array $assessmentData Data for all assessments compiled */ public static function doUpdates($titleObj, $assessmentData) { global $wgUpdateRowsPerQuery; $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory(); $ticket = $factory->getEmptyTransactionTicket(__METHOD__); $pageId = $titleObj->getArticleID(); $revisionId = $titleObj->getLatestRevID(); // Compile a list of projects to find out which ones to be deleted afterwards $projects = array(); foreach ($assessmentData as $parserData) { // For each project, get the corresponding ID from page_assessments_projects table $projectId = self::getProjectId($parserData[0]); if ($projectId === false) { $projectId = self::insertProject($parserData[0]); } $projects[$parserData[0]] = $projectId; } $projectsInDb = self::getAllProjects($pageId, self::READ_LATEST); $toInsert = array_diff($projects, $projectsInDb); $toDelete = array_diff($projectsInDb, $projects); $toUpdate = array_intersect($projects, $projectsInDb); $i = 0; // Add and update records to the database foreach ($assessmentData as $parserData) { $projectId = $projects[$parserData[0]]; if ($projectId) { $class = $parserData[1]; $importance = $parserData[2]; $values = array('pa_page_id' => $pageId, 'pa_project_id' => $projectId, 'pa_class' => $class, 'pa_importance' => $importance, 'pa_page_revision' => $revisionId); if (in_array($projectId, $toInsert)) { self::insertRecord($values); } elseif (in_array($projectId, $toUpdate)) { self::updateRecord($values); } // Check for database lag if there's a huge number of assessments if ($i > 0 && $i % $wgUpdateRowsPerQuery == 0) { $factory->commitAndWaitForReplication(__METHOD__, $ticket); } $i++; } } // Delete records from the database foreach ($toDelete as $project) { $values = array('pa_page_id' => $pageId, 'pa_project_id' => $project); self::deleteRecord($values); // Check for database lag if there's a huge number of deleted assessments if ($i > 0 && $i % $wgUpdateRowsPerQuery == 0) { $factory->commitAndWaitForReplication(__METHOD__, $ticket); } $i++; } return; }
public static function newFromTarget(Title $target) { $pageid = $target->getArticleID(); $dbr = wfGetDB(DB_SLAVE); $res = $dbr->select('indexes', array('in_namespace', 'in_title'), array('in_from' => $pageid), __METHOD__); if (!$res->numRows()) { return null; } $ind = new IndexFunction(); $row = $res->fetchRow(); $ind->mFrom = Title::makeTitle($row->in_namespace, $row->in_title); return $ind; }
/** * Extract information from a Title object for return to Lua * * This also records a link to this title in the current ParserOutput * and caches the title for repeated lookups. The caller should call * incrementExpensiveFunctionCount() if necessary. * * @param $title Title Title to return * @return array Lua data */ private function returnTitleToLua(Title $title) { // Cache it $this->titleCache[$title->getPrefixedDBkey()] = $title; if ($title->getArticleID() > 0) { $this->idCache[$title->getArticleID()] = $title; } // Record a link if ($this->getParser() && !$title->equals($this->getTitle())) { $this->getParser()->getOutput()->addLink($title); } $ns = $title->getNamespace(); $ret = array('isLocal' => (bool) $title->isLocal(), 'isRedirect' => (bool) $title->isRedirect(), 'interwiki' => $title->getInterwiki(), 'namespace' => $ns, 'nsText' => $title->getNsText(), 'text' => $title->getText(), 'id' => $title->getArticleID(), 'fragment' => $title->getFragment(), 'thePartialUrl' => $title->getPartialURL()); if ($ns === NS_SPECIAL) { $ret['exists'] = (bool) SpecialPageFactory::exists($title->getDBkey()); } else { $ret['exists'] = $ret['id'] > 0; } if ($ns !== NS_FILE && $ns !== NS_MEDIA) { $ret['fileExists'] = false; } return $ret; }
private function showHistory() { $this->showMergeForm(); # List all stored revisions $revisions = new MergeHistoryPager($this, [], $this->mTargetObj, $this->mDestObj); $haveRevisions = $revisions && $revisions->getNumRows() > 0; $out = $this->getOutput(); $titleObj = $this->getPageTitle(); $action = $titleObj->getLocalURL(['action' => 'submit']); # Start the form here $top = Xml::openElement('form', ['method' => 'post', 'action' => $action, 'id' => 'merge']); $out->addHTML($top); if ($haveRevisions) { # Format the user-visible controls (comment field, submission button) # in a nice little table $table = Xml::openElement('fieldset') . $this->msg('mergehistory-merge', $this->mTargetObj->getPrefixedText(), $this->mDestObj->getPrefixedText())->parse() . Xml::openElement('table', ['id' => 'mw-mergehistory-table']) . '<tr> <td class="mw-label">' . Xml::label($this->msg('mergehistory-reason')->text(), 'wpComment') . '</td> <td class="mw-input">' . Xml::input('wpComment', 50, $this->mComment, ['id' => 'wpComment']) . '</td> </tr> <tr> <td> </td> <td class="mw-submit">' . Xml::submitButton($this->msg('mergehistory-submit')->text(), ['name' => 'merge', 'id' => 'mw-merge-submit']) . '</td> </tr>' . Xml::closeElement('table') . Xml::closeElement('fieldset'); $out->addHTML($table); } $out->addHTML('<h2 id="mw-mergehistory">' . $this->msg('mergehistory-list')->escaped() . "</h2>\n"); if ($haveRevisions) { $out->addHTML($revisions->getNavigationBar()); $out->addHTML('<ul>'); $out->addHTML($revisions->getBody()); $out->addHTML('</ul>'); $out->addHTML($revisions->getNavigationBar()); } else { $out->addWikiMsg('mergehistory-empty'); } # Show relevant lines from the merge log: $mergeLogPage = new LogPage('merge'); $out->addHTML('<h2>' . $mergeLogPage->getName()->escaped() . "</h2>\n"); LogEventsList::showLogExtract($out, 'merge', $this->mTargetObj); # When we submit, go by page ID to avoid some nasty but unlikely collisions. # Such would happen if a page was renamed after the form loaded, but before submit $misc = Html::hidden('targetID', $this->mTargetObj->getArticleID()); $misc .= Html::hidden('destID', $this->mDestObj->getArticleID()); $misc .= Html::hidden('target', $this->mTarget); $misc .= Html::hidden('dest', $this->mDest); $misc .= Html::hidden('wpEditToken', $this->getUser()->getEditToken()); $misc .= Xml::closeElement('form'); $out->addHTML($misc); return true; }
/** * Dispatched from execute(); */ private function processParameter($sParameter) { try { $this->oRequestedTitle = Title::newFromText($sParameter); /*if( !$this->oRequestedTitle->exists() && $this->oRequestedTitle->getNamespace() != NS_SPECIAL ) { //!$this->mRequestedTitle->isSpecialPage() does not work in MW 1.13 throw new Exception( 'error-requested-title-does-not-exist' ); }*/ //Get relevant page props $dbr = wfGetDB(DB_SLAVE); $res = $dbr->selectField('page_props', 'pp_value', array('pp_propname' => 'bs-universalexport-params', 'pp_page' => $this->oRequestedTitle->getArticleID())); if ($res != false) { $res = FormatJson::decode($res, true); if (is_array($res)) { $this->aParams = array_merge($this->aParams, $res); } } BsUniversalExportHelper::getParamsFromQueryString($this->aParams); //Title::userCan always returns false on special pages (exept for createaccount action) if ($this->oRequestedTitle->getNamespace() === NS_SPECIAL) { if ($this->getUser()->isAllowed('universalexport-export') !== true) { throw new Exception('bs-universalexport-error-permission'); } } elseif ($this->oRequestedTitle->userCan('universalexport-export') === false) { throw new Exception('bs-universalexport-error-permission'); } // TODO RBV (24.01.11 17:37): array_intersect(), may be better? $aCategoryNames = BsUniversalExportHelper::getCategoriesForTitle($this->oRequestedTitle); foreach ($aCategoryNames as $sCategoryName) { if (in_array($sCategoryName, $this->aCategoryBlacklist)) { throw new Exception('bs-universalexport-error-requested-title-in-category-blacklist'); } } BsUniversalExportHelper::checkPermissionForTitle($this->oRequestedTitle, $this->aParams); //Throws Exception $sModuleKey = $this->aParams['module']; if (!isset($this->aModules[$sModuleKey]) || !$this->aModules[$sModuleKey] instanceof BsUniversalExportModule) { throw new Exception('bs-universalexport-error-requested-export-module-not-found'); } $oExportModule = $this->aModules[$sModuleKey]; $aFile = $oExportModule->createExportFile($this); $this->returnFile($aFile); } catch (Exception $oException) { //Display Exception-Message and Stacktrace $this->oOutputPage->setPageTitle(wfMessage('bs-universalexport-page-title-on-error')->text()); $oExceptionView = new ViewException($oException); $this->oOutputPage->addHtml($oExceptionView->execute()); } }
/** * Constructor * * @param Title $title Title of the page we're updating * @param ParserOutput $parserOutput Output from a full parse of this page * @param bool $recursive Queue jobs for recursive updates? * @throws MWException */ function __construct($title, $parserOutput, $recursive = true) { parent::__construct(false); // no implicit transaction if (!$title instanceof Title) { throw new MWException("The calling convention to LinksUpdate::LinksUpdate() has changed. " . "Please see Article::editUpdates() for an invocation example.\n"); } if (!$parserOutput instanceof ParserOutput) { throw new MWException("The calling convention to LinksUpdate::__construct() has changed. " . "Please see WikiPage::doEditUpdates() for an invocation example.\n"); } $this->mTitle = $title; $this->mId = $title->getArticleID(); if (!$this->mId) { throw new MWException("The Title object did not provide an article " . "ID. Perhaps the page doesn't exist?"); } $this->mParserOutput = $parserOutput; $this->mLinks = $parserOutput->getLinks(); $this->mImages = $parserOutput->getImages(); $this->mTemplates = $parserOutput->getTemplates(); $this->mExternals = $parserOutput->getExternalLinks(); $this->mCategories = $parserOutput->getCategories(); $this->mProperties = $parserOutput->getProperties(); $this->mInterwikis = $parserOutput->getInterwikiLinks(); # Convert the format of the interlanguage links # I didn't want to change it in the ParserOutput, because that array is passed all # the way back to the skin, so either a skin API break would be required, or an # inefficient back-conversion. $ill = $parserOutput->getLanguageLinks(); $this->mInterlangs = array(); foreach ($ill as $link) { list($key, $title) = explode(':', $link, 2); $this->mInterlangs[$key] = $title; } foreach ($this->mCategories as &$sortkey) { # If the sortkey is longer then 255 bytes, # it truncated by DB, and then doesn't get # matched when comparing existing vs current # categories, causing bug 25254. # Also. substr behaves weird when given "". if ($sortkey !== '') { $sortkey = substr($sortkey, 0, 255); } } $this->mRecursive = $recursive; Hooks::run('LinksUpdateConstructed', array(&$this)); }
/** * Get number of active users watching a page * @param Title $title * @return int */ public static function numUsersWatchingPage(Title $title) { global $wgMemc, $wgCookieExpiration; # Check the cache... $key = wfMemcKey('flaggedrevs', 'usersWatching', $title->getArticleID()); $val = $wgMemc->get($key); if (is_int($val)) { return $val; // cache hit } # Get number of active editors watching this page... $dbr = wfGetDB(DB_SLAVE); $cutoff = $dbr->timestamp(wfTimestamp(TS_UNIX) - 2 * $wgCookieExpiration); $count = (int) $dbr->selectField(array('watchlist', 'user'), 'COUNT(*)', array('wl_namespace' => $title->getNamespace(), 'wl_title' => $title->getDBkey(), 'wl_user = user_id', 'user_touched > ' . $dbr->addQuotes($cutoff)), __METHOD__); if ($count > 10) { # Save new value to cache (more aggresive for larger counts) $wgMemc->set($key, $count, $count > 200 ? 30 * 60 : 5 * 60); } return $count; }
/** * Constructor * * @param Title $title Title of the page we're updating * @param ParserOutput $parserOutput Output from a full parse of this page * @param bool $recursive Queue jobs for recursive updates? * @throws MWException */ function __construct(Title $title, ParserOutput $parserOutput, $recursive = true) { parent::__construct(false); // no implicit transaction $this->mTitle = $title; $this->mId = $title->getArticleID(Title::GAID_FOR_UPDATE); if (!$this->mId) { throw new InvalidArgumentException("The Title object yields no ID. Perhaps the page doesn't exist?"); } $this->mParserOutput = $parserOutput; $this->mLinks = $parserOutput->getLinks(); $this->mImages = $parserOutput->getImages(); $this->mTemplates = $parserOutput->getTemplates(); $this->mExternals = $parserOutput->getExternalLinks(); $this->mCategories = $parserOutput->getCategories(); $this->mProperties = $parserOutput->getProperties(); $this->mInterwikis = $parserOutput->getInterwikiLinks(); # Convert the format of the interlanguage links # I didn't want to change it in the ParserOutput, because that array is passed all # the way back to the skin, so either a skin API break would be required, or an # inefficient back-conversion. $ill = $parserOutput->getLanguageLinks(); $this->mInterlangs = array(); foreach ($ill as $link) { list($key, $title) = explode(':', $link, 2); $this->mInterlangs[$key] = $title; } foreach ($this->mCategories as &$sortkey) { # If the sortkey is longer then 255 bytes, # it truncated by DB, and then doesn't get # matched when comparing existing vs current # categories, causing bug 25254. # Also. substr behaves weird when given "". if ($sortkey !== '') { $sortkey = substr($sortkey, 0, 255); } } $this->mRecursive = $recursive; Hooks::run('LinksUpdateConstructed', array(&$this)); }
/** * Set the stability configuration settings for a page * @param Title $title * @param array $config * @return bool Row changed */ public static function setStabilitySettings(Title $title, array $config) { $dbw = wfGetDB(DB_MASTER); # If setting to site default values and there is a row then erase it if (self::configIsReset($config)) { $dbw->delete('flaggedpage_config', array('fpc_page_id' => $title->getArticleID()), __METHOD__); $changed = $dbw->affectedRows() != 0; // did this do anything? # Otherwise, add/replace row if we are not just setting it to the site default } else { $dbExpiry = Block::encodeExpiry($config['expiry'], $dbw); # Get current config... $oldRow = $dbw->selectRow('flaggedpage_config', array('fpc_override', 'fpc_level', 'fpc_expiry'), array('fpc_page_id' => $title->getArticleID()), __METHOD__, 'FOR UPDATE'); # Check if this is not the same config as the existing (if any) row $changed = !$oldRow || $oldRow->fpc_override != $config['override'] || $oldRow->fpc_level != $config['autoreview'] || $oldRow->fpc_expiry != $dbExpiry; # If the new config is different, replace the old row... if ($changed) { $dbw->replace('flaggedpage_config', array('PRIMARY'), array('fpc_page_id' => $title->getArticleID(), 'fpc_select' => -1, 'fpc_override' => (int) $config['override'], 'fpc_level' => $config['autoreview'], 'fpc_expiry' => $dbExpiry), __METHOD__); } } return $changed; }
/** * Update flaggedrevs page/tracking tables (revision moving) */ public static function onArticleMergeComplete(Title $sourceTitle, Title $destTitle) { $oldPageID = $sourceTitle->getArticleID(); $newPageID = $destTitle->getArticleID(); # Get flagged revisions from old page id that point to destination page $dbw = wfGetDB(DB_MASTER); $result = $dbw->select(array('flaggedrevs', 'revision'), array('fr_rev_id'), array('fr_page_id' => $oldPageID, 'fr_rev_id = rev_id', 'rev_page' => $newPageID), __METHOD__); # Update these rows $revIDs = array(); foreach ($result as $row) { $revIDs[] = $row->fr_rev_id; } if (!empty($revIDs)) { $dbw->update('flaggedrevs', array('fr_page_id' => $newPageID), array('fr_page_id' => $oldPageID, 'fr_rev_id' => $revIDs), __METHOD__); } # Update pages...stable versions possibly lost to another page FlaggedRevs::stableVersionUpdates($sourceTitle); FlaggedRevs::HTMLCacheUpdates($sourceTitle); FlaggedRevs::stableVersionUpdates($destTitle); FlaggedRevs::HTMLCacheUpdates($destTitle); return true; }
/** * Move this page's subpages to be subpages of $nt * * @param Title $nt Move target * @param bool $auth Whether $wgUser's permissions should be checked * @param string $reason The reason for the move * @param bool $createRedirect Whether to create redirects from the old subpages to * the new ones Ignored if the user doesn't have the 'suppressredirect' right * @return array Array with old page titles as keys, and strings (new page titles) or * arrays (errors) as values, or an error array with numeric indices if no pages * were moved */ public function moveSubpages($nt, $auth = true, $reason = '', $createRedirect = true) { global $wgMaximumMovedPages; // Check permissions if (!$this->userCan('move-subpages')) { return array('cant-move-subpages'); } // Do the source and target namespaces support subpages? if (!MWNamespace::hasSubpages($this->getNamespace())) { return array('namespace-nosubpages', MWNamespace::getCanonicalName($this->getNamespace())); } if (!MWNamespace::hasSubpages($nt->getNamespace())) { return array('namespace-nosubpages', MWNamespace::getCanonicalName($nt->getNamespace())); } $subpages = $this->getSubpages($wgMaximumMovedPages + 1); $retval = array(); $count = 0; foreach ($subpages as $oldSubpage) { $count++; if ($count > $wgMaximumMovedPages) { $retval[$oldSubpage->getPrefixedText()] = array('movepage-max-pages', $wgMaximumMovedPages); break; } // We don't know whether this function was called before // or after moving the root page, so check both // $this and $nt if ($oldSubpage->getArticleID() == $this->getArticleID() || $oldSubpage->getArticleID() == $nt->getArticleID()) { // When moving a page to a subpage of itself, // don't move it twice continue; } $newPageName = preg_replace('#^' . preg_quote($this->getDBkey(), '#') . '#', StringUtils::escapeRegexReplacement($nt->getDBkey()), $oldSubpage->getDBkey()); if ($oldSubpage->isTalkPage()) { $newNs = $nt->getTalkPage()->getNamespace(); } else { $newNs = $nt->getSubjectPage()->getNamespace(); } # Bug 14385: we need makeTitleSafe because the new page names may # be longer than 255 characters. $newSubpage = Title::makeTitleSafe($newNs, $newPageName); $success = $oldSubpage->moveTo($newSubpage, $auth, $reason, $createRedirect); if ($success === true) { $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText(); } else { $retval[$oldSubpage->getPrefixedText()] = $success; } } return $retval; }
/** * Returns page counts that would be too "expensive" to retrieve by normal means. * * @param Title $title Title to get counts for * @return array */ protected function pageCounts(Title $title) { $id = $title->getArticleID(); $config = $this->context->getConfig(); $dbrWatchlist = wfGetDB(DB_SLAVE, 'watchlist'); $result = array(); // Number of page watchers $watchers = (int) $dbrWatchlist->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $title->getNamespace(), 'wl_title' => $title->getDBkey()), __METHOD__); $result['watchers'] = $watchers; if ($config->get('ShowUpdatedMarker')) { // Threshold: last visited about 26 weeks before latest edit $updated = wfTimestamp(TS_UNIX, $this->page->getTimestamp()); $age = $config->get('WatchersMaxAge'); $threshold = $dbrWatchlist->timestamp($updated - $age); // Number of page watchers who also visited a "recent" edit $visitingWatchers = (int) $dbrWatchlist->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $title->getNamespace(), 'wl_title' => $title->getDBkey(), 'wl_notificationtimestamp >= ' . $dbrWatchlist->addQuotes($threshold) . ' OR wl_notificationtimestamp IS NULL'), __METHOD__); $result['visitingWatchers'] = $visitingWatchers; } $dbr = wfGetDB(DB_SLAVE); // Total number of edits $edits = (int) $dbr->selectField('revision', 'COUNT(*)', array('rev_page' => $id), __METHOD__); $result['edits'] = $edits; // Total number of distinct authors if ($config->get('MiserMode')) { $result['authors'] = 0; } else { $result['authors'] = (int) $dbr->selectField('revision', 'COUNT(DISTINCT rev_user_text)', array('rev_page' => $id), __METHOD__); } // "Recent" threshold defined by RCMaxAge setting $threshold = $dbr->timestamp(time() - $config->get('RCMaxAge')); // Recent number of edits $edits = (int) $dbr->selectField('revision', 'COUNT(rev_page)', array('rev_page' => $id, "rev_timestamp >= " . $dbr->addQuotes($threshold)), __METHOD__); $result['recent_edits'] = $edits; // Recent number of distinct authors $result['recent_authors'] = (int) $dbr->selectField('revision', 'COUNT(DISTINCT rev_user_text)', array('rev_page' => $id, "rev_timestamp >= " . $dbr->addQuotes($threshold)), __METHOD__); // Subpages (if enabled) if (MWNamespace::hasSubpages($title->getNamespace())) { $conds = array('page_namespace' => $title->getNamespace()); $conds[] = 'page_title ' . $dbr->buildLike($title->getDBkey() . '/', $dbr->anyString()); // Subpages of this page (redirects) $conds['page_is_redirect'] = 1; $result['subpages']['redirects'] = (int) $dbr->selectField('page', 'COUNT(page_id)', $conds, __METHOD__); // Subpages of this page (non-redirects) $conds['page_is_redirect'] = 0; $result['subpages']['nonredirects'] = (int) $dbr->selectField('page', 'COUNT(page_id)', $conds, __METHOD__); // Subpages of this page (total) $result['subpages']['total'] = $result['subpages']['redirects'] + $result['subpages']['nonredirects']; } // Counts for the number of transclusion links (to/from) if ($config->get('MiserMode')) { $result['transclusion']['to'] = 0; } else { $result['transclusion']['to'] = (int) $dbr->selectField('templatelinks', 'COUNT(tl_from)', array('tl_namespace' => $title->getNamespace(), 'tl_title' => $title->getDBkey()), __METHOD__); } $result['transclusion']['from'] = (int) $dbr->selectField('templatelinks', 'COUNT(*)', array('tl_from' => $title->getArticleID()), __METHOD__); return $result; }
/** * Attempt submission (no UI) * * @param $result * @param $bot bool * * @return Status object, possibly with a message, but always with one of the AS_* constants in $status->value, * * FIXME: This interface is TERRIBLE, but hard to get rid of due to various error display idiosyncrasies. There are * also lots of cases where error metadata is set in the object and retrieved later instead of being returned, e.g. * AS_CONTENT_TOO_BIG and AS_BLOCKED_PAGE_FOR_USER. All that stuff needs to be cleaned up some time. */ function internalAttemptSave(&$result, $bot = false) { global $wgFilterCallback, $wgUser, $wgRequest, $wgParser; global $wgMaxArticleSize; $status = Status::newGood(); wfProfileIn(__METHOD__); wfProfileIn(__METHOD__ . '-checks'); if (!wfRunHooks('EditPage::attemptSave', array($this))) { wfDebug("Hook 'EditPage::attemptSave' aborted article saving\n"); $status->fatal('hookaborted'); $status->value = self::AS_HOOK_ERROR; wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } # Check image redirect if ($this->mTitle->getNamespace() == NS_FILE && Title::newFromRedirect($this->textbox1) instanceof Title && !$wgUser->isAllowed('upload')) { $code = $wgUser->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED; $status->setResult(false, $code); wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } # Check for spam $match = self::matchSummarySpamRegex($this->summary); if ($match === false) { $match = self::matchSpamRegex($this->textbox1); } if ($match !== false) { $result['spam'] = $match; $ip = $wgRequest->getIP(); $pdbk = $this->mTitle->getPrefixedDBkey(); $match = str_replace("\n", '', $match); wfDebugLog('SpamRegex', "{$ip} spam regex hit [[{$pdbk}]]: \"{$match}\""); $status->fatal('spamprotectionmatch', $match); $status->value = self::AS_SPAM_ERROR; wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } if ($wgFilterCallback && is_callable($wgFilterCallback) && $wgFilterCallback($this->mTitle, $this->textbox1, $this->section, $this->hookError, $this->summary)) { # Error messages or other handling should be performed by the filter function $status->setResult(false, self::AS_FILTERING); wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } if (!wfRunHooks('EditFilter', array($this, $this->textbox1, $this->section, &$this->hookError, $this->summary))) { # Error messages etc. could be handled within the hook... $status->fatal('hookaborted'); $status->value = self::AS_HOOK_ERROR; wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } elseif ($this->hookError != '') { # ...or the hook could be expecting us to produce an error $status->fatal('hookaborted'); $status->value = self::AS_HOOK_ERROR_EXPECTED; wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } if ($wgUser->isBlockedFrom($this->mTitle, false)) { // Auto-block user's IP if the account was "hard" blocked $wgUser->spreadAnyEditBlock(); # Check block state against master, thus 'false'. $status->setResult(false, self::AS_BLOCKED_PAGE_FOR_USER); wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } $this->kblength = (int) (strlen($this->textbox1) / 1024); if ($this->kblength > $wgMaxArticleSize) { // Error will be displayed by showEditForm() $this->tooBig = true; $status->setResult(false, self::AS_CONTENT_TOO_BIG); wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } if (!$wgUser->isAllowed('edit')) { if ($wgUser->isAnon()) { $status->setResult(false, self::AS_READ_ONLY_PAGE_ANON); wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } else { $status->fatal('readonlytext'); $status->value = self::AS_READ_ONLY_PAGE_LOGGED; wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } } if (wfReadOnly()) { $status->fatal('readonlytext'); $status->value = self::AS_READ_ONLY_PAGE; wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } if ($wgUser->pingLimiter()) { $status->fatal('actionthrottledtext'); $status->value = self::AS_RATE_LIMITED; wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } # If the article has been deleted while editing, don't save it without # confirmation if ($this->wasDeletedSinceLastEdit() && !$this->recreate) { $status->setResult(false, self::AS_ARTICLE_WAS_DELETED); wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } wfProfileOut(__METHOD__ . '-checks'); # If article is new, insert it. $aid = $this->mTitle->getArticleID(Title::GAID_FOR_UPDATE); $new = $aid == 0; if ($new) { // Late check for create permission, just in case *PARANOIA* if (!$this->mTitle->userCan('create')) { $status->fatal('nocreatetext'); $status->value = self::AS_NO_CREATE_PERMISSION; wfDebug(__METHOD__ . ": no create permission\n"); wfProfileOut(__METHOD__); return $status; } # Don't save a new article if it's blank. if ($this->textbox1 == '') { $status->setResult(false, self::AS_BLANK_ARTICLE); wfProfileOut(__METHOD__); return $status; } // Run post-section-merge edit filter if (!wfRunHooks('EditFilterMerged', array($this, $this->textbox1, &$this->hookError, $this->summary))) { # Error messages etc. could be handled within the hook... $status->fatal('hookaborted'); $status->value = self::AS_HOOK_ERROR; wfProfileOut(__METHOD__); return $status; } elseif ($this->hookError != '') { # ...or the hook could be expecting us to produce an error $status->fatal('hookaborted'); $status->value = self::AS_HOOK_ERROR_EXPECTED; wfProfileOut(__METHOD__); return $status; } # Handle the user preference to force summaries here. Check if it's not a redirect. if (!$this->allowBlankSummary && !Title::newFromRedirect($this->textbox1)) { if (md5($this->summary) == $this->autoSumm) { $this->missingSummary = true; $status->fatal('missingsummary'); // or 'missingcommentheader' if $section == 'new'. Blegh $status->value = self::AS_SUMMARY_NEEDED; wfProfileOut(__METHOD__); return $status; } } $text = $this->textbox1; $result['sectionanchor'] = ''; if ($this->section == 'new') { if ($this->sectiontitle !== '') { // Insert the section title above the content. $text = wfMsgForContent('newsectionheaderdefaultlevel', $this->sectiontitle) . "\n\n" . $text; // Jump to the new section $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText($this->sectiontitle); // If no edit summary was specified, create one automatically from the section // title and have it link to the new section. Otherwise, respect the summary as // passed. if ($this->summary === '') { $cleanSectionTitle = $wgParser->stripSectionName($this->sectiontitle); $this->summary = wfMsgForContent('newsectionsummary', $cleanSectionTitle); } } elseif ($this->summary !== '') { // Insert the section title above the content. $text = wfMsgForContent('newsectionheaderdefaultlevel', $this->summary) . "\n\n" . $text; // Jump to the new section $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText($this->summary); // Create a link to the new section from the edit summary. $cleanSummary = $wgParser->stripSectionName($this->summary); $this->summary = wfMsgForContent('newsectionsummary', $cleanSummary); } } $status->value = self::AS_SUCCESS_NEW_ARTICLE; } else { # Article exists. Check for edit conflict. $this->mArticle->clear(); # Force reload of dates, etc. $timestamp = $this->mArticle->getTimestamp(); wfDebug("timestamp: {$timestamp}, edittime: {$this->edittime}\n"); if ($timestamp != $this->edittime) { $this->isConflict = true; if ($this->section == 'new') { if ($this->mArticle->getUserText() == $wgUser->getName() && $this->mArticle->getComment() == $this->summary) { // Probably a duplicate submission of a new comment. // This can happen when squid resends a request after // a timeout but the first one actually went through. wfDebug(__METHOD__ . ": duplicate new section submission; trigger edit conflict!\n"); } else { // New comment; suppress conflict. $this->isConflict = false; wfDebug(__METHOD__ . ": conflict suppressed; new section\n"); } } elseif ($this->section == '' && $this->userWasLastToEdit($wgUser->getId(), $this->edittime)) { # Suppress edit conflict with self, except for section edits where merging is required. wfDebug(__METHOD__ . ": Suppressing edit conflict, same user.\n"); $this->isConflict = false; } } // If sectiontitle is set, use it, otherwise use the summary as the section title (for // backwards compatibility with old forms/bots). if ($this->sectiontitle !== '') { $sectionTitle = $this->sectiontitle; } else { $sectionTitle = $this->summary; } if ($this->isConflict) { wfDebug(__METHOD__ . ": conflict! getting section '{$this->section}' for time '{$this->edittime}' (article time '{$timestamp}')\n"); $text = $this->mArticle->replaceSection($this->section, $this->textbox1, $sectionTitle, $this->edittime); } else { wfDebug(__METHOD__ . ": getting section '{$this->section}'\n"); $text = $this->mArticle->replaceSection($this->section, $this->textbox1, $sectionTitle); } if (is_null($text)) { wfDebug(__METHOD__ . ": activating conflict; section replace failed.\n"); $this->isConflict = true; $text = $this->textbox1; // do not try to merge here! } elseif ($this->isConflict) { # Attempt merge if ($this->mergeChangesInto($text)) { // Successful merge! Maybe we should tell the user the good news? $this->isConflict = false; wfDebug(__METHOD__ . ": Suppressing edit conflict, successful merge.\n"); } else { $this->section = ''; $this->textbox1 = $text; wfDebug(__METHOD__ . ": Keeping edit conflict, failed merge.\n"); } } if ($this->isConflict) { $status->setResult(false, self::AS_CONFLICT_DETECTED); wfProfileOut(__METHOD__); return $status; } // Run post-section-merge edit filter if (!wfRunHooks('EditFilterMerged', array($this, $text, &$this->hookError, $this->summary))) { # Error messages etc. could be handled within the hook... $status->fatal('hookaborted'); $status->value = self::AS_HOOK_ERROR; wfProfileOut(__METHOD__); return $status; } elseif ($this->hookError != '') { # ...or the hook could be expecting us to produce an error $status->fatal('hookaborted'); $status->value = self::AS_HOOK_ERROR_EXPECTED; wfProfileOut(__METHOD__); return $status; } # Handle the user preference to force summaries here, but not for null edits if ($this->section != 'new' && !$this->allowBlankSummary && $this->getOriginalContent() != $text && !Title::newFromRedirect($text)) { if (md5($this->summary) == $this->autoSumm) { $this->missingSummary = true; $status->fatal('missingsummary'); $status->value = self::AS_SUMMARY_NEEDED; wfProfileOut(__METHOD__); return $status; } } # And a similar thing for new sections if ($this->section == 'new' && !$this->allowBlankSummary) { if (trim($this->summary) == '') { $this->missingSummary = true; $status->fatal('missingsummary'); // or 'missingcommentheader' if $section == 'new'. Blegh $status->value = self::AS_SUMMARY_NEEDED; wfProfileOut(__METHOD__); return $status; } } # All's well wfProfileIn(__METHOD__ . '-sectionanchor'); $sectionanchor = ''; if ($this->section == 'new') { if ($this->textbox1 == '') { $this->missingComment = true; $status->fatal('missingcommenttext'); $status->value = self::AS_TEXTBOX_EMPTY; wfProfileOut(__METHOD__ . '-sectionanchor'); wfProfileOut(__METHOD__); return $status; } if ($this->sectiontitle !== '') { $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText($this->sectiontitle); // If no edit summary was specified, create one automatically from the section // title and have it link to the new section. Otherwise, respect the summary as // passed. if ($this->summary === '') { $cleanSectionTitle = $wgParser->stripSectionName($this->sectiontitle); $this->summary = wfMsgForContent('newsectionsummary', $cleanSectionTitle); } } elseif ($this->summary !== '') { $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText($this->summary); # This is a new section, so create a link to the new section # in the revision summary. $cleanSummary = $wgParser->stripSectionName($this->summary); $this->summary = wfMsgForContent('newsectionsummary', $cleanSummary); } } elseif ($this->section != '') { # Try to get a section anchor from the section source, redirect to edited section if header found # XXX: might be better to integrate this into Article::replaceSection # for duplicate heading checking and maybe parsing $hasmatch = preg_match("/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1, $matches); # we can't deal with anchors, includes, html etc in the header for now, # headline would need to be parsed to improve this if ($hasmatch && strlen($matches[2]) > 0) { $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText($matches[2]); } } $result['sectionanchor'] = $sectionanchor; wfProfileOut(__METHOD__ . '-sectionanchor'); // Save errors may fall down to the edit form, but we've now // merged the section into full text. Clear the section field // so that later submission of conflict forms won't try to // replace that into a duplicated mess. $this->textbox1 = $text; $this->section = ''; $status->value = self::AS_SUCCESS_UPDATE; } // Check for length errors again now that the section is merged in $this->kblength = (int) (strlen($text) / 1024); if ($this->kblength > $wgMaxArticleSize) { $this->tooBig = true; $status->setResult(false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED); wfProfileOut(__METHOD__); return $status; } $flags = EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY | ($new ? EDIT_NEW : EDIT_UPDATE) | ($this->minoredit && !$this->isNew ? EDIT_MINOR : 0) | ($bot ? EDIT_FORCE_BOT : 0); $doEditStatus = $this->mArticle->doEdit($text, $this->summary, $flags); if ($doEditStatus->isOK()) { $result['redirect'] = Title::newFromRedirect($text) !== null; $this->commitWatch(); wfProfileOut(__METHOD__); return $status; } else { $this->isConflict = true; $doEditStatus->value = self::AS_END; // Destroys data doEdit() put in $status->value but who cares wfProfileOut(__METHOD__); return $doEditStatus; } }
/** * Constructs a RecentChange object for the given categorization * This does not call save() on the object and thus does not write to the db * * @since 1.27 * * @param string $timestamp Timestamp of the recent change to occur * @param Title $categoryTitle Title of the category a page is being added to or removed from * @param User $user User object of the user that made the change * @param string $comment Change summary * @param Title $pageTitle Title of the page that is being added or removed * @param int $oldRevId Parent revision ID of this change * @param int $newRevId Revision ID of this change * @param string $lastTimestamp Parent revision timestamp of this change * @param bool $bot true, if the change was made by a bot * @param string $ip IP address of the user, if the change was made anonymously * @param int $deleted Indicates whether the change has been deleted * * @return RecentChange */ public static function newForCategorization($timestamp, Title $categoryTitle, User $user = null, $comment, Title $pageTitle, $oldRevId, $newRevId, $lastTimestamp, $bot, $ip = '', $deleted = 0) { $rc = new RecentChange(); $rc->mTitle = $categoryTitle; $rc->mPerformer = $user; $rc->mAttribs = array('rc_timestamp' => $timestamp, 'rc_namespace' => $categoryTitle->getNamespace(), 'rc_title' => $categoryTitle->getDBkey(), 'rc_type' => RC_CATEGORIZE, 'rc_source' => self::SRC_CATEGORIZE, 'rc_minor' => 0, 'rc_cur_id' => $pageTitle->getArticleID(), 'rc_user' => $user ? $user->getId() : 0, 'rc_user_text' => $user ? $user->getName() : '', 'rc_comment' => $comment, 'rc_this_oldid' => $newRevId, 'rc_last_oldid' => $oldRevId, 'rc_bot' => $bot ? 1 : 0, 'rc_ip' => self::checkIPAddress($ip), 'rc_patrolled' => 1, 'rc_new' => 0, 'rc_old_len' => 0, 'rc_new_len' => 0, 'rc_deleted' => $deleted, 'rc_logid' => 0, 'rc_log_type' => null, 'rc_log_action' => '', 'rc_params' => ''); $rc->mExtra = array('prefixedDBkey' => $categoryTitle->getPrefixedDBkey(), 'lastTimestamp' => $lastTimestamp, 'oldSize' => 0, 'newSize' => 0, 'pageStatus' => 'changed'); return $rc; }
/** * Store layers of a page to database. This will remove all previous layers * of that page from the database first. * * @since 3.0 * * @param MapsLayerGroup $layerGroup contains all layers of the page. * @param Title $title the page title the layers are associated with. * * @return boolean */ public static function storeLayers(MapsLayerGroup $layerGroup, Title $title) { // clear cache for this one: unset(self::$layerGroups[$title->getPrefixedDBkey()]); /* * create data for multiple row insert: */ $pageId = $title->getArticleID(); foreach ($layerGroup->getLayers() as $layer) { $dbLayers[] = self::databaseRowFromLayer($layer, $pageId); } /* * insert all layer rows of the page into database: */ $db = wfGetDB(DB_MASTER); // delete old, stored layers first: $db->delete('maps_layers', array('layer_page_id' => $pageId), __METHOD__); if (empty($dbLayers)) { // empty group, nothing to insert return true; } else { // insert new rows: return $db->insert('maps_layers', $dbLayers, __METHOD__); } }
/** * Get geo data for articles from categories given title belongs to * * @param Title $title page title to get places from categories this title belongs to * @return array set of PlaceModel objects */ public function getFromCategoriesByTitle(Title $title) { wfProfileIn(__METHOD__); // get article categories $dbr = $this->getDB(); $res = $dbr->select('categorylinks', 'cl_to', array('cl_from' => $title->getArticleID()), __METHOD__); $categories = array(); while ($row = $res->fetchObject()) { $categories[] = $row->cl_to; } $models = $this->getFromCategories($categories); wfProfileOut(__METHOD__); return $models; }
/** * Get all non-hidden categories for a title. * * Kind of similar to title::getParentCategories. * * @param Title $title Which title to get the categories for. * @return Array of String's that are the (non-prefixed) db-keys of the cats. */ private function getVisibleCategories(Title $title) { $dbr = wfGetDB(DB_SLAVE); $where = array('cl_from' => $title->getArticleID(), 'pp_propname' => null); $joins = array('page' => array('LEFT OUTER JOIN', array('page_namespace' => NS_CATEGORY, 'page_title=cl_to')), 'page_props' => array('LEFT OUTER JOIN', array('pp_page=page_id', 'pp_propname' => 'hiddencat'))); $res = $dbr->select(array('categorylinks', 'page', 'page_props'), 'cl_to', $where, __METHOD__, array(), $joins); $finalResult = array(); if ($res !== false) { foreach ($res as $row) { $finalResult[] = $row->cl_to; } } return $finalResult; }