修改URL格式

master
落雨楓 3 weeks ago
parent 63fea40ae4
commit 0179fe81ed

@ -1,4 +1,5 @@
<?php <?php
namespace LatinizeUrl; namespace LatinizeUrl;
use Exception; use Exception;
@ -7,6 +8,8 @@ use Fukuball\Jieba\Finalseg;
use Fukuball\Jieba\Posseg; use Fukuball\Jieba\Posseg;
use MediaWiki\MediaWikiServices; use MediaWiki\MediaWikiServices;
use Overtrue\Pinyin\Pinyin; use Overtrue\Pinyin\Pinyin;
use MediaWiki\Status\Status;
use FormatJson;
class ChineseConvertor extends BaseConvertor { class ChineseConvertor extends BaseConvertor {
private $config; private $config;
@ -15,8 +18,8 @@ class ChineseConvertor extends BaseConvertor {
private static $jiebaLoaded = false; private static $jiebaLoaded = false;
private static $pinyinParser = null; private static $pinyinParser = null;
public static function standalone(){ public static function standalone() {
if(!self::$standalone){ if (!self::$standalone) {
$service = MediaWikiServices::getInstance(); $service = MediaWikiServices::getInstance();
$config = $service->getMainConfig(); $config = $service->getMainConfig();
@ -27,31 +30,31 @@ class ChineseConvertor extends BaseConvertor {
return self::$standalone; return self::$standalone;
} }
public static function onGetConvertor($langCode, &$convertor){ public static function onGetConvertor($langCode, &$convertor) {
if(in_array($langCode, ['zh-cn', 'zh-hans'])){ if (in_array($langCode, ['zh-cn', 'zh-hans'])) {
$convertor = self::standalone(); $convertor = self::standalone();
} }
return true; return true;
} }
public function __construct($config){ public function __construct($config) {
$this->config = $config; $this->config = $config;
} }
public function parse($hanzi){ public function parse($hanzi) {
$method = $this->config['parser'] . 'Parse'; $method = $this->config['parser'] . 'Parse';
if(is_callable([$this, $method])){ if (is_callable([$this, $method])) {
return call_user_func([$this, $method], $hanzi); return call_user_func([$this, $method], $hanzi);
} else { } else {
throw new Exception('Cannot find pinyin parser: ' . $this->config['parser']); throw new Exception('Cannot find pinyin parser: ' . $this->config['parser']);
} }
} }
private function filteJiebaTag($segList){ private function filteJiebaTag($segList) {
$ret = []; $ret = [];
foreach($segList as $seg){ foreach ($segList as $seg) {
if($seg['tag'] === 'uv' || $seg['tag'] === 'ud'){ //介词 if ($seg['tag'] === 'uv' || $seg['tag'] === 'ud') { //介词
$index = count($ret) - 1; $index = count($ret) - 1;
$ret[$index] .= '的'; $ret[$index] .= '的';
} else { } else {
@ -64,16 +67,16 @@ class ChineseConvertor extends BaseConvertor {
/** /**
* 使用php内部方法实现汉字转拼音 * 使用php内部方法实现汉字转拼音
*/ */
public function innerParse($hanzi){ public function innerParse($hanzi) {
$ret = []; $ret = [];
if(!self::$libLoaded){ if (!self::$libLoaded) {
require_once(dirname(__DIR__) . '/vendor/autoload.php'); require_once(dirname(__DIR__) . '/vendor/autoload.php');
self::$libLoaded = true; self::$libLoaded = true;
} }
$originalSentenceList = explode('/', $hanzi); $originalSentenceList = explode('/', $hanzi);
$sentenceList = []; $sentenceList = [];
if(isset($this->config['cutWord']) && $this->config['cutWord']){ //需要分词 if (isset($this->config['cutWord']) && $this->config['cutWord']) { //需要分词
if(!self::$jiebaLoaded){ if (!self::$jiebaLoaded) {
ini_set('memory_limit', '1024M'); ini_set('memory_limit', '1024M');
Jieba::init(['test' => true]); Jieba::init(['test' => true]);
Finalseg::init(); Finalseg::init();
@ -82,33 +85,35 @@ class ChineseConvertor extends BaseConvertor {
self::$jiebaLoaded = true; self::$jiebaLoaded = true;
} }
$length = count($originalSentenceList); $length = count($originalSentenceList);
for($i = 0; $i < $length; $i ++){ for ($i = 0; $i < $length; $i++) {
$sentence = $originalSentenceList[$i]; $sentence = $originalSentenceList[$i];
$sentenceList[] = $this->filteJiebaTag(Posseg::cut($sentence)); $sentenceList[] = $this->filteJiebaTag(Posseg::cut($sentence));
if($i + 1 < $length){ if ($i + 1 < $length) {
$sentenceList[] = '/'; $sentenceList[] = '/';
} }
} }
} else { } else {
$length = count($originalSentenceList); $length = count($originalSentenceList);
for($i = 0; $i < $length; $i ++){ for ($i = 0; $i < $length; $i++) {
$sentence = $originalSentenceList[$i]; $sentence = $originalSentenceList[$i];
$sentenceList[] = [$sentence]; $sentenceList[] = [$sentence];
if($i + 1 < $length){ if ($i + 1 < $length) {
$sentenceList[] = '/'; $sentenceList[] = '/';
} }
} }
} }
//分词后,进行拼音标注 //分词后,进行拼音标注
if(!self::$pinyinParser){ if (!self::$pinyinParser) {
self::$pinyinParser = new Pinyin(); self::$pinyinParser = new Pinyin();
} }
foreach($sentenceList as $segList){ foreach ($sentenceList as $segList) {
if(is_array($segList)){ if (is_array($segList)) {
$segPinyin = []; $segPinyin = [];
foreach($segList as $seg){ foreach ($segList as $seg) {
$segPinyin[] = self::$pinyinParser->convert($seg, $segPinyin[] = self::$pinyinParser->convert(
PINYIN_NO_TONE | PINYIN_UMLAUT_V | PINYIN_KEEP_PUNCTUATION | PINYIN_KEEP_ENGLISH | PINYIN_KEEP_NUMBER); $seg,
PINYIN_NO_TONE | PINYIN_UMLAUT_V | PINYIN_KEEP_PUNCTUATION | PINYIN_KEEP_ENGLISH | PINYIN_KEEP_NUMBER
);
} }
$ret[] = $segPinyin; $ret[] = $segPinyin;
} else { } else {
@ -121,11 +126,11 @@ class ChineseConvertor extends BaseConvertor {
/** /**
* 使用hook进行汉字转拼音 * 使用hook进行汉字转拼音
*/ */
public function hookParse($hanzi){ public function hookParse($hanzi) {
$pinyinList = null; $pinyinList = null;
MediaWikiServices::getInstance()->getHookContainer()->run('Pinyin2Hanzi', [$hanzi, &$pinyinList]); MediaWikiServices::getInstance()->getHookContainer()->run('Pinyin2Hanzi', [$hanzi, &$pinyinList]);
if(!$pinyinList){ if (!$pinyinList) {
if(isset($this->config['fallback'])){ if (isset($this->config['fallback'])) {
return $this->parse($hanzi, $this->config['fallback']); return $this->parse($hanzi, $this->config['fallback']);
} else { } else {
throw new Exception('Hook Pinyin2Hanzi never handled.'); throw new Exception('Hook Pinyin2Hanzi never handled.');
@ -133,16 +138,16 @@ class ChineseConvertor extends BaseConvertor {
} }
} }
private function fallbackOrException($hanzi, $message){ private function fallbackOrException($hanzi, $message) {
if(isset($this->config['fallback']) && $this->config['fallback'] != false){ if (isset($this->config['fallback']) && $this->config['fallback'] != false) {
return $this->parse($hanzi, $this->config['fallback']); return $this->parse($hanzi, $this->config['fallback']);
} else { } else {
throw new Exception($message); throw new Exception($message);
} }
} }
public function apiParse($hanzi){ public function apiParse($hanzi) {
if(!isset($this->config['url'])){ if (!isset($this->config['url'])) {
throw new Exception('LatinizeUrl remote api url not set.'); throw new Exception('LatinizeUrl remote api url not set.');
} }
$factory = MediaWikiServices::getInstance()->getHttpRequestFactory(); $factory = MediaWikiServices::getInstance()->getHttpRequestFactory();
@ -152,15 +157,15 @@ class ChineseConvertor extends BaseConvertor {
'sentence' => $hanzi 'sentence' => $hanzi
], ],
], __METHOD__); ], __METHOD__);
$status = \Status::wrap($req->execute()); $status = Status::wrap($req->execute());
if(!$status->isOK()){ if (!$status->isOK()) {
$this->fallbackOrException($hanzi, 'Cannot use LatinizeUrl remote api.'); $this->fallbackOrException($hanzi, 'Cannot use LatinizeUrl remote api.');
} }
$json = \FormatJson::decode($req->getContent(), true); $json = FormatJson::decode($req->getContent(), true);
if(isset($json["error"])){ if (isset($json["error"])) {
$this->fallbackOrException($hanzi, 'LatinizeUrl remote api error: ' . $json["error"]); $this->fallbackOrException($hanzi, 'LatinizeUrl remote api error: ' . $json["error"]);
} }
if(!isset($json["status"]) || $json["status"] !== 1){ if (!isset($json["status"]) || $json["status"] !== 1) {
$this->fallbackOrException($hanzi, 'Cannot use LatinizeUrl remote api.'); $this->fallbackOrException($hanzi, 'Cannot use LatinizeUrl remote api.');
} }
return $json["data"]; return $json["data"];

@ -1,16 +1,19 @@
<?php <?php
/* Only support api parse yet */ /* Only support api parse yet */
namespace LatinizeUrl; namespace LatinizeUrl;
use Exception; use Exception;
use MediaWiki\MediaWikiServices; use MediaWiki\MediaWikiServices;
use MediaWiki\Status\Status;
use FormatJson;
class JapaneseConvertor extends BaseConvertor { class JapaneseConvertor extends BaseConvertor {
private $config; private $config;
private static $standalone = null; private static $standalone = null;
public static function standalone(){ public static function standalone() {
if(!self::$standalone){ if (!self::$standalone) {
$service = MediaWikiServices::getInstance(); $service = MediaWikiServices::getInstance();
$config = $service->getMainConfig(); $config = $service->getMainConfig();
@ -21,19 +24,19 @@ class JapaneseConvertor extends BaseConvertor {
return self::$standalone; return self::$standalone;
} }
public static function onGetConvertor($langCode, &$convertor){ public static function onGetConvertor($langCode, &$convertor) {
if(in_array($langCode, ['ja', 'ja-jp'])){ if (in_array($langCode, ['ja', 'ja-jp'])) {
$convertor = self::standalone(); $convertor = self::standalone();
} }
return true; return true;
} }
public function __construct($config){ public function __construct($config) {
$this->config = $config; $this->config = $config;
} }
public function parse($kanji){ public function parse($kanji) {
if(!isset($this->config['url'])){ if (!isset($this->config['url'])) {
throw new Exception('LatinizeUrl remote api url not set.'); throw new Exception('LatinizeUrl remote api url not set.');
} }
$factory = MediaWikiServices::getInstance()->getHttpRequestFactory(); $factory = MediaWikiServices::getInstance()->getHttpRequestFactory();
@ -43,15 +46,15 @@ class JapaneseConvertor extends BaseConvertor {
'sentence' => $kanji 'sentence' => $kanji
], ],
], __METHOD__); ], __METHOD__);
$status = \Status::wrap($req->execute()); $status = Status::wrap($req->execute());
if(!$status->isOK()){ if (!$status->isOK()) {
throw new Exception('Cannot use LatinizeUrl remote api.'); throw new Exception('Cannot use LatinizeUrl remote api.');
} }
$json = \FormatJson::decode($req->getContent(), true); $json = FormatJson::decode($req->getContent(), true);
if(isset($json["error"])){ if (isset($json["error"])) {
throw new Exception('LatinizeUrl remote api error: ' . $json["error"]); throw new Exception('LatinizeUrl remote api error: ' . $json["error"]);
} }
if(!isset($json["status"]) || $json["status"] !== 1){ if (!isset($json["status"]) || $json["status"] !== 1) {
throw new Exception('Cannot use LatinizeUrl remote api.'); throw new Exception('Cannot use LatinizeUrl remote api.');
} }
return $json["data"]; return $json["data"];

@ -3,11 +3,11 @@
"author": "Hyperzlib", "author": "Hyperzlib",
"url": "https://github.com/Isekai-Project/mediawiki-extension-LatinizeUrl", "url": "https://github.com/Isekai-Project/mediawiki-extension-LatinizeUrl",
"descriptionmsg": "latinizeurl-desc", "descriptionmsg": "latinizeurl-desc",
"version": "1.0.4", "version": "1.0.5",
"license-name": "MIT", "license-name": "MIT",
"type": "other", "type": "other",
"requires": { "requires": {
"MediaWiki": ">= 1.39.0" "MediaWiki": ">= 1.43.0"
}, },
"ExtensionMessagesFiles": { "ExtensionMessagesFiles": {
"LatinizeUrlAlias": "LatinizeUrl.alias.php" "LatinizeUrlAlias": "LatinizeUrl.alias.php"
@ -32,12 +32,15 @@
"Collation::factory": [ "Collation::factory": [
"LatinizeUrl\\Hooks::onCollationFactory" "LatinizeUrl\\Hooks::onCollationFactory"
], ],
"SkinTemplateNavigation::Universal": [ "SidebarBeforeOutput": [
"LatinizeUrl\\Hooks::addToolboxLink" "LatinizeUrl\\Hooks::addToolboxLink"
], ],
"BeforePageDisplay": [ "BeforePageDisplay": [
"LatinizeUrl\\Hooks::onBeforePageDisplay" "LatinizeUrl\\Hooks::onBeforePageDisplay"
], ],
"BeforeInitialize": [
"LatinizeUrl\\Hooks::onBeforeInitialize"
],
"InitializeParseTitle": [ "InitializeParseTitle": [
"LatinizeUrl\\Hooks::onInitializeParseTitle" "LatinizeUrl\\Hooks::onInitializeParseTitle"
], ],

@ -1,4 +1,5 @@
<?php <?php
namespace LatinizeUrl; namespace LatinizeUrl;
abstract class BaseConvertor { abstract class BaseConvertor {

@ -1,37 +1,44 @@
<?php <?php
namespace LatinizeUrl; namespace LatinizeUrl;
use MediaWiki\Actions\ActionEntryPoint;
use MediaWiki\Linker\LinkTarget; use MediaWiki\Linker\LinkTarget;
use Title; use MediaWiki\Title\Title;
use User; use MediaWiki\SpecialPage\SpecialPage;
use MediaWiki\MediaWikiServices; use MediaWiki\MediaWikiServices;
use MediaWiki\Page\ProperPageIdentity; use MediaWiki\Page\ProperPageIdentity;
use MediaWiki\Permissions\Authority; use MediaWiki\Permissions\Authority;
use MediaWiki\User\UserIdentity; use MediaWiki\User\UserIdentity;
use TitleValue; use MediaWiki\Title\TitleValue;
use Wikimedia\Rdbms\DBQueryError; use Wikimedia\Rdbms\DBQueryError;
use MediaWiki\Output\OutputPage;
use MediaWiki\User\User;
use MediaWiki\Request\WebRequest;
use MediaWiki\Context\RequestContext;
use MediaWiki\Installer\DatabaseUpdater;
class Hooks { class Hooks {
public static $allowedNS = [NS_MAIN, NS_TALK]; public static $allowedNS = [NS_MAIN, NS_TALK];
public static function onLoadExtensionSchemaUpdates($updater){ public static function onLoadExtensionSchemaUpdates(DatabaseUpdater $updater) {
//更新数据库 //更新数据库
$dir = dirname(__DIR__) . '/sql'; $dir = dirname(__DIR__) . '/sql';
$dbType = $updater->getDB()->getType(); $dbType = $updater->getDB()->getType();
// For non-MySQL/MariaDB/SQLite DBMSes, use the appropriately named file // For non-MySQL/MariaDB/SQLite DBMSes, use the appropriately named file
if($dbType == 'mysql'){ if ($dbType == 'mysql') {
$filename = 'mysql.sql'; $updater->addExtensionTable('url_slug', "{$dir}/mysql.sql");
} elseif($dbType == 'sqlite'){ } elseif ($dbType == 'sqlite') {
$filename = 'sqlite.sql'; $updater->addExtensionTable('url_slug', "{$dir}/sqlite.sql");
} else { } else {
throw new \Exception('Database type not currently supported'); throw new \Exception('Database type not currently supported');
} }
$updater->addExtensionTable('url_slug', "{$dir}/{$filename}");
//更新文件patch //更新文件patch
global $IP; global $IP;
$patcher = new Patcher($IP . '/includes/MediaWiki.php', 'LatinizeUrl', Utils::getVersion()); $patcher = new Patcher($IP . '/includes/actions/ActionEntryPoint.php', 'LatinizeUrl', Utils::getVersion());
$patcher->patchInitializeParseTitleHook(); $patcher->patchInitializeParseTitleHook();
$patcher->save(); $patcher->save();
} }
@ -43,40 +50,57 @@ class Hooks {
$config = $service->getMainConfig(); $config = $service->getMainConfig();
$wgLatinizeUrlForceRedirect = $config->get('LatinizeUrlForceRedirect'); $wgLatinizeUrlForceRedirect = $config->get('LatinizeUrlForceRedirect');
if(in_array($title->getNamespace(), self::$allowedNS)){ $slugText = $title->getText();
$realTitle = Utils::getTitleBySlugUrl($title, $title->getNamespace());
if($realTitle){ if (in_array($title->getNamespace(), self::$allowedNS)) {
$realTitle = Utils::getTitleBySlugUrl($slugText, $title->getNamespace());
if ($realTitle) {
$title = $realTitle; $title = $realTitle;
$request->setVal('title', $title->getPrefixedDBkey()); $request->setVal('title', $title->getPrefixedDBkey());
} elseif($wgLatinizeUrlForceRedirect
if (
$wgLatinizeUrlForceRedirect
&& !($request->getVal('action') && $request->getVal('action') != 'view') && !($request->getVal('action') && $request->getVal('action') != 'view')
&& !$request->getVal('veaction') && !$request->getVal('veaction')
&& !defined('MW_API') && !defined('MW_API')
&& in_array($title->getNamespace(), self::$allowedNS)) { //把原标题页面重定向到拼音页面 && in_array($title->getNamespace(), self::$allowedNS)
$slug = Utils::getSlugUrlByTitle($title); ) { //把原标题页面重定向到拼音页面
if($slug) $title = Title::newFromText($slug, $title->getNamespace()); $absoluteSlug = Utils::getSlugUrlByTitle($title);
$slugText = str_replace(' ', '_', $slugText);
$absoluteSlug = str_replace(' ', '_', $absoluteSlug);
if ($slugText !== $absoluteSlug) {
$title = Title::newFromText($absoluteSlug, $title->getNamespace());
}
}
} }
} }
} }
public static function onGetArticleUrl(\Title &$title, &$url, $query){ public static function onBeforeInitialize(Title &$title, $unused, OutputPage $output, User $user, WebRequest $request, ActionEntryPoint $entryPoint) {
}
public static function onGetArticleUrl(Title &$title, &$url, $query) {
try { try {
if(in_array($title->getNamespace(), self::$allowedNS) && Utils::titleSlugExists($title)){ if (in_array($title->getNamespace(), self::$allowedNS) && Utils::titleSlugExists($title)) {
$slug = Title::newFromText(Utils::getSlugUrlByTitle($title), $title->getNamespace()); $slugText = Utils::getSlugUrlByTitle($title);
if ($slug) { if (!$slugText) return;
$slugEncoded = Utils::encodeUriComponent($slug->getPrefixedText());
$slugTitle = Title::newFromText($slugText, $title->getNamespace());
if (!$slugTitle) return;
$slugEncoded = Utils::encodeUriComponent($slugTitle->getPrefixedText());
$titleEncoded = Utils::encodeUriComponent($title->getPrefixedText()); $titleEncoded = Utils::encodeUriComponent($title->getPrefixedText());
$url = str_replace($titleEncoded, $slugEncoded, $url); $url = str_replace($titleEncoded, $slugEncoded, $url);
} }
} } catch (DBQueryError $ex) {
} catch(DBQueryError $ex){
} }
} }
public static function onPageDeleteComplete(ProperPageIdentity $page, Authority $deleter, $reason, $pageID, $deletedRev, $logEntry, $archivedRevisionCount) { public static function onPageDeleteComplete(ProperPageIdentity $page, Authority $deleter, $reason, $pageID, $deletedRev, $logEntry, $archivedRevisionCount) {
$title = TitleValue::newFromPage( $page ); $title = TitleValue::newFromPage($page);
if(in_array($title->getNamespace(), self::$allowedNS)){ //不是普通页面就跳过 if (in_array($title->getNamespace(), self::$allowedNS)) { //不是普通页面就跳过
Utils::removeTitleSlugMap($title->getText()); Utils::removeTitleSlugMap($title->getText());
} }
} }
@ -90,17 +114,21 @@ class Hooks {
* @param \MediaWiki\Revision\RevisionRecord $revisionRecord * @param \MediaWiki\Revision\RevisionRecord $revisionRecord
* @param \MediaWiki\Storage\EditResult $editResult * @param \MediaWiki\Storage\EditResult $editResult
*/ */
public static function onPageSaveComplete(&$wikiPage, $user, $summary, $flags, $revisionRecord, $editResult){ public static function onPageSaveComplete(&$wikiPage, $user, $summary, $flags, $revisionRecord, $editResult) {
if(!in_array($wikiPage->getTitle()->getNamespace(), self::$allowedNS)){ //不是普通页面就跳过 if (!in_array($wikiPage->getTitle()->getNamespace(), self::$allowedNS)) { //不是普通页面就跳过
return; return;
} }
if ($flags & EDIT_NEW) { if ($flags & EDIT_NEW) {
$title = $wikiPage->getTitle(); $title = $wikiPage->getTitle();
$parsedData = Utils::parseTitleToAscii($title, $title->getPageLanguage()); $parsedData = Utils::parseTitleToAscii($title, $title->getPageLanguage());
if ($parsedData) {
Utils::addTitleSlugMap($title->getText(), $parsedData['slug'], $parsedData['latinize']); Utils::addTitleSlugMap($title->getText(), $parsedData['slug'], $parsedData['latinize']);
} }
} }
}
public static function onPageMoveComplete(LinkTarget $old, LinkTarget $new, UserIdentity $userIdentity, $pageid, $redirid, $reason, $revision) { public static function onPageMoveComplete(LinkTarget $old, LinkTarget $new, UserIdentity $userIdentity, $pageid, $redirid, $reason, $revision) {
if (!in_array($new->getNamespace(), self::$allowedNS)) { //不是普通页面就跳过 if (!in_array($new->getNamespace(), self::$allowedNS)) { //不是普通页面就跳过
@ -108,23 +136,22 @@ class Hooks {
} }
$title = MediaWikiServices::getInstance()->getTitleFactory()->newFromLinkTarget($new); $title = MediaWikiServices::getInstance()->getTitleFactory()->newFromLinkTarget($new);
try {
$parsedData = Utils::parseTitleToAscii($title, $title->getPageLanguage()); $parsedData = Utils::parseTitleToAscii($title, $title->getPageLanguage());
Utils::addTitleSlugMap($title->getText(), $parsedData['slug'], $parsedData['latinize']);
} catch (\Exception $e) {
if ($parsedData) {
Utils::addTitleSlugMap($title->getText(), $parsedData['slug'], $parsedData['latinize']);
} }
} }
public static function onApiBeforeMain(\ApiBase &$processor){ public static function onApiBeforeMain(\ApiBase &$processor) {
$request = $processor->getRequest(); $request = $processor->getRequest();
$titles = $request->getVal('titles'); $titles = $request->getVal('titles');
if($titles){ if ($titles) {
$titles = explode('|', $titles); $titles = explode('|', $titles);
foreach($titles as $id => $title){ foreach ($titles as $id => $title) {
$title = Title::newFromText($title); $title = Title::newFromText($title);
$realTitle = Utils::getTitleBySlugUrl($title, $title->getNamespace()); $realTitle = Utils::getTitleBySlugUrl($title, $title->getNamespace());
if($realTitle){ if ($realTitle) {
$titles[$id] = $realTitle->getPrefixedText(); $titles[$id] = $realTitle->getPrefixedText();
} }
} }
@ -132,31 +159,31 @@ class Hooks {
} }
} }
public static function addToolboxLink(\Skin $skin, array &$links){ public static function addToolboxLink(\Skin $skin, array &$links) {
$service = MediaWikiServices::getInstance(); $service = MediaWikiServices::getInstance();
$user = $skin->getContext()->getUser(); $user = $skin->getContext()->getUser();
$title = $skin->getRelevantTitle(); $title = $skin->getRelevantTitle();
if(in_array($title->getNamespace(), self::$allowedNS)){ if (in_array($title->getNamespace(), self::$allowedNS)) {
if($service->getPermissionManager()->userHasRight($user, 'delete') || Utils::hasUserEditedPage($title, $user)){ if ($service->getPermissionManager()->userHasRight($user, 'delete') || Utils::hasUserEditedPage($title, $user)) {
$links['page-secondary']['custom-url'] = [ $links['TOOLBOX']['custom-url'] = [
'class' => false, 'class' => false,
'text' => wfMessage('latinizeurl-customurl')->text(), 'text' => wfMessage('latinizeurl-customurl')->text(),
'href' => \SpecialPage::getTitleFor('CustomUrl', $title->getPrefixedDBKey())->getLocalURL(), 'href' => SpecialPage::getTitleFor('CustomUrl', $title->getPrefixedDBKey())->getLocalURL(),
'id' => 'ca-custom-url', 'id' => 'ca-custom-url',
]; ];
} }
} }
} }
public static function onBeforePageDisplay($out){ public static function onBeforePageDisplay($out) {
if($out->getSkin()->getSkinName() == 'timeless'){ if ($out->getSkin()->getSkinName() == 'timeless') {
$out->addModules('ext.latinizeurl.timeless'); $out->addModules('ext.latinizeurl.timeless');
} }
} }
public static function onCollationFactory($collationName, &$collationObject){ public static function onCollationFactory($collationName, &$collationObject) {
if($collationName == 'latinize'){ if ($collationName == 'latinize') {
$collationObject = new LatinizeCollation(); $collationObject = new LatinizeCollation();
} }
return true; return true;

@ -1,4 +1,5 @@
<?php <?php
namespace LatinizeUrl; namespace LatinizeUrl;
use Collation; use Collation;
@ -7,15 +8,15 @@ use MediaWiki\MediaWikiServices;
class LatinizeCollation extends Collation { class LatinizeCollation extends Collation {
private $cache; private $cache;
public function __construct(){ public function __construct() {
$this->cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); $this->cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
} }
private function getLatinize($string){ private function getLatinize($string) {
return $this->cache->getWithSetCallback( return $this->cache->getWithSetCallback(
$this->cache->makeKey('latinizeConvert', $string), $this->cache->makeKey('latinizeConvert', $string),
$this->cache::TTL_MINUTE * 10, $this->cache::TTL_MINUTE * 10,
function() use($string){ function () use ($string) {
$convertor = Utils::getConvertor(); $convertor = Utils::getConvertor();
$latinize = $convertor->parse($string); $latinize = $convertor->parse($string);
return Utils::wordListToUrl($latinize); return Utils::wordListToUrl($latinize);
@ -23,29 +24,41 @@ class LatinizeCollation extends Collation {
); );
} }
public function getSortKey($string){ public function getSortKey($string) {
if(defined('MW_UPDATER')){ if (defined('MW_UPDATER')) {
return $string; return $string;
} }
$slug = Utils::getSlugByTitle($string); $slug = Utils::getSlugByTitle($string);
if($slug){ if ($slug) {
return ucfirst($slug); return ucfirst($slug);
} else { } else {
return $this->getLatinize($string); $latinize = $this->getLatinize($string);
if ($latinize) {
return $latinize;
}
return ucfirst($slug);
} }
} }
public function getFirstLetter($string){ public function getFirstLetter($string) {
if(defined('MW_UPDATER')){ if (defined('MW_UPDATER')) {
return mb_substr(0, 1, $string, 'UTF-8'); return mb_substr(0, 1, $string, 'UTF-8');
} }
$slug = Utils::getSlugByTitle($string); $slug = Utils::getSlugByTitle($string);
if($slug){ if ($slug) {
return strtoupper($slug[0]); return strtoupper($slug[0]);
} else { } else {
return strtoupper(mb_substr($this->getLatinize($string), 0, 1, 'UTF-8')); $latinize = $this->getLatinize($string);
if ($latinize) {
return strtoupper(mb_substr($latinize, 0, 1, 'UTF-8'));
}
} }
return mb_substr(0, 1, $string, 'UTF-8');
} }
} }

@ -1,4 +1,5 @@
<?php <?php
namespace LatinizeUrl; namespace LatinizeUrl;
class Patcher { class Patcher {
@ -7,16 +8,16 @@ class Patcher {
private $tag; private $tag;
private $version; private $version;
public function __construct($file, $tag, $version){ public function __construct($file, $tag, $version) {
$this->file = $file; $this->file = $file;
$this->content = file_get_contents($file); $this->content = file_get_contents($file);
$this->tag = $tag; $this->tag = $tag;
$this->version = $version; $this->version = $version;
} }
public function findPatchVersion($name){ public function findPatchVersion($name) {
$regex = '/\/\/ Start ' . $this->tag . ' ([0-9.\-]+) ' . $name . ' Patch\n.*?\/\/ End ' . $this->tag . ' [0-9.\-]+ ' . $name . ' Patch/is'; $regex = '/\/\/ Start ' . $this->tag . ' ([0-9.\-]+) ' . $name . ' Patch\n.*?\/\/ End ' . $this->tag . ' [0-9.\-]+ ' . $name . ' Patch/is';
if(preg_match($regex, $this->content, $matches, PREG_OFFSET_CAPTURE)){ if (preg_match($regex, $this->content, $matches, PREG_OFFSET_CAPTURE)) {
$ret = []; $ret = [];
$ret['start'] = $matches[0][1]; $ret['start'] = $matches[0][1];
$ret['end'] = $ret['start'] + strlen($matches[0][0]); $ret['end'] = $ret['start'] + strlen($matches[0][0]);
@ -27,20 +28,20 @@ class Patcher {
} }
} }
public function patchInitializeParseTitleHook(){ public function patchInitializeParseTitleHook() {
$patchName = 'InitializeParseTitleHook'; $patchName = 'InitializeParseTitleHook';
$patchContent = ['MediaWikiServices::getInstance()->getHookContainer()->run( \'InitializeParseTitle\', [ &$ret, $request ] );']; $patchContent = ['\MediaWiki\MediaWikiServices::getInstance()->getHookContainer()->run( \'InitializeParseTitle\', [ &$ret, $request ] );'];
$patchFinalContent = $this->makePatchContent($patchName, $patchContent, 2); $patchFinalContent = $this->makePatchContent($patchName, $patchContent, 2);
$currentPatch = $this->findPatchVersion($patchName); $currentPatch = $this->findPatchVersion($patchName);
if ($currentPatch) { if ($currentPatch) {
if($currentPatch['version'] != $this->version){ //需要更新 if ($currentPatch['version'] != $this->version) { //需要更新
$this->content = substr($this->content, 0, $currentPatch['start'] - 2) $this->content = substr($this->content, 0, $currentPatch['start'] - 2)
. $patchFinalContent . $patchFinalContent
. substr($this->content, $currentPatch['end'] + 1); . substr($this->content, $currentPatch['end'] + 1);
} }
} else { //打新的补丁 } else { //打新的补丁
$regex = '/(?!private function parseTitle\(\) \{(.*?))(?=[\t ]+return \$ret;)/is'; $regex = '/(?!protected function parseTitle\(\) \{(.*?))(?=[\t ]+return \$ret;)/is';
if(preg_match($regex, $this->content, $matches, PREG_OFFSET_CAPTURE)){ if (preg_match($regex, $this->content, $matches, PREG_OFFSET_CAPTURE)) {
$splitPos = $matches[0][1]; $splitPos = $matches[0][1];
$this->content = substr($this->content, 0, $splitPos) $this->content = substr($this->content, 0, $splitPos)
. $patchFinalContent . $patchFinalContent
@ -49,8 +50,8 @@ class Patcher {
} }
} }
public function makePatchContent($name, $content, $indent = 0, $indentChar = "\t"){ public function makePatchContent($name, $content, $indent = 0, $indentChar = "\t") {
if(!is_array($content)) $content = explode("\n", $content); if (!is_array($content)) $content = explode("\n", $content);
$lines = array_merge([ $lines = array_merge([
'// Start ' . $this->tag . ' ' . $this->version . ' ' . $name . ' Patch', '// Start ' . $this->tag . ' ' . $this->version . ' ' . $name . ' Patch',
'// This code is added by ' . $this->tag . ' extension, Do not remove this code until you uninstall ' . $this->tag . ' extension.', '// This code is added by ' . $this->tag . ' extension, Do not remove this code until you uninstall ' . $this->tag . ' extension.',
@ -58,14 +59,14 @@ class Patcher {
'// End ' . $this->tag . ' ' . $this->version . ' ' . $name . ' Patch', '// End ' . $this->tag . ' ' . $this->version . ' ' . $name . ' Patch',
]); ]);
$contentText = ''; $contentText = '';
foreach($lines as $line){ foreach ($lines as $line) {
$contentText .= str_repeat($indentChar, $indent) . $line . "\n"; $contentText .= str_repeat($indentChar, $indent) . $line . "\n";
} }
return $contentText; return $contentText;
} }
public function save($file = null){ public function save($file = null) {
if(!$file) $file = $this->file; if (!$file) $file = $this->file;
file_put_contents($file, $this->content); file_put_contents($file, $this->content);
} }
} }

@ -1,43 +1,42 @@
<?php <?php
namespace LatinizeUrl; namespace LatinizeUrl;
use Article;
use Exception; use Exception;
use ExtensionRegistry; use MediaWiki\Title\Title;
use Title; use MediaWiki\User\User;
use User;
use Language; use Language;
use MediaWiki\Extension\AbuseFilter\Consequences\Consequence\Tag;
use MediaWiki\MediaWikiServices; use MediaWiki\MediaWikiServices;
use StubUserLang; use StubUserLang;
use MediaWiki\Registration\ExtensionRegistry;
class Utils { class Utils {
private static $dbr = null; private static $dbr = null;
private static $dbw = null; private static $dbw = null;
private static $cache = null; private static $cache = null;
public static function initMasterDb(){ public static function initMasterDb() {
if(!self::$dbw){ if (!self::$dbw) {
self::$dbw = MediaWikiServices::getInstance()->getDBLoadBalancer() self::$dbw = MediaWikiServices::getInstance()->getDBLoadBalancer()
->getMaintenanceConnectionRef(DB_PRIMARY); ->getMaintenanceConnectionRef(DB_PRIMARY);
} }
} }
public static function initReplicaDb(){ public static function initReplicaDb() {
if(!self::$dbr){ if (!self::$dbr) {
self::$dbr = MediaWikiServices::getInstance()->getDBLoadBalancer() self::$dbr = MediaWikiServices::getInstance()->getDBLoadBalancer()
->getMaintenanceConnectionRef(DB_REPLICA); ->getMaintenanceConnectionRef(DB_REPLICA);
} }
} }
public static function initCache(){ public static function initCache() {
if(!self::$cache){ if (!self::$cache) {
self::$cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); self::$cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
} }
} }
public static function slugExists($slug, $excludeUrl = null){ public static function slugExists($slug, $excludeUrl = null) {
if($excludeUrl){ if ($excludeUrl) {
self::initReplicaDb(); self::initReplicaDb();
$cond = [ $cond = [
@ -52,50 +51,67 @@ class Utils {
} }
} }
public static function slugUrlExists($url){ public static function slugUrlExists($url) {
return self::getTitleTextBySlugUrl($url) !== false; return self::getTitleTextBySlugUrl($url) !== false;
} }
public static function titleSlugExists($title){ public static function titleSlugExists($title) {
return self::getSlugByTitle($title) !== false; return self::getSlugByTitle($title) !== false;
} }
public static function getTitleBySlug($slug, $namespace = NS_MAIN){ public static function getTitleBySlug($slug, $namespace = NS_MAIN) {
if($slug instanceof Title){ if ($slug instanceof Title) {
$namespace = $slug->getNamespace(); $namespace = $slug->getNamespace();
$slug = $slug->getText(); $slug = $slug->getText();
} }
$titleText = self::getTitleTextBySlug($slug); $titleText = self::getTitleTextBySlug($slug);
if($titleText){ if ($titleText) {
return Title::newFromText($titleText, $namespace); return Title::newFromText($titleText, $namespace);
} else { } else {
return false; return false;
} }
} }
public static function getTitleBySlugUrl($url, $namespace = NS_MAIN){ /**
if($url instanceof Title){ * 从URL中获取标题
* @param Title|string $url
* @param int $namespace
*/
public static function getTitleBySlugUrl($url, $namespace = NS_MAIN) {
if ($url instanceof Title) {
$namespace = $url->getNamespace(); $namespace = $url->getNamespace();
$url = $url->getText(); $url = $url->getText();
} }
$wikiPageFactory = MediaWikiServices::getInstance()->getWikiPageFactory();
// 新版在URL中加了pageId
if (preg_match('/^(\d+?)~/', $url, $matches)) {
$pageId = intval($matches[1]);
$wikiPage = $wikiPageFactory->newFromID($pageId);
if ($wikiPage) {
return $wikiPage->getTitle();
}
}
// 旧版URL
$titleText = self::getTitleTextBySlugUrl($url); $titleText = self::getTitleTextBySlugUrl($url);
if($titleText){ if ($titleText) {
return Title::newFromText($titleText, $namespace); return Title::newFromText($titleText, $namespace);
} else { } else {
return false; return false;
} }
} }
public static function getTitleTextBySlug($slug){ public static function getTitleTextBySlug($slug) {
self::initCache(); self::initCache();
self::initReplicaDb(); self::initReplicaDb();
return self::$cache->getWithSetCallback( return self::$cache->getWithSetCallback(
self::$cache->makeKey('slug2title', $slug), self::$cache->makeKey('slug2title', $slug),
self::$cache::TTL_MINUTE * 10, self::$cache::TTL_MINUTE * 10,
function() use($slug){ function () use ($slug) {
$res = self::$dbr->select('url_slug', ['title'], [ $res = self::$dbr->select('url_slug', ['title'], [
'slug' => $slug, 'slug' => $slug,
], __METHOD__, [ ], __METHOD__, [
@ -111,20 +127,20 @@ class Utils {
); );
} }
public static function getTitleTextBySlugUrl($url){ public static function getTitleTextBySlugUrl($url) {
self::initCache(); self::initCache();
self::initReplicaDb(); self::initReplicaDb();
return self::$cache->getWithSetCallback( return self::$cache->getWithSetCallback(
self::$cache->makeKey('slugurl2title', $url), self::$cache->makeKey('slugurl2title', $url),
self::$cache::TTL_MINUTE * 10, self::$cache::TTL_MINUTE * 10,
function() use($url){ function () use ($url) {
$res = self::$dbr->select('url_slug', ['title'], [ $res = self::$dbr->select('url_slug', ['title'], [
'url' => $url, 'url' => $url,
], __METHOD__, [ ], __METHOD__, [
'LIMIT' => 1, 'LIMIT' => 1,
]); ]);
if($res->numRows() > 0){ if ($res->numRows() > 0) {
$data = $res->fetchRow(); $data = $res->fetchRow();
return $data['title']; return $data['title'];
} else { } else {
@ -134,8 +150,8 @@ class Utils {
); );
} }
public static function getSlugByTitle($title){ public static function getSlugByTitle($title) {
if($title instanceof Title){ if ($title instanceof Title) {
$title = $title->getText(); $title = $title->getText();
} }
@ -145,13 +161,13 @@ class Utils {
return self::$cache->getWithSetCallback( return self::$cache->getWithSetCallback(
self::$cache->makeKey('title2slug', $title), self::$cache->makeKey('title2slug', $title),
self::$cache::TTL_MINUTE * 10, self::$cache::TTL_MINUTE * 10,
function() use($title){ function () use ($title) {
$res = self::$dbr->select('url_slug', ['slug'], [ $res = self::$dbr->select('url_slug', ['slug'], [
'title' => $title, 'title' => $title,
], __METHOD__, [ ], __METHOD__, [
'LIMIT' => 1, 'LIMIT' => 1,
]); ]);
if($res->numRows() > 0){ if ($res->numRows() > 0) {
$data = $res->fetchRow(); $data = $res->fetchRow();
return $data['slug']; return $data['slug'];
} else { } else {
@ -161,26 +177,33 @@ class Utils {
); );
} }
public static function getSlugUrlByTitle($title){ public static function getSlugUrlByTitleWithoutId($title) {
if($title instanceof Title){ if ($title instanceof Title) {
$title = $title->getText(); $titleText = $title->getText();
} else {
$titleText = $title;
$title = Title::newFromText($title);
} }
self::initCache(); self::initCache();
self::initReplicaDb(); self::initReplicaDb();
return self::$cache->getWithSetCallback( return self::$cache->getWithSetCallback(
self::$cache->makeKey('title2slugurl', $title), self::$cache->makeKey('title2slugurlorig', $titleText),
self::$cache::TTL_MINUTE * 10, self::$cache::TTL_MINUTE * 10,
function() use($title){ function () use ($title, $titleText) {
$res = self::$dbr->select('url_slug', ['url'], [ $res = self::$dbr->select('url_slug', ['url'], [
'title' => $title, 'title' => $title,
], __METHOD__, [ ], __METHOD__, [
'LIMIT' => 1, 'LIMIT' => 1,
]); ]);
if($res->numRows() > 0){
if ($res->numRows() > 0) {
$data = $res->fetchRow(); $data = $res->fetchRow();
return $data['url'];
$url = $data['url'];
return $url;
} else { } else {
return false; return false;
} }
@ -188,8 +211,38 @@ class Utils {
); );
} }
public static function getSlugDataByTitle($title){ public static function getSlugUrlByTitle($title) {
if($title instanceof Title){ if (is_string($title)) {
$title = Title::newFromText($title);
}
if (!$title) return false;
return self::$cache->getWithSetCallback(
self::$cache->makeKey('title2slugurl', $title->getText()),
self::$cache::TTL_MINUTE * 10,
function () use ($title) {
$slugUrl = self::getSlugUrlByTitleWithoutId($title);
if (!$slugUrl) return false;
$wikiPageFactory = MediaWikiServices::getInstance()->getWikiPageFactory();
if (!$title->isMainPage()) {
$wikiPage = $wikiPageFactory->newFromTitle($title);
if ($wikiPage) {
$pageId = $wikiPage->getId();
$slugUrl = "{$pageId}~" . $slugUrl;
}
}
return $slugUrl;
}
);
}
public static function getSlugDataByTitle($title) {
if ($title instanceof Title) {
$title = $title->getText(); $title = $title->getText();
} }
@ -201,7 +254,7 @@ class Utils {
], __METHOD__, [ ], __METHOD__, [
'LIMIT' => 1, 'LIMIT' => 1,
]); ]);
if($res->numRows() > 0){ if ($res->numRows() > 0) {
$data = $res->fetchRow(); $data = $res->fetchRow();
return $data; return $data;
} else { } else {
@ -209,15 +262,15 @@ class Utils {
} }
} }
public static function addTitleSlugMap($title, $slug, $latinize = [], $custom = 0){ public static function addTitleSlugMap($title, $slug, $latinize = [], $custom = 0) {
if(self::titleSlugExists($title)){ if (self::titleSlugExists($title)) {
throw new Exception("Title slug map already exists: " . $title); throw new Exception("Title slug map already exists: " . $title);
} }
self::initMasterDb(); self::initMasterDb();
$exists = self::slugExists($slug); $exists = self::slugExists($slug);
if($exists){ if ($exists) {
$url = $slug . '-id'; $url = $slug . '-id';
} else { } else {
$url = $slug; $url = $slug;
@ -232,7 +285,7 @@ class Utils {
'latinize' => json_encode($latinize), 'latinize' => json_encode($latinize),
), __METHOD__); ), __METHOD__);
$lastId = self::$dbw->insertId(); $lastId = self::$dbw->insertId();
if($exists){ if ($exists) {
$url = $slug . '-' . $lastId; $url = $slug . '-' . $lastId;
self::$dbw->update('url_slug', [ self::$dbw->update('url_slug', [
'url' => $url, 'url' => $url,
@ -243,8 +296,8 @@ class Utils {
return $url; return $url;
} }
public static function updateTitleSlugMap($title, $slug, $latinize = [], $custom = 0){ public static function updateTitleSlugMap($title, $slug, $latinize = [], $custom = 0) {
if(!self::titleSlugExists($title)){ if (!self::titleSlugExists($title)) {
throw new Exception("Title slug map not exists: " . $title); throw new Exception("Title slug map not exists: " . $title);
} }
self::initMasterDb(); self::initMasterDb();
@ -258,10 +311,10 @@ class Utils {
$oldSlug = $res->slug; $oldSlug = $res->slug;
$oldUrl = $res->url; $oldUrl = $res->url;
if($oldSlug == $slug) return $oldUrl; if ($oldSlug == $slug) return $oldUrl;
$exists = self::slugExists($slug, $slug); $exists = self::slugExists($slug, $slug);
if($exists){ if ($exists) {
$url = $slug . '-' . strval($mapId); $url = $slug . '-' . strval($mapId);
} else { } else {
$url = $slug; $url = $slug;
@ -273,7 +326,7 @@ class Utils {
'show_id' => $exists ? 1 : 0, 'show_id' => $exists ? 1 : 0,
'is_custom' => $custom, 'is_custom' => $custom,
]; ];
if(!empty($latinize)){ if (!empty($latinize)) {
$data['latinize'] = json_encode($latinize); $data['latinize'] = json_encode($latinize);
} }
@ -288,18 +341,18 @@ class Utils {
return $url; return $url;
} }
public static function replaceTitleSlugMap($title, $slug, $latinize = [], $custom = 0){ public static function replaceTitleSlugMap($title, $slug, $latinize = [], $custom = 0) {
if(self::titleSlugExists($title)){ if (self::titleSlugExists($title)) {
return self::updateTitleSlugMap($title, $slug, $latinize, $custom); return self::updateTitleSlugMap($title, $slug, $latinize, $custom);
} else { } else {
return self::addTitleSlugMap($title, $slug, $latinize, $custom); return self::addTitleSlugMap($title, $slug, $latinize, $custom);
} }
} }
public static function removeTitleSlugMap($title){ public static function removeTitleSlugMap($title) {
self::initMasterDb(); self::initMasterDb();
if(self::titleSlugExists($title)){ if (self::titleSlugExists($title)) {
$oldData = self::$dbr->selectRow('url_slug', ['slug', 'url'], [ $oldData = self::$dbr->selectRow('url_slug', ['slug', 'url'], [
'title' => $title, 'title' => $title,
], __METHOD__); ], __METHOD__);
@ -318,12 +371,14 @@ class Utils {
} }
} }
public static function hasUserEditedPage(Title $title, User $user){ public static function hasUserEditedPage(Title $title, User $user) {
if($user->isAnon()) return false; if ($user->isAnon()) return false;
if(!$title->exists()) return false; if (!$title->exists()) return false;
$article = Article::newFromID($title->getArticleID());
if($article == null) return false; $wikiPageFactory = MediaWikiServices::getInstance()->getWikiPageFactory();
$wikiPage = Article::newFromID($title->getArticleID())->getPage();
$wikiPage = $wikiPageFactory->newFromTitle($title);
if (!$wikiPage) return false;
$contributors = $wikiPage->getContributors(); $contributors = $wikiPage->getContributors();
foreach ($contributors as $contributor) { foreach ($contributors as $contributor) {
if ($contributor->equals($user)) { if ($contributor->equals($user)) {
@ -367,27 +422,28 @@ class Utils {
* @param Language|StubUserLang|string|null $language - 语言 * @param Language|StubUserLang|string|null $language - 语言
* @return mixed 转换器 * @return mixed 转换器
*/ */
public static function parseTitleToAscii(Title $title, Language $language){ public static function parseTitleToAscii(Title $title, Language $language) {
try {
$convertor = self::getConvertor($language); $convertor = self::getConvertor($language);
if($title->isSubpage()){ if ($title->isSubpage()) {
//处理子页面,按照页面拆分 //处理子页面,按照页面拆分
$titlePathList = explode('/', $title->getText()); $titlePathList = explode('/', $title->getText());
$titlePathLen = count($titlePathList); $titlePathLen = count($titlePathList);
$unparsed = $title->getText(); $unparsed = $title->getText();
$baseSlug = false; $baseSlug = false;
for($i = $titlePathLen - 2; $i >= 0; $i --){ for ($i = $titlePathLen - 2; $i >= 0; $i--) {
$titleSubPath = implode('/', array_slice($titlePathList, 0, $i + 1)); $titleSubPath = implode('/', array_slice($titlePathList, 0, $i + 1));
$baseTitle = Title::newFromText($titleSubPath, $title->getNamespace()); $baseTitle = Title::newFromText($titleSubPath, $title->getNamespace());
$baseSlug = self::getSlugUrlByTitle($baseTitle); $baseSlug = self::getSlugUrlByTitle($baseTitle);
if($baseSlug){ if ($baseSlug) {
$unparsed = implode('/', array_slice($titlePathList, $i + 1)); $unparsed = implode('/', array_slice($titlePathList, $i + 1));
break; break;
} }
} }
$parsed = $convertor->parse($unparsed); $parsed = $convertor->parse($unparsed);
if($parsed){ if ($parsed) {
$parsedSlug = self::wordListToUrl($parsed); $parsedSlug = self::wordListToUrl($parsed);
if($baseSlug){ if ($baseSlug) {
return [ return [
'slug' => $baseSlug . '/' . $parsedSlug, 'slug' => $baseSlug . '/' . $parsedSlug,
'latinize' => array_merge([$baseSlug, '/'], $parsed), 'latinize' => array_merge([$baseSlug, '/'], $parsed),
@ -403,7 +459,7 @@ class Utils {
} }
} else { } else {
$parsed = $convertor->parse($title->getText()); $parsed = $convertor->parse($title->getText());
if($parsed){ if ($parsed) {
$parsedSlug = self::wordListToUrl($parsed); $parsedSlug = self::wordListToUrl($parsed);
return [ return [
'slug' => $parsedSlug, 'slug' => $parsedSlug,
@ -413,21 +469,30 @@ class Utils {
return false; return false;
} }
} }
} catch (Exception $ex) {
wfLogWarning('Cannot parse title to ascii: ' . $ex->getMessage());
wfLogWarning($ex->getTraceAsString(), E_USER_ERROR);
return false;
}
} }
public static function getVersion(){ public static function getVersion() {
return ExtensionRegistry::getInstance()->getAllThings()['LatinizeUrl']['version']; return ExtensionRegistry::getInstance()->getAllThings()['LatinizeUrl']['version'];
} }
public static function wordListToUrl($sentenceList){ /**
* @param string[] $sentenceList
* @return string
*/
public static function wordListToUrl($sentenceList) {
$strBuilder = []; $strBuilder = [];
foreach($sentenceList as $pinyinList){ foreach ($sentenceList as $pinyinList) {
if(is_array($pinyinList)){ if (is_array($pinyinList)) {
$segStrBuilder = []; $segStrBuilder = [];
foreach($pinyinList as $pinyinGroup){ foreach ($pinyinList as $pinyinGroup) {
if(is_array($pinyinGroup)){ if (is_array($pinyinGroup)) {
$groupStrBuilder = []; $groupStrBuilder = [];
foreach($pinyinGroup as $pinyin){ foreach ($pinyinGroup as $pinyin) {
$groupStrBuilder[] = ucfirst($pinyin); $groupStrBuilder[] = ucfirst($pinyin);
} }
$segStrBuilder[] = implode('', $groupStrBuilder); $segStrBuilder[] = implode('', $groupStrBuilder);

@ -1,10 +1,14 @@
<?php <?php
namespace LatinizeUrl; namespace LatinizeUrl;
use UnlistedSpecialPage;
use Html;
use MediaWiki\MediaWikiServices; use MediaWiki\MediaWikiServices;
use MediaWiki\SpecialPage\UnlistedSpecialPage;
use MediaWiki\Html\Html;
use MediaWiki\Session\CsrfTokenSet; use MediaWiki\Session\CsrfTokenSet;
use MediaWiki\Tests\Session\CsrfTokenSetTest;
use MediaWiki\Title\Title;
use MediaWiki\Title\TitleArrayFromResult;
use ThrottledError; use ThrottledError;
class SpecialCustomUrl extends UnlistedSpecialPage { class SpecialCustomUrl extends UnlistedSpecialPage {
@ -14,7 +18,7 @@ class SpecialCustomUrl extends UnlistedSpecialPage {
protected $target; protected $target;
/** /**
* @var \Title * @var Title
*/ */
protected $title; protected $title;
@ -54,21 +58,22 @@ class SpecialCustomUrl extends UnlistedSpecialPage {
$this->outputHeader(); $this->outputHeader();
$service = MediaWikiServices::getInstance(); $service = MediaWikiServices::getInstance();
$request = $this->getRequest(); $request = $this->getRequest();
$this->target = $par ?? $request->getText('target'); $this->target = $par ?? $request->getText('target');
$title = \Title::newFromText($this->target); $title = Title::newFromText($this->target);
$this->title = $title; $this->title = $title;
$this->getSkin()->setRelevantTitle($this->title); $this->getSkin()->setRelevantTitle($this->title);
$user = $this->getUser(); $user = $this->getUser();
if (!$title) { if (!$title) {
throw new \ErrorPageError( 'notargettitle', 'notargettext' ); throw new \ErrorPageError('notargettitle', 'notargettext');
return; return;
} }
if (!$title->exists()) { if (!$title->exists()) {
throw new \ErrorPageError( 'nopagetitle', 'nopagetext' ); throw new \ErrorPageError('nopagetitle', 'nopagetext');
} }
$isAdmin = $service->getPermissionManager()->userHasRight($this->getUser(), 'move'); $isAdmin = $service->getPermissionManager()->userHasRight($this->getUser(), 'move');
@ -76,27 +81,32 @@ class SpecialCustomUrl extends UnlistedSpecialPage {
$userEditedPage = Utils::hasUserEditedPage($this->title, $this->getUser()); $userEditedPage = Utils::hasUserEditedPage($this->title, $this->getUser());
$this->userEditedPage = $userEditedPage; $this->userEditedPage = $userEditedPage;
if (!$this->hasAccess()){ if (!$this->hasAccess()) {
throw new \PermissionsError('move'); throw new \PermissionsError('move');
} }
$this->slug = $this->getCurrentSlug(); $this->slug = $this->getCurrentSlug();
if ($request->getRawVal('action') == 'submit' && $request->wasPosted() && $user->matchEditToken($request->getVal('wpEditToken'))) { $csrfTokenObj = $request->getSession()->getToken();
if ($request->getRawVal('action') == 'submit' && $request->wasPosted() && $csrfTokenObj->match($request->getVal('wpEditToken'))) {
$this->doSubmit(); $this->doSubmit();
} else { } else {
$this->showForm( [] ); $this->showForm([]);
} }
} }
protected function hasAccess(){ protected function hasAccess() {
return $this->isAdmin || $this->userEditedPage; return $this->isAdmin || $this->userEditedPage;
} }
protected function showForm($err, $isPermError = false){ protected function showForm($err, $isPermError = false) {
$user = $this->getUser(); $user = $this->getUser();
$out = $this->getOutput(); $out = $this->getOutput();
$out->setPageTitle($this->msg('latinizeurl-customurl')); $request = $this->getRequest();
$csrfTokenObj = $request->getSession()->getToken();
$out->setPageTitle($this->msg('latinizeurl-customurl')->text());
$out->addModuleStyles([ $out->addModuleStyles([
'mediawiki.special', 'mediawiki.special',
'mediawiki.interface.helpers.styles' 'mediawiki.interface.helpers.styles'
@ -135,7 +145,7 @@ class SpecialCustomUrl extends UnlistedSpecialPage {
} }
$fields[] = new \OOUI\FieldLayout( $fields[] = new \OOUI\FieldLayout(
new \OOUI\ButtonInputWidget( [ new \OOUI\ButtonInputWidget([
'name' => 'wpConfirm', 'name' => 'wpConfirm',
'value' => $this->msg('htmlform-submit')->text(), 'value' => $this->msg('htmlform-submit')->text(),
'label' => $this->msg('htmlform-submit')->text(), 'label' => $this->msg('htmlform-submit')->text(),
@ -147,11 +157,11 @@ class SpecialCustomUrl extends UnlistedSpecialPage {
] ]
); );
$fieldset = new \OOUI\FieldsetLayout( [ $fieldset = new \OOUI\FieldsetLayout([
'label' => $this->msg('customurl-legend')->text(), 'label' => $this->msg('customurl-legend')->text(),
'id' => 'mw-customurl-table', 'id' => 'mw-customurl-table',
'items' => $fields, 'items' => $fields,
] ); ]);
$form = new \OOUI\FormLayout([ $form = new \OOUI\FormLayout([
'method' => 'post', 'method' => 'post',
@ -162,7 +172,7 @@ class SpecialCustomUrl extends UnlistedSpecialPage {
$form->appendContent( $form->appendContent(
$fieldset, $fieldset,
new \OOUI\HtmlSnippet( new \OOUI\HtmlSnippet(
Html::hidden('wpEditToken', $user->getEditToken()) Html::hidden('wpEditToken', $csrfTokenObj->toString())
) )
); );
@ -181,9 +191,9 @@ class SpecialCustomUrl extends UnlistedSpecialPage {
} }
} }
private function getCurrentSlug(){ private function getCurrentSlug() {
$slug = Utils::getSlugUrlByTitle($this->title); $slug = Utils::getSlugUrlByTitleWithoutId($this->title);
if($slug){ if ($slug) {
return $slug; return $slug;
} else { } else {
return $this->title->getText(); return $this->title->getText();
@ -215,20 +225,20 @@ class SpecialCustomUrl extends UnlistedSpecialPage {
$custom = 1; $custom = 1;
} }
if(Utils::titleSlugExists($this->title)){ if (Utils::titleSlugExists($this->title)) {
$realSlug = Utils::updateTitleSlugMap($this->title->getText(), $slug, $latinize, $custom); $realSlug = Utils::updateTitleSlugMap($this->title->getText(), $slug, $latinize, $custom);
} else { } else {
$realSlug = Utils::addTitleSlugMap($this->title->getText(), $slug, $latinize, $custom); $realSlug = Utils::addTitleSlugMap($this->title->getText(), $slug, $latinize, $custom);
} }
if($renameSubpages){ if ($renameSubpages) {
//更新子页面的slug //更新子页面的slug
$subpages = $this->title->getSubpages(); $subpages = $this->title->getSubpages();
$originSlugLen = strlen($originSlug); $originSlugLen = strlen($originSlug);
/** @var \Title $subpage */ /** @var \Title $subpage */
foreach($subpages as $subpage){ foreach ($subpages as $subpage) {
$originSubpaeSlug = Utils::getSlugByTitle($subpage); $originSubpaeSlug = Utils::getSlugByTitle($subpage);
if(strpos($originSubpaeSlug, $originSlug) === 0){ if (strpos($originSubpaeSlug, $originSlug) === 0) {
$newSubpageSlug = $realSlug . substr($originSubpaeSlug, $originSlugLen); $newSubpageSlug = $realSlug . substr($originSubpaeSlug, $originSlugLen);
Utils::updateTitleSlugMap($subpage->getText(), $newSubpageSlug, [$newSubpageSlug], 1); Utils::updateTitleSlugMap($subpage->getText(), $newSubpageSlug, [$newSubpageSlug], 1);
} }
@ -246,59 +256,66 @@ class SpecialCustomUrl extends UnlistedSpecialPage {
* *
* @param Title $title Page being moved. * @param Title $title Page being moved.
*/ */
private function showSubpages( $title ) { private function showSubpages($title) {
$nsHasSubpages = $this->nsInfo->hasSubpages( $title->getNamespace() ); $nsHasSubpages = $this->nsInfo->hasSubpages($title->getNamespace());
$subpages = $title->getSubpages(); $subpages = $title->getSubpages();
$count = $subpages instanceof \TitleArray ? $subpages->count() : 0; $count = $subpages instanceof TitleArrayFromResult ? $subpages->count() : 0;
$titleIsTalk = $title->isTalkPage(); $titleIsTalk = $title->isTalkPage();
$subpagesTalk = $title->getTalkPage()->getSubpages();
$countTalk = $subpagesTalk instanceof \TitleArray ? $subpagesTalk->count() : 0; $talkPage = $title->getTalkPageIfDefined();
if ($talkPage) {
$subpagesTalk = $talkPage->getSubpages();
} else {
$subpagesTalk = [];
}
$countTalk = $subpagesTalk instanceof TitleArrayFromResult ? $subpagesTalk->count() : 0;
$totalCount = $count + $countTalk; $totalCount = $count + $countTalk;
if ( !$nsHasSubpages && $countTalk == 0 ) { if (!$nsHasSubpages && $countTalk == 0) {
return; return;
} }
$this->getOutput()->wrapWikiMsg( $this->getOutput()->wrapWikiMsg(
'== $1 ==', '== $1 ==',
[ 'movesubpage', ( $titleIsTalk ? $count : $totalCount ) ] ['movesubpage', ($titleIsTalk ? $count : $totalCount)]
); );
if ( $nsHasSubpages ) { if ($nsHasSubpages) {
$this->showSubpagesList( $subpages, $count, 'movesubpagetext', true ); $this->showSubpagesList($subpages, $count, 'movesubpagetext', true);
} }
if ( !$titleIsTalk && $countTalk > 0 ) { if (!$titleIsTalk && $countTalk > 0) {
$this->showSubpagesList( $subpagesTalk, $countTalk, 'movesubpagetalktext' ); $this->showSubpagesList($subpagesTalk, $countTalk, 'movesubpagetalktext');
} }
} }
private function showSubpagesList( $subpages, $pagecount, $wikiMsg, $noSubpageMsg = false ) { private function showSubpagesList($subpages, $pagecount, $wikiMsg, $noSubpageMsg = false) {
$out = $this->getOutput(); $out = $this->getOutput();
# No subpages. # No subpages.
if ( $pagecount == 0 && $noSubpageMsg ) { if ($pagecount == 0 && $noSubpageMsg) {
$out->addWikiMsg( 'movenosubpage' ); $out->addWikiMsg('movenosubpage');
return; return;
} }
$out->addWikiMsg( $wikiMsg, $this->getLanguage()->formatNum( $pagecount ) ); $out->addWikiMsg($wikiMsg, $this->getLanguage()->formatNum($pagecount));
$out->addHTML( "<ul>\n" ); $out->addHTML("<ul>\n");
$linkBatch = $this->linkBatchFactory->newLinkBatch( $subpages ); $linkBatch = $this->linkBatchFactory->newLinkBatch($subpages);
$linkBatch->setCaller( __METHOD__ ); $linkBatch->setCaller(__METHOD__);
$linkBatch->execute(); $linkBatch->execute();
$linkRenderer = $this->getLinkRenderer(); $linkRenderer = $this->getLinkRenderer();
foreach ( $subpages as $subpage ) { foreach ($subpages as $subpage) {
$link = $linkRenderer->makeLink( $subpage ); $link = $linkRenderer->makeLink($subpage);
$out->addHTML( "<li>$link</li>\n" ); $out->addHTML("<li>$link</li>\n");
} }
$out->addHTML( "</ul>\n" ); $out->addHTML("</ul>\n");
} }
public function onSuccess(){ public function onSuccess() {
$out = $this->getOutput(); $out = $this->getOutput();
$out->setPageTitle($this->msg('latinizeurl-customurl')); $out->setPageTitle($this->msg('latinizeurl-customurl'));
$out->addWikiMsg('customurl-set-success', $this->title->getText(), str_replace(' ', '_', $this->slug)); $out->addWikiMsg('customurl-set-success', $this->title->getText(), str_replace(' ', '_', $this->slug));

Loading…
Cancel
Save