/** * setVarById * * used for saving new variable value, logging changes and update city_list * values * * Note that this function will return false and not do any updates if * wgWikicitiesReadOnly is true. * * @access public * @author eloy@wikia * @static * * @param integer $cv_variable_id variable id in city_variables_pool * @param integer $city_id wiki id in city list * @param mixed $value new value for variable * @param string $reason optional extra reason text * * @throws WikiFactoryDuplicateWgServer * @return boolean: transaction status */ public static function setVarById($cv_variable_id, $city_id, $value, $reason = null) { global $wgWikicitiesReadOnly; if (!self::isUsed()) { Wikia::log(__METHOD__, "", "WikiFactory is not used."); return false; } if ($wgWikicitiesReadOnly) { Wikia::log(__METHOD__, "", "wgWikicitiesReadOnly mode. Skipping update."); return false; } global $wgUser; if (empty($cv_variable_id) || empty($city_id)) { return; } wfProfileIn(__METHOD__); $dbw = self::db(DB_MASTER); $bStatus = true; $dbw->begin(); try { /** * use master connection for changing variables */ $variable = self::loadVariableFromDB($cv_variable_id, false, $city_id, true); $oldValue = isset($variable->cv_value) ? $variable->cv_value : false; /** * delete old value */ $dbw->delete("city_variables", ["cv_variable_id" => $cv_variable_id, "cv_city_id" => $city_id], __METHOD__); /** * insert new one */ $dbw->insert("city_variables", ["cv_variable_id" => $cv_variable_id, "cv_city_id" => $city_id, "cv_value" => serialize($value)], __METHOD__); wfProfileIn(__METHOD__ . "-changelog"); # if reason was passed non-null, prepare a string for sprintf, else a zero-len string $reason_extra = !empty($reason) ? " (reason: " . (string) $reason . ")" : ''; $needPreformat = in_array($variable->cv_variable_type, ['struct', 'array', 'hash', 'text']); if (isset($variable->cv_value)) { if (!$needPreformat) { $message = "Variable <strong>%s</strong> changed value from <strong>%s</strong> to <strong>%s</strong>%s"; } else { $message = '<div>Variable <strong>%s</strong> changed value </div>' . '<div class="v1"><strong>Old value:</strong><pre>%s</pre></div> ' . '<div class="v2"><strong>New value:</strong><pre>%s</pre></div>' . '<div class="clear">%s</div></div>'; } self::log(self::LOG_VARIABLE, sprintf($message, $variable->cv_name, var_export(unserialize($variable->cv_value), true), var_export($value, true), $reason_extra), $city_id, $cv_variable_id); } else { if (!$needPreformat) { $message = 'Variable <strong>%s</strong> set value: <strong>%s</strong> %s'; } else { $message = 'Variable <strong>%s</strong> set value: <pre>%s</pre> %s'; } self::log(self::LOG_VARIABLE, sprintf($message, $variable->cv_name, var_export($value, true), $reason_extra), $city_id, $cv_variable_id); } wfProfileOut(__METHOD__ . "-changelog"); /** * check if variable is connected with city_list (for example * city_language or city_url) and do some basic validation */ wfProfileIn(__METHOD__ . "-citylist"); wfRunHooks('WikiFactoryChanged', [$variable->cv_name, $city_id, $value]); switch ($variable->cv_name) { case "wgServer": case "wgScriptPath": /** * city_url is combination of $wgServer & $wgScriptPath */ /** * ...so get the other variable */ if ($variable->cv_name === "wgServer") { $tmp = self::getVarValueByName("wgScriptPath", $city_id); $server = is_null($value) ? "" : $value; $script_path = is_null($tmp) ? "/" : $tmp . "/"; } else { $tmp = self::getVarValueByName("wgServer", $city_id); $server = is_null($tmp) ? "" : $tmp; $script_path = is_null($value) ? "/" : $value . "/"; } $city_url = $server . $script_path; try { $dbw->update(self::table("city_list"), ["city_url" => $city_url], ["city_id" => $city_id], __METHOD__); } catch (DBQueryError $e) { if (preg_match("/Duplicate entry '[^']*' for key 'urlidx'/", $e->error)) { $res = $dbw->selectRow(self::table("city_list"), "city_id", ["city_url" => $city_url], __METHOD__); if (isset($res->city_id)) { $exc = new WikiFactoryDuplicateWgServer($city_id, $city_url, $res->city_id); Wikia::log(__METHOD__, "", $exc->getMessage()); $dbw->rollback(); throw $exc; } } throw $e; } break; case "wgLanguageCode": #--- city_lang $dbw->update(self::table("city_list"), ["city_lang" => $value], ["city_id" => $city_id], __METHOD__); #--- update language tags $tags = new WikiFactoryTags($city_id); $tags->removeTagsByName($oldValue); $tags->addTagsByName($value); break; case "wgSitename": #--- city_title $dbw->update(self::table("city_list"), ["city_title" => $value], ["city_id" => $city_id], __METHOD__); break; case "wgDBname": #--- city_dbname $dbw->update(self::table("city_list"), ["city_dbname" => $value], ["city_id" => $city_id], __METHOD__); break; case "wgDBcluster": /** * city_cluster * * city_cluster = null for first cluster * @todo handle deleting values of this variable */ $dbw->update(self::table("city_list"), ["city_cluster" => $value], ["city_id" => $city_id], __METHOD__); break; case 'wgMetaNamespace': case 'wgMetaNamespaceTalk': #--- these cannot contain spaces! if (strpos($value, ' ') !== false) { $value = str_replace(' ', '_', $value); $dbw->update(self::table('city_variables'), ['cv_value' => serialize($value)], ['cv_city_id' => $city_id, 'cv_variable_id' => $variable->cv_id], __METHOD__); } break; } wfProfileOut(__METHOD__ . "-citylist"); $dbw->commit(); $aHookParams = ['city_id' => $city_id, 'cv_name' => $variable->cv_name, 'cv_value' => $value]; wfRunHooks('WikiFactoryChangeCommitted', [$aHookParams]); } catch (DBQueryError $e) { Wikia::log(__METHOD__, "", "Database error, cannot write variable."); $dbw->rollback(); $bStatus = false; // rethrowing here does not seem to be right. Callers expect success or failure // as result value, not DBQueryError exception // throw $e; } self::clearCache($city_id); global $wgMemc; $wgMemc->delete(self::getVarValueKey($city_id, $variable->cv_id)); wfProfileOut(__METHOD__); return $bStatus; }
/** * setVarById * * used for saving new variable value, logging changes and update city_list * values * * Note that this function will return false and not do any updates if * wgWikicitiesReadOnly is true. * * @access public * @author eloy@wikia * @static * * @param integer $cv_variable_id variable id in city_variables_pool * @param integer $city_id wiki id in city list * @param mixed $value new value for variable * @param string $reason optional extra reason text * * @return boolean: transaction status */ public static function setVarById($cv_variable_id, $city_id, $value, $reason = null) { global $wgWikicitiesReadOnly; if (!self::isUsed()) { Wikia::log(__METHOD__, "", "WikiFactory is not used."); return false; } if ($wgWikicitiesReadOnly) { Wikia::log(__METHOD__, "", "wgWikicitiesReadOnly mode. Skipping update."); return false; } global $wgUser; if (empty($cv_variable_id) || empty($city_id)) { return; } wfProfileIn(__METHOD__); $dbw = self::db(DB_MASTER); $bStatus = true; $dbw->begin(); try { /** * use master connection for changing variables */ $variable = self::loadVariableFromDB($cv_variable_id, false, $city_id, true); $oldValue = isset($variable->cv_value) ? $variable->cv_value : false; /** * delete old value */ $dbw->delete("city_variables", array("cv_variable_id" => $cv_variable_id, "cv_city_id" => $city_id), __METHOD__); /** * insert new one */ $dbw->insert("city_variables", array("cv_variable_id" => $cv_variable_id, "cv_city_id" => $city_id, "cv_value" => serialize($value)), __METHOD__); wfProfileIn(__METHOD__ . "-changelog"); # if reason was passed non-null, prepare a string for sprintf, else a zero-len string $reason_extra = !empty($reason) ? " (reason: " . (string) $reason . ")" : ''; if (isset($variable->cv_value)) { self::log(self::LOG_VARIABLE, sprintf("Variable %s changed value from %s to %s%s", $variable->cv_name, var_export(unserialize($variable->cv_value), true), var_export($value, true), $reason_extra), $city_id, $cv_variable_id); } else { self::log(self::LOG_VARIABLE, sprintf("Variable %s set value: %s%s", $variable->cv_name, var_export($value, true), $reason_extra), $city_id, $cv_variable_id); } wfProfileOut(__METHOD__ . "-changelog"); /** * check if variable is connected with city_list (for example * city_language or city_url) and do some basic validation */ wfProfileIn(__METHOD__ . "-citylist"); wfRunHooks('WikiFactoryChanged', array($variable->cv_name, $city_id, $value)); switch ($variable->cv_name) { case "wgServer": case "wgScriptPath": /** * city_url is combination of $wgServer & $wgScriptPath */ /** * ...so get the other variable */ if ($variable->cv_name === "wgServer") { $tmp = self::getVarValueByName("wgScriptPath", $city_id); $server = is_null($value) ? "" : $value; $script_path = is_null($tmp) ? "/" : $tmp . "/"; } else { $tmp = self::getVarValueByName("wgServer", $city_id); $server = is_null($tmp) ? "" : $tmp; $script_path = is_null($value) ? "/" : $value . "/"; } $city_url = $server . $script_path; $dbw->update(self::table("city_list"), array("city_url" => $city_url), array("city_id" => $city_id), __METHOD__); /** * clear cache with old domain (stored in $oldValue) */ break; case "wgLanguageCode": #--- city_lang $dbw->update(self::table("city_list"), array("city_lang" => $value), array("city_id" => $city_id), __METHOD__); #--- update language tags $tags = new WikiFactoryTags($city_id); $tags->removeTagsByName($oldValue); $tags->addTagsByName($value); break; case "wgSitename": #--- city_title $dbw->update(self::table("city_list"), array("city_title" => $value), array("city_id" => $city_id), __METHOD__); break; case "wgDBname": #--- city_dbname $dbw->update(self::table("city_list"), array("city_dbname" => $value), array("city_id" => $city_id), __METHOD__); break; case "wgDBcluster": /** * city_cluster * * city_cluster = null for first cluster * @todo handle deleting values of this variable */ $dbw->update(self::table("city_list"), array("city_cluster" => $value), array("city_id" => $city_id), __METHOD__); break; case 'wgMetaNamespace': case 'wgMetaNamespaceTalk': #--- these cannot contain spaces! if (strpos($value, ' ') !== false) { $value = str_replace(' ', '_', $value); $dbw->update(self::table('city_variables'), array('cv_value' => serialize($value)), array('cv_city_id' => $city_id, 'cv_variable_id' => $variable->cv_id), __METHOD__); } break; } wfProfileOut(__METHOD__ . "-citylist"); $dbw->commit(); } catch (DBQueryError $e) { Wikia::log(__METHOD__, "", "Database error, cannot write variable."); $dbw->rollback(); $bStatus = false; throw $e; } self::clearCache($city_id); wfProfileOut(__METHOD__); return $bStatus; }