jenkins-bot has submitted this change and it was merged.
Change subject: Make protect.py more wiki-agnostic ......................................................................
Make protect.py more wiki-agnostic
- Removed most harcoded protection level and types - Applies protection levels only if the make sense depending on Page.applicable_protections().
- The ProtectionRobot class extends pywikibot.Bot - 'protections' dict replaces 'edit', 'move' and 'create' - the 'always' switch is now a standard option
- Clarified "-unprotect" option and added "-default[:<level>]" If a protection type (e.g. "edit") is not defined, it chooses by default "sysop". "-default" overwrites this and "-unprotect" acts like "-default:none". Only "-default" will change only the explicitily set protection types.
- Fixed in Page.protect() that prompt only defaults to True if protections was None ("deprecated mode")
Bug: 55057 - implicitly enable the 'upload'-type protection Change-Id: I41146fa0c161a242834ab0b1fe2954c7b89d7fd1 --- M pywikibot/page.py M scripts/protect.py 2 files changed, 152 insertions(+), 109 deletions(-)
Approvals: John Vandenberg: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/page.py b/pywikibot/page.py index 969d02b..7108a2b 100644 --- a/pywikibot/page.py +++ b/pywikibot/page.py @@ -1583,7 +1583,7 @@
@deprecate_arg("throttle", None) def protect(self, edit=False, move=False, create=None, upload=None, - unprotect=False, reason=None, prompt=True, protections=None, + unprotect=False, reason=None, prompt=None, protections=None, **kwargs): """(Un)protect a wiki page. Requires administrator status.
@@ -1596,7 +1596,8 @@ @type protections: dict @param reason: Reason for the action @type reason: basestring - @param prompt: Whether to ask user for confirmation + @param prompt: Whether to ask user for confirmation (deprecated). + Defaults to protections is None @type prompt: bool """ def deprecated(value, arg_name): @@ -1637,6 +1638,8 @@ protections = dict( [(p_type, "") for p_type in self.applicable_protections()]) answer = 'y' + if called_using_deprecated_arg and prompt is None: + prompt = True if prompt: pywikibot.bot.warning(u'"prompt" argument of protect() is ' 'deprecated') diff --git a/scripts/protect.py b/scripts/protect.py index d7a55b1..256d76b 100644 --- a/scripts/protect.py +++ b/scripts/protect.py @@ -1,41 +1,43 @@ # -*- coding: utf-8 -*- """ This script can be used to protect and unprotect pages en masse. + Of course, you will need an admin account on the relevant wiki. - - These command line parameters can be used to specify which pages to work on:
¶ms;
Furthermore, the following command line parameters are supported:
--always: Don't prompt to protect pages, just do it. +-always Don't prompt to protect pages, just do it.
--summary: Supply a custom edit summary. +-summary: Supply a custom edit summary. Tries to generate summary from + the page selector. If no summary is supplied or couldn't + determine one from the selector it'll ask for one.
--unprotect: Actually unprotect pages instead of protecting +-unprotect Acts like "default:none"
--edit:PROTECTION_LEVEL Set edit protection level to PROTECTION_LEVEL +-default: Sets the default protection level (default 'sysop'). If no + level is defined it doesn't change unspecified levels.
--move:PROTECTION_LEVEL Set move protection level to PROTECTION_LEVEL +-[type]:[level] Set [type] protection level to [level]
-## Without support ## -## -create:PROTECTION_LEVEL Set move protection level to PROTECTION_LEVEL ## +Usual values for [level] are: sysop, autoconfirmed, none; further levels may be +provided by some wikis.
-Values for PROTECTION_LEVEL are: sysop, autoconfirmed, none. -If an operation parameter (edit, move or create) is not specified, default -protection level is 'sysop' (or 'none' if -unprotect). +For all protection types (edit, move, etc.) it chooses the default protection +level. This is "sysop" or "none" if -unprotect was selected. If multiple +-unprotect or -default are used, only the last occurence is applied.
Usage: python protect.py <OPTIONS>
Examples:
-Protect everything in the category "To protect" prompting. - python protect.py -cat:"To protect" -always +Protect everything in the category 'To protect' prompting. + python protect.py -cat:'To protect'
-Unprotect all pages listed in text file "unprotect.txt" without prompting. - python protect.py -file:unprotect.txt -unprotect +Unprotect all pages listed in text file 'unprotect.txt' without prompting. + python protect.py -file:unprotect.txt -unprotect -always """
# @@ -50,8 +52,7 @@ #
import pywikibot -from pywikibot import i18n -from pywikibot import pagegenerators +from pywikibot import i18n, pagegenerators, Bot
# This is required for the text that is shown when you run this script # with the parameter -help. @@ -60,141 +61,180 @@ }
-class ProtectionRobot: +class ProtectionRobot(Bot): + """ This bot allows protection of pages en masse. """
- def __init__(self, generator, summary, always=False, unprotect=False, - edit='sysop', move='sysop', create='sysop'): + def __init__(self, generator, protections, **kwargs): """ - Arguments: - * generator - A page generator. - * always - Protect without prompting? - * edit, move, create - protection level for these operations - * unprotect - unprotect pages (and ignore edit, move, create params) + Create a new ProtectionRobot.
+ @param generator: the page generator + @type generator: generator + @param protections: protections as a dict with "type": "level" + @type protections: dict + @param kwargs: additional arguments directly feed to Bot.__init__() """ + self.availableOptions.update({ + 'summary': None, + }) + super(ProtectionRobot, self).__init__(**kwargs) self.generator = generator - self.summary = summary - self.prompt = not always - self.unprotect = unprotect - self.edit = edit - self.move = move + self.protections = protections
def run(self): - """ Start the bot's action. - Loop through everything in the page generator and (un)protect it. + """Start the bot's action.
+ Loop through everything in the page generator and apply the + protections. """ for page in self.generator: pywikibot.output(u'Processing page %s' % page.title()) - page.protect(unprotect=self.unprotect, reason=self.summary, - prompt=self.prompt, edit=self.edit, move=self.move) + if not self.getOption('always'): + choice = pywikibot.inputChoice( + u'Do you want to change the protection level of %s?' + % page.title(asLink=True, forceInterwiki=True), + ['yes', 'No', 'all'], + ['y', 'N', 'a'], + 'n') + if choice == 'n': + continue + elif choice == 'a': + self.option['always'] = True + applicable = page.applicable_protections() + protections = dict( + [prot for prot in self.protections if prot[0] in applicable]) + page.protect(reason=self.getOption('summary'), + protections=protections)
-def choiceProtectionLevel(operation, default, protectionLevels): - """ Asks a valid protection level for "operation". - Returns the protection level chosen by user. +def check_protection_level(operation, level, levels, default=None): + """Check if the protection level is valid or asks if necessary.
+ @return a valid protection level + @rtype string """ - default = default[0] - firstChar = map(lambda level: level[0], protectionLevels) - choiceChar = pywikibot.inputChoice('Choice a protection level to %s:' - % operation, - protectionLevels, firstChar, - default=default) - for level in protectionLevels: - if level.startswith(choiceChar): - return level + if level not in levels: + first_char = [] + default_char = None + num = 1 + for level in levels: + for c in level: + if c not in first_char: + first_char.append(c) + break + else: + first_char.append(unicode(num)) + num += 1 + if level == default: + default_char = first_char[-1] + choice = pywikibot.inputChoice('Choice a protection level to %s:' + % operation, levels, first_char, + default=default_char) + + return levels[first_char.index(choice)] + else: + return level
def main(*args): - protectionLevels = ['sysop', 'autoconfirmed', 'none'] - - # This factory is responsible for processing command line arguments - # that are also used by other scripts and that determine on which pages - # to work on. - pageName = '' - summary = None - always = False + options = {} + message_properties = {} generator = None - edit = '' - move = '' - defaultProtection = 'sysop' + protections = {} + default_level = 'sysop' + default_summaries = { + 'cat': 'category', + 'links': 'links', + 'ref': 'ref', + 'imageused': 'images', + 'file': 'simple', + }
# read command line parameters local_args = pywikibot.handleArgs(*args) genFactory = pagegenerators.GeneratorFactory() - mysite = pywikibot.Site() + site = pywikibot.Site()
+ protection_levels = set(site.protection_levels()) + protection_types = site.protection_types() + if '' in protection_levels: + protection_levels.remove('') + protection_levels.add('none') for arg in local_args: if arg == '-always': - always = True + options['always'] = True elif arg.startswith('-summary'): if len(arg) == len('-summary'): - summary = pywikibot.input(u'Enter a reason for the protection:') + # fill dummy value to prevent automatic generation + options['summary'] = None else: - summary = arg[len('-summary:'):] + options['summary'] = arg[len('-summary:'):] elif arg.startswith('-images'): pywikibot.output('\n\03{lightred}-image option is deprecated. ' 'Please use -imagelinks instead.\03{default}\n') local_args.append('-imagelinks' + arg[7:]) elif arg.startswith('-unprotect'): - defaultProtection = 'none' - elif arg.startswith('-edit'): - edit = arg[len('-edit:'):] - if edit not in protectionLevels: - edit = choiceProtectionLevel( - 'edit', defaultProtection, protectionLevels) - elif arg.startswith('-move'): - move = arg[len('-move:'):] - if move not in protectionLevels: - move = choiceProtectionLevel( - 'move', defaultProtection, protectionLevels) - elif arg.startswith('-create'): - create = arg[len('-create:'):] - if create not in protectionLevels: - create = choiceProtectionLevel( - 'create', defaultProtection, protectionLevels) + default_level = 'none' + elif arg.startswith('-default'): + if len(arg) == len('-default'): + default_level = None + else: + default_level = arg[len('-default:'):] else: - genFactory.handleArg(arg) - found = arg.find(':') + 1 - if found: - pageName = arg[found:] + is_p_type = False + if arg.startswith('-'): + delimiter = arg.find(':') + if delimiter > 0: + p_type = arg[1:delimiter] + level = arg[delimiter + 1:] + if p_type in protection_types: + protections[p_type] = level + is_p_type = True + if not is_p_type: + if not genFactory.handleArg(arg): + raise ValueError('Unknown parameter "{}"'.format(arg)) + found = arg.find(':') + 1 + if found: + message_properties.update({'cat': arg[found:], + 'page': arg[found:]})
- if not summary: - if pageName: - if arg.startswith('cat') or arg.startswith('subcats'): - summary = i18n.twtranslate(mysite, 'protect-category', - {'cat': pageName}) - elif arg.startswith('links'): - summary = i18n.twtranslate(mysite, 'protect-links', - {'page': pageName}) - elif arg.startswith('ref'): - summary = i18n.twtranslate(mysite, 'protect-ref', - {'page': pageName}) - elif arg.startswith('imageused'): - summary = i18n.twtranslate(mysite, 'protect-images', - {'page': pageName}) - elif arg.startswith('file'): - summary = i18n.twtranslate(mysite, 'protect-simple') + if 'summary' not in options: + generator_type = arg[1:found] if found > 0 else arg[1:] + if generator_type in default_summaries: + message_type = default_summaries[generator_type] + if message_type == 'simple' or message_properties: + options['summary'] = i18n.twtranslate( + site, 'protect-{}'.format(message_type), + message_properties)
generator = genFactory.getCombinedGenerator() # We are just protecting pages, so we have no need of using a preloading # page generator to actually get the text of those pages. if generator: - if summary is None: - summary = pywikibot.input(u'Enter a reason for the %sprotection:' - % ['', 'un'][protectionLevels == 'none']) - if not edit: - edit = defaultProtection - if not move: - move = defaultProtection - bot = ProtectionRobot(generator, summary, always, edit=edit, move=move) + if default_level: + default_level = check_protection_level('Default level', + default_level, + protection_levels) + # set the default value for all + # None (not the string 'none') will be ignored by Site.protect() + combined_protections = dict([ + (p_type, default_level) for p_type in protection_types]) + for p_type, level in protections.items(): + level = check_protection_level(p_type, level, protection_levels, + default_level) + if level == 'none': + level = '' + combined_protections[p_type] = level + if not options.get('summary'): + options['summary'] = pywikibot.input( + u'Enter a reason for the protection change:') + bot = ProtectionRobot(generator, combined_protections, **options) bot.run() else: # Show help text from the top of this file pywikibot.showHelp()
-if __name__ == "__main__": +if __name__ == '__main__': main()
pywikibot-commits@lists.wikimedia.org