MarkAHershberger has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/398618 )
Change subject: Refactor and bump version to 1.3
......................................................................
Refactor and bump version to 1.3
* Address any phpcs issues.
* Put everything in MediaWiki\Extension\ReplaceText namespace.
* Break up big functions into smaller bite-size pieces.
* Create and use modules for CSS and JS.
* Assign my copyright to NicheWork LLC
Change-Id: I251188efa2fc1fbf0563b0e1f792a912d077d8c9
---
M README
A ReplaceText.css
M ReplaceText.hooks.php
M ReplaceText.js
M ReplaceText.php
M ReplaceTextJob.php
A ReplaceTextSearch.js
M ReplaceTextSearch.php
M SpecialReplaceText.php
M extension.json
M replaceAll.php
11 files changed, 1,141 insertions(+), 468 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/ReplaceText
refs/changes/18/398618/1
diff --git a/README b/README
index 1e0859b..95bdc72 100644
--- a/README
+++ b/README
@@ -1,6 +1,6 @@
Replace Text Extension
- Version 1.2
+ Version 1.3
Yaron Koren and Niklas Laxström
This is free software licenced under the GNU General Public Licence. Please
diff --git a/ReplaceText.css b/ReplaceText.css
new file mode 100644
index 0000000..2b65254
--- /dev/null
+++ b/ReplaceText.css
@@ -0,0 +1,4 @@
+.rt-searchmatch {
+ font-weight: bold;
+ font-size: 1.4em;
+}
diff --git a/ReplaceText.hooks.php b/ReplaceText.hooks.php
index 4edf513..636cb1c 100644
--- a/ReplaceText.hooks.php
+++ b/ReplaceText.hooks.php
@@ -1,11 +1,40 @@
<?php
/**
+ * Hook for AdminLinks
+ *
+ * Copyright (C) 2015 Yaron Koren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
*/
-class ReplaceTextHooks {
+namespace MediaWiki\\Extension\\ReplaceText;
- public static function addToAdminLinks( ALTree &$adminLinksTree ) {
- $generalSection = $adminLinksTree->getSection( wfMessage(
'adminlinks_general' )->text() );
+use ALTree;
+use ALRow;
+
+class Hook {
+
+ /**
+ * Hook for AdminLinks to include ReplaceText
+ * @param ALTree $adminLinksTree the tree
+ */
+ public static function addToAdminLinks( ALTree $adminLinksTree ) {
+ $generalSection = $adminLinksTree->getSection(
+ wfMessage( 'adminlinks_general' )->text()
+ );
$extensionsRow = $generalSection->getRow( 'extensions' );
if ( is_null( $extensionsRow ) ) {
@@ -14,8 +43,6 @@
}
$extensionsRow->addItem( ALItem::newFromSpecialPage(
'ReplaceText' ) );
-
- return true;
}
}
diff --git a/ReplaceText.js b/ReplaceText.js
index 666c462..9efc327 100644
--- a/ReplaceText.js
+++ b/ReplaceText.js
@@ -1,17 +1,27 @@
-function invertSelections() {
- 'use strict';
+/* @license GPL 2.0 */
+/* @author Yaron Koren */
+( function ( mw, $ ) {
+ $( function () {
+ invertSelections = function() {
+ 'use strict';
- var form = document.getElementById('choose_pages' ),
- num_elements = form.elements.length,
- i,
- cur_element;
+ var form = document.getElementById( 'choose_pages' ),
+ num_elements = form.elements.length,
+ i,
+ cur_element;
- for (i = 0; i < num_elements; i++) {
- cur_element = form.elements[i];
+ for ( i = 0; i < num_elements; i++ ) {
+ cur_element = form.elements[i];
- if (cur_element.type === "checkbox" && cur_element.id !==
'create-redirect' &&
- cur_element.id !== 'watch-pages' && cur_element.id !==
'doEnotif' ) {
- form.elements[i].checked = form.elements[i].checked !==
true;
- }
- }
-}
+ if (
+ cur_element.type === "checkbox" &&
+ cur_element.id !==
'create-redirect' &&
+ cur_element.id !==
'watch-pages' &&
+ cur_element.id !== 'doEnotif'
+ ) {
+ form.elements[i].checked =
form.elements[i].checked !== true;
+ }
+ }
+ };
+ } );
+}( mediaWiki, jQuery ) );
diff --git a/ReplaceText.php b/ReplaceText.php
index 81c592a..62b12a3 100644
--- a/ReplaceText.php
+++ b/ReplaceText.php
@@ -34,14 +34,14 @@
die();
}
-define( 'REPLACE_TEXT_VERSION', '1.2' );
+define( 'REPLACE_TEXT_VERSION', '1.3' );
// credits
$wgExtensionCredits['specialpage'][] = [
'path' => __FILE__,
'name' => 'Replace Text',
'version' => REPLACE_TEXT_VERSION,
- 'author' => [ 'Yaron Koren', 'Niklas Laxström', '...' ],
+ 'author' => [ 'Yaron Koren', 'Niklas Laxström', '[https://hexmode.com/
Mark A. Hershberger]', '...' ],
'url' => 'https://www.mediawiki.org/wiki/Extension:Replace_Text',
'descriptionmsg' => 'replacetext-desc',
'license-name' => 'GPL-2.0+'
@@ -49,19 +49,19 @@
$wgMessagesDirs['ReplaceText'] = __DIR__ . '/i18n';
$wgExtensionMessagesFiles['ReplaceTextAlias'] = __DIR__ .
'/ReplaceText.alias.php';
-$wgJobClasses['replaceText'] = 'ReplaceTextJob';
+$wgJobClasses['replaceText'] = "MediaWiki\\Extension\\ReplaceText\\Job";
// This extension uses its own permission type, 'replacetext'
$wgAvailableRights[] = 'replacetext';
$wgGroupPermissions['sysop']['replacetext'] = true;
-$wgHooks['AdminLinks'][] = 'ReplaceTextHooks::addToAdminLinks';
+$wgHooks['AdminLinks'][] =
"MediaWiki\\Extension\\ReplaceText\\Hooks::addToAdminLinks";
-$wgSpecialPages['ReplaceText'] = 'SpecialReplaceText';
-$wgAutoloadClasses['ReplaceTextHooks'] = __DIR__ . '/ReplaceText.hooks.php';
-$wgAutoloadClasses['SpecialReplaceText'] = __DIR__ . '/SpecialReplaceText.php';
-$wgAutoloadClasses['ReplaceTextJob'] = __DIR__ . '/ReplaceTextJob.php';
-$wgAutoloadClasses['ReplaceTextSearch'] = __DIR__ . '/ReplaceTextSearch.php';
+$wgSpecialPages['ReplaceText'] =
"MediaWiki\\Extension\\ReplaceText\\SpecialPage";
+$wgAutoloadClasses["ReplaceTextHooks"] = __DIR__ . '/ReplaceText.hooks.php';
+$wgAutoloadClasses["MediaWiki\\Extension\\ReplaceText\\SpecialPage"] = __DIR__
. '/SpecialReplaceText.php';
+$wgAutoloadClasses["MediaWiki\\Extension\\ReplaceText\\Job"] = __DIR__ .
'/ReplaceTextJob.php';
+$wgAutoloadClasses["MediaWiki\\Extension\\ReplaceText\\Search"] = __DIR__ .
'/ReplaceTextSearch.php';
// Global variables
$wgReplaceTextUser = null;
diff --git a/ReplaceTextJob.php b/ReplaceTextJob.php
index 548f57d..090d2a5 100644
--- a/ReplaceTextJob.php
+++ b/ReplaceTextJob.php
@@ -4,11 +4,47 @@
* Background job to replace text in a given page
* - based on /includes/RefreshLinksJob.php
*
+ * Copyright (C) 2008-2017 Yaron Koren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
* @author Yaron Koren
* @author Ankit Garg
*/
-class ReplaceTextJob extends Job {
- function __construct( $title, $params = '', $id = 0 ) {
+
+namespace MediaWiki\\Extension\\ReplaceText;
+
+use Title;
+use User;
+use WatchAction;
+use WikiPage;
+use WikitextContent;
+
+class Job extends \Job {
+ protected $wikiPage;
+ protected $oldText;
+ protected $newText;
+
+ /**
+ * Ye ole constructor
+ * @param Title $title we're working on
+ * @param mixed $params for job
+ * @param int $id default
+ */
+ public function __construct( Title $title, array $params = [], $id = 0
) {
parent::__construct( 'replaceText', $title, $params, $id );
}
@@ -16,7 +52,7 @@
* Run a replaceText job
* @return bool success
*/
- function run() {
+ public function run() {
wfProfileIn( __METHOD__ );
if ( is_null( $this->title ) ) {
@@ -26,94 +62,150 @@
}
if ( array_key_exists( 'move_page', $this->params ) ) {
- global $wgUser;
- $actual_user = $wgUser;
- $wgUser = User::newFromId( $this->params['user_id'] );
- $cur_page_name = $this->title->getText();
- if ( $this->params['use_regex'] ) {
- $new_page_name = preg_replace(
- "/" . $this->params['target_str'] .
"/Uu", $this->params['replacement_str'], $cur_page_name
- );
- } else {
- $new_page_name =
- str_replace(
$this->params['target_str'], $this->params['replacement_str'], $cur_page_name );
- }
+ $this->movePage();
+ } elseif ( $this->replaceText() === false ) {
+ return false;
+ }
+ wfProfileOut( __METHOD__ );
+ return true;
+ }
- $new_title = Title::newFromText( $new_page_name,
$this->title->getNamespace() );
- $reason = $this->params['edit_summary'];
- $create_redirect = $this->params['create_redirect'];
- $this->title->moveTo( $new_title, true, $reason,
$create_redirect );
- if ( $this->params['watch_page'] ) {
- if ( class_exists( 'WatchAction' ) ) {
- // Class was added in MW 1.19
- WatchAction::doWatch( $new_title,
$wgUser );
- } else {
- Action::factory( 'watch', new WikiPage(
$new_title ) )->execute();
- }
- }
- $wgUser = $actual_user;
+ /**
+ * ReplaceText job to move the page.
+ */
+ protected function movePage() {
+ global $wgUser;
+ $actual_user = $wgUser;
+ $wgUser = User::newFromId( $this->params['user_id'] );
+
+ $cur_page_name = $this->title->getText();
+ if ( $this->params['use_regex'] ) {
+ $new_page_name = preg_replace(
+ "/" . $this->params['target_str'] . "/Uu",
+ $this->params['replacement_str'], $cur_page_name
+ );
} else {
- if ( $this->title->getContentModel() !==
CONTENT_MODEL_WIKITEXT ) {
- $this->error = 'replaceText: Wiki page "' .
- $this->title->getPrefixedDBkey() . '"
does not hold regular wikitext.';
- wfProfileOut( __METHOD__ );
- return false;
- }
- $wikiPage = new WikiPage( $this->title );
- // Is this check necessary?
- if ( !$wikiPage ) {
- $this->error =
- 'replaceText: Wiki page not found for
"' . $this->title->getPrefixedDBkey() . '."';
- wfProfileOut( __METHOD__ );
- return false;
- }
- $wikiPageContent = $wikiPage->getContent();
- if ( is_null( $wikiPageContent ) ) {
- $this->error =
- 'replaceText: No contents found for
wiki page at "' . $this->title->getPrefixedDBkey() . '."';
- wfProfileOut( __METHOD__ );
- return false;
- }
- $article_text = $wikiPageContent->getNativeData();
+ $new_page_name = str_replace(
+ $this->params['target_str'],
+ $this->params['replacement_str'],
+ $cur_page_name
+ );
+ }
+ $new_title = Title::newFromText(
+ $new_page_name,
+ $this->title->getNamespace()
+ );
+
+ $this->title->moveTo(
+ $new_title, true, $this->params['edit_summary'],
+ $this->params['create_redirect']
+ );
+
+ if ( $this->params['watch_page'] ) {
+ WatchAction::doWatch( $new_title, $wgUser );
+ }
+
+ $wgUser = $actual_user;
+ }
+
+ /**
+ * General ReplaceText
+ * @return bool for the job
+ */
+ protected function replaceText() {
+ if ( $this->canReplaceText() ) {
wfProfileIn( __METHOD__ . '-replace' );
- $target_str = $this->params['target_str'];
- $replacement_str = $this->params['replacement_str'];
- $num_matches = 0;
-
- if ( $this->params['use_regex'] ) {
- $new_text =
- preg_replace( '/' . $target_str .
'/Uu', $replacement_str, $article_text, -1, $num_matches );
- } else {
- $new_text = str_replace( $target_str,
$replacement_str, $article_text, $num_matches );
- }
-
- // If there's at least one replacement, modify the page,
- // using the passed-in edit summary.
- if ( $num_matches > 0 ) {
- // Change global $wgUser variable to the one
- // specified by the job only for the extent of
- // this replacement.
- global $wgUser;
- $actual_user = $wgUser;
- $wgUser = User::newFromId(
$this->params['user_id'] );
- $edit_summary = $this->params['edit_summary'];
- $flags = EDIT_MINOR;
- if ( $wgUser->isAllowed( 'bot' ) ) {
- $flags |= EDIT_FORCE_BOT;
- }
- if ( isset( $this->params['doEnotif'] ) &&
- !$this->params['doEnotif'] ) {
- $flags |= EDIT_SUPPRESS_RC;
- # fixme log this action
- }
- $new_content = new WikitextContent( $new_text );
- $wikiPage->doEditContent( $new_content,
$edit_summary, $flags );
- $wgUser = $actual_user;
+ if ( $this->hasReplacements() ) {
+ return $this->makeReplacments();
}
wfProfileOut( __METHOD__ . '-replace' );
}
- wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ /**
+ * Determine if we can even do this
+ * @return bool true if we can
+ */
+ protected function canReplaceText() {
+ if ( $this->title->getContentModel() !== CONTENT_MODEL_WIKITEXT
) {
+ $this->error = 'replaceText: Wiki page "' .
+
$this->title->getPrefixedDBkey() .
+ '" does not hold regular
wikitext.';
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ $this->wikiPage = new WikiPage( $this->title );
+ // Is this check necessary?
+ if ( !$this->wikiPage ) {
+ $this->error = 'replaceText: Wiki page not found for "'
+ .
$this->title->getPrefixedDBkey() . '."';
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ if ( is_null( $this->wikiPage->getContent() ) ) {
+ $this->error = 'replaceText: No contents found for wiki
page at "' .
+
$this->title->getPrefixedDBkey() . '."';
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ $this->oldText = $this->wikiPage->getContent()->getNativeData();
+
+ return true;
+ }
+
+ /**
+ * Determine if any replacments are needed.
+ * @return bool true if matches > 0
+ */
+ protected function hasReplacements() {
+ $target_str = $this->params['target_str'];
+ $replacement_str = $this->params['replacement_str'];
+ $num_matches = 0;
+
+ if ( $this->params['use_regex'] ) {
+ $this->newText = preg_replace(
+ '/' . $target_str . '/Uu', $replacement_str,
+ $this->oldText, -1, $num_matches
+ );
+ } else {
+ $this->newText = str_replace(
+ $target_str, $replacement_str,
+ $this->oldText, $num_matches );
+ }
+
+ return $num_matches > 0;
+ }
+
+ /**
+ * Take care of the actual replacements.
+ * @return bool true for now, may find errors that should be false later
+ */
+ protected function makeReplacments() {
+ // Change global $wgUser variable to the one
+ // specified by the job only for the extent of
+ // this replacement.
+ global $wgUser;
+ $actual_user = $wgUser;
+
+ $wgUser = User::newFromId( $this->params['user_id'] );
+ $flags = EDIT_MINOR;
+ if ( $wgUser->isAllowed( 'bot' ) ) {
+ $flags |= EDIT_FORCE_BOT;
+ }
+ if ( isset( $this->params['doEnotif'] ) &&
+ !$this->params['doEnotif'] ) {
+ $flags |= EDIT_SUPPRESS_RC;
+ # fixme log this action
+ }
+
+ $new_content = new WikitextContent( $this->newText );
+ $this->wikiPage->doEditContent(
+ $new_content, $this->params['edit_summary'], $flags
+ );
+
+ $wgUser = $actual_user;
return true;
}
}
diff --git a/ReplaceTextSearch.js b/ReplaceTextSearch.js
new file mode 100644
index 0000000..67e4185
--- /dev/null
+++ b/ReplaceTextSearch.js
@@ -0,0 +1,43 @@
+/*!
+ * JavaScript for Special:ReplaceText
+ */
+( function ( mw, $ ) {
+ $( function () {
+ var $checkboxes, $headerLinks, updateHeaderLinks, searchWidget;
+
+ // Emulate HTML5 autofocus behavior in non HTML5 compliant
browsers
+ if ( !( 'autofocus' in document.createElement( 'input' ) ) ) {
+ $( 'input[autofocus]' ).eq( 0 ).focus();
+ }
+
+ // Create check all/none button
+ $checkboxes = $( '#powersearch input[id^=mw-search-ns]' );
+ $( '#mw-search-togglebox' ).append(
+ $( '<label>' )
+ .text( mw.msg( 'powersearch-togglelabel' ) )
+ ).append(
+ $( '<input>' ).attr( 'type', 'button' )
+ .attr( 'id', 'mw-search-toggleall' )
+ .prop( 'value', mw.msg( 'powersearch-toggleall'
) )
+ .click( function () {
+ $checkboxes.prop( 'checked', true );
+ } )
+ ).append(
+ $( '<input>' ).attr( 'type', 'button' )
+ .attr( 'id', 'mw-search-togglenone' )
+ .prop( 'value', mw.msg(
'powersearch-togglenone' ) )
+ .click( function () {
+ $checkboxes.prop( 'checked', false );
+ } )
+ );
+
+ // Bit stripped here since it was OOjs
+
+ // When saving settings, use the proper request method (POST
instead of GET).
+ $( '#mw-search-powersearch-remember' ).change( function () {
+ this.form.method = this.checked ? 'post' : 'get';
+ } ).trigger( 'change' );
+
+ } );
+
+}( mediaWiki, jQuery ) );
diff --git a/ReplaceTextSearch.php b/ReplaceTextSearch.php
index f9bf4a3..74657e0 100644
--- a/ReplaceTextSearch.php
+++ b/ReplaceTextSearch.php
@@ -1,6 +1,34 @@
<?php
-class ReplaceTextSearch {
+/**
+ * Class to hold the logic for searches.
+ *
+ * Copyright (C) 2014-2017 NicheWork, LLC
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * @author Mark A. Hershberger <[email protected]>
+ */
+
+namespace MediaWiki\\Extension\\ReplaceText;
+
+use DatabasePostgres;
+use Title;
+
+class Search {
public static function doSearchQuery(
$search, $namespaces, $category, $prefix, $use_regex = false
) {
diff --git a/SpecialReplaceText.php b/SpecialReplaceText.php
index 46833a4..83e8445 100644
--- a/SpecialReplaceText.php
+++ b/SpecialReplaceText.php
@@ -1,6 +1,43 @@
<?php
-class SpecialReplaceText extends SpecialPage {
+/**
+ * Special Page for ReplaceText
+ *
+ * Copyright (C) 2008-2017 Yaron Koren
+ * Copyright (C) 2017 NicheWork, LLC
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * @author Yaron Koren
+ * @author Mark A. Hershberger <[email protected]>
+ */
+
+namespace MediaWiki\\Extension\\ReplaceText;
+
+use Html;
+use JobQueueGroup;
+use Linker;
+use MWNamespace;
+use PermissionsError;
+use SearchEngine;
+use Title;
+use User;
+use XML;
+
+class SpecialPage extends \SpecialPage {
private $target, $replacement, $use_regex,
$category, $prefix, $edit_pages, $move_pages,
$selected_namespaces, $doEnotif;
@@ -9,24 +46,40 @@
parent::__construct( 'ReplaceText', 'replacetext' );
}
+ /**
+ * Indicate that this page cannot operate on a read-only wiki
+ * @return bool
+ */
public function doesWrites() {
return true;
}
- function execute( $query ) {
+ /**
+ * Take care of the actual page load
+ * @param string $query any url stem (not used)
+ * @throws PermissionError
+ * @return mixed
+ */
+ public function execute( $query ) {
if ( !$this->getUser()->isAllowed( 'replacetext' ) ) {
throw new PermissionsError( 'replacetext' );
}
$this->setHeaders();
$out = $this->getOutput();
- if ( !is_null( $out->getResourceLoader()->getModule(
'mediawiki.special' ) ) ) {
+ if ( !is_null( $out->getResourceLoader()->getModule(
+ 'mediawiki.special'
+ ) ) ) {
$out->addModuleStyles( 'mediawiki.special' );
}
- $this->doSpecialReplaceText();
+ return $this->doSpecialReplaceText();
}
- function getSelectedNamespaces() {
+ /**
+ * Get the list of selected NS
+ * @return array
+ */
+ protected function getSelectedNamespaces() {
$all_namespaces = SearchEngine::searchableNamespaces();
$selected_namespaces = [];
foreach ( $all_namespaces as $ns => $name ) {
@@ -37,9 +90,12 @@
return $selected_namespaces;
}
- function doSpecialReplaceText() {
+ /**
+ * Take care of the form
+ * @return mixed
+ */
+ protected function doSpecialReplaceText() {
wfProfileIn( __METHOD__ );
- $out = $this->getOutput();
$request = $this->getRequest();
$this->target = $request->getText( 'target' );
@@ -58,220 +114,20 @@
return;
}
+ // Don't even try if it isn't going to work
+ if ( $this->hasBadCategory() ) {
+ $this->showBadCategory();
+ return;
+ }
+
if ( $request->getCheck( 'replace' ) ) {
- global $wgReplaceTextUser;
-
- $replacement_params = [];
- if ( $wgReplaceTextUser != null ) {
- $user = User::newFromName( $wgReplaceTextUser );
- } else {
- $user = $this->getUser();
- }
- $replacement_params['user_id'] = $user->getId();
- $replacement_params['target_str'] = $this->target;
- $replacement_params['replacement_str'] =
$this->replacement;
- $replacement_params['use_regex'] = $this->use_regex;
- $replacement_params['edit_summary'] = $this->msg(
- 'replacetext_editsummary',
- $this->target, $this->replacement
- )->inContentLanguage()->plain();
- $replacement_params['create_redirect'] = false;
- $replacement_params['watch_page'] = false;
- $replacement_params['doEnotif'] = $this->doEnotif;
- foreach ( $request->getValues() as $key => $value ) {
- if ( $key == 'create-redirect' && $value == '1'
) {
- $replacement_params['create_redirect']
= true;
- } elseif ( $key == 'watch-pages' && $value ==
'1' ) {
- $replacement_params['watch_page'] =
true;
- }
- }
- $jobs = [];
- foreach ( $request->getValues() as $key => $value ) {
- if ( $value == '1' && $key !== 'replace' &&
$key !== 'use_regex' ) {
- if ( strpos( $key, 'move-' ) !== false
) {
- $title = Title::newFromID(
substr( $key, 5 ) );
-
$replacement_params['move_page'] = true;
- } else {
- $title = Title::newFromID( $key
);
- }
- if ( $title !== null ) {
- $jobs[] = new ReplaceTextJob(
$title, $replacement_params );
- }
- }
- }
-
- JobQueueGroup::singleton()->push( $jobs );
-
- $count = $this->getLanguage()->formatNum( count( $jobs
) );
- $out->addWikiMsg(
- 'replacetext_success',
- "<code><nowiki>{$this->target}</nowiki></code>",
-
"<code><nowiki>{$this->replacement}</nowiki></code>",
- $count
- );
-
- // Link back
- $out->addHTML(
- Linker::link( $this->getTitle(),
- $this->msg( 'replacetext_return'
)->escaped() )
- );
-
+ $response = $this->doReplaceRequest();
wfProfileOut( __METHOD__ );
- return;
- } elseif ( $request->getCheck( 'target' ) ) { // very long
elseif, look for "end elseif"
- // first, check that at least one namespace has been
- // picked, and that either editing or moving pages
- // has been selected
- if ( count( $this->selected_namespaces ) == 0 ) {
- $this->showForm( 'replacetext_nonamespace' );
- wfProfileOut( __METHOD__ );
- return;
- }
- if ( ! $this->edit_pages && ! $this->move_pages ) {
- $this->showForm( 'replacetext_editormove' );
- wfProfileOut( __METHOD__ );
- return;
- }
-
- $titles_for_edit = [];
- $titles_for_move = [];
- $unmoveable_titles = [];
-
- // if user is replacing text within pages...
- if ( $this->edit_pages ) {
- $res = ReplaceTextSearch::doSearchQuery(
- $this->target,
- $this->selected_namespaces,
- $this->category,
- $this->prefix,
- $this->use_regex
- );
-
- foreach ( $res as $row ) {
- $title = Title::makeTitleSafe(
$row->page_namespace, $row->page_title );
- if ( $title == null ) {
- continue;
- }
- $context = $this->extractContext(
$row->old_text, $this->target, $this->use_regex );
- $titles_for_edit[] = [ $title, $context
];
- }
- }
- if ( $this->move_pages ) {
- $res = $this->getMatchingTitles(
- $this->target,
- $this->selected_namespaces,
- $this->category,
- $this->prefix,
- $this->use_regex
- );
-
- foreach ( $res as $row ) {
- $title = Title::makeTitleSafe(
$row->page_namespace, $row->page_title );
- if ( $title == null ) {
- continue;
- }
- // See if this move can happen.
- $cur_page_name = str_replace( '_', ' ',
$row->page_title );
-
- if ( $this->use_regex ) {
- $new_page_name =
- preg_replace( "/" .
$this->target . "/Uu", $this->replacement, $cur_page_name );
- } else {
- $new_page_name =
- str_replace(
$this->target, $this->replacement, $cur_page_name );
- }
-
- $new_title = Title::makeTitleSafe(
$row->page_namespace, $new_page_name );
- $err = $title->isValidMoveOperation(
$new_title );
-
- if ( $title->userCan( 'move' ) &&
!is_array( $err ) ) {
- $titles_for_move[] = $title;
- } else {
- $unmoveable_titles[] = $title;
- }
- }
- }
-
- // If no results were found, check to see if a bad
- // category name was entered.
- if ( count( $titles_for_edit ) == 0 && count(
$titles_for_move ) == 0 ) {
- $bad_cat_name = false;
-
- if ( !empty( $this->category ) ) {
- $category_title = Title::makeTitleSafe(
NS_CATEGORY, $this->category );
- if ( !$category_title->exists() ) {
- $bad_cat_name = true;
- }
- }
-
- if ( $bad_cat_name ) {
- $link = Linker::link( $category_title,
htmlspecialchars( ucfirst( $this->category ) ) );
- $out->addHTML(
- $this->msg(
'replacetext_nosuchcategory' )->rawParams( $link )->escaped()
- );
- } else {
- if ( $this->edit_pages ) {
- $out->addWikiMsg(
-
'replacetext_noreplacement', "<code><nowiki>{$this->target}</nowiki></code>"
- );
- }
-
- if ( $this->move_pages ) {
- $out->addWikiMsg(
'replacetext_nomove', "<code><nowiki>{$this->target}</nowiki></code>" );
- }
- }
- // link back to starting form
- $out->addHTML(
- '<p>' .
- Linker::link(
- $this->getTitle(),
- $this->msg( 'replacetext_return'
)->escaped() )
- . '</p>'
- );
- } else {
- // Show a warning message if the replacement
- // string is either blank or found elsewhere on
- // the wiki (since undoing the replacement
- // would be difficult in either case).
- $warning_msg = null;
-
- if ( $this->replacement === '' ) {
- $warning_msg = $this->msg(
'replacetext_blankwarning' )->text();
- } elseif ( count( $titles_for_edit ) > 0 ) {
- $res = ReplaceTextSearch::doSearchQuery(
- $this->replacement,
- $this->selected_namespaces,
- $this->category,
- $this->prefix,
- $this->use_regex
- );
- $count = $res->numRows();
- if ( $count > 0 ) {
- $warning_msg = $this->msg(
'replacetext_warning' )->numParams( $count )
- ->params(
"<code><nowiki>{$this->replacement}</nowiki></code>" )->text();
- }
- } elseif ( count( $titles_for_move ) > 0 ) {
- $res = $this->getMatchingTitles(
- $this->replacement,
- $this->selected_namespaces,
- $this->category,
- $this->prefix, $this->use_regex
- );
- $count = $res->numRows();
- if ( $count > 0 ) {
- $warning_msg = $this->msg(
'replacetext_warning' )->numParams( $count )
- ->params(
$this->replacement )->text();
- }
- }
-
- if ( ! is_null( $warning_msg ) ) {
- $out->addWikiText( "<div
class=\"errorbox\">$warning_msg</div><br clear=\"both\" />" );
- }
-
- $this->pageListForm( $titles_for_edit,
$titles_for_move, $unmoveable_titles );
- }
+ return $response;
+ } elseif ( $request->getCheck( 'target' ) ) {
+ $response = $this->doTargetRequest();
wfProfileOut( __METHOD__ );
- return;
+ return $response;
}
// If we're still here, show the starting form.
@@ -279,9 +135,402 @@
wfProfileOut( __METHOD__ );
}
- function showForm( $warning_msg = null ) {
+ /**
+ * Examine the request for parameters & replacements
+ * @return array
+ */
+ protected function getReplacementParamsFromRequest() {
+ global $wgReplaceTextUser;
+
+ $request = $this->getRequest();
+ $user = $this->getUser();
+ if ( $wgReplaceTextUser != null ) {
+ $user = User::newFromName( $wgReplaceTextUser );
+ }
+ $replacement_params['user_id'] = $user->getId();
+ $replacement_params['target_str'] = $this->target;
+ $replacement_params['replacement_str'] = $this->replacement;
+ $replacement_params['use_regex'] = $this->use_regex;
+ $replacement_params['edit_summary'] = $this->msg(
+ 'replacetext_editsummary',
+ $this->target, $this->replacement
+ )->inContentLanguage()->plain();
+ $replacement_params['create_redirect'] = false;
+ $replacement_params['watch_page'] = false;
+ $replacement_params['doEnotif'] = $this->doEnotif;
+ foreach ( $request->getValues() as $key => $value ) {
+ if ( $key == 'create-redirect' && $value == '1' ) {
+ $replacement_params['create_redirect'] = true;
+ } elseif ( $key == 'watch-pages' && $value == '1' ) {
+ $replacement_params['watch_page'] = true;
+ }
+ }
+ return $replacement_params;
+ }
+
+ /**
+ * See what ReplaceText jobs we can get from this request
+ * @param array $replacement_params the args we already have
+ * @return array
+ */
+ protected function getJobsFromRequest( $replacement_params ) {
+ $request = $this->getRequest();
+ $jobs = [];
+ foreach ( $request->getValues() as $key => $value ) {
+ if (
+ $value == '1'
+ && $key !== 'replace'
+ && $key !== 'use_regex'
+ ) {
+ if ( strpos( $key, 'move-' ) !== false ) {
+ $title = Title::newFromID( substr(
$key, 5 ) );
+ $replacement_params['move_page'] = true;
+ } else {
+ $title = Title::newFromID( $key );
+ }
+ if ( $title !== null ) {
+ $jobs[] = new Job( $title,
$replacement_params );
+ }
+ }
+ }
+ return $jobs;
+ }
+
+ /**
+ * Handle requests for replacments
+ */
+ protected function doReplaceRequest() {
+ $request = $this->getRequest();
+
+ $jobs = $this->getJobsFromRequest(
+ $this->getReplacementParamsFromRequest()
+ );
+
+ JobQueueGroup::singleton()->push( $jobs );
+
+ $count = $this->getLanguage()->formatNum( count( $jobs ) );
+ $out = $this->getOutput();
+ $out->addWikiMsg(
+ 'replacetext_success',
+ "<code><nowiki>{$this->target}</nowiki></code>",
+ "<code><nowiki>{$this->replacement}</nowiki></code>",
+ $count
+ );
+
+ // Link back
+ $out->addHTML(
+ Linker::link( $this->getTitle(),
+ $this->msg(
'replacetext_return' )->escaped() )
+ );
+
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+
+ /**
+ * Transform the page name
+ * @param string $cur_page_name currently called
+ * @return string
+ */
+ protected function getNewPageName( $cur_page_name ) {
+ // See if this move can happen.
+ $cur_page_name = str_replace( '_', ' ', $cur_page_name );
+
+ if ( $this->use_regex ) {
+ $new_page_name = preg_replace(
+ "/" . $this->target . "/Uu",
+ $this->replacement, $cur_page_name
+ );
+ } else {
+ $new_page_name = str_replace(
+ $this->target, $this->replacement,
+ $cur_page_name
+ );
+ }
+ return $new_page_name;
+ }
+
+ /**
+ * Get the title we are editing
+ * @return array
+ */
+ protected function getTitlesForEdit() {
+ $titles_for_edit = [];
+
+ $res = Search::doSearchQuery(
+ $this->target,
+ $this->selected_namespaces,
+ $this->category,
+ $this->prefix,
+ $this->use_regex
+ );
+
+ foreach ( $res as $row ) {
+ $title = Title::makeTitleSafe(
+ $row->page_namespace, $row->page_title
+ );
+ if ( $title == null ) {
+ continue;
+ }
+ $context = $this->extractContext(
+ $row->old_text, $this->target, $this->use_regex
+ );
+ $titles_for_edit[] = [ $title, $context ];
+ }
+
+ return $titles_for_edit;
+ }
+
+ /**
+ * Get the titles that we need to move
+ * @return array
+ */
+ protected function getTitlesForMove() {
+ $titles_for_move = [];
+ $unmoveable_titles = [];
+
+ $res = $this->getMatchingTitles(
+ $this->target,
+ $this->selected_namespaces,
+ $this->category,
+ $this->prefix,
+ $this->use_regex
+ );
+
+ foreach ( $res as $row ) {
+ $title = Title::makeTitleSafe(
+ $row->page_namespace, $row->page_title
+ );
+ if ( $title == null ) {
+ continue;
+ }
+
+ $new_title = Title::makeTitleSafe(
+ $row->page_namespace, $this->getNewPageName(
$row->page_title )
+ );
+ $err = $title->isValidMoveOperation( $new_title );
+
+ if ( $title->userCan( 'move' ) && !is_array( $err ) ) {
+ $titles_for_move[] = $title;
+ } else {
+ $unmoveable_titles[] = $title;
+ }
+ }
+ return [ $titles_for_move, $unmoveable_titles ];
+ }
+
+ /**
+ * Get the titles we want to edit
+ * @return array
+ */
+ protected function getTitlesFromTargetRequest() {
+ $res = Search::doSearchQuery(
+ $this->target,
+ $this->selected_namespaces,
+ $this->category,
+ $this->prefix,
+ $this->use_regex
+ );
+
+ $titles_for_edit = [];
+ foreach ( $res as $row ) {
+ $title = Title::makeTitleSafe(
+ $row->page_namespace, $row->page_title
+ );
+ if ( $title == null ) {
+ continue;
+ }
+ $context = $this->extractContext(
+ $row->old_text, $this->target, $this->use_regex
+ );
+ $titles_for_edit[] = [ $title, $context ];
+ }
+ return $titles_for_edit;
+ }
+
+ /**
+ * Get Title object for the chosen category.
+ * @return Title
+ */
+ protected function getCategoryTitle() {
+ return Title::makeTitleSafe( NS_CATEGORY, $this->category );
+ }
+
+ /**
+ * Make sure we have a usable category
+ * @return bool
+ */
+ protected function hasBadCategory() {
+ // If no results were found, check to see if a bad
+ // category name was entered.
+ if ( !empty( $this->category ) ) {
+ if ( !$this->getCategoryTitle()->exists() ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Show them the way back since they chose a bad category
+ */
+ protected function showBadCategory() {
+ $link = Linker::link(
+ $this->getCategoryTitle(),
+ htmlspecialchars( ucfirst( $this->category ) )
+ );
+ $this->getOutput()->addHTML(
+ $this->msg( 'replacetext_nosuchcategory' )
+ ->rawParams( $link )->escaped()
+ );
+ $this->showLinkBack();
+ }
+
+ /**
+ * Show a link back to the start
+ */
+ protected function showLinkBack() {
+ $this->getOutput()->addHTML(
+ '<p>' .
+ Linker::link(
+ $this->getTitle(),
+ $this->msg( 'replacetext_return' )->escaped() )
+ . '</p>'
+ );
+ }
+
+ /**
+ * What to do if we don't match anything
+ */
+ protected function handleNoMatches() {
$out = $this->getOutput();
+ if ( $this->hasBadCategory() ) {
+ $this->showBadCategory();
+ } else {
+ if ( $this->edit_pages ) {
+ $out->addWikiMsg(
+ 'replacetext_noreplacement',
+
"<code><nowiki>{$this->target}</nowiki></code>"
+ );
+ }
+
+ if ( $this->move_pages ) {
+ $out->addWikiMsg(
+ 'replacetext_nomove',
+
"<code><nowiki>{$this->target}</nowiki></code>"
+ );
+ }
+ $this->showLinkBack();
+ }
+ }
+
+ /**
+ * Give the list of pages, maybe get a warning message
+ * @param array $titles_for_edit list of titles to edit
+ * @param array $titles_for_move list of titles to move
+ * @return string
+ */
+ protected function maybeShowWarningMsg( $titles_for_edit,
$titles_for_move ) {
+ $warning_msg = '';
+ if ( $this->replacement === '' ) {
+ $warning_msg = $this->msg(
+ 'replacetext_blankwarning'
+ )->text();
+ } elseif ( count( $titles_for_edit ) > 0 ) {
+ $res = Search::doSearchQuery(
+ $this->replacement,
+ $this->selected_namespaces,
+ $this->category,
+ $this->prefix,
+ $this->use_regex
+ );
+ $count = $res->numRows();
+ if ( $count > 0 ) {
+ $warning_msg = $this->msg(
'replacetext_warning' )
+ ->numParams( $count )
+ ->params(
'<code><nowiki>' .
+
$this->replacement .
+
'</nowiki></code>' )->text();
+ }
+ } elseif ( count( $titles_for_move ) > 0 ) {
+ $res = $this->getMatchingTitles(
+ $this->replacement,
+ $this->selected_namespaces,
+ $this->category,
+ $this->prefix, $this->use_regex
+ );
+ $count = $res->numRows();
+ if ( $count > 0 ) {
+ $warning_msg = $this->msg(
'replacetext_warning' )
+ ->numParams( $count )
+ ->params(
$this->replacement )->text();
+ }
+ }
+ if ( $warning_msg ) {
+ $this->getOutput()->addWikiText(
+ '<div class="errorbox">' . $warning_msg .
+ '</div><br clear="both" />'
+ );
+ }
+ }
+
+ /**
+ * Handle pages we have targeted
+ */
+ protected function doTargetRequest() {
+ $out = $this->getOutput();
+ wfProfileIn( __METHOD__ );
+
+ // first, check that at least one namespace has been
+ // picked, and that either editing or moving pages
+ // has been selected
+ if ( count( $this->selected_namespaces ) == 0 ) {
+ $this->showForm( 'replacetext_nonamespace' );
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+ if ( ! $this->edit_pages && ! $this->move_pages ) {
+ $this->showForm( 'replacetext_editormove' );
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+
+ $titles_for_edit = $titles_for_move = $unmoveable_titles = [];
+
+ // if user is replacing text within pages...
+ if ( $this->edit_pages ) {
+ $titles_for_edit = $this->getTitlesForEdit();
+ }
+ if ( $this->move_pages ) {
+ list( $titles_for_move, $unmoveable_titles )
+ = $this->getTitlesForMove();
+ }
+
+ if (
+ count( $titles_for_edit ) == 0
+ && count( $titles_for_move ) == 0
+ ) {
+ $this->handleNoMatches();
+ } else {
+ $this->maybeShowWarningMsg(
+ $titles_for_edit, $titles_for_move
+ );
+
+ $this->pageListForm(
+ $titles_for_edit, $titles_for_move,
$unmoveable_titles
+ );
+ }
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+
+ /**
+ * Header of the form
+ * @param string $warning_msg to show
+ */
+ protected function showFormHeader( $warning_msg = null ) {
+ $out = $this->getOutput();
$out->addHTML(
Xml::openElement(
'form',
@@ -302,7 +551,13 @@
$warning_msg
);
}
+ }
+ /**
+ * The boxes to specify search/replace text.
+ */
+ protected function showFormSearchAndReplace() {
+ $out = $this->getOutput();
$out->addHTML( '<table><tr><td style="vertical-align: top;">' );
$out->addWikiMsg( 'replacetext_originaltext' );
$out->addHTML( '</td><td>' );
@@ -310,13 +565,18 @@
// normal 'width: 100%', which causes the textarea to get
// zero width in IE
$out->addHTML(
- Xml::textarea( 'target', $this->target, 100, 5, [
'style' => 'width: auto;' ] )
+ Xml::textarea(
+ 'target', $this->target, 100, 5, [ 'style' =>
'width: auto;' ]
+ )
);
$out->addHTML( '</td></tr><tr><td style="vertical-align:
top;">' );
$out->addWikiMsg( 'replacetext_replacementtext' );
$out->addHTML( '</td><td>' );
$out->addHTML(
- Xml::textarea( 'replacement', $this->replacement, 100,
5, [ 'style' => 'width: auto;' ] )
+ Xml::textarea(
+ 'replacement', $this->replacement, 100, 5,
+ [ 'style' => 'width: auto;' ]
+ )
);
$out->addHTML( '</td></tr></table>' );
$out->addHTML( Xml::tags( 'p', null,
@@ -330,7 +590,13 @@
$this->msg( 'replacetext_regexdocu' )->text()
)
);
+ }
+ /**
+ * The namespace picker
+ */
+ protected function showFormNamespaces() {
+ $out = $this->getOutput();
// The interface is heavily based on the one in Special:Search.
$namespaces = SearchEngine::searchableNamespaces();
$tables = $this->namespaceTables( $namespaces );
@@ -343,9 +609,7 @@
// search interface exists only in some skins, like Vector -
// check for the presence of the 'powersearch-togglelabel'
// message to see if we can use this functionality here.
- if ( $this->msg( 'powersearch-togglelabel' )->isDisabled() ) {
- // do nothing
- } else {
+ if ( !$this->msg( 'powersearch-togglelabel' )->isDisabled() ) {
$out->addHTML(
Html::element(
'div',
@@ -357,41 +621,75 @@
Xml::element( 'div', [ 'class' => 'divider' ], '',
false ) .
"$tables\n</fieldset>"
);
+
+ // Add Javascript specific to Special:ReplaceText
+ $out->addModules( 'ext.ReplaceText.search' );
+ }
+
+ /**
+ * Our optional filters
+ */
+ protected function showFormOptionalFilters() {
+ $out = $this->getOutput();
+
// @todo FIXME: raw html messages
- $category_search_label = $this->msg(
'replacetext_categorysearch' )->escaped();
- $prefix_search_label = $this->msg( 'replacetext_prefixsearch'
)->escaped();
+ $category_search_label
+ = $this->msg( 'replacetext_categorysearch' )->escaped();
+ $prefix_search_label
+ = $this->msg( 'replacetext_prefixsearch' )->escaped();
$out->addHTML(
"<fieldset id=\"mw-searchoptions\">\n" .
- Xml::tags( 'h4', null, $this->msg(
'replacetext_optionalfilters' )->parse() ) .
+ Xml::tags(
+ 'h4', null,
+ $this->msg( 'replacetext_optionalfilters'
)->parse()
+ ) .
Xml::element( 'div', [ 'class' => 'divider' ], '',
false ) .
"<p>$category_search_label\n" .
- Xml::input( 'category', 20, $this->category, [ 'type'
=> 'text' ] ) . '</p>' .
- "<p>$prefix_search_label\n" .
- Xml::input( 'prefix', 20, $this->prefix, [ 'type' =>
'text' ] ) . '</p>' .
- "</fieldset>\n" .
- "<p>\n" .
+ Xml::input(
+ 'category', 20,
+ $this->category, [ 'type' => 'text' ]
+ ) . "</p><p>$prefix_search_label\n" .
+ Xml::input(
+ 'prefix', 20,
+ $this->prefix, [ 'type' => 'text' ]
+ ) . "</p></fieldset>\n<p>\n" .
Xml::checkLabel(
- $this->msg( 'replacetext_editpages' )->text(),
'edit_pages', 'edit_pages', true
- ) . '<br />' .
+ $this->msg( 'replacetext_editpages' )->text(),
+ 'edit_pages', 'edit_pages', true
+ ) . '<br>' .
Xml::checkLabel(
- $this->msg( 'replacetext_movepages' )->text(),
'move_pages', 'move_pages'
- ) . '<br />' .
+ $this->msg( 'replacetext_movepages' )->text(),
+ 'move_pages', 'move_pages'
+ ) . '<br>' .
Xml::checkLabel(
- $this->msg( 'replacetext_enotif' )->text(),
'doEnotif', 'doEnotif', true
+ $this->msg( 'replacetext_enotif' )->text(),
+ 'doEnotif', 'doEnotif', true
) .
"</p>\n" .
Xml::submitButton( $this->msg( 'replacetext_continue'
)->text() ) .
Xml::closeElement( 'form' )
);
- // Add Javascript specific to Special:Search
- $out->addModules( 'mediawiki.special.search' );
+ }
+
+ /**
+ * Show the ReplaceText form
+ * @param string $warning_msg to show, maybe
+ */
+ protected function showForm( $warning_msg = null ) {
+ $this->showFormHeader( $warning_msg );
+ $this->showFormSearchAndReplace();
+ $this->showFormNamespaces();
+ $this->showFormOptionalFilters();
}
/**
* Copied almost exactly from MediaWiki's SpecialSearch class, i.e.
* the search page
+ * @param array $namespaces to list
+ * @param int $rowsPerTable to show
+ * @return string
*/
- function namespaceTables( $namespaces, $rowsPerTable = 3 ) {
+ protected function namespaceTables( $namespaces, $rowsPerTable = 3 ) {
global $wgContLang;
// Group namespaces into rows according to subject.
// Try not to make too many assumptions about namespace
numbering.
@@ -406,17 +704,21 @@
if ( '' == $name ) {
$name = $this->msg( 'blanknamespace' )->text();
}
- $rows[$subj] .= Xml::openElement( 'td', [ 'style' =>
'white-space: nowrap' ] ) .
- Xml::checkLabel( $name, "ns{$ns}",
"mw-search-ns{$ns}", in_array( $ns, $namespaces ) ) .
- Xml::closeElement( 'td' ) . "\n";
+ $rows[$subj] .= Xml::openElement(
+ 'td', [ 'style' => 'white-space: nowrap' ]
+ ) . Xml::checkLabel(
+ $name, "ns{$ns}", "mw-search-ns{$ns}",
+ in_array( $ns, $namespaces )
+ ) . Xml::closeElement( 'td' ) . "\n";
}
$rows = array_values( $rows );
$numRows = count( $rows );
// Lay out namespaces in multiple floating two-column tables so
they'll
// be arranged nicely while still accommodating different
screen widths
// Float to the right on RTL wikis
- $tableStyle = $wgContLang->isRTL() ?
- 'float: right; margin: 0 0 0em 1em' : 'float: left;
margin: 0 1em 0em 0';
+ $tableStyle = $wgContLang->isRTL()
+ ? 'float: right; margin: 0 0 0em 1em'
+ : 'float: left; margin: 0 1em 0em 0';
// Build the final HTML table...
for ( $i = 0; $i < $numRows; $i += $rowsPerTable ) {
$tables .= Xml::openElement( 'table', [ 'style' =>
$tableStyle ] );
@@ -428,9 +730,78 @@
return $tables;
}
- function pageListForm( $titles_for_edit, $titles_for_move,
$unmoveable_titles ) {
- global $wgLang, $wgScriptPath;
+ /**
+ * Show the titles that should be edited
+ * @param array $titles_for_edit list of titles to show
+ */
+ protected function showTitlesForEdit( $titles_for_edit ) {
+ global $wgLang;
+ $out = $this->getOutput();
+ if ( count( $titles_for_edit ) > 0 ) {
+ $out->addWikiMsg(
+ 'replacetext_choosepagesforedit',
+ "<code><nowiki>{$this->target}</nowiki></code>",
+
"<code><nowiki>{$this->replacement}</nowiki></code>",
+ $wgLang->formatNum( count( $titles_for_edit ) )
+ );
+
+ foreach ( $titles_for_edit as $title_and_context ) {
+ /**
+ * @var $title Title
+ */
+ list( $title, $context ) = $title_and_context;
+ $out->addHTML(
+ Xml::check( $title->getArticleID(),
true ) .
+ Linker::link( $title ) .
+ " - <small>$context</small><br />\n"
+ );
+ }
+ $out->addHTML( '<br />' );
+ }
+ }
+
+ /**
+ * Show the titles slated to be moved
+ * @param array $titles_for_move the titles
+ */
+ protected function showTitlesForMove( $titles_for_move ) {
+ global $wgLang;
+ $out = $this->getOutput();
+
+ if ( count( $titles_for_move ) > 0 ) {
+ $out->addWikiMsg(
+ 'replacetext_choosepagesformove',
+ $this->target, $this->replacement,
+ $wgLang->formatNum( count( $titles_for_move ) )
+ );
+ foreach ( $titles_for_move as $title ) {
+ $out->addHTML(
+ Xml::check( 'move-' .
$title->getArticleID(), true ) .
+ Linker::link( $title ) . "<br />\n"
+ );
+ }
+ $out->addHTML( '<br />' );
+ $out->addWikiMsg( 'replacetext_formovedpages' );
+ $out->addHTML(
+ Xml::checkLabel(
+ $this->msg(
'replacetext_savemovedpages' )->text(),
+ 'create-redirect', 'create-redirect',
true ) . "<br>" .
+ Xml::checkLabel(
+ $this->msg(
'replacetext_watchmovedpages' )->text(),
+ 'watch-pages', 'watch-pages', false
+ ) . '<br>' .
+ Xml::checkLabel(
+ $this->msg( 'replacetext_doenotif'
)->text(),
+ 'doEnotif', 'doEnotif', true ) . '<br>'
+ );
+ }
+ }
+
+ /**
+ * Show the page list form's header
+ */
+ protected function showPageListHeader() {
$out = $this->getOutput();
$formOpts = [
@@ -454,64 +825,28 @@
$out->addHTML( Html::hidden( 'ns' . $ns, 1 ) );
}
- $out->addScriptFile(
"$wgScriptPath/extensions/ReplaceText/ReplaceText.js" );
+ $out->addModules( 'ext.ReplaceText.form' );
+ }
- if ( count( $titles_for_edit ) > 0 ) {
- $out->addWikiMsg(
- 'replacetext_choosepagesforedit',
- "<code><nowiki>{$this->target}</nowiki></code>",
-
"<code><nowiki>{$this->replacement}</nowiki></code>",
- $wgLang->formatNum( count( $titles_for_edit ) )
- );
-
- foreach ( $titles_for_edit as $title_and_context ) {
- /**
- * @var $title Title
- */
- list( $title, $context ) = $title_and_context;
- $out->addHTML(
- Xml::check( $title->getArticleID(),
true ) .
- Linker::link( $title ) . " -
<small>$context</small><br />\n"
- );
- }
- $out->addHTML( '<br />' );
- }
-
- if ( count( $titles_for_move ) > 0 ) {
- $out->addWikiMsg(
- 'replacetext_choosepagesformove',
- $this->target, $this->replacement,
$wgLang->formatNum( count( $titles_for_move ) )
- );
- foreach ( $titles_for_move as $title ) {
- $out->addHTML(
- Xml::check( 'move-' .
$title->getArticleID(), true ) .
- Linker::link( $title ) . "<br />\n"
- );
- }
- $out->addHTML( '<br />' );
- $out->addWikiMsg( 'replacetext_formovedpages' );
- $out->addHTML(
- Xml::checkLabel(
- $this->msg(
'replacetext_savemovedpages' )->text(),
- 'create-redirect',
'create-redirect', true ) . "<br />\n" .
- Xml::checkLabel(
- $this->msg(
'replacetext_watchmovedpages' )->text(),
- 'watch-pages', 'watch-pages', false ) .
'<br />' .
- Xml::checkLabel(
- $this->msg( 'replacetext_doenotif'
)->text(),
- 'doEnotif', 'doEnotif', true ) . '<br
/>'
- );
- $out->addHTML( '<br />' );
- }
-
+ /**
+ * Show the submit button
+ * @param array $titles_for_edit list of titles to edit
+ * @param array $titles_for_move list of titles to move
+ * @param int $limit if there are more show the invert selection
+ */
+ protected function showPageListFooter(
+ $titles_for_edit, $titles_for_move, $limit
+ ) {
+ $out = $this->getOutput();
$out->addHTML(
"<br />\n" .
- Xml::submitButton( $this->msg( 'replacetext_replace'
)->text() ) . "\n"
+ Xml::submitButton(
+ $this->msg( 'replacetext_replace' )->text()
+ ) . "\n"
);
-
// Only show "invert selections" link if there are more than
// five pages.
- if ( count( $titles_for_edit ) + count( $titles_for_move ) > 5
) {
+ if ( count( $titles_for_edit ) + count( $titles_for_move ) >
$limit ) {
$buttonOpts = [
'type' => 'button',
'value' => $this->msg(
'replacetext_invertselections' )->text(),
@@ -524,9 +859,21 @@
}
$out->addHTML( '</form>' );
+ }
+
+ /**
+ * Show the unmovable titles, if any
+ * @param array $unmoveable_titles list of titles
+ */
+ protected function showUnmoveableTitles( $unmoveable_titles ) {
+ global $wgLang;
+ $out = $this->getOutput();
if ( count( $unmoveable_titles ) > 0 ) {
- $out->addWikiMsg( 'replacetext_cannotmove',
$wgLang->formatNum( count( $unmoveable_titles ) ) );
+ $out->addWikiMsg(
+ 'replacetext_cannotmove',
+ $wgLang->formatNum( count( $unmoveable_titles )
)
+ );
$text = "<ul>\n";
foreach ( $unmoveable_titles as $title ) {
$text .= "<li>" . Linker::link( $title ) . "<br
/>\n";
@@ -537,30 +884,32 @@
}
/**
- * Extract context and highlights search text
- *
- * @todo The bolding needs to be fixed for regular expressions.
+ * Get the form for the page lists given
+ * @param array $titles_for_edit list of titles to edit
+ * @param array $titles_for_move list of titles to move
+ * @param array $unmoveable_titles titles that can't be moved
*/
- function extractContext( $text, $target, $use_regex = false ) {
- global $wgLang;
+ protected function pageListForm(
+ $titles_for_edit, $titles_for_move, $unmoveable_titles
+ ) {
+ $this->showPageListHeader();
- wfProfileIn( __METHOD__ );
+ $this->showTitlesForEdit( $titles_for_edit );
+ $this->showTitlesForMove( $titles_for_move );
+ $this->showPageListFooter( $titles_for_edit, $titles_for_move,
5 );
+ $this->showUnmoveableTitles( $unmoveable_titles );
+ }
+
+ /**
+ * Get the cuts of text
+ * @param array $poss list of positions
+ * @param string $target to match on
+ * @return array
+ */
+ protected function getCuts( $poss, $target ) {
+ $cuts = [];
$cw = $this->getUser()->getOption( 'contextchars', 40 );
- // Get all indexes
- if ( $use_regex ) {
- preg_match_all( "/$target/Uu", $text, $matches,
PREG_OFFSET_CAPTURE );
- } else {
- $targetq = preg_quote( $target, '/' );
- preg_match_all( "/$targetq/", $text, $matches,
PREG_OFFSET_CAPTURE );
- }
-
- $poss = [];
- foreach ( $matches[0] as $_ ) {
- $poss[] = $_[1];
- }
-
- $cuts = [];
// @codingStandardsIgnoreStart
for ( $i = 0; $i < count( $poss ); $i++ ) {
// @codingStandardsIgnoreEnd
@@ -573,36 +922,101 @@
$len += $poss[$i + 1] - $poss[$i];
$i++;
} else {
- break; // Can't merge, exit the inner
loop
+ // Can't merge, exit the inner loop
+ break;
}
}
$cuts[] = [ $index, $len ];
}
+ return $cuts;
+ }
+
+ /**
+ * Extract context and highlights search text
+ * @param string $text string to match
+ * @param string $target string to perform match on
+ * @param bool $use_regex treat text as regex or no
+ * @return string
+ * @todo The bolding needs to be fixed for regular expressions.
+ */
+ protected function extractContext( $text, $target, $use_regex = false )
{
+ wfProfileIn( __METHOD__ );
+
+ // Get all indexes
+ if ( $use_regex ) {
+ preg_match_all(
+ "/$target/Uu", $text, $matches,
PREG_OFFSET_CAPTURE
+ );
+ } else {
+ $targetq = preg_quote( $target, '/' );
+ preg_match_all(
+ "/$targetq/", $text, $matches,
PREG_OFFSET_CAPTURE
+ );
+ }
+
+ $poss = [];
+ foreach ( $matches[0] as $_ ) {
+ $poss[] = $_[1];
+ }
+
+ $cuts = $this->getCuts( $poss, $target );
+
+ $context = $this->getCutContext( $cuts, $text, $target,
$use_regex );
+
+ wfProfileOut( __METHOD__ );
+ return $context;
+ }
+
+ /**
+ * Get the context of the cut
+ * @param array $cuts list of cuts
+ * @param string $text matching string
+ * @param string $target string to perform match on
+ * @param bool $use_regex treat text as regex or no
+ * @return string
+ */
+ protected function getCutContext( $cuts, $text, $target, $use_regex ) {
+ global $wgLang;
+ $cw = $this->getUser()->getOption( 'contextchars', 40 );
$context = '';
foreach ( $cuts as $_ ) {
list( $index, $len, ) = $_;
$context .= $this->convertWhiteSpaceToHTML(
- $wgLang->truncate( substr( $text, 0, $index ),
- $cw, '...', false )
+ $wgLang->truncate(
+ substr( $text, 0, $index ), - $cw,
'...', false
+ )
);
- $snippet = $this->convertWhiteSpaceToHTML( substr(
$text, $index, $len ) );
+ $snippet = $this->convertWhiteSpaceToHTML(
+ substr( $text, $index, $len )
+ );
if ( $use_regex ) {
$targetStr = "/$target/Uu";
} else {
- $targetq = preg_quote(
$this->convertWhiteSpaceToHTML( $target ), '/' );
+ $targetq = preg_quote(
+ $this->convertWhiteSpaceToHTML( $target
), '/'
+ );
$targetStr = "/$targetq/i";
}
- $context .= preg_replace( $targetStr, '<span
class="searchmatch">\0</span>', $snippet );
-
- $context .= $this->convertWhiteSpaceToHTML(
- $wgLang->truncate( substr( $text, $index + $len
), $cw, '...', false )
+ $context .= preg_replace(
+ $targetStr, '<span
class="rt-searchmatch">\0</span>', $snippet
);
+
+ $context .= $this->convertWhiteSpaceToHTML(
$wgLang->truncate(
+ substr( $text, $index + $len ), $cw, '...',
false
+ ) );
}
- wfProfileOut( __METHOD__ );
+ $this->getOutput()->addModules( 'ext.ReplaceText.results' );
+
return $context;
}
- private function convertWhiteSpaceToHTML( $msg ) {
+ /**
+ * convert the w/s to something htmlish
+ * @param string $msg to convert
+ * @return string
+ */
+ protected function convertWhiteSpaceToHTML( $msg ) {
$msg = htmlspecialchars( $msg );
$msg = preg_replace( '/^ /m', '  ', $msg );
$msg = preg_replace( '/ $/m', '  ', $msg );
@@ -611,7 +1025,18 @@
return $msg;
}
- function getMatchingTitles( $str, $namespaces, $category, $prefix,
$use_regex = false ) {
+ /**
+ * Get a list of matching titles
+ * @param string $str what to match
+ * @param array $namespaces where to look
+ * @param string $category in which category
+ * @param string $prefix starts with
+ * @param bool $use_regex match uses regular expressions
+ * @return ResultWrapper
+ */
+ protected function getMatchingTitles(
+ $str, array $namespaces, $category, $prefix, $use_regex = false
+ ) {
$dbr = wfGetDB( DB_REPLICA );
$tables = [ 'page' ];
@@ -619,24 +1044,31 @@
$str = str_replace( ' ', '_', $str );
if ( $use_regex ) {
- $comparisonCond = ReplaceTextSearch::regexCond( $dbr,
'page_title', $str );
+ $comparisonCond = Search::regexCond(
+ $dbr, 'page_title', $str
+ );
} else {
$any = $dbr->anyString();
- $comparisonCond = 'page_title ' . $dbr->buildLike(
$any, $str, $any );
+ $comparisonCond = 'page_title ' .
+ $dbr->buildLike( $any,
$str, $any );
}
$conds = [
$comparisonCond,
'page_namespace' => $namespaces,
];
- ReplaceTextSearch::categoryCondition( $category, $tables,
$conds );
- ReplaceTextSearch::prefixCondition( $prefix, $conds );
+ Search::categoryCondition( $category, $tables, $conds );
+ Search::prefixCondition( $prefix, $conds );
$sort = [ 'ORDER BY' => 'page_namespace, page_title' ];
return $dbr->select( $tables, $vars, $conds, __METHOD__, $sort
);
}
- protected function getGroupName() {
+ /**
+ * Where we want this special page to show up
+ * @return string
+ */
+ public function getGroupName() {
return 'wiki';
}
}
diff --git a/extension.json b/extension.json
index b239331..a4f4ccf 100644
--- a/extension.json
+++ b/extension.json
@@ -1,9 +1,10 @@
{
"name": "Replace Text",
- "version": "1.2",
+ "version": "1.3",
"author": [
"Yaron Koren",
"Niklas Laxström",
+ "[https://hexmode.com/ Mark A. Hershberger]",
"..."
],
"url": "https://www.mediawiki.org/wiki/Extension:Replace_Text",
@@ -19,10 +20,10 @@
"replacetext"
],
"SpecialPages": {
- "ReplaceText": "SpecialReplaceText"
+ "ReplaceText": "MediaWiki\\Extension\\ReplaceText\\SpecialPage"
},
"JobClasses": {
- "replaceText": "ReplaceTextJob"
+ "replaceText": "MediaWiki\\Extension\\ReplaceText\\Job"
},
"MessagesDirs": {
"ReplaceText": [
@@ -33,18 +34,44 @@
"ReplaceTextAlias": "ReplaceText.alias.php"
},
"AutoloadClasses": {
- "ReplaceTextHooks": "ReplaceText.hooks.php",
- "SpecialReplaceText": "SpecialReplaceText.php",
- "ReplaceTextJob": "ReplaceTextJob.php",
- "ReplaceTextSearch": "ReplaceTextSearch.php"
+ "MediaWiki\\Extension\\ReplaceText\\Hook":
"ReplaceText.hooks.php",
+ "MediaWiki\\Extension\\ReplaceText\\SpecialPage":
"SpecialReplaceText.php",
+ "MediaWiki\\Extension\\ReplaceText\\Job": "ReplaceTextJob.php",
+ "MediaWiki\\Extension\\ReplaceText\\Search":
"ReplaceTextSearch.php"
},
"Hooks": {
"AdminLinks": [
- "ReplaceTextHooks::addToAdminLinks"
+
"MediaWiki\\Extension\\ReplaceText\\Hook::addToAdminLinks"
]
},
"config": {
"ReplaceTextUser": null
},
+ "ResourceModules": {
+ "ext.ReplaceText.results": {
+ "styles": "ReplaceText.css"
+ },
+ "ext.ReplaceText.form": {
+ "scripts": "ReplaceText.js",
+ "dependencies": [
+ "mediawiki.special"
+ ]
+ },
+ "ext.ReplaceText.search": {
+ "scripts": "ReplaceTextSearch.js",
+ "dependencies": [
+ "oojs-ui-core"
+ ],
+ "messages": [
+ "powersearch-togglelabel",
+ "powersearch-toggleall",
+ "powersearch-togglenone"
+ ]
+ }
+ },
+ "ResourceFileModulePaths": {
+ "localBasePath": "",
+ "remoteExtPath": "ReplaceText"
+ },
"manifest_version": 1
}
diff --git a/replaceAll.php b/replaceAll.php
index 5f91d70..a93c52a 100755
--- a/replaceAll.php
+++ b/replaceAll.php
@@ -4,7 +4,7 @@
* Insert jobs into the job queue to replace text bits.
* Or execute immediately... your choice.
*
- * Copyright © 2014 Mark A. Hershberger <[email protected]>
+ * Copyright (c) 2014 NicheWork, LLC
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,16 +31,26 @@
* @link https://www.mediawiki.org/wiki/Extension:Replace_Text
*
*/
-// @codingStandardsIgnoreStart
-$IP = "../..";
+
+namespace MediaWiki\\Extension\\ReplaceText;
+
+use Maintenance;
+use MWException;
+use Title;
+use User;
+
+$IP = __DIR__ . '/../../';
if ( !is_readable( "$IP/maintenance/Maintenance.php" ) ) {
$IP = getenv( "MW_INSTALL_PATH" );
if ( $IP === false ) {
- die( "MW_INSTALL_PATH needs to be set to your MediaWiki
installation." );
+ if ( !is_readable( getcwd() . "/maintenance/Maintenance.php" )
) {
+ die( "MW_INSTALL_PATH needs to be set to your MediaWiki
installation." );
+ }
+ $IP = getcwd();
}
}
-require_once ( "$IP/maintenance/Maintenance.php" );
-// @codingStandardsIgnoreEnd
+
+require_once "$IP/maintenance/Maintenance.php";
/**
* Maintenance script that generates a plaintext link dump.
@@ -49,7 +59,7 @@
* @SuppressWarnings(StaticAccess)
* @SuppressWarnings(LongVariable)
*/
-class ReplaceText extends Maintenance {
+class ReplaceAll extends Maintenance {
protected $user;
protected $target;
protected $replacement;
@@ -301,7 +311,7 @@
'doEnotif' => $this->doEnotif
];
echo "Replacing on $title... ";
- $job = new ReplaceTextJob( $title, $param, 0 );
+ $job = new Job( $title, $param, 0 );
if ( $job->run() !== true ) {
$this->error( "Trouble on the page '$title'." );
}
@@ -365,7 +375,7 @@
}
echo "\n";
}
- $res = ReplaceTextSearch::doSearchQuery(
$target,
+ $res = Search::doSearchQuery( $target,
$this->namespaces, $this->category,
$this->prefix, $useRegex );
if ( $res->numRows() === 0 ) {
@@ -398,5 +408,5 @@
}
}
-$maintClass = "ReplaceText";
+$maintClass = "ReplaceText\\ReplaceAll";
require_once RUN_MAINTENANCE_IF_MAIN;
--
To view, visit https://gerrit.wikimedia.org/r/398618
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I251188efa2fc1fbf0563b0e1f792a912d077d8c9
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/ReplaceText
Gerrit-Branch: master
Gerrit-Owner: MarkAHershberger <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits