http://centenaire.boulognebillancourt.com/mediawiki/api.php?action=feedcontributions&user=Mr.+Stradivarius&feedformat=atomwikivbb - Contributions de l’utilisateur [fr]2024-03-29T12:10:19ZContributions de l’utilisateurMediaWiki 1.23.1http://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Protection_banner/configModule:Protection banner/config2014-08-14T05:00:48Z<p>Mr. Stradivarius : remove Template:Pp-protected from the wrappers table, as it is now a redirect to Template:Pp</p>
<hr />
<div>-- This module provides configuration data for [[Module:Protection banner]].<br />
<br />
return {<br />
<br />
--------------------------------------------------------------------------------<br />
--<br />
-- BANNER DATA<br />
--<br />
--------------------------------------------------------------------------------<br />
<br />
--[[<br />
-- Banner data consists of six fields:<br />
-- * text - the main protection text that appears at the top of protection<br />
-- banners.<br />
-- * explanation - the text that appears below the main protection text, used<br />
-- to explain the details of the protection.<br />
-- * tooltip - the tooltip text you see when you move the mouse over a small<br />
-- padlock icon.<br />
-- * link - the page that the small padlock icon links to.<br />
-- * alt - the alt text for the small padlock icon. This is also used as tooltip<br />
-- text for the large protection banners.<br />
-- * image - the padlock image used in both protection banners and small padlock<br />
-- icons.<br />
--<br />
-- The module checks in three separate tables to find a value for each field.<br />
-- First it checks the banners table, which has values specific to the reason<br />
-- for the page being protected. Then the module checks the defaultBanners<br />
-- table, which has values specific to each protection level. Finally, the<br />
-- module checks the masterBanner table, which holds data for protection<br />
-- templates to use if no data has been found in the previous two tables.<br />
--<br />
-- The values in the banner data can take parameters. These are specified<br />
-- using ${TEXTLIKETHIS} (a dollar sign preceding a parameter name<br />
-- enclosed in curly braces).<br />
--<br />
-- Available parameters:<br />
--<br />
-- ${CURRENTVERSION} - a link to the page history or the move log, with the<br />
-- display message "current-version-edit-display" or<br />
-- "current-version-move-display".<br />
--<br />
-- ${EDITREQUEST} - a link to create an edit request for the current page.<br />
--<br />
-- ${EXPIRY} - the protection expiry date in the format DD Month YYYY. If<br />
-- protection is indefinite or is not set, this is the blank string.<br />
--<br />
-- ${EXPLANATIONBLURB} - an explanation blurb, e.g. "Please discuss any changes<br />
-- on the talk page; you may submit a request to ask an administrator to make<br />
-- an edit if it is minor or supported by consensus."<br />
--<br />
-- ${IMAGELINK} - a link to set the image to, depending on the protection<br />
-- action and protection level.<br />
--<br />
-- ${INTROBLURB} - the PROTECTIONBLURB parameter, plus the expiry if an expiry<br />
-- is set. E.g. "Editing of this page by new or unregistered users is currently <br />
-- disabled until dd Month YYYY."<br />
--<br />
-- ${INTROFRAGMENT} - the same as ${INTROBLURB}, but without final punctuation<br />
-- so that it can be used in run-on sentences.<br />
--<br />
-- ${PAGETYPE} - the type of the page, e.g. "article" or "template".<br />
-- Defined in the cfg.pagetypes table.<br />
--<br />
-- ${PROTECTIONBLURB} - a blurb explaining the protection level of the page, e.g.<br />
-- "Editing of this page by new or unregistered users is currently disabled"<br />
--<br />
-- ${PROTECTIONDATE} - the protection date, if it has been supplied to the<br />
-- template.<br />
--<br />
-- ${PROTECTIONLEVEL} - the protection level, e.g. "fully protected" or<br />
-- "semi-protected".<br />
--<br />
-- ${PROTECTIONLOG} - a link to the protection log or the pending changes log,<br />
-- depending on the protection action.<br />
--<br />
-- ${TALKPAGE} - a link to the talk page. If a section is specified, links<br />
-- straight to that talk page section.<br />
--<br />
-- ${TOOLTIPBLURB} - uses the PAGETYPE, PROTECTIONTYPE and EXPIRY parameters to<br />
-- create a blurb like "This template is semi-protected", or "This article is<br />
-- move-protected until DD Month YYYY".<br />
--<br />
-- ${VANDAL} - links for the specified username (or the root page name)<br />
-- using Module:Vandal-m.<br />
--<br />
-- Functions<br />
--<br />
-- For advanced users, it is possible to use Lua functions instead of strings<br />
-- in the banner config tables. Using functions gives flexibility that is not<br />
-- possible just by using parameters. Functions take two arguments, the<br />
-- protection object and the template arguments, and they must output a string.<br />
--<br />
-- For example:<br />
--<br />
-- text = function (protectionObj, args)<br />
-- if protectionObj.level == 'autoconfirmed' then<br />
-- return 'foo'<br />
-- else<br />
-- return 'bar'<br />
-- end<br />
-- end<br />
--<br />
-- Some protection object properties and methods that may be useful:<br />
-- protectionObj.action - the protection action<br />
-- protectionObj.level - the protection level<br />
-- protectionObj.reason - the protection reason<br />
-- protectionObj.expiry - the expiry. Nil if unset, the string "indef" if set<br />
-- to indefinite, and the protection time in unix time if temporary.<br />
-- protectionObj.protectionDate - the protection date in unix time, or nil if<br />
-- unspecified.<br />
-- protectionObj.bannerConfig - the banner config found by the module. Beware<br />
-- of editing the config field used by the function, as it could create an<br />
-- infinite loop.<br />
-- protectionObj:isProtected - returns a boolean showing whether the page is<br />
-- protected.<br />
-- protectionObj:isTemporary - returns a boolean showing whether the expiry is<br />
-- temporary.<br />
-- protectionObj:isIncorrect - returns a boolean showing whether the protection<br />
-- template is incorrect.<br />
--]]<br />
<br />
-- The master banner data, used if no values have been found in banners or<br />
-- defaultBanners.<br />
masterBanner = {<br />
text = '${INTROBLURB}',<br />
explanation = '${EXPLANATIONBLURB}',<br />
tooltip = '${TOOLTIPBLURB}',<br />
link = '${IMAGELINK}',<br />
alt = 'Page ${PROTECTIONLEVEL}'<br />
},<br />
<br />
-- The default banner data. This holds banner data for different protection<br />
-- levels.<br />
-- *required* - this table needs edit, move, and autoreview subtables.<br />
defaultBanners = {<br />
edit = {},<br />
move = {},<br />
autoreview = {<br />
autoconfirmed = {<br />
alt = 'Page protected with pending changes level 1',<br />
tooltip = 'All edits by unregistered and new users are subject to review',<br />
image = 'Padlock-silver-light.svg'<br />
},<br />
default = {<br />
alt = 'Page protected with pending changes level 2',<br />
tooltip = 'All edits by users who are not reviewers or administrators are'<br />
.. ' subject to review',<br />
}<br />
}<br />
},<br />
<br />
-- The banner data. This holds banner data for different protection reasons.<br />
-- In fact, the reasons specified in this table control which reasons are<br />
-- valid inputs to the first positional parameter.<br />
--<br />
-- There is also a non-standard "description" field that can be used for items<br />
-- in this table. This is a description of the protection reason for use in the<br />
-- module documentation.<br />
--<br />
-- *required* - this table needs edit, move, and autoreview subtables.<br />
banners = {<br />
edit = {<br />
blp = {<br />
description = 'For pages protected to promote compliance with the'<br />
.. ' [[Wikipedia:Biographies of living persons'<br />
.. '|biographies of living persons]] policy.',<br />
text = '${INTROFRAGMENT} to promote compliance with'<br />
.. ' [[Wikipedia:Biographies of living persons'<br />
.. "|Wikipedia's&nbsp;policy on&nbsp;the&nbsp;biographies"<br />
.. ' of&nbsp;living&nbsp;people]].',<br />
tooltip = '${TOOLTIPFRAGMENT} to promote compliance with the policy on'<br />
.. ' biographies of living people',<br />
},<br />
dmca = {<br />
description = 'For pages protected by the Wikimedia Foundation'<br />
.. ' due to Digital Millennium Copyright Act takedown requests.',<br />
explanation = function (protectionObj, args)<br />
local ret = 'Pursuant to a rights owner notice under the Digital'<br />
.. ' Millennium Copyright Act (DMCA) regarding some content'<br />
.. ' in this article, the Wikimedia Foundation acted under'<br />
.. ' applicable law and took down and restricted the content'<br />
.. ' in question.'<br />
if args.notice then<br />
ret = ret .. ' A copy of the received notice can be found here: '<br />
.. args.notice .. '.'<br />
end<br />
ret = ret .. ' For more information, including websites discussing'<br />
.. ' how to file a counter-notice, please see'<br />
.. " [[Wikipedia:Office actions]] and the article's ${TALKPAGE}."<br />
.. "'''Do not remove this template from the article until the"<br />
.. " restrictions are withdrawn'''."<br />
return ret<br />
end,<br />
image = 'Padlock-black.svg',<br />
},<br />
dispute = {<br />
description = 'For pages protected due to editing disputes.',<br />
text = function (protectionObj, args)<br />
-- Find the value of "disputes".<br />
local display = 'disputes'<br />
local disputes<br />
if args.section then<br />
disputes = string.format(<br />
'[[%s:%s#%s|%s]]',<br />
mw.site.namespaces[protectionObj.title.namespace].talk.name,<br />
protectionObj.title.text,<br />
args.section,<br />
display<br />
)<br />
else<br />
disputes = display<br />
end<br />
<br />
-- Make the blurb, depending on the expiry.<br />
local msg<br />
if type(protectionObj.expiry) == 'number' then<br />
msg = '${INTROFRAGMENT} or until editing %s have been resolved.'<br />
else<br />
msg = '${INTROFRAGMENT} until editing %s have been resolved.'<br />
end<br />
return string.format(msg, disputes)<br />
end,<br />
explanation = "This protection is '''not''' an endorsement of the"<br />
.. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}',<br />
tooltip = '${TOOLTIPFRAGMENT} due to editing disputes',<br />
},<br />
mainpage = {<br />
description = 'For pages protected for being displayed on the [[Main Page]].',<br />
text = 'This file is currently'<br />
.. ' [[Wikipedia:This page is protected|protected]] from'<br />
.. ' editing because it is currently or will soon be displayed'<br />
.. ' on the [[Main Page]].',<br />
explanation = 'Images on the Main Page are protected due to their high'<br />
.. ' visibility. Please discuss any necessary changes on the ${TALKPAGE}.'<br />
.. '<br /><span style="font-size:90%;">'<br />
.. "'''Administrators:''' Once this image is definitely off the Main Page,"<br />
.. ' please unprotect this file, or reduce to semi-protection,'<br />
.. ' as appropriate.</span>',<br />
},<br />
office = {<br />
description = 'For pages protected by the Wikimedia Foundation.',<br />
text = function (protectionObj, args)<br />
local ret = 'This ${PAGETYPE} is currently under the'<br />
.. ' scrutiny of the'<br />
.. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]'<br />
.. ' and is protected.'<br />
if protectionObj.protectionDate then<br />
ret = ret .. ' It has been protected since ${PROTECTIONDATE}.'<br />
end<br />
return ret<br />
end,<br />
explanation = "If you can edit this page, please discuss all changes and"<br />
.. " additions on the ${TALKPAGE} first. '''Do not remove protection from this"<br />
.. " page unless you are authorized by the Wikimedia Foundation to do"<br />
.. " so.'''",<br />
image = 'Padlock-black.svg',<br />
},<br />
reset = {<br />
description = 'For pages protected by the Wikimedia Foundation and'<br />
.. ' "reset" to a bare-bones version.',<br />
text = 'This ${PAGETYPE} is currently under the'<br />
.. ' scrutiny of the'<br />
.. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]'<br />
.. ' and is protected.',<br />
explanation = function (protectionObj, args)<br />
local ret = ''<br />
if protectionObj.protectionDate then<br />
ret = ret .. 'On ${PROTECTIONDATE} this ${PAGETYPE} was'<br />
else<br />
ret = ret .. 'This ${PAGETYPE} has been'<br />
end<br />
ret = ret .. ' reduced to a'<br />
.. ' simplified, "bare bones" version so that it may be completely'<br />
.. ' rewritten to ensure it meets the policies of'<br />
.. ' [[WP:NPOV|Neutral Point of View]] and [[WP:V|Verifiability]].'<br />
.. ' Standard Wikipedia policies will apply to its rewriting—which'<br />
.. ' will eventually be open to all editors—and will be strictly'<br />
.. ' enforced. The ${PAGETYPE} has been ${PROTECTIONLEVEL} while'<br />
.. ' it is being rebuilt.\n\n'<br />
.. 'Any insertion of material directly from'<br />
.. ' pre-protection revisions of the ${PAGETYPE} will be removed, as'<br />
.. ' will any material added to the ${PAGETYPE} that is not properly'<br />
.. ' sourced. The associated talk page(s) were also cleared on the'<br />
.. " same date.\n\n"<br />
.. "If you can edit this page, please discuss all changes and"<br />
.. " additions on the ${TALKPAGE} first. '''Do not override"<br />
.. " this action, and do not remove protection from this page,"<br />
.. " unless you are authorized by the Wikimedia Foundation"<br />
.. " to do so. No editor may remove this notice.'''"<br />
<br />
return ret<br />
end,<br />
image = 'Padlock-black.svg',<br />
},<br />
sock = {<br />
description = 'For pages protected due to'<br />
.. ' [[Wikipedia:Sock puppetry|sock puppetry]].',<br />
text = '${INTROFRAGMENT} to prevent [[Wikipedia:Sock puppetry|sock puppets]] of'<br />
.. ' [[Wikipedia:Blocking policy|blocked]] or'<br />
.. ' [[Wikipedia:List of banned users|banned users]]'<br />
.. ' from editing it.',<br />
tooltip = '${TOOLTIPFRAGMENT} to prevent sock puppets of blocked or banned users from'<br />
.. ' editing it',<br />
},<br />
template = {<br />
description = 'For [[Wikipedia:High-risk templates|high-risk]]'<br />
.. ' templates and Lua modules.',<br />
text = 'This is a permanently [[Help:Protection|protected]] ${PAGETYPE},'<br />
.. ' as it is [[Wikipedia:High-risk templates|high-risk]].',<br />
explanation = 'Please discuss any changes on the ${TALKPAGE}; you may'<br />
.. ' ${EDITREQUEST} to ask an'<br />
.. ' [[Wikipedia:Administrators|administrator]] or'<br />
.. ' [[Wikipedia:Template editor|template editor]] to make an edit if'<br />
.. ' it is [[Help:Minor edit#When to mark an edit as a minor edit'<br />
.. '|uncontroversial]] or supported by'<br />
.. ' [[Wikipedia:Consensus|consensus]]. You can also'<br />
.. ' [[Wikipedia:Requests for page protection|request]] that the page be'<br />
.. ' unprotected.',<br />
tooltip = 'This high-risk ${PAGETYPE} is permanently ${PROTECTIONLEVEL}'<br />
.. ' to prevent vandalism',<br />
alt = 'Permanently protected ${PAGETYPE}',<br />
},<br />
usertalk = {<br />
description = 'For pages protected against disruptive edits by a'<br />
.. ' particular user.',<br />
text = '${INTROFRAGMENT} to prevent ${VANDAL} from using it to make disruptive edits,'<br />
.. ' such as abusing the'<br />
.. ' &#123;&#123;[[Template:unblock|unblock]]&#125;&#125; template.',<br />
explanation = 'If you cannot edit this user talk page and you need to'<br />
.. ' make a change or leave a message, you can'<br />
.. ' [[Wikipedia:Requests for page protection'<br />
.. '#Current requests for edits to a protected page'<br />
.. '|request an edit]],'<br />
.. ' [[Wikipedia:Requests for page protection'<br />
.. '#Current requests for reduction in protection level'<br />
.. '|request unprotection]],'<br />
.. ' [[Special:Userlogin|log in]],'<br />
.. ' or [[Special:UserLogin/signup|create an account]].',<br />
},<br />
vandalism = {<br />
description = 'For pages protected against'<br />
.. ' [[Wikipedia:Vandalism|vandalism]].',<br />
text = '${INTROFRAGMENT} due to [[Wikipedia:Vandalism|vandalism]].',<br />
explanation = function (protectionObj, args)<br />
local ret = ''<br />
if protectionObj.level == 'sysop' then<br />
ret = ret .. "This protection is '''not''' an endorsement of the"<br />
.. ' ${CURRENTVERSION}. '<br />
end<br />
return ret .. '${EXPLANATIONBLURB}'<br />
end,<br />
tooltip = '${TOOLTIPFRAGMENT} due to vandalism',<br />
}<br />
},<br />
move = {<br />
dispute = {<br />
description = 'For pages protected against page moves due to'<br />
.. ' disputes over the page title.',<br />
explanation = "This protection is '''not''' an endorsement of the"<br />
.. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}',<br />
image = 'Padlock-olive.svg'<br />
},<br />
vandalism = {<br />
description = 'For pages protected against'<br />
.. ' [[Wikipedia:Vandalism#Page-move vandalism'<br />
.. ' |page-move vandalism]].'<br />
}<br />
},<br />
autoreview = {}<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
--<br />
-- GENERAL DATA TABLES<br />
--<br />
--------------------------------------------------------------------------------<br />
<br />
--------------------------------------------------------------------------------<br />
-- Protection blurbs<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table produces the protection blurbs available with the<br />
-- ${PROTECTIONBLURB} parameter. It is sorted by protection action and<br />
-- protection level, and is checked by the module in the following order:<br />
-- 1. page's protection action, page's protection level<br />
-- 2. page's protection action, default protection level<br />
-- 3. "edit" protection action, default protection level<br />
--<br />
-- It is possible to use banner parameters inside this table.<br />
-- *required* - this table needs edit, move, and autoreview subtables.<br />
protectionBlurbs = {<br />
edit = {<br />
default = 'This ${PAGETYPE} is currently [[Help:Protection|'<br />
.. 'protected]] from editing',<br />
autoconfirmed = 'Editing of this ${PAGETYPE} by [[Wikipedia:User access'<br />
.. ' levels#New users|new]] or [[Wikipedia:User access levels#Unregistered'<br />
.. ' users|unregistered]] users is currently [[Help:Protection|disabled]]'<br />
},<br />
move = {<br />
default = 'This ${PAGETYPE} is currently [[Help:Protection|protected]]'<br />
.. ' from [[Help:Moving a page|page moves]]'<br />
},<br />
autoreview = {<br />
autoconfirmed = 'All edits made to this ${PAGETYPE} by'<br />
.. ' [[Wikipedia:User access levels#New users|new]] or'<br />
.. ' [[Wikipedia:User access levels#Unregistered users|unregistered]]'<br />
.. ' users are currently'<br />
.. ' [[Wikipedia:Pending changes|subject to review]]',<br />
default = 'All edits made to this ${PAGETYPE} by users who are not'<br />
.. ' [[Wikipedia:Reviewing|reviewers]] or'<br />
.. ' [[Wikipedia:Administrators|administrators]] are currently'<br />
.. ' [[Wikipedia:Pending changes|subject to review]]'<br />
}<br />
},<br />
<br />
<br />
--------------------------------------------------------------------------------<br />
-- Explanation blurbs<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table produces the explanation blurbs available with the<br />
-- ${EXPLANATIONBLURB} parameter. It is sorted by protection action,<br />
-- protection level, and whether the page is a talk page or not. If the page is<br />
-- a talk page it will have a talk key of "talk"; otherwise it will have a talk<br />
-- key of "subject". The table is checked in the following order:<br />
-- 1. page's protection action, page's protection level, page's talk key<br />
-- 2. page's protection action, page's protection level, default talk key<br />
-- 3. page's protection action, default protection level, page's talk key<br />
-- 4. page's protection action, default protection level, default talk key<br />
--<br />
-- It is possible to use banner parameters inside this table.<br />
-- *required* - this table needs edit, move, and autoreview subtables.<br />
explanationBlurbs = {<br />
edit = {<br />
autoconfirmed = {<br />
subject = 'See the [[Wikipedia:Protection policy|'<br />
.. 'protection policy]] and ${PROTECTIONLOG} for more details. If you'<br />
.. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can'<br />
.. ' ${EDITREQUEST}, discuss changes on the ${TALKPAGE},'<br />
.. ' [[Wikipedia:Requests for page protection'<br />
.. '#Current requests for reduction in protection level'<br />
.. '|request unprotection]], [[Special:Userlogin|log in]], or'<br />
.. ' [[Special:UserLogin/signup|create an account]].',<br />
default = 'See the [[Wikipedia:Protection policy|'<br />
.. 'protection policy]] and ${PROTECTIONLOG} for more details. If you'<br />
.. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can'<br />
.. ' [[Wikipedia:Requests for page protection'<br />
.. '#Current requests for reduction in protection level'<br />
.. '|request unprotection]], [[Special:Userlogin|log in]], or'<br />
.. ' [[Special:UserLogin/signup|create an account]].',<br />
},<br />
default = {<br />
subject = 'See the [[Wikipedia:Protection policy|'<br />
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'<br />
.. ' Please discuss any changes on the ${TALKPAGE}; you'<br />
.. ' may ${EDITREQUEST} to ask an'<br />
.. ' [[Wikipedia:Administrators|administrator]] to make an edit if it'<br />
.. ' is [[Help:Minor edit#When to mark an edit as a minor edit'<br />
.. '|uncontroversial]] or supported by [[Wikipedia:Consensus'<br />
.. '|consensus]]. You may also [[Wikipedia:Requests for'<br />
.. ' page protection#Current requests for reduction in protection level'<br />
.. '|request]] that this page be unprotected.',<br />
default = 'See the [[Wikipedia:Protection policy|'<br />
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'<br />
.. ' You may [[Wikipedia:Requests for page'<br />
.. ' protection#Current requests for edits to a protected page|request an'<br />
.. ' edit]] to this page, or [[Wikipedia:Requests for'<br />
.. ' page protection#Current requests for reduction in protection level'<br />
.. '|ask]] for it to be unprotected.'<br />
}<br />
},<br />
move = {<br />
default = {<br />
subject = 'See the [[Wikipedia:Protection policy|'<br />
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'<br />
.. ' The page may still be edited but cannot be moved'<br />
.. ' until unprotected. Please discuss any suggested moves on the'<br />
.. ' ${TALKPAGE} or at [[Wikipedia:Requested moves]]. You can also'<br />
.. ' [[Wikipedia:Requests for page protection|request]] that the page be'<br />
.. ' unprotected.',<br />
default = 'See the [[Wikipedia:Protection policy|'<br />
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'<br />
.. ' The page may still be edited but cannot be moved'<br />
.. ' until unprotected. Please discuss any suggested moves at'<br />
.. ' [[Wikipedia:Requested moves]]. You can also'<br />
.. ' [[Wikipedia:Requests for page protection|request]] that the page be'<br />
.. ' unprotected.'<br />
}<br />
},<br />
autoreview = {<br />
default = {<br />
reviewer = 'See the [[Wikipedia:Protection policy|'<br />
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'<br />
.. ' Edits to this ${PAGETYPE} will not be visible to readers'<br />
.. ' until they are accepted by a reviewer or an administrator.'<br />
.. ' To avoid the need for your edits to be reviewed, you may'<br />
.. ' [[Wikipedia:Requests for page protection'<br />
.. '#Current requests for reduction in protection level'<br />
.. '|request unprotection]]. Experienced editors may also'<br />
.. ' request the [[Wikipedia:Reviewing|reviewer user right]].',<br />
default = 'See the [[Wikipedia:Protection policy|'<br />
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'<br />
.. ' Edits to this ${PAGETYPE} by new and unregistered users'<br />
.. ' will not be visible to readers until they are accepted by'<br />
.. ' a reviewer. To avoid the need for your edits to be'<br />
.. ' reviewed, you may'<br />
.. ' [[Wikipedia:Requests for page protection'<br />
.. '#Current requests for reduction in protection level'<br />
.. '|request unprotection]], [[Special:Userlogin|log in]], or'<br />
.. ' [[Special:UserLogin/signup|create an account]].'<br />
},<br />
}<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- Protection levels<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table provides the data for the ${PROTECTIONLEVEL} parameter, which<br />
-- produces a short label for different protection levels. It is sorted by<br />
-- protection action and proteciton level, and is checked in the following<br />
-- order:<br />
-- 1. page's protection action, page's protection level<br />
-- 2. page's protection action, default protection level<br />
-- 3. "edit" protection action, default protection level<br />
--<br />
-- It is possible to use banner parameters inside this table.<br />
-- *required* - this table needs edit, move, and autoreview subtables.<br />
protectionLevels = {<br />
edit = {<br />
default = 'protected',<br />
templateeditor = 'template-protected',<br />
autoconfirmed = 'semi-protected',<br />
},<br />
move = {<br />
default = 'move-protected'<br />
},<br />
autoreview = {<br />
}<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- Images<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table lists different padlock images for each protection action and<br />
-- protection level. It is used if an image is not specified in any of the<br />
-- banner data tables, and if the page does not satisfy the conditions for using<br />
-- the ['image-filename-indef'] image. It is checked in the following order:<br />
-- 1. page's protection action, page's protection level<br />
-- 2. page's protection action, default protection level<br />
images = {<br />
edit = {<br />
default = 'Padlock.svg',<br />
templateeditor = 'Padlock-pink.svg',<br />
autoconfirmed = 'Padlock-silver.svg'<br />
},<br />
move = {<br />
default = 'Padlock-olive.svg',<br />
},<br />
autoreview = {<br />
autoconfirmed = 'Padlock-silver-light.svg',<br />
default = 'Padlock-orange.svg'<br />
}<br />
},<br />
<br />
-- Pages with a reason specified in this table will show the special "indef"<br />
-- padlock, defined in the 'image-filename-indef' message, if no expiry is set.<br />
indefImageReasons = {<br />
template = true<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- Image links<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table provides the data for the ${IMAGELINK} parameter, which gets<br />
-- the image link for small padlock icons based on the page's protection action<br />
-- and protection level. It is checked in the following order:<br />
-- 1. page's protection action, page's protection level<br />
-- 2. page's protection action, default protection level<br />
-- 3. "edit" protection action, default protection level<br />
--<br />
-- It is possible to use banner parameters inside this table.<br />
-- *required* - this table needs edit, move, and autoreview subtables.<br />
imageLinks = {<br />
edit = {<br />
default = 'Wikipedia:Protection policy#full',<br />
templateeditor = 'Wikipedia:Protection policy#template',<br />
autoconfirmed = 'Wikipedia:Protection policy#semi'<br />
},<br />
move = {<br />
default = 'Wikipedia:Protection policy#move'<br />
},<br />
autoreview = {<br />
autoconfirmed = 'Wikipedia:Protection policy#pc1',<br />
reviewer = 'Wikipedia:Protection policy#pc2'<br />
}<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- Padlock positions<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table provides the data for the "right" CSS property for small padlock<br />
-- icons, which determines where the icon appears on the top bar among the other<br />
-- top icons. The data is stored by protection action. If no value is found for<br />
-- the action, the default field is used.<br />
padlockPositions = {<br />
autoreview = '85px',<br />
default = '55px'<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- Protection categories<br />
--------------------------------------------------------------------------------<br />
<br />
--[[<br />
-- The protection categories are stored in the protectionCategories table.<br />
-- Keys to this table are made up of the following strings:<br />
--<br />
-- 1. the expiry date<br />
-- 2. the namespace<br />
-- 3. the protection reason (e.g. "dispute" or "vandalism")<br />
-- 4. the protection level (e.g. "sysop" or "autoconfirmed")<br />
-- 5. the action (e.g. "edit" or "move")<br />
-- <br />
-- When the module looks up a category in the table, first it will will check to<br />
-- see a key exists that corresponds to all five parameters. For example, a<br />
-- user page semi-protected from vandalism for two weeks would have the key<br />
-- "temp-user-vandalism-autoconfirmed-edit". If no match is found, the module<br />
-- changes the first part of the key to "all" and checks the table again. It<br />
-- keeps checking increasingly generic key combinations until it finds the<br />
-- field, or until it reaches the key "all-all-all-all-all".<br />
--<br />
-- The module uses a binary matrix to determine the order in which to search.<br />
-- This is best demonstrated by a table. In this table, the "0" values<br />
-- represent "all", and the "1" values represent the original data (e.g.<br />
-- "indef" or "file" or "vandalism").<br />
--<br />
-- expiry namespace reason level action<br />
-- order<br />
-- 1 1 1 1 1 1<br />
-- 2 0 1 1 1 1<br />
-- 3 1 0 1 1 1<br />
-- 4 0 0 1 1 1<br />
-- 5 1 1 0 1 1<br />
-- 6 0 1 0 1 1<br />
-- 7 1 0 0 1 1<br />
-- 8 0 0 0 1 1<br />
-- 9 1 1 1 0 1<br />
-- 10 0 1 1 0 1<br />
-- 11 1 0 1 0 1<br />
-- 12 0 0 1 0 1<br />
-- 13 1 1 0 0 1<br />
-- 14 0 1 0 0 1<br />
-- 15 1 0 0 0 1<br />
-- 16 0 0 0 0 1<br />
-- 17 1 1 1 1 0<br />
-- 18 0 1 1 1 0<br />
-- 19 1 0 1 1 0<br />
-- 20 0 0 1 1 0<br />
-- 21 1 1 0 1 0<br />
-- 22 0 1 0 1 0<br />
-- 23 1 0 0 1 0<br />
-- 24 0 0 0 1 0<br />
-- 25 1 1 1 0 0<br />
-- 26 0 1 1 0 0<br />
-- 27 1 0 1 0 0<br />
-- 28 0 0 1 0 0<br />
-- 29 1 1 0 0 0<br />
-- 30 0 1 0 0 0<br />
-- 31 1 0 0 0 0<br />
-- 32 0 0 0 0 0<br />
--<br />
-- In this scheme the action has the highest priority, as it is the last<br />
-- to change, and the expiry has the least priority, as it changes the most.<br />
-- The priorities of the expiry, the protection level and the action are<br />
-- fixed, but the priorities of the reason and the namespace can be swapped<br />
-- through the use of the cfg.bannerDataNamespaceHasPriority table.<br />
--]]<br />
<br />
-- If the reason specified to the template is listed in this table,<br />
-- namespace data will take priority over reason data in the protectionCategories<br />
-- table.<br />
reasonsWithNamespacePriority = {<br />
vandalism = true,<br />
},<br />
<br />
-- The string to use as a namespace key for the protectionCategories table for each<br />
-- namespace number.<br />
categoryNamespaceKeys = {<br />
[ 2] = 'user',<br />
[ 3] = 'user',<br />
[ 4] = 'project',<br />
[ 6] = 'file',<br />
[ 8] = 'mediawiki',<br />
[ 10] = 'template',<br />
[ 12] = 'project',<br />
[ 14] = 'category',<br />
[100] = 'portal',<br />
},<br />
<br />
protectionCategories = {<br />
['all|all|all|all|all'] = 'Wikipedia protected pages',<br />
['all|all|office|all|all'] = 'Wikipedia Office-protected pages',<br />
['all|all|reset|all|all'] = 'Wikipedia Office-protected pages',<br />
['all|all|dmca|all|all'] = 'Wikipedia Office-protected pages',<br />
['all|all|mainpage|all|all'] = 'Protected main page images',<br />
['all|template|all|all|edit'] = 'Wikipedia protected templates',<br />
['all|all|all|autoconfirmed|edit'] = 'Wikipedia semi-protected pages',<br />
['indef|all|all|autoconfirmed|edit'] = 'Wikipedia indefinitely semi-protected pages',<br />
['all|all|blp|autoconfirmed|edit'] = 'Wikipedia indefinitely semi-protected biographies of living people',<br />
['temp|all|blp|autoconfirmed|edit'] = 'Wikipedia temporarily semi-protected biographies of living people',<br />
['all|all|dispute|autoconfirmed|edit'] = 'Wikipedia pages semi-protected due to dispute',<br />
['all|all|sock|autoconfirmed|edit'] = 'Wikipedia pages semi-protected from banned users',<br />
['all|all|vandalism|autoconfirmed|edit'] = 'Wikipedia pages semi-protected against vandalism',<br />
['all|category|all|autoconfirmed|edit'] = 'Wikipedia semi-protected categories',<br />
['all|file|all|autoconfirmed|edit'] = 'Semi-protected images',<br />
['all|portal|all|autoconfirmed|edit'] = 'Semi-protected portals',<br />
['all|project|all|autoconfirmed|edit'] = 'Semi-protected project pages',<br />
['all|talk|all|autoconfirmed|edit'] = 'Semi-protected talk pages',<br />
['all|template|all|autoconfirmed|edit'] = 'Wikipedia semi-protected templates',<br />
['all|template|all|autoconfirmed|edit'] = 'Wikipedia semi-protected templates',<br />
['all|user|all|autoconfirmed|edit'] = 'Wikipedia semi-protected user and user talk pages',<br />
['all|all|blp|sysop|edit'] = 'Wikipedia indefinitely protected biographies of living people',<br />
['temp|all|blp|sysop|edit'] = 'Wikipedia temporarily protected biographies of living people',<br />
['all|all|dispute|sysop|edit'] = 'Wikipedia pages protected due to dispute',<br />
['all|all|sock|sysop|edit'] = 'Wikipedia pages protected from banned users',<br />
['all|all|vandalism|sysop|edit'] = 'Wikipedia pages protected against vandalism',<br />
['all|category|all|sysop|edit'] = 'Wikipedia protected categories',<br />
['all|file|all|sysop|edit'] = 'Protected images',<br />
['all|project|all|sysop|edit'] = 'Protected project pages',<br />
['all|talk|all|sysop|edit'] = 'Protected talk pages',<br />
['all|template|all|sysop|edit'] = 'Wikipedia protected templates',<br />
['all|user|all|sysop|edit'] = 'Wikipedia protected user and user talk pages',<br />
['all|all|all|sysop|move'] = 'Wikipedia move-protected pages',<br />
['indef|all|all|sysop|move'] = 'Wikipedia indefinitely move-protected pages',<br />
['all|all|dispute|sysop|move'] = 'Wikipedia pages move-protected due to dispute',<br />
['all|all|vandalism|sysop|move'] = 'Wikipedia pages move-protected due to vandalism',<br />
['all|portal|all|sysop|move'] = 'Wikipedia move-protected portals',<br />
['all|portal|all|sysop|move'] = 'Wikipedia move-protected portals',<br />
['all|project|all|sysop|move'] = 'Wikipedia move-protected project pages',<br />
['all|talk|all|sysop|move'] = 'Wikipedia move-protected talk pages',<br />
['all|template|all|sysop|move'] = 'Wikipedia move-protected templates',<br />
['all|user|all|sysop|move'] = 'Wikipedia move-protected user and user talk pages',<br />
['all|all|all|autoconfirmed|autoreview'] = 'Wikipedia pending changes protected pages (level 1)',<br />
['all|all|all|reviewer|autoreview'] = 'Wikipedia pending changes protected pages (level 2)',<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- Expiry category config<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table configures the expiry category behaviour for each protection<br />
-- action.<br />
-- * If set to true, setting that action will always categorise the page if<br />
-- an expiry parameter is not set.<br />
-- * If set to false, setting that action will never categorise the page.<br />
-- * If set to nil, the module will categorise the page if:<br />
-- 1) an expiry parameter is not set, and<br />
-- 2) a reason is provided, and<br />
-- 3) the specified reason is not blacklisted in the reasonsWithoutExpiryCheck<br />
-- table.<br />
<br />
expiryCheckActions = {<br />
edit = nil,<br />
move = false,<br />
autoreview = true<br />
},<br />
<br />
reasonsWithoutExpiryCheck = {<br />
blp = true,<br />
template = true,<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- Pagetypes<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table produces the page types available with the ${PAGETYPE} parameter.<br />
-- Keys are namespace numbers, or the string "default" for the default value.<br />
pagetypes = {<br />
[0] = 'article',<br />
[6] = 'file',<br />
[10] = 'template',<br />
[14] = 'category',<br />
[828] = 'module',<br />
default = 'page'<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- Strings marking indefinite protection<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table contains values passed to the expiry parameter that mean the page<br />
-- is protected indefinitely.<br />
indefStrings = {<br />
['indef'] = true,<br />
['indefinite'] = true,<br />
['indefinitely'] = true,<br />
['infinite'] = true,<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- Wrapper templates and their default arguments<br />
--------------------------------------------------------------------------------<br />
<br />
-- This table contains wrapper templates used with the module, and their<br />
-- default arguments. Templates specified in this table should contain the<br />
-- following invocation, and no other template content:<br />
--<br />
-- {{#invoke:Protection banner|main}}<br />
--<br />
-- If other content is desired, it can be added between<br />
-- <noinclude>...</noinclude> tags.<br />
--<br />
-- When a user calls one of these wrapper templates, they will use the<br />
-- default arguments automatically. The arguments cannot be overwritten by the<br />
-- user.<br />
wrappers = {<br />
['Template:Pp'] = {},<br />
['Template:Pp-blp'] = {'blp'},<br />
-- we don't need Template:Pp-create<br />
['Template:Pp-dispute'] = {'dispute'},<br />
['Template:Pp-main-page'] = {'mainpage'},<br />
['Template:Pp-move'] = {action = 'move'},<br />
['Template:Pp-move-dispute'] = {'dispute', action = 'move'},<br />
-- we don't need Template:Pp-move-indef<br />
['Template:Pp-move-vandalism'] = {'vandalism', action = 'move'},<br />
['Template:Pp-office'] = {'office'},<br />
['Template:Pp-office-dmca'] = {'dmca'},<br />
['Template:Pp-pc1'] = {action = 'autoreview', small = true},<br />
['Template:Pp-pc2'] = {action = 'autoreview', small = true},<br />
['Template:Pp-reset'] = {'reset'},<br />
['Template:Pp-semi-indef'] = {expiry = 'indef', small = true},<br />
['Template:Pp-sock'] = {'sock'},<br />
['Template:Pp-template'] = {'template', small = true},<br />
['Template:Pp-usertalk'] = {'usertalk'},<br />
['Template:Pp-vandalism'] = {'vandalism'},<br />
},<br />
<br />
--------------------------------------------------------------------------------<br />
-- <br />
-- MESSAGES<br />
-- <br />
--------------------------------------------------------------------------------<br />
<br />
msg = {<br />
<br />
--------------------------------------------------------------------------------<br />
-- Intro blurb and intro fragment<br />
--------------------------------------------------------------------------------<br />
<br />
-- These messages specify what is produced by the ${INTROBLURB} and<br />
-- ${INTROFRAGMENT} parameters. If the protection is temporary they use the<br />
-- intro-blurb-expiry or intro-fragment-expiry, and if not they use<br />
-- intro-blurb-noexpiry or intro-fragment-noexpiry.<br />
-- It is possible to use banner parameters in these messages.<br />
['intro-blurb-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY}.',<br />
['intro-blurb-noexpiry'] = '${PROTECTIONBLURB}.',<br />
['intro-fragment-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY},',<br />
['intro-fragment-noexpiry'] = '${PROTECTIONBLURB}',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Tooltip blurb<br />
--------------------------------------------------------------------------------<br />
<br />
-- These messages specify what is produced by the ${TOOLTIPBLURB} parameter.<br />
-- If the protection is temporary the tooltip-blurb-expiry message is used, and<br />
-- if not the tooltip-blurb-noexpiry message is used.<br />
-- It is possible to use banner parameters in these messages.<br />
['tooltip-blurb-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY}.',<br />
['tooltip-blurb-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}.',<br />
['tooltip-fragment-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY},',<br />
['tooltip-fragment-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Special explanation blurb<br />
--------------------------------------------------------------------------------<br />
<br />
-- An explanation blurb for pages that cannot be unprotected, e.g. for pages<br />
-- in the MediaWiki namespace.<br />
-- It is possible to use banner parameters in this message.<br />
['explanation-blurb-nounprotect'] = 'See the [[Wikipedia:Protection policy|'<br />
.. 'protection policy]] and ${PROTECTIONLOG} for more details.'<br />
.. ' Please discuss any changes on the ${TALKPAGE}; you'<br />
.. ' may ${EDITREQUEST} to ask an'<br />
.. ' [[Wikipedia:Administrators|administrator]] to make an edit if it'<br />
.. ' is [[Help:Minor edit#When to mark an edit as a minor edit'<br />
.. '|uncontroversial]] or supported by [[Wikipedia:Consensus'<br />
.. '|consensus]].',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Protection log display values<br />
--------------------------------------------------------------------------------<br />
<br />
-- These messages determine the display values for the protection log link<br />
-- or the pending changes log link produced by the ${PROTECTIONLOG} parameter.<br />
-- It is possible to use banner parameters in these messages.<br />
['protection-log-display'] = 'protection log',<br />
['pc-log-display'] = 'pending changes log',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Current version display values<br />
--------------------------------------------------------------------------------<br />
<br />
-- These messages determine the display values for the page history link<br />
-- or the move log link produced by the ${CURRENTVERSION} parameter.<br />
-- It is possible to use banner parameters in these messages.<br />
['current-version-move-display'] = 'current title',<br />
['current-version-edit-display'] = 'current version',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Talk page<br />
--------------------------------------------------------------------------------<br />
<br />
-- This message determines the display value of the talk page link produced<br />
-- with the ${TALKPAGE} parameter.<br />
-- It is possible to use banner parameters in this message.<br />
['talk-page-link-display'] = 'talk page',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Edit requests<br />
--------------------------------------------------------------------------------<br />
<br />
-- This message determines the display value of the edit request link produced<br />
-- with the ${EDITREQUEST} parameter.<br />
-- It is possible to use banner parameters in this message.<br />
['edit-request-display'] = 'submit an edit request',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Expiry date format<br />
--------------------------------------------------------------------------------<br />
<br />
-- This is the format for the blurb expiry date. It should be valid input for<br />
-- the first parameter of the #time parser function.<br />
['expiry-date-format'] = 'F j, Y',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Tracking categories<br />
--------------------------------------------------------------------------------<br />
<br />
-- These messages determine which tracking categories the module outputs.<br />
['tracking-category-incorrect'] = 'Wikipedia pages with incorrect protection templates',<br />
['tracking-category-expiry'] = 'Wikipedia protected pages without expiry',<br />
['tracking-category-template'] = 'Wikipedia template-protected pages other than templates and modules',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Images<br />
--------------------------------------------------------------------------------<br />
<br />
-- These are images that are not defined by their protection action and protection level.<br />
['image-filename-indef'] = 'Padlock-red.svg',<br />
['image-filename-default'] = 'Transparent.gif',<br />
<br />
--------------------------------------------------------------------------------<br />
-- Error message blurb<br />
--------------------------------------------------------------------------------<br />
<br />
-- This is the blurb that is outputted with error messages produced by the<br />
-- module. $1 is replaced with the error message text. The whole message is<br />
-- turned into a large red error message, so there is no need to add HTML<br />
-- styling.<br />
['error-message-blurb'] = 'Error: $1 ([[Module:Protection banner#Errors|help]])'<br />
<br />
--------------------------------------------------------------------------------<br />
-- End messages<br />
--------------------------------------------------------------------------------<br />
}<br />
<br />
--------------------------------------------------------------------------------<br />
-- End configuration<br />
--------------------------------------------------------------------------------<br />
}</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Effective_protection_levelModule:Effective protection level2014-08-10T20:42:07Z<p>Mr. Stradivarius : Changed protection level of Module:Effective protection level: High-risk Lua module ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))</p>
<hr />
<div>local p = {}<br />
<br />
-- Returns the permission required to perform a given action on a given title.<br />
-- If no title is specified, the title of the page being displayed is used.<br />
function p._main(action, pagename)<br />
local title<br />
if type(pagename) == 'table' and pagename.prefixedText then<br />
title = pagename<br />
elseif pagename then<br />
title = mw.title.new(pagename)<br />
else<br />
title = mw.title.getCurrentTitle()<br />
end<br />
pagename = title.prefixedText<br />
if action == 'autoreview' then<br />
local level = mw.getCurrentFrame():callParserFunction('PENDINGCHANGELEVEL', pagename)<br />
if level == 'review' then<br />
return 'reviewer'<br />
elseif level ~= '' then<br />
return level<br />
else<br />
return nil -- not '*'. a page not being PC-protected is distinct from it being PC-protected with anyone able to review. also not '', as that would mean PC-protected but nobody can review<br />
end<br />
elseif action ~= 'edit' and action ~= 'move' and action ~= 'create' and action ~= 'upload' then<br />
error( 'First parameter must be one of edit, move, create, upload, autoreview', 2 )<br />
end<br />
if title.namespace == 8 then -- MediaWiki namespace<br />
return 'sysop'<br />
elseif title.namespace == 2 and ( mw.ustring.find( pagename, '/.*%.js$') or mw.ustring.find( pagename, '/.*%.css$') ) then -- user .js or .css page<br />
return 'sysop'<br />
end<br />
local level = title.protectionLevels[action] and title.protectionLevels[action][1]<br />
if level == 'sysop' then<br />
return 'sysop'<br />
elseif mw.getCurrentFrame():callParserFunction('CASCADINGSOURCES', pagename) ~= '' then -- used by a cascading-protected page<br />
return 'sysop'<br />
elseif level == 'templateeditor' then<br />
return 'templateeditor'<br />
elseif action == 'move' then<br />
local blacklistentry = mw.ext.TitleBlacklist.test('edit', pagename) -- Testing action edit is correct, since this is for the source page. The target page name gets tested with action move.<br />
if blacklistentry and not blacklistentry.params.autoconfirmed then<br />
return 'accountcreator'<br />
elseif title.namespace == 6 then<br />
return 'filemover'<br />
else<br />
return 'autoconfirmed'<br />
end<br />
end<br />
local blacklistentry = mw.ext.TitleBlacklist.test(action, pagename)<br />
if blacklistentry then<br />
return blacklistentry.params.autoconfirmed and 'autoconfirmed' or 'accountcreator'<br />
elseif level then<br />
return level<br />
elseif action == 'upload' then<br />
return 'autoconfirmed'<br />
elseif action == 'create' and title.namespace % 2 == 0 and title.namespace ~= 118 then -- You need to be registered, but not autoconfirmed, to create non-talk pages other than drafts<br />
return 'user'<br />
else<br />
return '*'<br />
end<br />
end<br />
<br />
setmetatable(p, { __index = function(t, k)<br />
return function(frame)<br />
return t._main(k, frame.args[1])<br />
end<br />
end })<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Protection_bannerModule:Protection banner2014-08-10T17:25:15Z<p>Mr. Stradivarius : allow user-specified args to overwrite default args; updated from sandbox</p>
<hr />
<div>-- This module implements {{pp-meta}} and its daughter templates such as<br />
-- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.<br />
<br />
-- Initialise necessary modules.<br />
require('Module:No globals')<br />
local newFileLink = require('Module:File link').new<br />
local effectiveProtectionLevel = require('Module:Effective protection level')._main<br />
local yesno = require('Module:Yesno')<br />
<br />
-- Lazily initialise modules and objects we don't always need.<br />
local getArgs, makeMessageBox, lang<br />
<br />
-- Set constants.<br />
local CONFIG_MODULE = 'Module:Protection banner/config'<br />
<br />
--------------------------------------------------------------------------------<br />
-- Helper functions<br />
--------------------------------------------------------------------------------<br />
<br />
local function makeCategoryLink(cat, sort)<br />
local nsText = mw.site.namespaces[14].name<br />
if cat and sort then<br />
return string.format(<br />
'[[%s:%s|%s]]',<br />
nsText,<br />
cat,<br />
sort<br />
)<br />
elseif cat then<br />
return string.format(<br />
'[[%s:%s]]',<br />
nsText,<br />
cat<br />
)<br />
else<br />
return ''<br />
end<br />
end<br />
<br />
-- Validation function for the expiry and the protection date<br />
local function validateDate(dateString, dateType)<br />
lang = lang or mw.language.getContentLanguage()<br />
local success, result = pcall(lang.formatDate, lang, 'U', dateString)<br />
if success then<br />
result = tonumber(result)<br />
if result then<br />
return result<br />
end<br />
end<br />
error(string.format(<br />
'invalid %s ("%s")',<br />
dateType,<br />
tostring(dateString)<br />
), 0)<br />
end<br />
<br />
local function makeFullUrl(page, query, display)<br />
return string.format(<br />
'[%s %s]',<br />
tostring(mw.uri.fullUrl(page, query)),<br />
display<br />
)<br />
end<br />
<br />
local function toTableEnd(t, pos)<br />
-- Sends the value at position pos to the end of array t, and shifts the<br />
-- other items down accordingly.<br />
return table.insert(t, table.remove(t, pos))<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Protection class<br />
--------------------------------------------------------------------------------<br />
<br />
local Protection = {}<br />
Protection.__index = Protection<br />
<br />
Protection.supportedActions = {<br />
edit = true,<br />
move = true,<br />
autoreview = true<br />
}<br />
<br />
Protection.bannerConfigFields = {<br />
'text',<br />
'explanation',<br />
'tooltip',<br />
'alt',<br />
'link',<br />
'image'<br />
}<br />
<br />
function Protection.new(args, cfg, title)<br />
local obj = {}<br />
obj._cfg = cfg<br />
obj.title = title or mw.title.getCurrentTitle()<br />
<br />
-- Set action<br />
if not args.action then<br />
obj.action = 'edit'<br />
elseif Protection.supportedActions[args.action] then<br />
obj.action = args.action<br />
else<br />
error(string.format(<br />
'invalid action ("%s")',<br />
tostring(args.action)<br />
), 0)<br />
end<br />
<br />
-- Set level<br />
obj.level = effectiveProtectionLevel(obj.action, obj.title)<br />
if obj.level == 'accountcreator' then<br />
-- Lump titleblacklisted pages in with template-protected pages,<br />
-- since templateeditors can do both.<br />
obj.level = 'templateeditor'<br />
elseif not obj.level or (obj.action == 'move' and obj.level == 'autoconfirmed') then<br />
-- Users need to be autoconfirmed to move pages anyway, so treat<br />
-- semi-move-protected pages as unprotected.<br />
obj.level = '*'<br />
end<br />
<br />
-- Set expiry<br />
if args.expiry then<br />
if cfg.indefStrings[args.expiry] then<br />
obj.expiry = 'indef'<br />
elseif type(args.expiry) == 'number' then<br />
obj.expiry = args.expiry<br />
else<br />
obj.expiry = validateDate(args.expiry, 'expiry date')<br />
end<br />
end<br />
<br />
-- Set reason<br />
if args[1] then<br />
obj.reason = mw.ustring.lower(args[1])<br />
if obj.reason:find('|') then<br />
error('reasons cannot contain the pipe character ("|")', 0)<br />
end<br />
end<br />
<br />
-- Set protection date<br />
if args.date then<br />
obj.protectionDate = validateDate(args.date, 'protection date')<br />
end<br />
<br />
-- Set banner config<br />
do<br />
obj.bannerConfig = {}<br />
local configTables = {}<br />
if cfg.banners[obj.action] then<br />
configTables[#configTables + 1] = cfg.banners[obj.action][obj.reason]<br />
end<br />
if cfg.defaultBanners[obj.action] then<br />
configTables[#configTables + 1] = cfg.defaultBanners[obj.action][obj.level]<br />
configTables[#configTables + 1] = cfg.defaultBanners[obj.action].default<br />
end<br />
configTables[#configTables + 1] = cfg.masterBanner<br />
for i, field in ipairs(Protection.bannerConfigFields) do<br />
for j, t in ipairs(configTables) do<br />
if t[field] then<br />
obj.bannerConfig[field] = t[field]<br />
break<br />
end<br />
end<br />
end<br />
end<br />
return setmetatable(obj, Protection)<br />
end<br />
<br />
function Protection:isProtected()<br />
return self.level ~= '*'<br />
end<br />
<br />
function Protection:isTemporary()<br />
return type(self.expiry) == 'number'<br />
end<br />
<br />
function Protection:makeProtectionCategory()<br />
local cfg = self._cfg<br />
local title = self.title<br />
<br />
-- Exit if the page is not protected.<br />
if not self:isProtected() then<br />
return ''<br />
end<br />
<br />
-- Get the expiry key fragment.<br />
local expiryFragment<br />
if self.expiry == 'indef' then<br />
expiryFragment = self.expiry<br />
elseif type(self.expiry) == 'number' then<br />
expiryFragment = 'temp'<br />
end<br />
<br />
-- Get the namespace key fragment.<br />
local namespaceFragment<br />
do<br />
namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]<br />
if not namespaceFragment and title.namespace % 2 == 1 then<br />
namespaceFragment = 'talk'<br />
end<br />
end<br />
<br />
-- Define the order that key fragments are tested in. This is done with an<br />
-- array of tables containing the value to be tested, along with its<br />
-- position in the cfg.protectionCategories table.<br />
local order = {<br />
{val = expiryFragment, keypos = 1},<br />
{val = namespaceFragment, keypos = 2},<br />
{val = self.reason, keypos = 3},<br />
{val = self.level, keypos = 4},<br />
{val = self.action, keypos = 5}<br />
}<br />
<br />
--[[<br />
-- The old protection templates used an ad-hoc protection category system,<br />
-- with some templates prioritising namespaces in their categories, and<br />
-- others prioritising the protection reason. To emulate this in this module<br />
-- we use the config table cfg.reasonsWithNamespacePriority to set the<br />
-- reasons for which namespaces have priority over protection reason.<br />
-- If we are dealing with one of those reasons, move the namespace table to<br />
-- the end of the order table, i.e. give it highest priority. If not, the<br />
-- reason should have highest priority, so move that to the end of the table<br />
-- instead.<br />
--]]<br />
if self.reason and cfg.reasonsWithNamespacePriority[self.reason] then<br />
-- table.insert(order, 3, table.remove(order, 2))<br />
toTableEnd(order, 2)<br />
else<br />
toTableEnd(order, 3)<br />
end<br />
<br />
--[[<br />
-- Define the attempt order. Inactive subtables (subtables with nil "value"<br />
-- fields) are moved to the end, where they will later be given the key<br />
-- "all". This is to cut down on the number of table lookups in<br />
-- cfg.protectionCategories, which grows exponentially with the number of<br />
-- non-nil keys. We keep track of the number of active subtables with the<br />
-- noActive parameter.<br />
--]]<br />
local noActive, attemptOrder<br />
do<br />
local active, inactive = {}, {}<br />
for i, t in ipairs(order) do<br />
if t.val then<br />
active[#active + 1] = t<br />
else<br />
inactive[#inactive + 1] = t<br />
end<br />
end<br />
noActive = #active<br />
attemptOrder = active<br />
for i, t in ipairs(inactive) do<br />
attemptOrder[#attemptOrder + 1] = t<br />
end<br />
end<br />
<br />
--[[<br />
-- Check increasingly generic key combinations until we find a match. If a<br />
-- specific category exists for the combination of key fragments we are<br />
-- given, that match will be found first. If not, we keep trying different<br />
-- key fragment combinations until we match using the key<br />
-- "all-all-all-all-all".<br />
--<br />
-- To generate the keys, we index the key subtables using a binary matrix<br />
-- with indexes i and j. j is only calculated up to the number of active<br />
-- subtables. For example, if there were three active subtables, the matrix<br />
-- would look like this, with 0 corresponding to the key fragment "all", and<br />
-- 1 corresponding to other key fragments.<br />
-- <br />
-- j 1 2 3<br />
-- i <br />
-- 1 1 1 1<br />
-- 2 0 1 1<br />
-- 3 1 0 1<br />
-- 4 0 0 1<br />
-- 5 1 1 0<br />
-- 6 0 1 0<br />
-- 7 1 0 0<br />
-- 8 0 0 0<br />
-- <br />
-- Values of j higher than the number of active subtables are set<br />
-- to the string "all".<br />
--<br />
-- A key for cfg.protectionCategories is constructed for each value of i.<br />
-- The position of the value in the key is determined by the keypos field in<br />
-- each subtable.<br />
--]]<br />
local cats = cfg.protectionCategories<br />
for i = 1, 2^noActive do<br />
local key = {}<br />
for j, t in ipairs(attemptOrder) do<br />
if j > noActive then<br />
key[t.keypos] = 'all'<br />
else<br />
local quotient = i / 2 ^ (j - 1)<br />
quotient = math.ceil(quotient)<br />
if quotient % 2 == 1 then<br />
key[t.keypos] = t.val<br />
else<br />
key[t.keypos] = 'all'<br />
end<br />
end<br />
end<br />
key = table.concat(key, '|')<br />
local attempt = cats[key]<br />
if attempt then<br />
return makeCategoryLink(attempt, title.text)<br />
end<br />
end<br />
return ''<br />
end<br />
<br />
function Protection:needsExpiry()<br />
local cfg = self._cfg<br />
local actionNeedsCheck = cfg.expiryCheckActions[self.action]<br />
return not self.expiry and (<br />
actionNeedsCheck or (<br />
actionNeedsCheck == nil<br />
and self.reason -- the old {{pp-protected}} didn't check for expiry<br />
and not cfg.reasonsWithoutExpiryCheck[self.reason]<br />
)<br />
)<br />
end<br />
<br />
function Protection:isIncorrect()<br />
local expiry = self.expiry<br />
return not self:isProtected()<br />
or type(expiry) == 'number' and expiry < os.time()<br />
end<br />
<br />
function Protection:isTemplateProtectedNonTemplate()<br />
local action, namespace = self.action, self.title.namespace<br />
return self.level == 'templateeditor'<br />
and (<br />
(action ~= 'edit' and action ~= 'move')<br />
or (namespace ~= 10 and namespace ~= 828)<br />
)<br />
end<br />
<br />
function Protection:makeCategoryLinks()<br />
local msg = self._cfg.msg<br />
local ret = { self:makeProtectionCategory() }<br />
if self:needsExpiry() then<br />
ret[#ret + 1] = makeCategoryLink(<br />
msg['tracking-category-expiry'],<br />
self.title.text<br />
)<br />
end<br />
if self:isIncorrect() then<br />
ret[#ret + 1] = makeCategoryLink(<br />
msg['tracking-category-incorrect'],<br />
self.title.text<br />
)<br />
end<br />
if self:isTemplateProtectedNonTemplate() then<br />
ret[#ret + 1] = makeCategoryLink(<br />
msg['tracking-category-template'],<br />
self.title.text<br />
)<br />
end<br />
return table.concat(ret)<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Blurb class<br />
--------------------------------------------------------------------------------<br />
<br />
local Blurb = {}<br />
Blurb.__index = Blurb<br />
<br />
Blurb.bannerTextFields = {<br />
text = true,<br />
explanation = true,<br />
tooltip = true,<br />
alt = true,<br />
link = true<br />
}<br />
<br />
function Blurb.new(protectionObj, args, cfg)<br />
return setmetatable({<br />
_cfg = cfg,<br />
_protectionObj = protectionObj,<br />
_args = args<br />
}, Blurb)<br />
end<br />
<br />
-- Private methods --<br />
<br />
function Blurb:_formatDate(num)<br />
-- Formats a Unix timestamp into dd Month, YYYY format.<br />
lang = lang or mw.language.getContentLanguage()<br />
local success, date = pcall(<br />
lang.formatDate,<br />
lang,<br />
self._cfg.msg['expiry-date-format'] or 'j F Y',<br />
'@' .. tostring(num)<br />
)<br />
if success then<br />
return date<br />
end<br />
end<br />
<br />
function Blurb:_getExpandedMessage(msgKey)<br />
return self:_substituteParameters(self._cfg.msg[msgKey])<br />
end<br />
<br />
function Blurb:_substituteParameters(msg)<br />
if not self._params then<br />
local parameterFuncs = {}<br />
<br />
parameterFuncs.CURRENTVERSION = self._makeCurrentVersionParameter<br />
parameterFuncs.EDITREQUEST = self._makeEditRequestParameter<br />
parameterFuncs.EXPIRY = self._makeExpiryParameter<br />
parameterFuncs.EXPLANATIONBLURB = self._makeExplanationBlurbParameter<br />
parameterFuncs.IMAGELINK = self._makeImageLinkParameter<br />
parameterFuncs.INTROBLURB = self._makeIntroBlurbParameter<br />
parameterFuncs.INTROFRAGMENT = self._makeIntroFragmentParameter<br />
parameterFuncs.PAGETYPE = self._makePagetypeParameter<br />
parameterFuncs.PROTECTIONBLURB = self._makeProtectionBlurbParameter<br />
parameterFuncs.PROTECTIONDATE = self._makeProtectionDateParameter<br />
parameterFuncs.PROTECTIONLEVEL = self._makeProtectionLevelParameter<br />
parameterFuncs.PROTECTIONLOG = self._makeProtectionLogParameter<br />
parameterFuncs.TALKPAGE = self._makeTalkPageParameter<br />
parameterFuncs.TOOLTIPBLURB = self._makeTooltipBlurbParameter<br />
parameterFuncs.TOOLTIPFRAGMENT = self._makeTooltipFragmentParameter<br />
parameterFuncs.VANDAL = self._makeVandalTemplateParameter<br />
<br />
self._params = setmetatable({}, {<br />
__index = function (t, k)<br />
local param<br />
if parameterFuncs[k] then<br />
param = parameterFuncs[k](self)<br />
end<br />
param = param or ''<br />
t[k] = param<br />
return param<br />
end<br />
})<br />
end<br />
<br />
msg = msg:gsub('${(%u+)}', self._params)<br />
return msg<br />
end<br />
<br />
function Blurb:_makeCurrentVersionParameter()<br />
-- A link to the page history or the move log, depending on the kind of<br />
-- protection.<br />
local pagename = self._protectionObj.title.prefixedText<br />
if self._protectionObj.action == 'move' then<br />
-- We need the move log link.<br />
return makeFullUrl(<br />
'Special:Log',<br />
{type = 'move', page = pagename},<br />
self:_getExpandedMessage('current-version-move-display')<br />
)<br />
else<br />
-- We need the history link.<br />
return makeFullUrl(<br />
pagename,<br />
{action = 'history'},<br />
self:_getExpandedMessage('current-version-edit-display')<br />
)<br />
end<br />
end<br />
<br />
function Blurb:_makeEditRequestParameter()<br />
local mEditRequest = require('Module:Submit an edit request')<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
<br />
-- Get the edit request type.<br />
local requestType<br />
if action == 'edit' then<br />
if level == 'autoconfirmed' then<br />
requestType = 'semi'<br />
elseif level == 'templateeditor' then<br />
requestType = 'template'<br />
end<br />
end<br />
requestType = requestType or 'full'<br />
<br />
-- Get the display value.<br />
local display = self:_getExpandedMessage('edit-request-display')<br />
<br />
return mEditRequest._link{type = requestType, display = display}<br />
end<br />
<br />
function Blurb:_makeExpiryParameter()<br />
local expiry = self._protectionObj.expiry<br />
if type(expiry) == 'number' then<br />
return self:_formatDate(expiry)<br />
else<br />
return expiry<br />
end<br />
end<br />
<br />
function Blurb:_makeExplanationBlurbParameter()<br />
-- Cover special cases first.<br />
if self._protectionObj.title.namespace == 8 then<br />
-- MediaWiki namespace<br />
return self:_getExpandedMessage('explanation-blurb-nounprotect')<br />
end<br />
<br />
-- Get explanation blurb table keys<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
local talkKey = self._protectionObj.title.isTalkPage and 'talk' or 'subject'<br />
<br />
-- Find the message in the explanation blurb table and substitute any<br />
-- parameters.<br />
local explanations = self._cfg.explanationBlurbs<br />
local msg<br />
if explanations[action][level] and explanations[action][level][talkKey] then<br />
msg = explanations[action][level][talkKey]<br />
elseif explanations[action][level] and explanations[action][level].default then<br />
msg = explanations[action][level].default<br />
elseif explanations[action].default and explanations[action].default[talkKey] then<br />
msg = explanations[action].default[talkKey]<br />
elseif explanations[action].default and explanations[action].default.default then<br />
msg = explanations[action].default.default<br />
else<br />
error(string.format(<br />
'could not find explanation blurb for action "%s", level "%s" and talk key "%s"',<br />
action,<br />
level,<br />
talkKey<br />
))<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
<br />
function Blurb:_makeImageLinkParameter()<br />
local imageLinks = self._cfg.imageLinks<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
local msg<br />
if imageLinks[action][level] then<br />
msg = imageLinks[action][level]<br />
elseif imageLinks[action].default then<br />
msg = imageLinks[action].default<br />
else<br />
msg = imageLinks.edit.default<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
<br />
function Blurb:_makeIntroBlurbParameter()<br />
if self._protectionObj:isTemporary() then<br />
return self:_getExpandedMessage('intro-blurb-expiry')<br />
else<br />
return self:_getExpandedMessage('intro-blurb-noexpiry')<br />
end<br />
end<br />
<br />
function Blurb:_makeIntroFragmentParameter()<br />
if self._protectionObj:isTemporary() then<br />
return self:_getExpandedMessage('intro-fragment-expiry')<br />
else<br />
return self:_getExpandedMessage('intro-fragment-noexpiry')<br />
end<br />
end<br />
<br />
function Blurb:_makePagetypeParameter()<br />
local pagetypes = self._cfg.pagetypes<br />
return pagetypes[self._protectionObj.title.namespace]<br />
or pagetypes.default<br />
or error('no default pagetype defined')<br />
end<br />
<br />
function Blurb:_makeProtectionBlurbParameter()<br />
local protectionBlurbs = self._cfg.protectionBlurbs<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
local msg<br />
if protectionBlurbs[action][level] then<br />
msg = protectionBlurbs[action][level]<br />
elseif protectionBlurbs[action].default then<br />
msg = protectionBlurbs[action].default<br />
elseif protectionBlurbs.edit.default then<br />
msg = protectionBlurbs.edit.default<br />
else<br />
error('no protection blurb defined for protectionBlurbs.edit.default')<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
<br />
function Blurb:_makeProtectionDateParameter()<br />
local protectionDate = self._protectionObj.protectionDate<br />
if type(protectionDate) == 'number' then<br />
return self:_formatDate(protectionDate)<br />
else<br />
return protectionDate<br />
end<br />
end<br />
<br />
function Blurb:_makeProtectionLevelParameter()<br />
local protectionLevels = self._cfg.protectionLevels<br />
local action = self._protectionObj.action<br />
local level = self._protectionObj.level<br />
local msg<br />
if protectionLevels[action][level] then<br />
msg = protectionLevels[action][level]<br />
elseif protectionLevels[action].default then<br />
msg = protectionLevels[action].default<br />
elseif protectionLevels.edit.default then<br />
msg = protectionLevels.edit.default<br />
else<br />
error('no protection level defined for protectionLevels.edit.default')<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
<br />
function Blurb:_makeProtectionLogParameter()<br />
local pagename = self._protectionObj.title.prefixedText<br />
if self._protectionObj.action == 'autoreview' then<br />
-- We need the pending changes log.<br />
return makeFullUrl(<br />
'Special:Log',<br />
{type = 'stable', page = pagename},<br />
self:_getExpandedMessage('pc-log-display')<br />
)<br />
else<br />
-- We need the protection log.<br />
return makeFullUrl(<br />
'Special:Log',<br />
{type = 'protect', page = pagename},<br />
self:_getExpandedMessage('protection-log-display')<br />
)<br />
end<br />
end<br />
<br />
function Blurb:_makeTalkPageParameter()<br />
return string.format(<br />
'[[%s:%s#%s|%s]]',<br />
mw.site.namespaces[self._protectionObj.title.namespace].talk.name,<br />
self._protectionObj.title.text,<br />
self._args.section or 'top',<br />
self:_getExpandedMessage('talk-page-link-display')<br />
)<br />
end<br />
<br />
function Blurb:_makeTooltipBlurbParameter()<br />
if self._protectionObj:isTemporary() then<br />
return self:_getExpandedMessage('tooltip-blurb-expiry')<br />
else<br />
return self:_getExpandedMessage('tooltip-blurb-noexpiry')<br />
end<br />
end<br />
<br />
function Blurb:_makeTooltipFragmentParameter()<br />
if self._protectionObj:isTemporary() then<br />
return self:_getExpandedMessage('tooltip-fragment-expiry')<br />
else<br />
return self:_getExpandedMessage('tooltip-fragment-noexpiry')<br />
end<br />
end<br />
<br />
function Blurb:_makeVandalTemplateParameter()<br />
return require('Module:Vandal-m')._main{<br />
self._args.user or self._protectionObj.title.baseText<br />
}<br />
end<br />
<br />
-- Public methods --<br />
<br />
function Blurb:makeBannerText(key)<br />
-- Validate input.<br />
if not key or not Blurb.bannerTextFields[key] then<br />
error(string.format(<br />
'"%s" is not a valid banner config field',<br />
tostring(key)<br />
), 2)<br />
end<br />
<br />
-- Generate the text.<br />
local msg = self._protectionObj.bannerConfig[key]<br />
if type(msg) == 'string' then<br />
return self:_substituteParameters(msg)<br />
elseif type(msg) == 'function' then<br />
msg = msg(self._protectionObj, self._args)<br />
if type(msg) ~= 'string' then<br />
error(string.format(<br />
'bad output from banner config function with key "%s"'<br />
.. ' (expected string, got %s)',<br />
tostring(key),<br />
type(msg)<br />
))<br />
end<br />
return self:_substituteParameters(msg)<br />
end<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- BannerTemplate class<br />
--------------------------------------------------------------------------------<br />
<br />
local BannerTemplate = {}<br />
BannerTemplate.__index = BannerTemplate<br />
<br />
function BannerTemplate.new(protectionObj, cfg)<br />
local obj = {}<br />
obj._cfg = cfg<br />
<br />
-- Set the image filename.<br />
local imageFilename = protectionObj.bannerConfig.image<br />
if imageFilename then<br />
obj._imageFilename = imageFilename<br />
else<br />
-- If an image filename isn't specified explicitly in the banner config,<br />
-- generate it from the protection status and the namespace.<br />
local action = protectionObj.action<br />
local level = protectionObj.level<br />
local namespace = protectionObj.title.namespace<br />
local reason = protectionObj.reason<br />
<br />
-- Deal with special cases first.<br />
if (<br />
namespace == 10<br />
or namespace == 828<br />
or reason and obj._cfg.indefImageReasons[reason]<br />
)<br />
and action == 'edit'<br />
and level == 'sysop'<br />
and not protectionObj:isTemporary()<br />
then<br />
-- Fully protected modules and templates get the special red "indef"<br />
-- padlock.<br />
obj._imageFilename = obj._cfg.msg['image-filename-indef']<br />
else<br />
-- Deal with regular protection types.<br />
local images = obj._cfg.images<br />
if images[action] then<br />
if images[action][level] then<br />
obj._imageFilename = images[action][level]<br />
elseif images[action].default then<br />
obj._imageFilename = images[action].default<br />
end<br />
end<br />
end<br />
end<br />
return setmetatable(obj, BannerTemplate)<br />
end<br />
<br />
function BannerTemplate:setImageWidth(width)<br />
self._imageWidth = width<br />
end<br />
<br />
function BannerTemplate:setImageTooltip(tooltip)<br />
self._imageCaption = tooltip<br />
end<br />
<br />
function BannerTemplate:renderImage()<br />
local filename = self._imageFilename<br />
or self._cfg.msg['image-filename-default']<br />
or 'Transparent.gif'<br />
return newFileLink(filename)<br />
:width(self._imageWidth or 20)<br />
:alt(self._imageAlt)<br />
:link(self._imageLink)<br />
:caption(self._imageCaption)<br />
:render()<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Banner class<br />
--------------------------------------------------------------------------------<br />
<br />
local Banner = setmetatable({}, BannerTemplate)<br />
Banner.__index = Banner<br />
<br />
function Banner.new(protectionObj, blurbObj, cfg)<br />
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.<br />
obj:setImageWidth(40)<br />
obj:setImageTooltip(blurbObj:makeBannerText('alt')) -- Large banners use the alt text for the tooltip.<br />
obj._reasonText = blurbObj:makeBannerText('text')<br />
obj._explanationText = blurbObj:makeBannerText('explanation')<br />
obj._page = protectionObj.title.prefixedText -- Only makes a difference in testing.<br />
return setmetatable(obj, Banner)<br />
end<br />
<br />
function Banner:__tostring()<br />
-- Renders the banner.<br />
makeMessageBox = makeMessageBox or require('Module:Message box').main<br />
local reasonText = self._reasonText or error('no reason text set')<br />
local explanationText = self._explanationText<br />
local mbargs = {<br />
page = self._page,<br />
type = 'protection',<br />
image = self:renderImage(),<br />
text = string.format(<br />
"'''%s'''%s",<br />
reasonText,<br />
explanationText and '<br />' .. explanationText or ''<br />
)<br />
}<br />
return makeMessageBox('mbox', mbargs)<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Padlock class<br />
--------------------------------------------------------------------------------<br />
<br />
local Padlock = setmetatable({}, BannerTemplate)<br />
Padlock.__index = Padlock<br />
<br />
function Padlock.new(protectionObj, blurbObj, cfg)<br />
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.<br />
obj:setImageWidth(20)<br />
obj:setImageTooltip(blurbObj:makeBannerText('tooltip'))<br />
obj._imageAlt = blurbObj:makeBannerText('alt')<br />
obj._imageLink = blurbObj:makeBannerText('link')<br />
obj._right = cfg.padlockPositions[protectionObj.action]<br />
or cfg.padlockPositions.default<br />
or '55px'<br />
return setmetatable(obj, Padlock)<br />
end<br />
<br />
function Padlock:__tostring()<br />
local root = mw.html.create('div')<br />
root<br />
:addClass('metadata topicon nopopups')<br />
:attr('id', 'protected-icon')<br />
:css{display = 'none', right = self._right}<br />
:wikitext(self:renderImage())<br />
return tostring(root)<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Exports<br />
--------------------------------------------------------------------------------<br />
<br />
local p = {}<br />
<br />
function p._exportClasses()<br />
-- This is used for testing purposes.<br />
return {<br />
Protection = Protection,<br />
Blurb = Blurb,<br />
BannerTemplate = BannerTemplate,<br />
Banner = Banner,<br />
Padlock = Padlock,<br />
}<br />
end<br />
<br />
function p._main(args, cfg, title)<br />
args = args or {}<br />
cfg = cfg or require('Module:Protection banner/config')<br />
<br />
-- Initialise the protection object and check for errors<br />
local protectionObjCreated, protectionObj = pcall(<br />
Protection.new,<br />
args,<br />
cfg,<br />
title<br />
)<br />
if not protectionObjCreated then<br />
local errorBlurb = cfg.msg['error-message-blurb'] or 'Error: $1.'<br />
local errorText = mw.message.newRawMessage(errorBlurb)<br />
:params(protectionObj) -- protectionObj is the error message<br />
:plain()<br />
return string.format(<br />
'<strong class="error">%s</strong>%s',<br />
errorText,<br />
makeCategoryLink(<br />
cfg.msg['tracking-category-incorrect'],<br />
title and title.text or mw.title.getCurrentTitle().text<br />
)<br />
)<br />
end<br />
<br />
-- Initialise the blurb object<br />
local blurbObj = Blurb.new(protectionObj, args, cfg)<br />
<br />
local ret = {}<br />
<br />
-- Render the banner<br />
if protectionObj:isProtected() then<br />
ret[#ret + 1] = tostring(<br />
(yesno(args.small) and Padlock or Banner)<br />
.new(protectionObj, blurbObj, cfg)<br />
)<br />
end<br />
<br />
-- Render the categories<br />
if yesno(args.category) ~= false then<br />
ret[#ret + 1] = protectionObj:makeCategoryLinks()<br />
end<br />
<br />
return table.concat(ret) <br />
end<br />
<br />
function p.main(frame, cfg)<br />
cfg = cfg or require(CONFIG_MODULE)<br />
<br />
-- Find default args, if any.<br />
local parentTitle = frame:getParent():getTitle()<br />
parentTitle = parentTitle:gsub('/sandbox$', '')<br />
local defaultArgs = cfg.wrappers[parentTitle] or {}<br />
<br />
-- Find user args, and use the parent frame if we are being called from a<br />
-- wrapper template.<br />
getArgs = getArgs or require('Module:Arguments').getArgs<br />
local userArgs = getArgs(frame, {<br />
parentOnly = defaultArgs,<br />
frameOnly = not defaultArgs<br />
})<br />
<br />
-- Build the args table. User-specified args overwrite default args.<br />
local args = {}<br />
for k, v in pairs(defaultArgs) do<br />
args[k] = v<br />
end<br />
for k, v in pairs(userArgs) do<br />
args[k] = v<br />
end<br />
return p._main(args, cfg)<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Mod%C3%A8le:Pp-templateModèle:Pp-template2014-08-05T17:17:27Z<p>Mr. Stradivarius : convert this to use Module:Protection banner; see here for details</p>
<hr />
<div><includeonly>{{#invoke:Protection banner|main}}</includeonly><noinclude><br />
{{documentation}}<br />
<!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --><br />
</noinclude></div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Category_handlerModule:Category handler2014-07-22T05:08:26Z<p>Mr. Stradivarius : allow invocations specifying the page parameter to use the mw.loadData optimisations, and don't call mw.title.new every time</p>
<hr />
<div>--------------------------------------------------------------------------------<br />
-- --<br />
-- CATEGORY HANDLER --<br />
-- --<br />
-- This module implements the {{category handler}} template in Lua, --<br />
-- with a few improvements: all namespaces and all namespace aliases --<br />
-- are supported, and namespace names are detected automatically for --<br />
-- the local wiki. This module requires [[Module:Namespace detect]] --<br />
-- and [[Module:Yesno]] to be available on the local wiki. It can be --<br />
-- configured for different wikis by altering the values in --<br />
-- [[Module:Category handler/config]], and pages can be blacklisted --<br />
-- from categorisation by using [[Module:Category handler/blacklist]]. --<br />
-- --<br />
--------------------------------------------------------------------------------<br />
<br />
-- Load required modules<br />
local yesno = require('Module:Yesno')<br />
<br />
-- Lazily load things we don't always need<br />
local mShared, mappings<br />
<br />
local p = {}<br />
<br />
--------------------------------------------------------------------------------<br />
-- Helper functions<br />
--------------------------------------------------------------------------------<br />
<br />
local function trimWhitespace(s, removeBlanks)<br />
if type(s) ~= 'string' then<br />
return s<br />
end<br />
s = s:match('^%s*(.-)%s*$')<br />
if removeBlanks then<br />
if s ~= '' then<br />
return s<br />
else<br />
return nil<br />
end<br />
else<br />
return s<br />
end<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- CategoryHandler class<br />
--------------------------------------------------------------------------------<br />
<br />
local CategoryHandler = {}<br />
CategoryHandler.__index = CategoryHandler<br />
<br />
function CategoryHandler.new(data, args)<br />
local obj = setmetatable({ _data = data, _args = args }, CategoryHandler)<br />
<br />
-- Set the title object<br />
do<br />
local pagename = obj:parameter('demopage')<br />
local success, titleObj<br />
if pagename then<br />
success, titleObj = pcall(mw.title.new, pagename)<br />
end<br />
if success and titleObj then<br />
obj.title = titleObj<br />
if titleObj == mw.title.getCurrentTitle() then<br />
obj._usesCurrentTitle = true<br />
end<br />
else<br />
obj.title = mw.title.getCurrentTitle()<br />
obj._usesCurrentTitle = true<br />
end<br />
end<br />
<br />
-- Set suppression parameter values<br />
for _, key in ipairs{'nocat', 'categories'} do<br />
local value = obj:parameter(key)<br />
value = trimWhitespace(value, true)<br />
obj['_' .. key] = yesno(value)<br />
end<br />
do<br />
local subpage = obj:parameter('subpage')<br />
local category2 = obj:parameter('category2')<br />
if type(subpage) == 'string' then<br />
subpage = mw.ustring.lower(subpage)<br />
end<br />
if type(category2) == 'string' then<br />
subpage = mw.ustring.lower(category2)<br />
end<br />
obj._subpage = trimWhitespace(subpage, true)<br />
obj._category2 = trimWhitespace(category2) -- don't remove blank values<br />
end<br />
return obj<br />
end<br />
<br />
function CategoryHandler:parameter(key)<br />
local parameterNames = self._data.parameters[key]<br />
local pntype = type(parameterNames)<br />
if pntype == 'string' or pntype == 'number' then<br />
return self._args[parameterNames]<br />
elseif pntype == 'table' then<br />
for _, name in ipairs(parameterNames) do<br />
local value = self._args[name]<br />
if value ~= nil then<br />
return value<br />
end<br />
end<br />
return nil<br />
else<br />
error(string.format(<br />
'invalid config key "%s"',<br />
tostring(key)<br />
), 2)<br />
end<br />
end<br />
<br />
function CategoryHandler:isSuppressedByArguments()<br />
return<br />
-- See if a category suppression argument has been set.<br />
self._nocat == true<br />
or self._categories == false<br />
or (<br />
self._category2<br />
and self._category2 ~= self._data.category2Yes<br />
and self._category2 ~= self._data.category2Negative<br />
)<br />
<br />
-- Check whether we are on a subpage, and see if categories are<br />
-- suppressed based on our subpage status.<br />
or self._subpage == self._data.subpageNo and self.title.isSubpage<br />
or self._subpage == self._data.subpageOnly and not self.title.isSubpage<br />
end<br />
<br />
function CategoryHandler:shouldSkipBlacklistCheck()<br />
-- Check whether the category suppression arguments indicate we<br />
-- should skip the blacklist check.<br />
return self._nocat == false<br />
or self._categories == true<br />
or self._category2 == self._data.category2Yes<br />
end<br />
<br />
function CategoryHandler:matchesBlacklist()<br />
if self._usesCurrentTitle then<br />
return self._data.currentTitleMatchesBlacklist<br />
else<br />
mShared = mShared or require('Module:Category handler/shared')<br />
return mShared.matchesBlacklist(<br />
self.title.prefixedText,<br />
mw.loadData('Module:Category handler/blacklist')<br />
)<br />
end<br />
end<br />
<br />
function CategoryHandler:isSuppressed()<br />
-- Find if categories are suppressed by either the arguments or by<br />
-- matching the blacklist.<br />
return self:isSuppressedByArguments()<br />
or not self:shouldSkipBlacklistCheck() and self:matchesBlacklist()<br />
end<br />
<br />
function CategoryHandler:getNamespaceParameters()<br />
if self._usesCurrentTitle then<br />
return self._data.currentTitleNamespaceParameters<br />
else<br />
if not mappings then<br />
mShared = mShared or require('Module:Category handler/shared')<br />
mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData<br />
end<br />
return mShared.getNamespaceParameters(<br />
self.title,<br />
mappings<br />
)<br />
end<br />
end<br />
<br />
function CategoryHandler:namespaceParametersExist()<br />
-- Find whether any namespace parameters have been specified.<br />
-- We use the order "all" --> namespace params --> "other" as this is what<br />
-- the old template did.<br />
if self:parameter('all') then<br />
return true<br />
end<br />
if not mappings then<br />
mShared = mShared or require('Module:Category handler/shared')<br />
mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData<br />
end<br />
for ns, params in pairs(mappings) do<br />
for i, param in ipairs(params) do<br />
if self._args[param] then<br />
return true<br />
end<br />
end<br />
end<br />
if self:parameter('other') then<br />
return true<br />
end<br />
return false<br />
end<br />
<br />
function CategoryHandler:getCategories()<br />
local params = self:getNamespaceParameters()<br />
local nsCategory<br />
for i, param in ipairs(params) do<br />
local value = self._args[param]<br />
if value ~= nil then<br />
nsCategory = value<br />
break<br />
end<br />
end<br />
if nsCategory ~= nil or self:namespaceParametersExist() then<br />
-- Namespace parameters exist - advanced usage.<br />
if nsCategory == nil then<br />
nsCategory = self:parameter('other')<br />
end<br />
local ret = {self:parameter('all')}<br />
local numParam = tonumber(nsCategory)<br />
if numParam and numParam >= 1 and math.floor(numParam) == numParam then<br />
-- nsCategory is an integer<br />
ret[#ret + 1] = self._args[numParam]<br />
else<br />
ret[#ret + 1] = nsCategory<br />
end<br />
if #ret < 1 then<br />
return nil<br />
else<br />
return table.concat(ret)<br />
end<br />
elseif self._data.defaultNamespaces[self.title.namespace] then<br />
-- Namespace parameters don't exist, simple usage.<br />
return self._args[1]<br />
end<br />
return nil<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Exports<br />
--------------------------------------------------------------------------------<br />
<br />
local p = {}<br />
<br />
function p._exportClasses()<br />
-- Used for testing purposes.<br />
return {<br />
CategoryHandler = CategoryHandler<br />
}<br />
end<br />
<br />
function p._main(args, data)<br />
data = data or mw.loadData('Module:Category handler/data')<br />
local handler = CategoryHandler.new(data, args)<br />
if handler:isSuppressed() then<br />
return nil<br />
end<br />
return handler:getCategories()<br />
end<br />
<br />
function p.main(frame, data)<br />
data = data or mw.loadData('Module:Category handler/data')<br />
local args = require('Module:Arguments').getArgs(frame, {<br />
wrappers = data.wrappers,<br />
valueFunc = function (k, v)<br />
v = trimWhitespace(v)<br />
if type(k) == 'number' then<br />
if v ~= '' then<br />
return v<br />
else<br />
return nil<br />
end<br />
else<br />
return v<br />
end<br />
end<br />
})<br />
return p._main(args, data)<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Category_handler/blacklistModule:Category handler/blacklist2014-07-13T09:07:09Z<p>Mr. Stradivarius : Protected Module:Category handler/blacklist: High-risk Lua module ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))</p>
<hr />
<div>-- This module contains the blacklist used by [[Module:Category handler]].<br />
-- Pages that match Lua patterns in this list will not be categorised unless<br />
-- categorisation is explicitly requested.<br />
<br />
return {<br />
'^Main Page$', -- don't categorise the main page.<br />
<br />
-- Don't categorise the following pages or their subpages.<br />
-- "%f[/\0]" matches if the next character is "/" or the end of the string.<br />
'^Wikipedia:Cascade%-protected items%f[/\0]',<br />
'^User:UBX%f[/\0]', -- The userbox "template" space.<br />
'^User talk:UBX%f[/\0]',<br />
<br />
-- Don't categorise subpages of these pages, but allow<br />
-- categorisation of the base page.<br />
'^Wikipedia:Template messages/.*$',<br />
<br />
'/[aA]rchive' -- Don't categorise archives.<br />
}</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Category_handler/sharedModule:Category handler/shared2014-07-13T09:06:15Z<p>Mr. Stradivarius : Protected Module:Category handler/shared: High-risk Lua module ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))</p>
<hr />
<div>-- This module contains shared functions used by [[Module:Category handler]]<br />
-- and its submodules.<br />
<br />
local p = {}<br />
<br />
function p.matchesBlacklist(page, blacklist)<br />
for i, pattern in ipairs(blacklist) do<br />
local match = mw.ustring.match(page, pattern)<br />
if match then<br />
return true<br />
end<br />
end<br />
return false<br />
end<br />
<br />
function p.getParamMappings(useLoadData)<br />
local dataPage = 'Module:Namespace detect/data'<br />
if useLoadData then<br />
return mw.loadData(dataPage).mappings<br />
else<br />
return require(dataPage).mappings<br />
end<br />
end<br />
<br />
function p.getNamespaceParameters(titleObj, mappings)<br />
-- We don't use title.nsText for the namespace name because it adds<br />
-- underscores.<br />
local mappingsKey<br />
if titleObj.isTalkPage then<br />
mappingsKey = 'talk'<br />
else<br />
mappingsKey = mw.site.namespaces[titleObj.namespace].name<br />
end<br />
mappingsKey = mw.ustring.lower(mappingsKey)<br />
return mappings[mappingsKey] or {}<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Category_handler/dataModule:Category handler/data2014-07-13T09:05:55Z<p>Mr. Stradivarius : Protected Module:Category handler/data: High-risk Lua module ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))</p>
<hr />
<div>-- This module assembles data to be passed to [[Module:Category handler]] using<br />
-- mw.loadData. This includes the configuration data and whether the current<br />
-- page matches the title blacklist.<br />
<br />
local data = require('Module:Category handler/config')<br />
local mShared = require('Module:Category handler/shared')<br />
local blacklist = require('Module:Category handler/blacklist')<br />
local title = mw.title.getCurrentTitle()<br />
<br />
data.currentTitleMatchesBlacklist = mShared.matchesBlacklist(<br />
title.prefixedText,<br />
blacklist<br />
)<br />
<br />
data.currentTitleNamespaceParameters = mShared.getNamespaceParameters(<br />
title,<br />
mShared.getParamMappings()<br />
)<br />
<br />
return data</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Category_handler/configModule:Category handler/config2014-07-13T09:05:36Z<p>Mr. Stradivarius : Protected Module:Category handler/config: High-risk Lua module ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))</p>
<hr />
<div>--------------------------------------------------------------------------------<br />
-- [[Module:Category handler]] configuration data --<br />
-- Language-specific parameter names and values can be set here. --<br />
-- For blacklist config, see [[Module:Category handler/blacklist]]. --<br />
--------------------------------------------------------------------------------<br />
<br />
local cfg = {} -- Don't edit this line.<br />
<br />
--------------------------------------------------------------------------------<br />
-- Start configuration data --<br />
--------------------------------------------------------------------------------<br />
<br />
--------------------------------------------------------------------------------<br />
-- Parameter names --<br />
-- These configuration items specify custom parameter names. --<br />
-- To add one extra name, you can use this format: --<br />
-- --<br />
-- foo = 'parameter name', --<br />
-- --<br />
-- To add multiple names, you can use this format: --<br />
-- --<br />
-- foo = {'parameter name 1', 'parameter name 2', 'parameter name 3'}, --<br />
--------------------------------------------------------------------------------<br />
<br />
cfg.parameters = {<br />
<br />
-- The nocat and categories parameter suppress<br />
-- categorisation. They are used with Module:Yesno, and work as follows:<br />
--<br />
-- cfg.nocat:<br />
-- Result of yesno() Effect<br />
-- true Categorisation is suppressed<br />
-- false Categorisation is allowed, and<br />
-- the blacklist check is skipped<br />
-- nil Categorisation is allowed<br />
--<br />
-- cfg.categories:<br />
-- Result of yesno() Effect<br />
-- true Categorisation is allowed, and<br />
-- the blacklist check is skipped<br />
-- false Categorisation is suppressed<br />
-- nil Categorisation is allowed<br />
nocat = 'nocat',<br />
categories = 'categories',<br />
<br />
-- The parameter name for the legacy "category2" parameter. This skips the<br />
-- blacklist if set to the cfg.category2Yes value, and suppresses<br />
-- categorisation if present but equal to anything other than<br />
-- cfg.category2Yes or cfg.category2Negative.<br />
category2 = 'category2',<br />
<br />
-- cfg.subpage is the parameter name to specify how to behave on subpages.<br />
subpage = 'subpage',<br />
<br />
-- The parameter for data to return in all namespaces.<br />
all = 'all',<br />
<br />
-- The parameter name for data to return if no data is specified for the<br />
-- namespace that is detected.<br />
other = 'other',<br />
<br />
-- The parameter name used to specify a page other than the current page;<br />
-- used for testing and demonstration.<br />
demopage = 'page',<br />
}<br />
<br />
--------------------------------------------------------------------------------<br />
-- Parameter values --<br />
-- These are set values that can be used with certain parameters. Only one --<br />
-- value can be specified, like this: --<br />
-- --<br />
-- cfg.foo = 'value name' -- --<br />
--------------------------------------------------------------------------------<br />
<br />
-- The following settings are used with the cfg.category2 parameter. Setting<br />
-- cfg.category2 to cfg.category2Yes skips the blacklist, and if cfg.category2<br />
-- is present but equal to anything other than cfg.category2Yes or<br />
-- cfg.category2Negative then it supresses cateogrisation.<br />
cfg.category2Yes = 'yes'<br />
cfg.category2Negative = '¬'<br />
<br />
-- The following settings are used with the cfg.subpage parameter.<br />
-- cfg.subpageNo is the value to specify to not categorise on subpages;<br />
-- cfg.subpageOnly is the value to specify to only categorise on subpages.<br />
cfg.subpageNo = 'no'<br />
cfg.subpageOnly = 'only'<br />
<br />
--------------------------------------------------------------------------------<br />
-- Default namespaces --<br />
-- This is a table of namespaces to categorise by default. The keys are the --<br />
-- namespace numbers. --<br />
--------------------------------------------------------------------------------<br />
<br />
cfg.defaultNamespaces = {<br />
[ 0] = true, -- main<br />
[ 6] = true, -- file<br />
[ 12] = true, -- help<br />
[ 14] = true, -- category<br />
[100] = true, -- portal<br />
[108] = true, -- book<br />
}<br />
<br />
--------------------------------------------------------------------------------<br />
-- Wrappers --<br />
-- This is a wrapper template or a list of wrapper templates to be passed to --<br />
-- [[Module:Arguments]]. --<br />
--------------------------------------------------------------------------------<br />
<br />
cfg.wrappers = 'Template:Category handler'<br />
<br />
--------------------------------------------------------------------------------<br />
-- End configuration data --<br />
--------------------------------------------------------------------------------<br />
<br />
return cfg -- Don't edit this line.</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:ArgumentsModule:Arguments2014-07-05T03:52:56Z<p>Mr. Stradivarius : allow using both the frame and parent frame arguments with the wrappers option if specifically requested, per protected edit request by User:Jackmcbarn</p>
<hr />
<div>-- This module provides easy processing of arguments passed to Scribunto from<br />
-- #invoke. It is intended for use by other Lua modules, and should not be<br />
-- called from #invoke directly.<br />
<br />
local libraryUtil = require('libraryUtil')<br />
local checkType = libraryUtil.checkType<br />
<br />
local arguments = {}<br />
<br />
-- Generate four different tidyVal functions, so that we don't have to check the<br />
-- options every time we call it.<br />
<br />
local function tidyValDefault(key, val)<br />
if type(val) == 'string' then<br />
val = val:match('^%s*(.-)%s*$')<br />
if val == '' then<br />
return nil<br />
else<br />
return val<br />
end<br />
else<br />
return val<br />
end<br />
end<br />
<br />
local function tidyValTrimOnly(key, val)<br />
if type(val) == 'string' then<br />
return val:match('^%s*(.-)%s*$')<br />
else<br />
return val<br />
end<br />
end<br />
<br />
local function tidyValRemoveBlanksOnly(key, val)<br />
if type(val) == 'string' then<br />
if val:find('%S') then<br />
return val<br />
else<br />
return nil<br />
end<br />
else<br />
return val<br />
end<br />
end<br />
<br />
local function tidyValNoChange(key, val)<br />
return val<br />
end<br />
<br />
function arguments.getArgs(frame, options)<br />
checkType('getArgs', 1, frame, 'table', true)<br />
checkType('getArgs', 2, options, 'table', true)<br />
frame = frame or {}<br />
options = options or {}<br />
<br />
--[[<br />
-- Get the argument tables. If we were passed a valid frame object, get the<br />
-- frame arguments (fargs) and the parent frame arguments (pargs), depending<br />
-- on the options set and on the parent frame's availability. If we weren't<br />
-- passed a valid frame object, we are being called from another Lua module<br />
-- or from the debug console, so assume that we were passed a table of args<br />
-- directly, and assign it to a new variable (luaArgs).<br />
--]]<br />
local fargs, pargs, luaArgs<br />
if type(frame.args) == 'table' and type(frame.getParent) == 'function' then<br />
if options.wrappers then<br />
--[[<br />
-- The wrappers option makes Module:Arguments look up arguments in<br />
-- either the frame argument table or the parent argument table, but<br />
-- not both. This means that users can use either the #invoke syntax<br />
-- or a wrapper template without the loss of performance associated<br />
-- with looking arguments up in both the frame and the parent frame.<br />
-- Module:Arguments will look up arguments in the parent frame<br />
-- if it finds the parent frame's title in options.wrapper;<br />
-- otherwise it will look up arguments in the frame object passed<br />
-- to getArgs.<br />
--]]<br />
local parent = frame:getParent()<br />
if not parent then<br />
fargs = frame.args<br />
else<br />
local title = parent:getTitle():gsub('/sandbox$', '')<br />
local found = false<br />
if type(options.wrappers) == 'table' then<br />
for _,v in pairs(options.wrappers) do<br />
if v == title then<br />
found = true<br />
break<br />
end<br />
end<br />
elseif options.wrappers == title then<br />
found = true<br />
end<br />
<br />
-- We test for false specifically here so that nil (the default) acts like true.<br />
if found or options.frameOnly == false then<br />
pargs = parent.args<br />
end<br />
if not found or options.parentOnly == false then<br />
fargs = frame.args<br />
end<br />
end<br />
else<br />
-- options.wrapper isn't set, so check the other options.<br />
if not options.parentOnly then<br />
fargs = frame.args<br />
end<br />
if not options.frameOnly then<br />
local parent = frame:getParent()<br />
pargs = parent and parent.args or nil<br />
end<br />
end<br />
if options.parentFirst then<br />
fargs, pargs = pargs, fargs<br />
end<br />
else<br />
luaArgs = frame<br />
end<br />
<br />
-- Set the order of precedence of the argument tables. If the variables are<br />
-- nil, nothing will be added to the table, which is how we avoid clashes<br />
-- between the frame/parent args and the Lua args. <br />
local argTables = {fargs}<br />
argTables[#argTables + 1] = pargs<br />
argTables[#argTables + 1] = luaArgs<br />
<br />
--[[<br />
-- Generate the tidyVal function. If it has been specified by the user, we<br />
-- use that; if not, we choose one of four functions depending on the<br />
-- options chosen. This is so that we don't have to call the options table<br />
-- every time the function is called.<br />
--]]<br />
local tidyVal = options.valueFunc<br />
if tidyVal then<br />
if type(tidyVal) ~= 'function' then<br />
error(<br />
"bad value assigned to option 'valueFunc'"<br />
.. '(function expected, got '<br />
.. type(tidyVal)<br />
.. ')',<br />
2<br />
)<br />
end<br />
elseif options.trim ~= false then<br />
if options.removeBlanks ~= false then<br />
tidyVal = tidyValDefault<br />
else<br />
tidyVal = tidyValTrimOnly<br />
end<br />
else<br />
if options.removeBlanks ~= false then<br />
tidyVal = tidyValRemoveBlanksOnly<br />
else<br />
tidyVal = tidyValNoChange<br />
end<br />
end<br />
<br />
--[[<br />
-- Set up the args, metaArgs and nilArgs tables. args will be the one<br />
-- accessed from functions, and metaArgs will hold the actual arguments. Nil<br />
-- arguments are memoized in nilArgs, and the metatable connects all of them<br />
-- together.<br />
--]]<br />
local args, metaArgs, nilArgs, metatable = {}, {}, {}, {}<br />
setmetatable(args, metatable)<br />
<br />
local function mergeArgs(iterator, tables)<br />
--[[<br />
-- Accepts multiple tables as input and merges their keys and values<br />
-- into one table using the specified iterator. If a value is already<br />
-- present it is not overwritten; tables listed earlier have precedence.<br />
-- We are also memoizing nil values, but those values can be<br />
-- overwritten.<br />
--]]<br />
for _, t in ipairs(tables) do<br />
for key, val in iterator(t) do<br />
if metaArgs[key] == nil then<br />
local tidiedVal = tidyVal(key, val)<br />
if tidiedVal == nil then<br />
nilArgs[key] = true<br />
else<br />
metaArgs[key] = tidiedVal<br />
end<br />
end<br />
end<br />
end<br />
end<br />
<br />
--[[<br />
-- Define metatable behaviour. Arguments are memoized in the metaArgs table,<br />
-- and are only fetched from the argument tables once. Fetching arguments<br />
-- from the argument tables is the most resource-intensive step in this<br />
-- module, so we try and avoid it where possible. For this reason, nil<br />
-- arguments are also memoized, in the nilArgs table. Also, we keep a record<br />
-- in the metatable of when pairs and ipairs have been called, so we do not<br />
-- run pairs and ipairs on the argument tables more than once. We also do<br />
-- not run ipairs on fargs and pargs if pairs has already been run, as all<br />
-- the arguments will already have been copied over.<br />
--]]<br />
<br />
metatable.__index = function (t, key)<br />
--[[<br />
-- Fetches an argument when the args table is indexed. First we check<br />
-- to see if the value is memoized, and if not we try and fetch it from<br />
-- the argument tables. When we check memoization, we need to check<br />
-- metaArgs before nilArgs, as both can be non-nil at the same time.<br />
-- If the argument is not present in metaArgs, we also check whether<br />
-- pairs has been run yet. If pairs has already been run, we return nil.<br />
-- This is because all the arguments will have already been copied into<br />
-- metaArgs by the mergeArgs function, meaning that any other arguments<br />
-- must be nil.<br />
--]]<br />
local val = metaArgs[key]<br />
if val ~= nil then<br />
return val<br />
elseif metatable.donePairs or nilArgs[key] then<br />
return nil<br />
end<br />
for _, argTable in ipairs(argTables) do<br />
local argTableVal = tidyVal(key, argTable[key])<br />
if argTableVal == nil then<br />
nilArgs[key] = true<br />
else<br />
metaArgs[key] = argTableVal<br />
return argTableVal<br />
end<br />
end<br />
return nil<br />
end<br />
<br />
metatable.__newindex = function (t, key, val)<br />
-- This function is called when a module tries to add a new value to the<br />
-- args table, or tries to change an existing value.<br />
if options.readOnly then<br />
error(<br />
'could not write to argument table key "'<br />
.. tostring(key)<br />
.. '"; the table is read-only',<br />
2<br />
)<br />
elseif options.noOverwrite and args[key] ~= nil then<br />
error(<br />
'could not write to argument table key "'<br />
.. tostring(key)<br />
.. '"; overwriting existing arguments is not permitted',<br />
2<br />
)<br />
elseif val == nil then<br />
--[[<br />
-- If the argument is to be overwritten with nil, we need to erase<br />
-- the value in metaArgs, so that __index, __pairs and __ipairs do<br />
-- not use a previous existing value, if present; and we also need<br />
-- to memoize the nil in nilArgs, so that the value isn't looked<br />
-- up in the argument tables if it is accessed again.<br />
--]]<br />
metaArgs[key] = nil<br />
nilArgs[key] = true<br />
else<br />
metaArgs[key] = val<br />
end<br />
end<br />
<br />
metatable.__pairs = function ()<br />
-- Called when pairs is run on the args table.<br />
if not metatable.donePairs then<br />
mergeArgs(pairs, argTables)<br />
metatable.donePairs = true<br />
metatable.doneIpairs = true<br />
end<br />
return pairs(metaArgs)<br />
end<br />
<br />
metatable.__ipairs = function ()<br />
-- Called when ipairs is run on the args table.<br />
if not metatable.doneIpairs then<br />
mergeArgs(ipairs, argTables)<br />
metatable.doneIpairs = true<br />
end<br />
return ipairs(metaArgs)<br />
end<br />
<br />
return args<br />
end<br />
<br />
return arguments</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:DocumentationModule:Documentation2014-07-04T14:26:55Z<p>Mr. Stradivarius : Undid revision 615575005 by Technical 13 (talk) this puts the end box inside the main div, so we get one blue box inside another blue box; not really what we want</p>
<hr />
<div>-- This module implements {{documentation}}.<br />
<br />
-- Get required modules.<br />
local getArgs = require('Module:Arguments').getArgs<br />
local htmlBuilder = require('Module:HtmlBuilder')<br />
local messageBox = require('Module:Message box')<br />
<br />
-- Get the config table.<br />
local cfg = mw.loadData('Module:Documentation/config')<br />
<br />
local p = {}<br />
<br />
-- Often-used functions.<br />
local ugsub = mw.ustring.gsub<br />
<br />
----------------------------------------------------------------------------<br />
-- Helper functions<br />
--<br />
-- These are defined as local functions, but are made available in the p<br />
-- table for testing purposes.<br />
----------------------------------------------------------------------------<br />
<br />
local function message(cfgKey, valArray, expectType)<br />
--[[<br />
-- Gets a message from the cfg table and formats it if appropriate.<br />
-- The function raises an error if the value from the cfg table is not<br />
-- of the type expectType. The default type for expectType is 'string'.<br />
-- If the table valArray is present, strings such as $1, $2 etc. in the<br />
-- message are substituted with values from the table keys [1], [2] etc.<br />
-- For example, if the message "foo-message" had the value 'Foo $2 bar $1.',<br />
-- message('foo-message', {'baz', 'qux'}) would return "Foo qux bar baz."<br />
--]]<br />
local msg = cfg[cfgKey]<br />
expectType = expectType or 'string'<br />
if type(msg) ~= expectType then<br />
error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2)<br />
end<br />
if not valArray then<br />
return msg<br />
end<br />
<br />
local function getMessageVal(match)<br />
match = tonumber(match)<br />
return valArray[match] or error('message: no value found for key $' .. match .. ' in message cfg.' .. cfgKey, 4)<br />
end<br />
<br />
local ret = ugsub(msg, '$([1-9][0-9]*)', getMessageVal)<br />
return ret<br />
end<br />
<br />
p.message = message<br />
<br />
local function makeWikilink(page, display)<br />
if display then<br />
return mw.ustring.format('[[%s|%s]]', page, display)<br />
else<br />
return mw.ustring.format('[[%s]]', page)<br />
end<br />
end<br />
<br />
p.makeWikilink = makeWikilink<br />
<br />
local function makeCategoryLink(cat, sort)<br />
local catns = mw.site.namespaces[14].name<br />
return makeWikilink(catns .. ':' .. cat, sort)<br />
end<br />
<br />
p.makeCategoryLink = makeCategoryLink<br />
<br />
local function makeUrlLink(url, display)<br />
return mw.ustring.format('[%s %s]', url, display)<br />
end<br />
<br />
p.makeUrlLink = makeUrlLink<br />
<br />
local function makeToolbar(...)<br />
local ret = {}<br />
local lim = select('#', ...)<br />
if lim < 1 then<br />
return nil<br />
end<br />
for i = 1, lim do<br />
ret[#ret + 1] = select(i, ...)<br />
end<br />
return '<small style="font-style: normal;">(' .. table.concat(ret, ' &#124; ') .. ')</small>'<br />
end <br />
<br />
p.makeToolbar = makeToolbar<br />
<br />
----------------------------------------------------------------------------<br />
-- Argument processing<br />
----------------------------------------------------------------------------<br />
<br />
local function makeInvokeFunc(funcName)<br />
return function (frame)<br />
local args = getArgs(frame, {<br />
valueFunc = function (key, value)<br />
if type(value) == 'string' then<br />
value = value:match('^%s*(.-)%s*$') -- Remove whitespace.<br />
if key == 'heading' or value ~= '' then<br />
return value<br />
else<br />
return nil<br />
end<br />
else<br />
return value<br />
end<br />
end<br />
})<br />
return p[funcName](args)<br />
end<br />
end<br />
<br />
----------------------------------------------------------------------------<br />
-- Main function<br />
----------------------------------------------------------------------------<br />
<br />
p.main = makeInvokeFunc('_main')<br />
<br />
function p._main(args)<br />
--[[<br />
-- This function defines logic flow for the module.<br />
-- @args - table of arguments passed by the user<br />
-- <br />
-- Messages:<br />
-- 'main-div-id' --> 'template-documentation'<br />
-- 'main-div-classes' --> 'template-documentation iezoomfix'<br />
--]]<br />
local env = p.getEnvironment(args)<br />
local root = htmlBuilder.create()<br />
root<br />
.wikitext(p.protectionTemplate(env))<br />
.wikitext(p.sandboxNotice(args, env))<br />
-- This div tag is from {{documentation/start box}}, but moving it here<br />
-- so that we don't have to worry about unclosed tags.<br />
.tag('div')<br />
.attr('id', message('main-div-id'))<br />
.addClass(message('main-div-classes'))<br />
.newline()<br />
.wikitext(p._startBox(args, env))<br />
.wikitext(p._content(args, env))<br />
.tag('div')<br />
.css('clear', 'both') -- So right or left floating items don't stick out of the doc box.<br />
.newline()<br />
.done()<br />
.done()<br />
.wikitext(p._endBox(args, env))<br />
.wikitext(p.addTrackingCategories(env))<br />
return tostring(root)<br />
end<br />
<br />
----------------------------------------------------------------------------<br />
-- Environment settings<br />
----------------------------------------------------------------------------<br />
<br />
function p.getEnvironment(args)<br />
--[[<br />
-- Returns a table with information about the environment, including title objects and other namespace- or<br />
-- path-related data.<br />
-- @args - table of arguments passed by the user<br />
--<br />
-- Title objects include:<br />
-- env.title - the page we are making documentation for (usually the current title)<br />
-- env.templateTitle - the template (or module, file, etc.)<br />
-- env.docTitle - the /doc subpage.<br />
-- env.sandboxTitle - the /sandbox subpage.<br />
-- env.testcasesTitle - the /testcases subpage.<br />
-- env.printTitle - the print version of the template, located at the /Print subpage.<br />
--<br />
-- Data includes:<br />
-- env.protectionLevels - the protection levels table of the title object.<br />
-- env.subjectSpace - the number of the title's subject namespace.<br />
-- env.docSpace - the number of the namespace the title puts its documentation in.<br />
-- env.docpageBase - the text of the base page of the /doc, /sandbox and /testcases pages, with namespace.<br />
-- env.compareUrl - URL of the Special:ComparePages page comparing the sandbox with the template.<br />
-- <br />
-- All table lookups are passed through pcall so that errors are caught. If an error occurs, the value<br />
-- returned will be nil.<br />
--]]<br />
<br />
local env, envFuncs = {}, {}<br />
<br />
-- Set up the metatable. If triggered we call the corresponding function in the envFuncs table. The value<br />
-- returned by that function is memoized in the env table so that we don't call any of the functions<br />
-- more than once. (Nils won't be memoized.)<br />
setmetatable(env, {<br />
__index = function (t, key)<br />
local envFunc = envFuncs[key]<br />
if envFunc then<br />
local success, val = pcall(envFunc)<br />
if success then<br />
env[key] = val -- Memoise the value.<br />
return val<br />
end<br />
end<br />
return nil<br />
end<br />
}) <br />
<br />
function envFuncs.title()<br />
-- The title object for the current page, or a test page passed with args.page.<br />
local title<br />
local titleArg = args.page<br />
if titleArg then<br />
title = mw.title.new(titleArg)<br />
else<br />
title = mw.title.getCurrentTitle()<br />
end<br />
return title<br />
end<br />
<br />
function envFuncs.templateTitle()<br />
--[[<br />
-- The template (or module, etc.) title object.<br />
-- Messages:<br />
-- 'sandbox-subpage' --> 'sandbox'<br />
-- 'testcases-subpage' --> 'testcases'<br />
--]]<br />
local subjectSpace = env.subjectSpace<br />
local title = env.title<br />
local subpage = title.subpageText<br />
if subpage == message('sandbox-subpage') or subpage == message('testcases-subpage') then<br />
return mw.title.makeTitle(subjectSpace, title.baseText)<br />
else<br />
return mw.title.makeTitle(subjectSpace, title.text)<br />
end<br />
end<br />
<br />
function envFuncs.docTitle()<br />
--[[<br />
-- Title object of the /doc subpage.<br />
-- Messages:<br />
-- 'doc-subpage' --> 'doc'<br />
--]]<br />
local title = env.title<br />
local docname = args[1] -- User-specified doc page.<br />
local docpage<br />
if docname then<br />
docpage = docname<br />
else<br />
docpage = env.docpageBase .. '/' .. message('doc-subpage')<br />
end<br />
return mw.title.new(docpage)<br />
end<br />
<br />
function envFuncs.sandboxTitle()<br />
--[[<br />
-- Title object for the /sandbox subpage.<br />
-- Messages:<br />
-- 'sandbox-subpage' --> 'sandbox'<br />
--]]<br />
return mw.title.new(env.docpageBase .. '/' .. message('sandbox-subpage'))<br />
end<br />
<br />
function envFuncs.testcasesTitle()<br />
--[[<br />
-- Title object for the /testcases subpage.<br />
-- Messages:<br />
-- 'testcases-subpage' --> 'testcases'<br />
--]]<br />
return mw.title.new(env.docpageBase .. '/' .. message('testcases-subpage'))<br />
end<br />
<br />
function envFuncs.printTitle()<br />
--[[<br />
-- Title object for the /Print subpage.<br />
-- Messages:<br />
-- 'print-subpage' --> 'Print'<br />
--]]<br />
return env.templateTitle:subPageTitle(message('print-subpage'))<br />
end<br />
<br />
function envFuncs.protectionLevels()<br />
-- The protection levels table of the title object.<br />
return env.title.protectionLevels<br />
end<br />
<br />
function envFuncs.subjectSpace()<br />
-- The subject namespace number.<br />
return mw.site.namespaces[env.title.namespace].subject.id<br />
end<br />
<br />
function envFuncs.docSpace()<br />
-- The documentation namespace number. For most namespaces this is the same as the<br />
-- subject namespace. However, pages in the Article, File, MediaWiki or Category<br />
-- namespaces must have their /doc, /sandbox and /testcases pages in talk space.<br />
local subjectSpace = env.subjectSpace<br />
if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 then<br />
return subjectSpace + 1<br />
else<br />
return subjectSpace<br />
end<br />
end<br />
<br />
function envFuncs.docpageBase()<br />
-- The base page of the /doc, /sandbox, and /testcases subpages.<br />
-- For some namespaces this is the talk page, rather than the template page.<br />
local templateTitle = env.templateTitle<br />
local docSpace = env.docSpace<br />
local docSpaceText = mw.site.namespaces[docSpace].name<br />
-- Assemble the link. docSpace is never the main namespace, so we can hardcode the colon.<br />
return docSpaceText .. ':' .. templateTitle.text<br />
end<br />
<br />
function envFuncs.compareUrl()<br />
-- Diff link between the sandbox and the main template using [[Special:ComparePages]].<br />
local templateTitle = env.templateTitle<br />
local sandboxTitle = env.sandboxTitle<br />
if templateTitle.exists and sandboxTitle.exists then<br />
local compareUrl = mw.uri.fullUrl(<br />
'Special:ComparePages',<br />
{page1 = templateTitle.prefixedText, page2 = sandboxTitle.prefixedText}<br />
)<br />
return tostring(compareUrl)<br />
else<br />
return nil<br />
end<br />
end <br />
<br />
return env<br />
end <br />
<br />
----------------------------------------------------------------------------<br />
-- Auxiliary templates<br />
----------------------------------------------------------------------------<br />
<br />
function p.sandboxNotice(args, env)<br />
--[=[<br />
-- Generates a sandbox notice for display above sandbox pages.<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
-- <br />
-- Messages:<br />
-- 'sandbox-notice-image' --> '[[Image:Sandbox.svg|50px|alt=|link=]]'<br />
-- 'sandbox-notice-blurb' --> 'This is the $1 for $2.'<br />
-- 'sandbox-notice-diff-blurb' --> 'This is the $1 for $2 ($3).'<br />
-- 'sandbox-notice-pagetype-template' --> '[[Wikipedia:Template test cases|template sandbox]] page'<br />
-- 'sandbox-notice-pagetype-module' --> '[[Wikipedia:Template test cases|module sandbox]] page'<br />
-- 'sandbox-notice-pagetype-other' --> 'sandbox page'<br />
-- 'sandbox-notice-compare-link-display' --> 'diff'<br />
-- 'sandbox-notice-testcases-blurb' --> 'See also the companion subpage for $1.'<br />
-- 'sandbox-notice-testcases-link-display' --> 'test cases'<br />
-- 'sandbox-category' --> 'Template sandboxes'<br />
--]=]<br />
local title = env.title<br />
local sandboxTitle = env.sandboxTitle<br />
local templateTitle = env.templateTitle<br />
local subjectSpace = env.subjectSpace<br />
if not (subjectSpace and title and sandboxTitle and templateTitle and mw.title.equals(title, sandboxTitle)) then<br />
return nil<br />
end<br />
-- Build the table of arguments to pass to {{ombox}}. We need just two fields, "image" and "text".<br />
local omargs = {}<br />
omargs.image = message('sandbox-notice-image')<br />
-- Get the text. We start with the opening blurb, which is something like<br />
-- "This is the template sandbox for [[Template:Foo]] (diff)."<br />
local text = ''<br />
local frame = mw.getCurrentFrame()<br />
local isPreviewing = frame:preprocess('{{REVISIONID}}') == '' -- True if the page is being previewed.<br />
local pagetype<br />
if subjectSpace == 10 then<br />
pagetype = message('sandbox-notice-pagetype-template')<br />
elseif subjectSpace == 828 then<br />
pagetype = message('sandbox-notice-pagetype-module')<br />
else<br />
pagetype = message('sandbox-notice-pagetype-other')<br />
end<br />
local templateLink = makeWikilink(templateTitle.prefixedText)<br />
local compareUrl = env.compareUrl<br />
if isPreviewing or not compareUrl then<br />
text = text .. message('sandbox-notice-blurb', {pagetype, templateLink})<br />
else<br />
local compareDisplay = message('sandbox-notice-compare-link-display')<br />
local compareLink = makeUrlLink(compareUrl, compareDisplay)<br />
text = text .. message('sandbox-notice-diff-blurb', {pagetype, templateLink, compareLink})<br />
end<br />
-- Get the test cases page blurb if the page exists. This is something like<br />
-- "See also the companion subpage for [[Template:Foo/testcases|test cases]]."<br />
local testcasesTitle = env.testcasesTitle<br />
if testcasesTitle and testcasesTitle.exists then<br />
if testcasesTitle.namespace == mw.site.namespaces.Module.id then<br />
local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')<br />
local testcasesRunLinkDisplay = message('sandbox-notice-testcases-run-link-display')<br />
local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)<br />
local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay)<br />
text = text .. '<br />' .. message('sandbox-notice-testcases-run-blurb', {testcasesLink, testcasesRunLink})<br />
else<br />
local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')<br />
local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)<br />
text = text .. '<br />' .. message('sandbox-notice-testcases-blurb', {testcasesLink})<br />
end<br />
end<br />
-- Add the sandbox to the sandbox category.<br />
text = text .. makeCategoryLink(message('sandbox-category'))<br />
omargs.text = text<br />
local ret = '<div style="clear: both;"></div>'<br />
ret = ret .. messageBox.main('ombox', omargs)<br />
return ret<br />
end<br />
<br />
function p.protectionTemplate(env)<br />
-- Generates the padlock icon in the top right.<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
-- Messages:<br />
-- 'protection-template' --> 'pp-template'<br />
-- 'protection-template-args' --> {docusage = 'yes'}<br />
local title = env.title<br />
local protectionLevels<br />
local protectionTemplate = message('protection-template')<br />
local namespace = title.namespace<br />
if not (protectionTemplate and (namespace == 10 or namespace == 828)) then<br />
-- Don't display the protection template if we are not in the template or module namespaces.<br />
return nil<br />
end<br />
protectionLevels = env.protectionLevels<br />
if not protectionLevels then<br />
return nil<br />
end<br />
local editLevels = protectionLevels.edit<br />
local moveLevels = protectionLevels.move<br />
if moveLevels and moveLevels[1] == 'sysop' or editLevels and editLevels[1] then<br />
-- The page is full-move protected, or full, template, or semi-protected.<br />
local frame = mw.getCurrentFrame()<br />
return frame:expandTemplate{title = protectionTemplate, args = message('protection-template-args', nil, 'table')}<br />
else<br />
return nil<br />
end<br />
end<br />
<br />
----------------------------------------------------------------------------<br />
-- Start box<br />
----------------------------------------------------------------------------<br />
<br />
p.startBox = makeInvokeFunc('_startBox')<br />
<br />
function p._startBox(args, env)<br />
--[[<br />
-- This function generates the start box.<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
-- <br />
-- The actual work is done by p.makeStartBoxLinksData and p.renderStartBoxLinks which make<br />
-- the [view] [edit] [history] [purge] links, and by p.makeStartBoxData and p.renderStartBox<br />
-- which generate the box HTML.<br />
--]]<br />
env = env or p.getEnvironment(args)<br />
local links<br />
local content = args.content<br />
if not content then<br />
-- No need to include the links if the documentation is on the template page itself.<br />
local linksData = p.makeStartBoxLinksData(args, env)<br />
if linksData then<br />
links = p.renderStartBoxLinks(linksData)<br />
end<br />
end<br />
-- Generate the start box html.<br />
local data = p.makeStartBoxData(args, env, links)<br />
if data then<br />
return p.renderStartBox(data)<br />
else<br />
-- User specified no heading.<br />
return nil<br />
end<br />
end<br />
<br />
function p.makeStartBoxLinksData(args, env)<br />
--[[<br />
-- Does initial processing of data to make the [view] [edit] [history] [purge] links.<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
-- <br />
-- Messages:<br />
-- 'view-link-display' --> 'view'<br />
-- 'edit-link-display' --> 'edit'<br />
-- 'history-link-display' --> 'history'<br />
-- 'purge-link-display' --> 'purge'<br />
-- 'file-docpage-preload' --> 'Template:Documentation/preload-filespace'<br />
-- 'module-preload' --> 'Template:Documentation/preload-module-doc'<br />
-- 'docpage-preload' --> 'Template:Documentation/preload'<br />
-- 'create-link-display' --> 'create'<br />
--]]<br />
local subjectSpace = env.subjectSpace<br />
local title = env.title<br />
local docTitle = env.docTitle<br />
if not title or not docTitle then<br />
return nil<br />
end<br />
<br />
local data = {}<br />
data.title = title<br />
data.docTitle = docTitle<br />
-- View, display, edit, and purge links if /doc exists.<br />
data.viewLinkDisplay = message('view-link-display')<br />
data.editLinkDisplay = message('edit-link-display')<br />
data.historyLinkDisplay = message('history-link-display')<br />
data.purgeLinkDisplay = message('purge-link-display')<br />
-- Create link if /doc doesn't exist.<br />
local preload = args.preload<br />
if not preload then<br />
if subjectSpace == 6 then -- File namespace<br />
preload = message('file-docpage-preload')<br />
elseif subjectSpace == 828 then -- Module namespace<br />
preload = message('module-preload')<br />
else<br />
preload = message('docpage-preload')<br />
end<br />
end<br />
data.preload = preload<br />
data.createLinkDisplay = message('create-link-display')<br />
return data<br />
end<br />
<br />
function p.renderStartBoxLinks(data)<br />
--[[<br />
-- Generates the [view][edit][history][purge] or [create] links from the data table.<br />
-- @data - a table of data generated by p.makeStartBoxLinksData<br />
--]]<br />
<br />
local function escapeBrackets(s)<br />
-- Escapes square brackets with HTML entities.<br />
s = s:gsub('%[', '&#91;') -- Replace square brackets with HTML entities.<br />
s = s:gsub('%]', '&#93;')<br />
return s<br />
end<br />
<br />
local ret<br />
local docTitle = data.docTitle<br />
local title = data.title<br />
if docTitle.exists then<br />
local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)<br />
local editLink = makeUrlLink(docTitle:fullUrl{action = 'edit'}, data.editLinkDisplay)<br />
local historyLink = makeUrlLink(docTitle:fullUrl{action = 'history'}, data.historyLinkDisplay)<br />
local purgeLink = makeUrlLink(title:fullUrl{action = 'purge'}, data.purgeLinkDisplay)<br />
ret = '[%s] [%s] [%s] [%s]'<br />
ret = escapeBrackets(ret)<br />
ret = mw.ustring.format(ret, viewLink, editLink, historyLink, purgeLink)<br />
else<br />
local createLink = makeUrlLink(docTitle:fullUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay)<br />
ret = '[%s]'<br />
ret = escapeBrackets(ret)<br />
ret = mw.ustring.format(ret, createLink)<br />
end<br />
return ret<br />
end<br />
<br />
function p.makeStartBoxData(args, env, links)<br />
--[=[<br />
-- Does initial processing of data to pass to the start-box render function, p.renderStartBox.<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
-- @links - a string containing the [view][edit][history][purge] links - could be nil if there's an error.<br />
--<br />
-- Messages:<br />
-- 'documentation-icon-wikitext' --> '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=Documentation icon]]'<br />
-- 'template-namespace-heading' --> 'Template documentation'<br />
-- 'module-namespace-heading' --> 'Module documentation'<br />
-- 'file-namespace-heading' --> 'Summary'<br />
-- 'other-namespaces-heading' --> 'Documentation'<br />
-- 'start-box-linkclasses' --> 'mw-editsection-like plainlinks'<br />
-- 'start-box-link-id' --> 'doc_editlinks'<br />
-- 'testcases-create-link-display' --> 'create'<br />
--]=]<br />
local subjectSpace = env.subjectSpace<br />
if not subjectSpace then<br />
-- Default to an "other namespaces" namespace, so that we get at least some output<br />
-- if an error occurs.<br />
subjectSpace = 2<br />
end<br />
local data = {}<br />
<br />
-- Heading<br />
local heading = args.heading -- Blank values are not removed.<br />
if heading == '' then<br />
-- Don't display the start box if the heading arg is defined but blank.<br />
return nil<br />
end<br />
if heading then<br />
data.heading = heading<br />
elseif subjectSpace == 10 then -- Template namespace<br />
data.heading = message('documentation-icon-wikitext') .. ' ' .. message('template-namespace-heading')<br />
elseif subjectSpace == 828 then -- Module namespace<br />
data.heading = message('documentation-icon-wikitext') .. ' ' .. message('module-namespace-heading')<br />
elseif subjectSpace == 6 then -- File namespace<br />
data.heading = message('file-namespace-heading')<br />
else<br />
data.heading = message('other-namespaces-heading')<br />
end<br />
<br />
-- Heading CSS<br />
local headingStyle = args['heading-style']<br />
if headingStyle then<br />
data.headingStyleText = headingStyle<br />
elseif subjectSpace == 10 then<br />
-- We are in the template or template talk namespaces.<br />
data.headingFontWeight = 'bold'<br />
data.headingFontSize = '125%'<br />
else<br />
data.headingFontSize = '150%'<br />
end<br />
<br />
-- Data for the [view][edit][history][purge] or [create] links.<br />
if links then<br />
data.linksClass = message('start-box-linkclasses')<br />
data.linksId = message('start-box-link-id')<br />
data.links = links<br />
end<br />
<br />
return data<br />
end<br />
<br />
function p.renderStartBox(data)<br />
-- Renders the start box html.<br />
-- @data - a table of data generated by p.makeStartBoxData.<br />
local sbox = htmlBuilder.create('div')<br />
sbox<br />
.css('padding-bottom', '3px')<br />
.css('border-bottom', '1px solid #aaa')<br />
.css('margin-bottom', '1ex')<br />
.newline()<br />
.tag('span')<br />
.cssText(data.headingStyleText)<br />
.css('font-weight', data.headingFontWeight)<br />
.css('font-size', data.headingFontSize)<br />
.wikitext(data.heading)<br />
local links = data.links<br />
if links then<br />
sbox.tag('span')<br />
.addClass(data.linksClass)<br />
.attr('id', data.linksId)<br />
.wikitext(links)<br />
end<br />
return tostring(sbox)<br />
end<br />
<br />
----------------------------------------------------------------------------<br />
-- Documentation content<br />
----------------------------------------------------------------------------<br />
<br />
p.content = makeInvokeFunc('_content')<br />
<br />
function p._content(args, env)<br />
-- Displays the documentation contents<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
env = env or p.getEnvironment(args)<br />
local docTitle = env.docTitle<br />
local content = args.content<br />
if not content and docTitle and docTitle.exists then<br />
content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText}<br />
end<br />
-- The line breaks below are necessary so that "=== Headings ===" at the start and end<br />
-- of docs are interpreted correctly.<br />
return '\n' .. (content or '') .. '\n' <br />
end<br />
<br />
p.contentTitle = makeInvokeFunc('_contentTitle')<br />
<br />
function p._contentTitle(args, env)<br />
env = env or p.getEnvironment(args)<br />
local docTitle = env.docTitle<br />
if not args.content and docTitle and docTitle.exists then<br />
return docTitle.prefixedText<br />
else<br />
return ''<br />
end<br />
end<br />
<br />
----------------------------------------------------------------------------<br />
-- End box<br />
----------------------------------------------------------------------------<br />
<br />
p.endBox = makeInvokeFunc('_endBox')<br />
<br />
function p._endBox(args, env)<br />
--[=[<br />
-- This function generates the end box (also known as the link box).<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
-- <br />
-- Messages:<br />
-- 'fmbox-id' --> 'documentation-meta-data'<br />
-- 'fmbox-style' --> 'background-color: #ecfcf4'<br />
-- 'fmbox-textstyle' --> 'font-style: italic'<br />
-- <br />
-- The HTML is generated by the {{fmbox}} template, courtesy of [[Module:Message box]].<br />
--]=]<br />
<br />
-- Get environment data.<br />
env = env or p.getEnvironment(args)<br />
local subjectSpace = env.subjectSpace<br />
local docTitle = env.docTitle<br />
if not subjectSpace or not docTitle then<br />
return nil<br />
end<br />
<br />
-- Check whether we should output the end box at all. Add the end<br />
-- box by default if the documentation exists or if we are in the<br />
-- user, module or template namespaces.<br />
local linkBox = args['link box']<br />
if linkBox == 'off'<br />
or not (<br />
docTitle.exists<br />
or subjectSpace == 2<br />
or subjectSpace == 828<br />
or subjectSpace == 10<br />
)<br />
then<br />
return nil<br />
end<br />
<br />
-- Assemble the arguments for {{fmbox}}.<br />
local fmargs = {}<br />
fmargs.id = message('fmbox-id') -- Sets 'documentation-meta-data'<br />
fmargs.image = 'none'<br />
fmargs.style = message('fmbox-style') -- Sets 'background-color: #ecfcf4'<br />
fmargs.textstyle = message('fmbox-textstyle') -- 'font-style: italic;'<br />
<br />
-- Assemble the fmbox text field.<br />
local text = ''<br />
if linkBox then<br />
text = text .. linkBox<br />
else<br />
text = text .. (p.makeDocPageBlurb(args, env) or '') -- "This documentation is transcluded from [[Foo]]." <br />
if subjectSpace == 2 or subjectSpace == 10 or subjectSpace == 828 then<br />
-- We are in the user, template or module namespaces.<br />
-- Add sandbox and testcases links.<br />
-- "Editors can experiment in this template's sandbox and testcases pages."<br />
text = text .. p.makeExperimentBlurb(args, env)<br />
text = text .. '<br />'<br />
if not args.content and not args[1] then<br />
-- "Please add categories to the /doc subpage."<br />
-- Don't show this message with inline docs or with an explicitly specified doc page,<br />
-- as then it is unclear where to add the categories.<br />
text = text .. (p.makeCategoriesBlurb(args, env) or '')<br />
end<br />
text = text .. ' ' .. (p.makeSubpagesBlurb(args, env) or '') --"Subpages of this template"<br />
local printBlurb = p.makePrintBlurb(args, env) -- Two-line blurb about print versions of templates.<br />
if printBlurb then<br />
text = text .. '<br />' .. printBlurb<br />
end<br />
end<br />
end<br />
fmargs.text = text<br />
<br />
return messageBox.main('fmbox', fmargs)<br />
end<br />
<br />
function p.makeDocPageBlurb(args, env)<br />
--[=[<br />
-- Makes the blurb "This documentation is transcluded from [[Template:Foo]] (edit, history)".<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
-- <br />
-- Messages:<br />
-- 'edit-link-display' --> 'edit'<br />
-- 'history-link-display' --> 'history'<br />
-- 'transcluded-from-blurb' --> <br />
-- 'The above [[Wikipedia:Template documentation|documentation]] <br />
-- is [[Wikipedia:Transclusion|transcluded]] from $1.'<br />
-- 'module-preload' --> 'Template:Documentation/preload-module-doc'<br />
-- 'create-link-display' --> 'create'<br />
-- 'create-module-doc-blurb' --><br />
-- 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].'<br />
--]=]<br />
local docTitle = env.docTitle<br />
if not docTitle then<br />
return nil<br />
end<br />
local ret<br />
if docTitle.exists then<br />
-- /doc exists; link to it.<br />
local docLink = makeWikilink(docTitle.prefixedText)<br />
local editUrl = docTitle:fullUrl{action = 'edit'}<br />
local editDisplay = message('edit-link-display')<br />
local editLink = makeUrlLink(editUrl, editDisplay)<br />
local historyUrl = docTitle:fullUrl{action = 'history'}<br />
local historyDisplay = message('history-link-display')<br />
local historyLink = makeUrlLink(historyUrl, historyDisplay)<br />
ret = message('transcluded-from-blurb', {docLink})<br />
.. ' '<br />
.. makeToolbar(editLink, historyLink)<br />
.. '<br />'<br />
elseif env.subjectSpace == 828 then<br />
-- /doc does not exist; ask to create it.<br />
local createUrl = docTitle:fullUrl{action = 'edit', preload = message('module-preload')}<br />
local createDisplay = message('create-link-display')<br />
local createLink = makeUrlLink(createUrl, createDisplay)<br />
ret = message('create-module-doc-blurb', {createLink})<br />
.. '<br />'<br />
end<br />
return ret<br />
end<br />
<br />
function p.makeExperimentBlurb(args, env)<br />
--[[<br />
-- Renders the text "Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages."<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
-- <br />
-- Messages:<br />
-- 'sandbox-link-display' --> 'sandbox'<br />
-- 'sandbox-edit-link-display' --> 'edit'<br />
-- 'compare-link-display' --> 'diff'<br />
-- 'module-sandbox-preload' --> 'Template:Documentation/preload-module-sandbox'<br />
-- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox'<br />
-- 'sandbox-create-link-display' --> 'create'<br />
-- 'mirror-edit-summary' --> 'Create sandbox version of $1'<br />
-- 'mirror-link-display' --> 'mirror'<br />
-- 'sandbox-link-display' --> 'sandbox'<br />
-- 'testcases-link-display' --> 'testcases'<br />
-- 'testcases-edit-link-display'--> 'edit'<br />
-- 'module-testcases-preload' --> 'Template:Documentation/preload-module-testcases'<br />
-- 'template-sandbox-preload' --> 'Template:Documentation/preload-sandbox'<br />
-- 'testcases-create-link-display' --> 'create'<br />
-- 'testcases-link-display' --> 'testcases'<br />
-- 'testcases-edit-link-display' --> 'edit'<br />
-- 'module-testcases-preload' --> 'Template:Documentation/preload-module-testcases'<br />
-- 'template-testcases-preload' --> 'Template:Documentation/preload-testcases'<br />
-- 'experiment-blurb-module' --> 'Editors can experiment in this module's $1 and $2 pages.'<br />
-- 'experiment-blurb-template' --> 'Editors can experiment in this template's $1 and $2 pages.'<br />
--]]<br />
local subjectSpace = env.subjectSpace<br />
local templateTitle = env.templateTitle<br />
local sandboxTitle = env.sandboxTitle<br />
local testcasesTitle = env.testcasesTitle<br />
local templatePage = templateTitle.prefixedText<br />
if not subjectSpace or not templateTitle or not sandboxTitle or not testcasesTitle then<br />
return nil<br />
end<br />
-- Make links.<br />
local sandboxLinks, testcasesLinks<br />
if sandboxTitle.exists then<br />
local sandboxPage = sandboxTitle.prefixedText<br />
local sandboxDisplay = message('sandbox-link-display')<br />
local sandboxLink = makeWikilink(sandboxPage, sandboxDisplay)<br />
local sandboxEditUrl = sandboxTitle:fullUrl{action = 'edit'}<br />
local sandboxEditDisplay = message('sandbox-edit-link-display')<br />
local sandboxEditLink = makeUrlLink(sandboxEditUrl, sandboxEditDisplay)<br />
local compareUrl = env.compareUrl<br />
local compareLink<br />
if compareUrl then<br />
local compareDisplay = message('compare-link-display')<br />
compareLink = makeUrlLink(compareUrl, compareDisplay)<br />
end<br />
sandboxLinks = sandboxLink .. ' ' .. makeToolbar(sandboxEditLink, compareLink)<br />
else<br />
local sandboxPreload<br />
if subjectSpace == 828 then<br />
sandboxPreload = message('module-sandbox-preload')<br />
else<br />
sandboxPreload = message('template-sandbox-preload')<br />
end<br />
local sandboxCreateUrl = sandboxTitle:fullUrl{action = 'edit', preload = sandboxPreload}<br />
local sandboxCreateDisplay = message('sandbox-create-link-display')<br />
local sandboxCreateLink = makeUrlLink(sandboxCreateUrl, sandboxCreateDisplay)<br />
local mirrorSummary = message('mirror-edit-summary', {makeWikilink(templatePage)})<br />
local mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = templatePage, summary = mirrorSummary}<br />
local mirrorDisplay = message('mirror-link-display')<br />
local mirrorLink = makeUrlLink(mirrorUrl, mirrorDisplay)<br />
sandboxLinks = message('sandbox-link-display') .. ' ' .. makeToolbar(sandboxCreateLink, mirrorLink)<br />
end<br />
if testcasesTitle.exists then<br />
local testcasesPage = testcasesTitle.prefixedText<br />
local testcasesDisplay = message('testcases-link-display')<br />
local testcasesLink = makeWikilink(testcasesPage, testcasesDisplay)<br />
local testcasesEditUrl = testcasesTitle:fullUrl{action = 'edit'}<br />
local testcasesEditDisplay = message('testcases-edit-link-display')<br />
local testcasesEditLink = makeUrlLink(testcasesEditUrl, testcasesEditDisplay)<br />
testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink)<br />
else<br />
local testcasesPreload<br />
if subjectSpace == 828 then<br />
testcasesPreload = message('module-testcases-preload')<br />
else<br />
testcasesPreload = message('template-testcases-preload')<br />
end<br />
local testcasesCreateUrl = testcasesTitle:fullUrl{action = 'edit', preload = testcasesPreload}<br />
local testcasesCreateDisplay = message('testcases-create-link-display')<br />
local testcasesCreateLink = makeUrlLink(testcasesCreateUrl, testcasesCreateDisplay)<br />
testcasesLinks = message('testcases-link-display') .. ' ' .. makeToolbar(testcasesCreateLink)<br />
end<br />
local messageName<br />
if subjectSpace == 828 then<br />
messageName = 'experiment-blurb-module'<br />
else<br />
messageName = 'experiment-blurb-template'<br />
end<br />
return message(messageName, {sandboxLinks, testcasesLinks})<br />
end<br />
<br />
function p.makeCategoriesBlurb(args, env)<br />
--[[<br />
-- Generates the text "Please add categories to the /doc subpage."<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
-- Messages:<br />
-- 'doc-link-display' --> '/doc'<br />
-- 'add-categories-blurb' --> 'Please add categories to the $1 subpage.'<br />
--]]<br />
local docTitle = env.docTitle<br />
if not docTitle then<br />
return nil<br />
end<br />
local docPathLink = makeWikilink(docTitle.prefixedText, message('doc-link-display'))<br />
return message('add-categories-blurb', {docPathLink})<br />
end<br />
<br />
function p.makeSubpagesBlurb(args, env)<br />
--[[<br />
-- Generates the "Subpages of this template" link.<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
<br />
-- Messages:<br />
-- 'template-pagetype' --> 'template'<br />
-- 'module-pagetype' --> 'module'<br />
-- 'default-pagetype' --> 'page'<br />
-- 'subpages-link-display' --> 'Subpages of this $1'<br />
--]]<br />
local subjectSpace = env.subjectSpace<br />
local templateTitle = env.templateTitle<br />
if not subjectSpace or not templateTitle then<br />
return nil<br />
end<br />
local pagetype<br />
if subjectSpace == 10 then<br />
pagetype = message('template-pagetype')<br />
elseif subjectSpace == 828 then<br />
pagetype = message('module-pagetype')<br />
else<br />
pagetype = message('default-pagetype')<br />
end<br />
local subpagesLink = makeWikilink(<br />
'Special:PrefixIndex/' .. templateTitle.prefixedText .. '/',<br />
message('subpages-link-display', {pagetype})<br />
)<br />
return message('subpages-blurb', {subpagesLink})<br />
end<br />
<br />
function p.makePrintBlurb(args, env)<br />
--[=[<br />
-- Generates the blurb displayed when there is a print version of the template available.<br />
-- @args - a table of arguments passed by the user<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
--<br />
-- Messages:<br />
-- 'print-link-display' --> '/Print'<br />
-- 'print-blurb' --> 'A [[Help:Books/for experts#Improving the book layout|print version]]'<br />
-- .. ' of this template exists at $1.'<br />
-- .. ' If you make a change to this template, please update the print version as well.'<br />
-- 'display-print-category' --> true<br />
-- 'print-category' --> 'Templates with print versions'<br />
--]=]<br />
local printTitle = env.printTitle<br />
if not printTitle then<br />
return nil<br />
end<br />
local ret<br />
if printTitle.exists then<br />
local printLink = makeWikilink(printTitle.prefixedText, message('print-link-display'))<br />
ret = message('print-blurb', {printLink})<br />
local displayPrintCategory = message('display-print-category', nil, 'boolean')<br />
if displayPrintCategory then<br />
ret = ret .. makeCategoryLink(message('print-category'))<br />
end<br />
end<br />
return ret<br />
end<br />
<br />
----------------------------------------------------------------------------<br />
-- Tracking categories<br />
----------------------------------------------------------------------------<br />
<br />
function p.addTrackingCategories(env)<br />
--[[<br />
-- Check if {{documentation}} is transcluded on a /doc or /testcases page.<br />
-- @env - environment table containing title objects, etc., generated with p.getEnvironment<br />
<br />
-- Messages:<br />
-- 'display-strange-usage-category' --> true<br />
-- 'doc-subpage' --> 'doc'<br />
-- 'testcases-subpage' --> 'testcases'<br />
-- 'strange-usage-category' --> 'Wikipedia pages with strange ((documentation)) usage'<br />
-- <br />
-- /testcases pages in the module namespace are not categorised, as they may have<br />
-- {{documentation}} transcluded automatically.<br />
--]]<br />
local title = env.title<br />
local subjectSpace = env.subjectSpace<br />
if not title or not subjectSpace then<br />
return nil<br />
end<br />
local subpage = title.subpageText<br />
local ret = ''<br />
if message('display-strange-usage-category', nil, 'boolean')<br />
and (<br />
subpage == message('doc-subpage')<br />
or subjectSpace ~= 828 and subpage == message('testcases-subpage')<br />
)<br />
then<br />
ret = ret .. makeCategoryLink(message('strange-usage-category'))<br />
end<br />
return ret<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Mod%C3%A8le:DocumentationModèle:Documentation2014-07-03T04:12:19Z<p>Mr. Stradivarius : Changed protection level of Template:Documentation: Edit warring / content dispute: making this indef so that the template won't become unprotected accidentally ([Edit=Allow only administrators] (indefinite) [Move=Allow only adm</p>
<hr />
<div>{{#invoke:documentation|main|_content={{ {{#invoke:documentation|contentTitle}}}}}}<noinclude><br />
<!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --><br />
</noinclude></div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:InfoboxModule:Infobox2014-06-18T23:27:08Z<p>Mr. Stradivarius : allow setting ids for rows, headers, labels and data fields, per protected edit request by User:Czarkoff</p>
<hr />
<div>--<br />
-- This module implements {{Infobox}}<br />
--<br />
<br />
local p = {}<br />
<br />
local HtmlBuilder = require('Module:HtmlBuilder')<br />
<br />
local args = {}<br />
local origArgs<br />
local root<br />
<br />
local function union(t1, t2)<br />
-- Returns the union of the values of two tables, as a sequence.<br />
local vals = {}<br />
for k, v in pairs(t1) do<br />
vals[v] = true<br />
end<br />
for k, v in pairs(t2) do<br />
vals[v] = true<br />
end<br />
local ret = {}<br />
for k, v in pairs(vals) do<br />
table.insert(ret, k)<br />
end<br />
return ret<br />
end<br />
<br />
local function getArgNums(prefix)<br />
-- Returns a table containing the numbers of the arguments that exist<br />
-- for the specified prefix. For example, if the prefix was 'data', and<br />
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.<br />
local nums = {}<br />
for k, v in pairs(args) do<br />
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')<br />
if num then table.insert(nums, tonumber(num)) end<br />
end<br />
table.sort(nums)<br />
return nums<br />
end<br />
<br />
local function addRow(rowArgs)<br />
-- Adds a row to the infobox, with either a header cell<br />
-- or a label/data cell combination.<br />
if rowArgs.header then<br />
root<br />
.tag('tr')<br />
.addClass(rowArgs.rowclass)<br />
.attr('id', rowArgs.rowid)<br />
.tag('th')<br />
.attr('colspan', 2)<br />
.attr('id', rowArgs.headerid)<br />
.addClass(rowArgs.class)<br />
.addClass(args.headerclass)<br />
.css('text-align', 'center')<br />
.cssText(args.headerstyle)<br />
.wikitext(rowArgs.header)<br />
elseif rowArgs.data then<br />
local row = root.tag('tr')<br />
row.addClass(rowArgs.rowclass)<br />
row.attr('id', rowArgs.rowid)<br />
if rowArgs.label then<br />
row<br />
.tag('th')<br />
.attr('scope', 'row')<br />
.attr('id', rowArgs.labelid)<br />
.css('text-align', 'left')<br />
.cssText(args.labelstyle)<br />
.wikitext(rowArgs.label)<br />
.done()<br />
end<br />
<br />
local dataCell = row.tag('td')<br />
if not rowArgs.label then <br />
dataCell<br />
.attr('colspan', 2)<br />
.css('text-align', 'center') <br />
end<br />
dataCell<br />
.attr('id', rowArgs.dataid)<br />
.addClass(rowArgs.class)<br />
.cssText(rowArgs.datastyle)<br />
.newline()<br />
.wikitext(rowArgs.data)<br />
end<br />
end<br />
<br />
local function renderTitle()<br />
if not args.title then return end<br />
<br />
root<br />
.tag('caption')<br />
.addClass(args.titleclass)<br />
.cssText(args.titlestyle)<br />
.wikitext(args.title)<br />
end<br />
<br />
local function renderAboveRow()<br />
if not args.above then return end<br />
<br />
root<br />
.tag('tr')<br />
.tag('th')<br />
.attr('colspan', 2)<br />
.addClass(args.aboveclass)<br />
.css('text-align', 'center')<br />
.css('font-size', '125%')<br />
.css('font-weight', 'bold')<br />
.cssText(args.abovestyle)<br />
.wikitext(args.above)<br />
end<br />
<br />
local function renderBelowRow()<br />
if not args.below then return end<br />
<br />
root<br />
.tag('tr')<br />
.tag('td')<br />
.attr('colspan', '2')<br />
.addClass(args.belowclass)<br />
.css('text-align', 'center')<br />
.cssText(args.belowstyle)<br />
.newline()<br />
.wikitext(args.below)<br />
end<br />
<br />
local function renderSubheaders()<br />
if args.subheader then<br />
args.subheader1 = args.subheader<br />
end<br />
if args.subheaderrowclass then<br />
args.subheaderrowclass1 = args.subheaderrowclass<br />
end<br />
local subheadernums = getArgNums('subheader')<br />
for k, num in ipairs(subheadernums) do<br />
addRow({<br />
data = args['subheader' .. tostring(num)],<br />
datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],<br />
class = args.subheaderclass,<br />
rowclass = args['subheaderrowclass' .. tostring(num)]<br />
})<br />
end<br />
end<br />
<br />
local function renderImages()<br />
if args.image then<br />
args.image1 = args.image<br />
end<br />
if args.caption then<br />
args.caption1 = args.caption<br />
end<br />
local imagenums = getArgNums('image')<br />
for k, num in ipairs(imagenums) do<br />
local caption = args['caption' .. tostring(num)]<br />
local data = HtmlBuilder.create().wikitext(args['image' .. tostring(num)])<br />
if caption then<br />
data<br />
.tag('div')<br />
.cssText(args.captionstyle)<br />
.wikitext(caption)<br />
end<br />
addRow({<br />
data = tostring(data),<br />
datastyle = args.imagestyle,<br />
class = args.imageclass,<br />
rowclass = args['imagerowclass' .. tostring(num)]<br />
})<br />
end<br />
end<br />
<br />
local function renderRows()<br />
-- Gets the union of the header and data argument numbers,<br />
-- and renders them all in order using addRow.<br />
local rownums = union(getArgNums('header'), getArgNums('data'))<br />
table.sort(rownums)<br />
for k, num in ipairs(rownums) do<br />
addRow({<br />
header = args['header' .. tostring(num)],<br />
label = args['label' .. tostring(num)],<br />
data = args['data' .. tostring(num)],<br />
datastyle = args.datastyle,<br />
class = args['class' .. tostring(num)],<br />
rowclass = args['rowclass' .. tostring(num)],<br />
dataid = args['dataid' .. tostring(num)],<br />
labelid = args['labelid' .. tostring(num)],<br />
headerid = args['headerid' .. tostring(num)],<br />
rowid = args['rowid' .. tostring(num)]<br />
})<br />
end<br />
end<br />
<br />
local function renderNavBar()<br />
if not args.name then return end<br />
<br />
root<br />
.tag('tr')<br />
.tag('td')<br />
.attr('colspan', '2')<br />
.css('text-align', 'right')<br />
.wikitext(mw.getCurrentFrame():expandTemplate({ <br />
title = 'navbar', <br />
args = { args.name, mini = 1 }<br />
}))<br />
end<br />
<br />
local function renderItalicTitle()<br />
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])<br />
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then<br />
root.wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))<br />
end<br />
end<br />
<br />
local function renderTrackingCategories()<br />
if args.decat ~= 'yes' then<br />
if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then<br />
root.wikitext('[[Category:Articles which use infobox templates with no data rows]]')<br />
end<br />
if args.child == 'yes' and args.title then<br />
root.wikitext('[[Category:Articles which use embedded infobox templates with the title parameter]]')<br />
end<br />
end<br />
end<br />
<br />
local function _infobox()<br />
-- Specify the overall layout of the infobox, with special settings<br />
-- if the infobox is used as a 'child' inside another infobox.<br />
if args.child ~= 'yes' then<br />
root = HtmlBuilder.create('table')<br />
<br />
root<br />
.addClass('infobox')<br />
.addClass(args.bodyclass)<br />
.attr('cellspacing', 3)<br />
.css('border-spacing', '3px')<br />
<br />
if args.subbox == 'yes' then<br />
root<br />
.css('padding', '0')<br />
.css('border', 'none')<br />
.css('margin', '-3px')<br />
.css('width', 'auto')<br />
.css('min-width', '100%')<br />
.css('font-size', '100%')<br />
.css('clear', 'none')<br />
.css('float', 'none')<br />
.css('background-color', 'transparent')<br />
else<br />
root<br />
.css('width', '22em')<br />
end<br />
root<br />
.cssText(args.bodystyle)<br />
<br />
renderTitle()<br />
renderAboveRow()<br />
else<br />
root = HtmlBuilder.create()<br />
<br />
root<br />
.wikitext(args.title)<br />
end<br />
<br />
renderSubheaders()<br />
renderImages() <br />
renderRows() <br />
renderBelowRow() <br />
renderNavBar()<br />
renderItalicTitle()<br />
renderTrackingCategories()<br />
<br />
return tostring(root)<br />
end<br />
<br />
local function preprocessSingleArg(argName)<br />
-- If the argument exists and isn't blank, add it to the argument table.<br />
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.<br />
if origArgs[argName] and origArgs[argName] ~= '' then<br />
args[argName] = origArgs[argName]<br />
end<br />
end<br />
<br />
local function preprocessArgs(prefixTable, step)<br />
-- Assign the parameters with the given prefixes to the args table, in order, in batches<br />
-- of the step size specified. This is to prevent references etc. from appearing in the<br />
-- wrong order. The prefixTable should be an array containing tables, each of which has<br />
-- two possible fields, a "prefix" string and a "depend" table. The function always parses<br />
-- parameters containing the "prefix" string, but only parses parameters in the "depend"<br />
-- table if the prefix parameter is present and non-blank.<br />
if type(prefixTable) ~= 'table' then<br />
error("Non-table value detected for the prefix table", 2)<br />
end<br />
if type(step) ~= 'number' then<br />
error("Invalid step value detected", 2)<br />
end<br />
<br />
-- Get arguments without a number suffix, and check for bad input.<br />
for i,v in ipairs(prefixTable) do<br />
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then<br />
error('Invalid input detected to preprocessArgs prefix table', 2)<br />
end<br />
preprocessSingleArg(v.prefix)<br />
-- Only parse the depend parameter if the prefix parameter is present and not blank.<br />
if args[v.prefix] and v.depend then<br />
for j, dependValue in ipairs(v.depend) do<br />
if type(dependValue) ~= 'string' then<br />
error('Invalid "depend" parameter value detected in preprocessArgs')<br />
end<br />
preprocessSingleArg(dependValue)<br />
end<br />
end<br />
end<br />
<br />
-- Get arguments with number suffixes.<br />
local a = 1 -- Counter variable.<br />
local moreArgumentsExist = true<br />
while moreArgumentsExist == true do<br />
moreArgumentsExist = false<br />
for i = a, a + step - 1 do<br />
for j,v in ipairs(prefixTable) do<br />
local prefixArgName = v.prefix .. tostring(i)<br />
if origArgs[prefixArgName] then<br />
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.<br />
preprocessSingleArg(prefixArgName)<br />
end<br />
-- Process the depend table if the prefix argument is present and not blank, or<br />
-- we are processing "prefix1" and "prefix" is present and not blank, and<br />
-- if the depend table is present.<br />
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then<br />
for j,dependValue in ipairs(v.depend) do<br />
local dependArgName = dependValue .. tostring(i)<br />
preprocessSingleArg(dependArgName)<br />
end<br />
end<br />
end<br />
end<br />
a = a + step<br />
end<br />
end<br />
<br />
function p.infobox(frame)<br />
-- If called via #invoke, use the args passed into the invoking template.<br />
-- Otherwise, for testing purposes, assume args are being passed directly in.<br />
if frame == mw.getCurrentFrame() then<br />
origArgs = frame:getParent().args<br />
else<br />
origArgs = frame<br />
end<br />
<br />
-- Parse the data parameters in the same order that the old {{infobox}} did, so that<br />
-- references etc. will display in the expected places. Parameters that depend on<br />
-- another parameter are only processed if that parameter is present, to avoid<br />
-- phantom references appearing in article reference lists.<br />
preprocessSingleArg('child')<br />
preprocessSingleArg('bodyclass')<br />
preprocessSingleArg('subbox')<br />
preprocessSingleArg('bodystyle')<br />
preprocessSingleArg('title')<br />
preprocessSingleArg('titleclass')<br />
preprocessSingleArg('titlestyle')<br />
preprocessSingleArg('above')<br />
preprocessSingleArg('aboveclass')<br />
preprocessSingleArg('abovestyle')<br />
preprocessArgs({<br />
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}<br />
}, 10)<br />
preprocessSingleArg('subheaderstyle')<br />
preprocessSingleArg('subheaderclass')<br />
preprocessArgs({<br />
{prefix = 'image', depend = {'caption', 'imagerowclass'}}<br />
}, 10)<br />
preprocessSingleArg('captionstyle')<br />
preprocessSingleArg('imagestyle')<br />
preprocessSingleArg('imageclass')<br />
preprocessArgs({<br />
{prefix = 'header'},<br />
{prefix = 'data', depend = {'label'}},<br />
{prefix = 'rowclass'},<br />
{prefix = 'class'},<br />
{prefix = 'dataid'},<br />
{prefix = 'labelid'},<br />
{prefix = 'headerid'},<br />
{prefix = 'rowid'}<br />
}, 50)<br />
preprocessSingleArg('headerclass')<br />
preprocessSingleArg('headerstyle')<br />
preprocessSingleArg('labelstyle')<br />
preprocessSingleArg('datastyle')<br />
preprocessSingleArg('below')<br />
preprocessSingleArg('belowclass')<br />
preprocessSingleArg('belowstyle')<br />
preprocessSingleArg('name')<br />
args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent<br />
preprocessSingleArg('decat')<br />
<br />
return _infobox()<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:ListModule:List2014-06-12T09:21:46Z<p>Mr. Stradivarius : remove li_style, ul_style and ol_style parameters, allow itemn_style and itemn_value parameters, and add a tracking category for item_stylen and item_valuen parameters</p>
<hr />
<div>-- This module outputs different kinds of lists. At the moment, bulleted,<br />
-- unbulleted, horizontal, ordered, and horizontal ordered lists are supported.<br />
<br />
local libUtil = require('libraryUtil')<br />
local checkType = libUtil.checkType<br />
local mTableTools = require('Module:TableTools')<br />
<br />
local p = {}<br />
<br />
local listTypes = {<br />
['bulleted'] = true,<br />
['unbulleted'] = true,<br />
['horizontal'] = true,<br />
['ordered'] = true,<br />
['horizontal_ordered'] = true<br />
}<br />
<br />
function p.makeListData(listType, args)<br />
-- Constructs a data table to be passed to p.renderList.<br />
local data = {}<br />
<br />
-- Classes<br />
data.classes = {}<br />
if listType == 'horizontal' or listType == 'horizontal_ordered' then<br />
table.insert(data.classes, 'hlist')<br />
elseif listType == 'unbulleted' then<br />
table.insert(data.classes, 'plainlist')<br />
end<br />
table.insert(data.classes, args.class)<br />
<br />
-- Main div style<br />
data.style = args.style<br />
<br />
-- Indent for horizontal lists<br />
if listType == 'horizontal' or listType == 'horizontal_ordered' then<br />
local indent = tonumber(args.indent)<br />
indent = indent and indent * 1.6 or 0<br />
if indent > 0 then<br />
data.marginLeft = indent .. 'em'<br />
end<br />
end<br />
<br />
-- List style types for ordered lists<br />
-- This could be "1, 2, 3", "a, b, c", or a number of others. The list style<br />
-- type is either set by the "type" attribute or the "list-style-type" CSS<br />
-- property.<br />
if listType == 'ordered' or listType == 'horizontal_ordered' then <br />
data.listStyleType = args.list_style_type or args['list-style-type']<br />
data.type = args['type']<br />
<br />
-- Detect invalid type attributes and attempt to convert them to<br />
-- list-style-type CSS properties.<br />
if data.type <br />
and not data.listStyleType<br />
and not tostring(data.type):find('^%s*[1AaIi]%s*$')<br />
then<br />
data.listStyleType = data.type<br />
data.type = nil<br />
end<br />
end<br />
<br />
-- List tag type<br />
if listType == 'ordered' or listType == 'horizontal_ordered' then<br />
data.listTag = 'ol'<br />
else<br />
data.listTag = 'ul'<br />
end<br />
<br />
-- Start number for ordered lists<br />
data.start = args.start<br />
if listType == 'horizontal_ordered' then<br />
-- Apply fix to get start numbers working with horizontal ordered lists.<br />
local startNum = tonumber(data.start)<br />
if startNum then<br />
data.counterReset = 'listitem ' .. tostring(startNum - 1)<br />
end<br />
end<br />
<br />
-- List style<br />
-- ul_style and ol_style are included for backwards compatibility. No<br />
-- distinction is made for ordered or unordered lists.<br />
data.listStyle = args.list_style<br />
<br />
-- List items<br />
-- li_style is included for backwards compatibility. item_style was included<br />
-- to be easier to understand for non-coders.<br />
data.itemStyle = args.item_style or args.li_style<br />
data.items = {}<br />
for i, num in ipairs(mTableTools.numKeys(args)) do<br />
local item = {}<br />
item.content = args[num]<br />
item.style = args['item' .. tostring(num) .. '_style']<br />
or args['item_style' .. tostring(num)]<br />
item.value = args['item' .. tostring(num) .. '_value']<br />
or args['item_value' .. tostring(num)]<br />
table.insert(data.items, item)<br />
end<br />
<br />
return data<br />
end<br />
<br />
function p.renderList(data)<br />
-- Renders the list HTML.<br />
<br />
-- Return the blank string if there are no list items.<br />
if type(data.items) ~= 'table' or #data.items < 1 then<br />
return ''<br />
end<br />
<br />
-- Render the main div tag.<br />
local root = mw.html.create('div')<br />
for i, class in ipairs(data.classes or {}) do<br />
root:addClass(class)<br />
end<br />
root:css{['margin-left'] = data.marginLeft}<br />
if data.style then<br />
root:cssText(data.style)<br />
end<br />
<br />
-- Render the list tag.<br />
local list = root:tag(data.listTag or 'ul')<br />
list<br />
:attr{start = data.start, type = data.type}<br />
:css{<br />
['counter-reset'] = data.counterReset,<br />
['list-style-type'] = data.listStyleType<br />
}<br />
if data.listStyle then<br />
list:cssText(data.listStyle)<br />
end<br />
<br />
-- Render the list items<br />
for i, t in ipairs(data.items or {}) do<br />
local item = list:tag('li')<br />
if data.itemStyle then<br />
item:cssText(data.itemStyle)<br />
end<br />
if t.style then<br />
item:cssText(t.style)<br />
end<br />
item<br />
:attr{value = t.value}<br />
:wikitext(t.content)<br />
end<br />
<br />
return tostring(root)<br />
end<br />
<br />
function p.renderTrackingCategories(args)<br />
local isDeprecated = false -- Tracks deprecated parameters.<br />
for k, v in pairs(args) do<br />
k = tostring(k)<br />
if k:find('^item_style%d+$') or k:find('^item_value%d+$') then<br />
isDeprecated = true<br />
break<br />
end<br />
end<br />
local ret = ''<br />
if isDeprecated then<br />
ret = ret .. '[[Category:List templates with deprecated parameters]]'<br />
end<br />
return ret<br />
end<br />
<br />
function p.makeList(listType, args)<br />
if not listType or not listTypes[listType] then<br />
error(string.format(<br />
"bad argument #1 to 'makeList' ('%s' is not a valid list type)",<br />
tostring(listType)<br />
), 2)<br />
end<br />
checkType('makeList', 2, args, 'table')<br />
local data = p.makeListData(listType, args)<br />
local list = p.renderList(data)<br />
local trackingCategories = p.renderTrackingCategories(args)<br />
return list .. trackingCategories<br />
end<br />
<br />
for listType in pairs(listTypes) do<br />
p[listType] = function (frame)<br />
local mArguments = require('Module:Arguments')<br />
local origArgs = mArguments.getArgs(frame)<br />
-- Copy all the arguments to a new table, for faster indexing.<br />
local args = {}<br />
for k, v in pairs(origArgs) do<br />
args[k] = v<br />
end<br />
return p.makeList(listType, args)<br />
end<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:File_linkModule:File link2014-06-04T00:06:16Z<p>Mr. Stradivarius : Undid revision 611451330 by Mr. Stradivarius (talk) whoops, that edit was supposed to be to the sandbox...</p>
<hr />
<div>-- This module provides a library for formatting file wikilinks.<br />
<br />
local libraryUtil = require('libraryUtil')<br />
local checkType = libraryUtil.checkType<br />
<br />
local fileLink = {}<br />
<br />
function fileLink.new(filename)<br />
checkType('fileLink.new', 1, filename, 'string', true)<br />
local obj, data = {}, {}<br />
<br />
local checkSelf = libraryUtil.makeCheckSelfFunction(<br />
'fileLink',<br />
'fileLink',<br />
obj,<br />
'fileLink object'<br />
)<br />
<br />
-- Set the filename if we were passed it as an input to fileLink.new.<br />
if filename then<br />
data.theName = filename<br />
end<br />
<br />
function data:name(s)<br />
checkSelf(self, 'name')<br />
checkType('fileLink:name', 1, s, 'string')<br />
data.theName = s<br />
return self<br />
end<br />
<br />
function data:format(s, filename)<br />
checkSelf(self, 'format')<br />
checkType('fileLink:format', 1, s, 'string', true)<br />
checkType('fileLink:format', 2, format, 'string', true)<br />
local validFormats = {<br />
thumb = true,<br />
thumbnail = true,<br />
frame = true,<br />
framed = true,<br />
frameless = true<br />
}<br />
if s == nil or validFormats[s] then<br />
data.theFormat = s<br />
data.theFormatFilename = filename<br />
else<br />
error(string.format(<br />
"bad argument #1 to 'fileLink:format' ('%s' is not a valid format)",<br />
s<br />
), 2)<br />
end<br />
return self<br />
end<br />
<br />
local function sizeError(methodName)<br />
-- Used for formatting duplication errors in size-related methods.<br />
error(string.format(<br />
"duplicate size argument detected in '%s'"<br />
.. " ('upright' cannot be used in conjunction with height or width)",<br />
methodName<br />
), 3)<br />
end<br />
<br />
function data:width(px)<br />
checkSelf(self, 'width')<br />
checkType('fileLink:width', 1, px, 'number', true)<br />
if px and data.isUpright then<br />
sizeError('fileLink:width')<br />
end<br />
data.theWidth = px<br />
return self<br />
end<br />
<br />
function data:height(px)<br />
checkSelf(self, 'height')<br />
checkType('fileLink:height', 1, px, 'number', true)<br />
if px and data.isUpright then<br />
sizeError('fileLink:height')<br />
end<br />
data.theHeight = px<br />
return self<br />
end<br />
<br />
function data:upright(isUpright, factor)<br />
checkSelf(self, 'upright')<br />
checkType('fileLink:upright', 1, isUpright, 'boolean', true)<br />
checkType('fileLink:upright', 2, factor, 'number', true)<br />
if isUpright and (data.theWidth or data.theHeight) then<br />
sizeError('fileLink:upright')<br />
end<br />
data.isUpright = isUpright<br />
data.uprightFactor = factor<br />
return self<br />
end<br />
<br />
function data:resetSize()<br />
checkSelf(self, 'resetSize')<br />
for i, field in ipairs{'theWidth', 'theHeight', 'isUpright', 'uprightFactor'} do<br />
data[field] = nil<br />
end<br />
return self<br />
end<br />
<br />
function data:location(s)<br />
checkSelf(self, 'location')<br />
checkType('fileLink:location', 1, s, 'string', true)<br />
local validLocations = {<br />
right = true,<br />
left = true,<br />
center = true,<br />
none = true<br />
}<br />
if s == nil or validLocations[s] then<br />
data.theLocation = s<br />
else<br />
error(string.format(<br />
"bad argument #1 to 'fileLink:location' ('%s' is not a valid location)",<br />
s<br />
), 2)<br />
end<br />
return self<br />
end<br />
<br />
function data:alignment(s)<br />
checkSelf(self, 'alignment')<br />
checkType('fileLink:alignment', 1, s, 'string', true)<br />
local validAlignments = {<br />
baseline = true,<br />
middle = true,<br />
sub = true,<br />
super = true,<br />
['text-top'] = true,<br />
['text-bottom'] = true,<br />
top = true,<br />
bottom = true<br />
}<br />
if s == nil or validAlignments[s] then<br />
data.theAlignment = s<br />
else<br />
error(string.format(<br />
"bad argument #1 to 'fileLink:alignment' ('%s' is not a valid alignment)",<br />
s<br />
), 2)<br />
end<br />
return self<br />
end<br />
<br />
function data:border(hasBorder)<br />
checkSelf(self, 'border')<br />
checkType('fileLink:border', 1, hasBorder, 'boolean', true)<br />
data.hasBorder = hasBorder<br />
return self<br />
end<br />
<br />
function data:link(s)<br />
checkSelf(self, 'link')<br />
checkType('fileLink:link', 1, s, 'string', true)<br />
data.theLink = s<br />
return self<br />
end<br />
<br />
function data:alt(s)<br />
checkSelf(self, 'alt')<br />
checkType('fileLink:alt', 1, s, 'string', true)<br />
data.theAlt = s<br />
return self<br />
end<br />
<br />
function data:page(num)<br />
checkSelf(self, 'page')<br />
checkType('fileLink:page', 1, num, 'number', true)<br />
data.thePage = s<br />
return self<br />
end<br />
<br />
function data:class(s)<br />
checkSelf(self, 'class')<br />
checkType('fileLink:class', 1, s, 'string', true)<br />
data.theClass = s<br />
return self<br />
end<br />
<br />
function data:lang(s)<br />
checkSelf(self, 'lang')<br />
checkType('fileLink:lang', 1, s, 'string', true)<br />
data.theLang = s<br />
return self<br />
end<br />
<br />
local function checkTypeStringOrNum(funcName, pos, arg)<br />
local argType = type(arg)<br />
if argType ~= 'nil' and argType ~= 'string' and argType ~= 'number' then<br />
error(string.format(<br />
"bad argument #%d to '%s' (string or number expected, got %s)",<br />
pos,<br />
funcName,<br />
argType<br />
), 3)<br />
end<br />
end<br />
<br />
function data:startTime(time)<br />
checkSelf(self, 'startTime')<br />
checkTypeStringOrNum('fileLink:startTime', 1, time)<br />
data.theStartTime = time<br />
return self<br />
end<br />
<br />
function data:endTime(time)<br />
checkSelf(self, 'endTime')<br />
checkTypeStringOrNum('fileLink:endTime', 1, time)<br />
data.theEndTime = time<br />
return self<br />
end<br />
<br />
function data:thumbTime(time)<br />
checkSelf(self, 'thumbTime')<br />
checkTypeStringOrNum('fileLink:thumbTime', 1, time)<br />
data.theThumbTime = time<br />
return self<br />
end<br />
<br />
function data:caption(s)<br />
checkSelf(self, 'caption')<br />
checkType('fileLink:caption', 1, s, 'string', true)<br />
data.theCaption = s<br />
return self<br />
end<br />
<br />
function data:render()<br />
checkSelf(self, 'render')<br />
local ret = {}<br />
<br />
-- Filename<br />
if not data.theName then<br />
error('fileLink:render: no filename was found')<br />
end<br />
ret[#ret + 1] = 'File:' .. data.theName<br />
<br />
-- Format<br />
if data.theFormat and data.theFormatFilename then<br />
ret[#ret + 1] = data.theFormat .. '=' .. data.theFormatFilename<br />
elseif data.theFormat then<br />
ret[#ret + 1] = data.theFormat<br />
end<br />
<br />
-- Border<br />
if data.hasBorder then<br />
ret[#ret + 1] = 'border'<br />
end<br />
<br />
-- Location<br />
ret[#ret + 1] = data.theLocation<br />
<br />
-- Alignment<br />
ret[#ret + 1] = data.theAlignment<br />
<br />
-- Size<br />
if data.isUpright and data.uprightFactor then<br />
ret[#ret + 1] = 'upright=' .. tostring(data.uprightFactor)<br />
elseif data.isUpright then<br />
ret[#ret + 1] = 'upright'<br />
elseif data.theWidth and data.theHeight then<br />
ret[#ret + 1] = string.format('%dx%dpx', data.theWidth, data.theHeight)<br />
elseif data.theWidth then<br />
ret[#ret + 1] = tostring(data.theWidth) .. 'px'<br />
elseif data.theHeight then<br />
ret[#ret + 1] = string.format('x%dpx', data.theHeight)<br />
end<br />
<br />
-- Render named parameters.<br />
-- That includes link, alt, page, class, lang, start, end, and thumbtime.<br />
do<br />
local namedParameters = {<br />
{'link', 'theLink'},<br />
{'alt', 'theAlt'},<br />
{'page', 'thePage'},<br />
{'class', 'theClass'},<br />
{'lang', 'theLang'},<br />
{'start', 'theStartTime'},<br />
{'end', 'theEndTime'},<br />
{'thumbtime', 'theThumbTime'}<br />
}<br />
for i, t in ipairs(namedParameters) do<br />
local parameter = t[1]<br />
local value = data[t[2]]<br />
if value then<br />
ret[#ret + 1] = parameter .. '=' .. tostring(value)<br />
end<br />
end<br />
end<br />
<br />
-- Caption<br />
ret[#ret + 1] = data.theCaption<br />
<br />
return string.format('[[%s]]', table.concat(ret, '|'))<br />
end<br />
<br />
local privateFields = {<br />
theName = true,<br />
theFormat = true,<br />
theFormatFilename = true,<br />
theWidth = true,<br />
theHeight = true,<br />
isUpright = true,<br />
uprightFactor = true,<br />
theLocation = true,<br />
theAlignment = true,<br />
hasBorder = true,<br />
theLink = true,<br />
theAlt = true,<br />
thePage = true,<br />
theClass = true,<br />
theLang = true,<br />
theCaption = true<br />
}<br />
<br />
local readOnlyFields = {}<br />
for field in pairs(data) do<br />
readOnlyFields[field] = true<br />
end<br />
readOnlyFields.theName = nil -- This is set if a filename is given to fileLink.new, so remove it.<br />
<br />
local function restrictedFieldError(key, restriction)<br />
error(string.format(<br />
"fileLink object field '%s' is %s",<br />
tostring(key),<br />
restriction<br />
), 3)<br />
end<br />
<br />
setmetatable(obj, {<br />
__index = function (t, key)<br />
if privateFields[key] then<br />
restrictedFieldError(key, 'private')<br />
else<br />
return data[key]<br />
end<br />
end,<br />
__newindex = function (t, key, value)<br />
if privateFields[key] then<br />
restrictedFieldError(key, 'private')<br />
elseif readOnlyFields[key] then<br />
restrictedFieldError(key, 'read-only')<br />
else<br />
data[key] = value<br />
end<br />
end,<br />
__tostring = function (t)<br />
return t:render()<br />
end,<br />
__pairs = function ()<br />
local temp = {}<br />
for k, v in pairs(data) do<br />
if not privateFields[k] then<br />
temp[k] = v<br />
end<br />
end<br />
return pairs(temp)<br />
end<br />
})<br />
<br />
return obj<br />
end<br />
<br />
return fileLink</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Lua_bannerModule:Lua banner2014-06-03T11:28:49Z<p>Mr. Stradivarius : p.renderTrackingCategories fix - titleObj is already local</p>
<hr />
<div>-- This module implements the {{lua}} template.<br />
<br />
local yesno = require('Module:Yesno')<br />
local mList = require('Module:List')<br />
local mTableTools = require('Module:TableTools')<br />
local mMessageBox = require('Module:Message box')<br />
<br />
local p = {}<br />
<br />
function p.main(frame)<br />
local origArgs = frame:getParent().args<br />
local args = {}<br />
for k, v in pairs(origArgs) do<br />
v = v:match('^%s*(.-)%s*$')<br />
if v ~= '' then<br />
args[k] = v<br />
end<br />
end<br />
return p._main(args)<br />
end<br />
<br />
function p._main(args)<br />
local modules = mTableTools.compressSparseArray(args)<br />
local box = p.renderBox(modules)<br />
local trackingCategories = p.renderTrackingCategories(args, modules)<br />
return box .. trackingCategories<br />
end<br />
<br />
function p.renderBox(modules)<br />
local boxArgs = {}<br />
if #modules < 1 then<br />
boxArgs.text = '<strong class="error">Error: no modules specified</strong>'<br />
else<br />
local moduleLinks = {}<br />
for i, module in ipairs(modules) do<br />
moduleLinks[i] = string.format('[[:%s]]', module)<br />
end<br />
local moduleList = mList.makeList('bulleted', moduleLinks)<br />
boxArgs.text = 'Uses [[Wikipedia:Lua|Lua]]:\n' .. moduleList<br />
end<br />
boxArgs.type = 'notice'<br />
boxArgs.small = true<br />
boxArgs.image = '[[File:Lua-logo-nolabel.svg|30px|alt=Lua logo|link=Wikipedia:Lua]]'<br />
return mMessageBox.main('mbox', boxArgs)<br />
end<br />
<br />
function p.renderTrackingCategories(args, modules, titleObj)<br />
if yesno(args.nocat) then<br />
return ''<br />
end<br />
<br />
local cats = {}<br />
<br />
-- Error category<br />
if #modules < 1 then<br />
cats[#cats + 1] = 'Lua templates with errors'<br />
end<br />
<br />
-- Lua templates category<br />
titleObj = titleObj or mw.title.getCurrentTitle()<br />
local subpageBlacklist = {<br />
doc = true,<br />
sandbox = true,<br />
sandbox2 = true,<br />
testcases = true<br />
}<br />
if titleObj.namespace == 10 <br />
and not subpageBlacklist[titleObj.subpageText]<br />
then<br />
local category = args.category<br />
if not category then<br />
local categories = {<br />
['Module:String'] = 'Lua String-based templates',<br />
['Module:Math'] = 'Templates based on the Math Lua module',<br />
['Module:BaseConvert'] = 'Templates based on the BaseConvert Lua module',<br />
['Module:Citation'] = 'Lua-based citation templates'<br />
}<br />
categories['Module:Citation/CS1'] = categories['Module:Citation']<br />
category = modules[1] and categories[modules[1]]<br />
category = category or 'Lua-based templates'<br />
end<br />
cats[#cats + 1] = category<br />
end<br />
<br />
for i, cat in ipairs(cats) do<br />
cats[i] = string.format('[[Category:%s]]', cat)<br />
end<br />
return table.concat(cats)<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:TableToolsModule:TableTools2014-05-25T14:11:02Z<p>Mr. Stradivarius : fix cleanPattern function - some of the character classes were still being magic even when they were inside the set, so escape them all</p>
<hr />
<div>--[[<br />
------------------------------------------------------------------------------------<br />
-- TableTools --<br />
-- --<br />
-- This module includes a number of functions for dealing with Lua tables. --<br />
-- It is a meta-module, meant to be called from other Lua modules, and should --<br />
-- not be called directly from #invoke. --<br />
------------------------------------------------------------------------------------<br />
--]]<br />
<br />
local libraryUtil = require('libraryUtil')<br />
<br />
local p = {}<br />
<br />
-- Define often-used variables and functions.<br />
local floor = math.floor<br />
local infinity = math.huge<br />
local checkType = libraryUtil.checkType<br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- isPositiveInteger<br />
--<br />
-- This function returns true if the given value is a positive integer, and false<br />
-- if not. Although it doesn't operate on tables, it is included here as it is<br />
-- useful for determining whether a given table key is in the array part or the<br />
-- hash part of a table.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.isPositiveInteger(v)<br />
if type(v) == 'number' and v >= 1 and floor(v) == v and v < infinity then<br />
return true<br />
else<br />
return false<br />
end<br />
end<br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- isNan<br />
--<br />
-- This function returns true if the given number is a NaN value, and false<br />
-- if not. Although it doesn't operate on tables, it is included here as it is<br />
-- useful for determining whether a value can be a valid table key. Lua will<br />
-- generate an error if a NaN is used as a table key.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.isNan(v)<br />
if type(v) == 'number' and tostring(v) == '-nan' then<br />
return true<br />
else<br />
return false<br />
end<br />
end<br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- shallowClone<br />
--<br />
-- This returns a clone of a table. The value returned is a new table, but all<br />
-- subtables and functions are shared. Metamethods are respected, but the returned<br />
-- table will have no metatable of its own.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.shallowClone(t)<br />
local ret = {}<br />
for k, v in pairs(t) do<br />
ret[k] = v<br />
end<br />
return ret<br />
end<br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- removeDuplicates<br />
--<br />
-- This removes duplicate values from an array. Non-positive-integer keys are<br />
-- ignored. The earliest value is kept, and all subsequent duplicate values are<br />
-- removed, but otherwise the array order is unchanged.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.removeDuplicates(t)<br />
checkType('removeDuplicates', 1, t, 'table')<br />
local isNan = p.isNan<br />
local ret, exists = {}, {}<br />
for i, v in ipairs(t) do<br />
if isNan(v) then<br />
-- NaNs can't be table keys, and they are also unique, so we don't need to check existence.<br />
ret[#ret + 1] = v<br />
else<br />
if not exists[v] then<br />
ret[#ret + 1] = v<br />
exists[v] = true<br />
end<br />
end <br />
end<br />
return ret<br />
end <br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- numKeys<br />
--<br />
-- This takes a table and returns an array containing the numbers of any numerical<br />
-- keys that have non-nil values, sorted in numerical order.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.numKeys(t)<br />
checkType('numKeys', 1, t, 'table')<br />
local isPositiveInteger = p.isPositiveInteger<br />
local nums = {}<br />
for k, v in pairs(t) do<br />
if isPositiveInteger(k) then<br />
nums[#nums + 1] = k<br />
end<br />
end<br />
table.sort(nums)<br />
return nums<br />
end<br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- affixNums<br />
--<br />
-- This takes a table and returns an array containing the numbers of keys with the<br />
-- specified prefix and suffix. For example, for the table<br />
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix "a", affixNums will<br />
-- return {1, 3, 6}.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.affixNums(t, prefix, suffix)<br />
checkType('affixNums', 1, t, 'table')<br />
checkType('affixNums', 2, prefix, 'string', true)<br />
checkType('affixNums', 3, suffix, 'string', true)<br />
<br />
local function cleanPattern(s)<br />
-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.<br />
s = s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')<br />
return s<br />
end<br />
<br />
prefix = prefix or ''<br />
suffix = suffix or ''<br />
prefix = cleanPattern(prefix)<br />
suffix = cleanPattern(suffix)<br />
local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'<br />
<br />
local nums = {}<br />
for k, v in pairs(t) do<br />
if type(k) == 'string' then <br />
local num = mw.ustring.match(k, pattern)<br />
if num then<br />
nums[#nums + 1] = tonumber(num)<br />
end<br />
end<br />
end<br />
table.sort(nums)<br />
return nums<br />
end<br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- numData<br />
--<br />
-- Given a table with keys like ("foo1", "bar1", "foo2", "baz2"), returns a table<br />
-- of subtables in the format <br />
-- { [1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'} }<br />
-- Keys that don't end with an integer are stored in a subtable named "other".<br />
-- The compress option compresses the table so that it can be iterated over with<br />
-- ipairs.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.numData(t, compress)<br />
checkType('numData', 1, t, 'table')<br />
checkType('numData', 2, compress, 'boolean', true)<br />
local ret = {}<br />
for k, v in pairs(t) do<br />
local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')<br />
if num then<br />
num = tonumber(num)<br />
local subtable = ret[num] or {}<br />
if prefix == '' then<br />
-- Positional parameters match the blank string; put them at the start of the subtable instead.<br />
prefix = 1<br />
end<br />
subtable[prefix] = v<br />
ret[num] = subtable<br />
else<br />
local subtable = ret.other or {}<br />
subtable[k] = v<br />
ret.other = subtable<br />
end<br />
end<br />
if compress then<br />
local other = ret.other<br />
ret = p.compressSparseArray(ret)<br />
ret.other = other<br />
end<br />
return ret<br />
end<br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- compressSparseArray<br />
--<br />
-- This takes an array with one or more nil values, and removes the nil values<br />
-- while preserving the order, so that the array can be safely traversed with<br />
-- ipairs.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.compressSparseArray(t)<br />
checkType('compressSparseArray', 1, t, 'table')<br />
local ret = {}<br />
local nums = p.numKeys(t)<br />
for _, num in ipairs(nums) do<br />
ret[#ret + 1] = t[num]<br />
end<br />
return ret<br />
end<br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- sparseIpairs<br />
--<br />
-- This is an iterator for sparse arrays. It can be used like ipairs, but can<br />
-- handle nil values.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.sparseIpairs(t)<br />
checkType('sparseIpairs', 1, t, 'table')<br />
local nums = p.numKeys(t)<br />
local i = 0<br />
local lim = #nums<br />
return function ()<br />
i = i + 1<br />
if i <= lim then<br />
local key = nums[i]<br />
return key, t[key]<br />
else<br />
return nil, nil<br />
end<br />
end<br />
end<br />
<br />
--[[<br />
------------------------------------------------------------------------------------<br />
-- size<br />
--<br />
-- This returns the size of a key/value pair table. It will also work on arrays,<br />
-- but for arrays it is more efficient to use the # operator.<br />
------------------------------------------------------------------------------------<br />
--]]<br />
function p.size(t)<br />
checkType('size', 1, t, 'table')<br />
local i = 0<br />
for k in pairs(t) do<br />
i = i + 1<br />
end<br />
return i<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:HatnoteModule:Hatnote2014-05-23T23:28:43Z<p>Mr. Stradivarius : Undid revision 609825678 by SMcCandlish (talk) this change will have quite far-reaching consequences, so should be discussed first</p>
<hr />
<div>--------------------------------------------------------------------------------<br />
-- Module:Hatnote --<br />
-- --<br />
-- This module produces hatnote links and links to related articles. It --<br />
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --<br />
-- includes helper functions for other Lua hatnote modules. --<br />
--------------------------------------------------------------------------------<br />
<br />
local libraryUtil = require('libraryUtil')<br />
local checkType = libraryUtil.checkType<br />
local mArguments -- lazily initialise [[Module:Arguments]]<br />
local yesno -- lazily initialise [[Module:Yesno]]<br />
<br />
local p = {}<br />
<br />
--------------------------------------------------------------------------------<br />
-- Helper functions<br />
--------------------------------------------------------------------------------<br />
<br />
local function getArgs(frame)<br />
-- Fetches the arguments from the parent frame. Whitespace is trimmed and<br />
-- blanks are removed.<br />
mArguments = require('Module:Arguments')<br />
return mArguments.getArgs(frame, {parentOnly = true})<br />
end<br />
<br />
local function removeInitialColon(s)<br />
-- Removes the initial colon from a string, if present.<br />
return s:match('^:?(.*)')<br />
end<br />
<br />
function p.findNamespaceId(link, removeColon)<br />
-- Finds the namespace id (namespace number) of a link or a pagename. This<br />
-- function will not work if the link is enclosed in double brackets. Colons<br />
-- are trimmed from the start of the link by default. To skip colon<br />
-- trimming, set the removeColon parameter to true.<br />
checkType('findNamespaceId', 1, link, 'string')<br />
checkType('findNamespaceId', 2, removeColon, 'boolean', true)<br />
if removeColon ~= false then<br />
link = removeInitialColon(link)<br />
end<br />
local namespace = link:match('^(.-):')<br />
if namespace then<br />
local nsTable = mw.site.namespaces[namespace]<br />
if nsTable then<br />
return nsTable.id<br />
end<br />
end<br />
return 0<br />
end<br />
<br />
function p.formatPages(...)<br />
-- Formats a list of pages using formatLink and returns it as an array. Nil<br />
-- values are not allowed.<br />
local pages = {...}<br />
local ret = {}<br />
for i, page in ipairs(pages) do<br />
ret[i] = p._formatLink(page)<br />
end<br />
return ret<br />
end<br />
<br />
function p.formatPageTables(...)<br />
-- Takes a list of page/display tables and returns it as a list of<br />
-- formatted links. Nil values are not allowed.<br />
local pages = {...}<br />
local links = {}<br />
for i, t in ipairs(pages) do<br />
checkType('formatPageTables', i, t, 'table')<br />
local link = t[1]<br />
local display = t[2]<br />
links[i] = p._formatLink(link, display)<br />
end<br />
return links<br />
end<br />
<br />
function p.makeWikitextError(msg, helpLink, addTrackingCategory)<br />
-- Formats an error message to be returned to wikitext. If<br />
-- addTrackingCategory is not false after being returned from<br />
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category<br />
-- is added.<br />
checkType('makeWikitextError', 1, msg, 'string')<br />
checkType('makeWikitextError', 2, helpLink, 'string', true)<br />
yesno = require('Module:Yesno')<br />
local title = mw.title.getCurrentTitle()<br />
-- Make the help link text.<br />
local helpText<br />
if helpLink then<br />
helpText = ' ([[' .. helpLink .. '|help]])'<br />
else<br />
helpText = ''<br />
end<br />
-- Make the category text.<br />
local category<br />
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then<br />
category = 'Hatnote templates with errors'<br />
category = string.format(<br />
'[[%s:%s]]',<br />
mw.site.namespaces[14].name,<br />
category<br />
)<br />
else<br />
category = ''<br />
end<br />
return string.format(<br />
'<strong class="error">Error: %s%s.</strong>%s',<br />
msg,<br />
helpText,<br />
category<br />
)<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Format link<br />
--<br />
-- Makes a wikilink from the given link and display values. Links are escaped<br />
-- with colons if necessary, and links to sections are detected and displayed<br />
-- with " § " as a separator rather than the standard MediaWiki "#". Used in<br />
-- the {{format hatnote link}} template.<br />
--------------------------------------------------------------------------------<br />
<br />
function p.formatLink(frame)<br />
local args = getArgs(frame)<br />
local link = args[1]<br />
local display = args[2]<br />
if not link then<br />
return p.makeWikitextError(<br />
'no link specified',<br />
'Template:Format hatnote link#Errors',<br />
args.category<br />
)<br />
end<br />
return p._formatLink(link, display)<br />
end<br />
<br />
function p._formatLink(link, display)<br />
-- Find whether we need to use the colon trick or not. We need to use the<br />
-- colon trick for categories and files, as otherwise category links<br />
-- categorise the page and file links display the file.<br />
checkType('_formatLink', 1, link, 'string')<br />
checkType('_formatLink', 2, display, 'string', true)<br />
link = removeInitialColon(link)<br />
local namespace = p.findNamespaceId(link, false)<br />
local colon<br />
if namespace == 6 or namespace == 14 then<br />
colon = ':'<br />
else<br />
colon = ''<br />
end<br />
<br />
-- Find the display value.<br />
if not display then<br />
local page, section = link:match('^(.-)#(.*)$')<br />
if page then<br />
display = page .. ' § ' .. section<br />
end<br />
end<br />
<br />
-- Assemble the link.<br />
if display then<br />
return string.format('[[%s%s|%s]]', colon, link, display)<br />
else<br />
return string.format('[[%s%s]]', colon, link)<br />
end<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Hatnote<br />
--<br />
-- Produces standard hatnote text. Implements the {{hatnote}} template.<br />
--------------------------------------------------------------------------------<br />
<br />
function p.hatnote(frame)<br />
local args = getArgs(frame)<br />
local s = args[1]<br />
local options = {}<br />
if not s then<br />
return p.makeWikitextError(<br />
'no text specified',<br />
'Template:Hatnote#Errors',<br />
args.category<br />
)<br />
end<br />
options.extraclasses = args.extraclasses<br />
options.selfref = args.selfref<br />
return p._hatnote(s, options)<br />
end<br />
<br />
function p._hatnote(s, options)<br />
checkType('_hatnote', 1, s, 'string')<br />
checkType('_hatnote', 2, options, 'table', true)<br />
local classes = {'hatnote'}<br />
local extraclasses = options.extraclasses<br />
local selfref = options.selfref<br />
if type(extraclasses) == 'string' then<br />
classes[#classes + 1] = extraclasses<br />
end<br />
if selfref then<br />
classes[#classes + 1] = 'selfref'<br />
end<br />
return string.format(<br />
'<div class="%s">%s</div>',<br />
table.concat(classes, ' '),<br />
s<br />
)<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:YesnoModule:Yesno2014-04-18T10:35:42Z<p>Mr. Stradivarius : use the Lua string.lower function instead of mw.ustring.lower; this makes the function around 25x faster</p>
<hr />
<div>-- Function allowing for consistent treatment of boolean-like wikitext input.<br />
-- It works similarly to the template {{yesno}}.<br />
<br />
return function (val, default)<br />
-- If your wiki uses non-ascii characters for any of "yes", "no", etc., you<br />
-- should replace "val:lower()" with "mw.ustring.lower(val)" in the<br />
-- following line.<br />
val = type(val) == 'string' and val:lower() or val<br />
if val == nil then<br />
return nil<br />
elseif val == true <br />
or val == 'yes'<br />
or val == 'y'<br />
or val == 'true'<br />
or tonumber(val) == 1<br />
then<br />
return true<br />
elseif val == false<br />
or val == 'no'<br />
or val == 'n'<br />
or val == 'false'<br />
or tonumber(val) == 0<br />
then<br />
return false<br />
else<br />
return default<br />
end<br />
end</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Message_boxModule:Message box2014-04-08T04:39:12Z<p>Mr. Stradivarius : Allow passing attributes per protected edit request by User:Jackmcbarn. Also remove undocumented "hidden" parameter and format the module so it mostly fits within 80 chars.</p>
<hr />
<div>-- This is a meta-module for producing message box templates, including<br />
-- {{mbox}}, {{ambox}}, {{imbox}}, {{tmbox}}, {{ombox}}, {{cmbox}} and {{fmbox}}.<br />
<br />
-- Require necessary modules.<br />
local getArgs = require('Module:Arguments').getArgs<br />
local htmlBuilder = require('Module:HtmlBuilder')<br />
local categoryHandler = require('Module:Category handler').main<br />
local yesno = require('Module:Yesno')<br />
<br />
-- Load the configuration page.<br />
local cfgTables = mw.loadData('Module:Message box/configuration')<br />
<br />
-- Get a language object for formatDate and ucfirst.<br />
local lang = mw.language.getContentLanguage()<br />
<br />
-- Set aliases for often-used functions to reduce table lookups.<br />
local format = mw.ustring.format<br />
local tinsert = table.insert<br />
local tconcat = table.concat<br />
local trim = mw.text.trim<br />
<br />
--------------------------------------------------------------------------------<br />
-- Helper functions<br />
--------------------------------------------------------------------------------<br />
<br />
local function getTitleObject(page, ...)<br />
if type(page) == 'string' then<br />
-- Get the title object, passing the function through pcall <br />
-- in case we are over the expensive function count limit.<br />
local success, title = pcall(mw.title.new, page, ...)<br />
if success then<br />
return title<br />
end<br />
end<br />
end<br />
<br />
local function union(t1, t2)<br />
-- Returns the union of two arrays.<br />
local vals = {}<br />
for i, v in ipairs(t1) do<br />
vals[v] = true<br />
end<br />
for i, v in ipairs(t2) do<br />
vals[v] = true<br />
end<br />
local ret = {}<br />
for k in pairs(vals) do<br />
tinsert(ret, k)<br />
end<br />
table.sort(ret)<br />
return ret<br />
end<br />
<br />
local function getArgNums(args, prefix)<br />
local nums = {}<br />
for k, v in pairs(args) do<br />
local num = mw.ustring.match(tostring(k), '^' .. prefix .. '([1-9]%d*)$')<br />
if num then<br />
tinsert(nums, tonumber(num))<br />
end<br />
end<br />
table.sort(nums)<br />
return nums<br />
end<br />
<br />
--------------------------------------------------------------------------------<br />
-- Box class definition<br />
--------------------------------------------------------------------------------<br />
<br />
local box = {}<br />
box.__index = box<br />
<br />
function box.new()<br />
local obj = {}<br />
setmetatable(obj, box)<br />
return obj<br />
end<br />
<br />
function box.getNamespaceId(ns)<br />
if not ns then return end<br />
if type(ns) == 'string' then<br />
ns = lang:ucfirst(mw.ustring.lower(ns))<br />
if ns == 'Main' then<br />
ns = 0<br />
end<br />
end<br />
local nsTable = mw.site.namespaces[ns]<br />
if nsTable then<br />
return nsTable.id<br />
end<br />
end<br />
<br />
function box.getMboxType(nsid)<br />
-- Gets the mbox type from a namespace number.<br />
if nsid == 0 then<br />
return 'ambox' -- main namespace<br />
elseif nsid == 6 then<br />
return 'imbox' -- file namespace<br />
elseif nsid == 14 then<br />
return 'cmbox' -- category namespace<br />
else<br />
local nsTable = mw.site.namespaces[nsid]<br />
if nsTable and nsTable.isTalk then<br />
return 'tmbox' -- any talk namespace<br />
else<br />
return 'ombox' -- other namespaces or invalid input<br />
end<br />
end<br />
end<br />
<br />
function box:addCat(ns, cat, sort)<br />
if type(cat) ~= 'string' then return end<br />
local nsVals = {'main', 'template', 'all'}<br />
local tname<br />
for i, val in ipairs(nsVals) do<br />
if ns == val then<br />
tname = ns .. 'Cats'<br />
end<br />
end<br />
if not tname then<br />
for i, val in ipairs(nsVals) do<br />
nsVals[i] = format('"%s"', val)<br />
end<br />
error(<br />
'invalid ns parameter passed to box:addCat; valid values are '<br />
.. mw.text.listToText(nsVals, nil, ' or ')<br />
)<br />
end<br />
self[tname] = self[tname] or {}<br />
if type(sort) == 'string' then<br />
tinsert(self[tname], format('[[Category:%s|%s]]', cat, sort))<br />
else<br />
tinsert(self[tname], format('[[Category:%s]]', cat))<br />
end<br />
end<br />
<br />
function box:addClass(class)<br />
if type(class) ~= 'string' then return end<br />
self.classes = self.classes or {}<br />
tinsert(self.classes, class)<br />
end<br />
<br />
function box:addAttr(attr, val)<br />
if type(attr) ~= 'string' or type(val) ~= 'string' then return end<br />
self.attrs = self.attrs or {}<br />
tinsert(self.attrs, attr)<br />
end<br />
<br />
function box:setTitle(args)<br />
-- Get the title object and the namespace.<br />
self.pageTitle = getTitleObject(args.page ~= '' and args.page)<br />
self.title = self.pageTitle or mw.title.getCurrentTitle()<br />
self.demospace = args.demospace ~= '' and args.demospace or nil<br />
self.nsid = box.getNamespaceId(self.demospace) or self.title.namespace<br />
end<br />
<br />
function box:getConfig(boxType)<br />
-- Get the box config data from the data page.<br />
if boxType == 'mbox' then<br />
boxType = box.getMboxType(self.nsid)<br />
end<br />
local cfg = cfgTables[boxType]<br />
if not cfg then<br />
local boxTypes = {}<br />
for k, v in pairs(dataTables) do<br />
tinsert(boxTypes, format('"%s"', k))<br />
end<br />
tinsert(boxTypes, '"mbox"')<br />
error(format(<br />
'invalid message box type "%s"; valid types are %s',<br />
tostring(boxType),<br />
mw.text.listToText(boxTypes)<br />
), 2)<br />
end<br />
return cfg<br />
end<br />
<br />
function box:removeBlankArgs(cfg, args)<br />
-- Only allow blank arguments for the parameter names listed in<br />
-- cfg.allowBlankParams.<br />
local newArgs = {}<br />
for k, v in pairs(args) do<br />
if v ~= '' then<br />
newArgs[k] = v<br />
end<br />
end<br />
for i, param in ipairs(cfg.allowBlankParams or {}) do<br />
newArgs[param] = args[param]<br />
end<br />
return newArgs<br />
end<br />
<br />
function box:setBoxParameters(cfg, args)<br />
-- Get type data.<br />
self.type = args.type<br />
local typeData = cfg.types[self.type]<br />
self.invalidTypeError = cfg.showInvalidTypeError<br />
and self.type<br />
and not typeData<br />
and true<br />
or false<br />
typeData = typeData or cfg.types[cfg.default]<br />
self.typeClass = typeData.class<br />
self.typeImage = typeData.image<br />
<br />
-- Find if the box has been wrongly substituted.<br />
if cfg.substCheck and args.subst == 'SUBST' then<br />
self.isSubstituted = true<br />
end<br />
<br />
-- Find whether we are using a small message box.<br />
if cfg.allowSmall and (<br />
cfg.smallParam and args.small == cfg.smallParam<br />
or not cfg.smallParam and yesno(args.small)<br />
)<br />
then<br />
self.isSmall = true<br />
else<br />
self.isSmall = false<br />
end<br />
<br />
-- Add attributes, classes and styles.<br />
if cfg.allowId then<br />
self.id = args.id<br />
end<br />
self:addClass(<br />
cfg.usePlainlinksParam and yesno(args.plainlinks or true) and 'plainlinks'<br />
)<br />
for _, class in ipairs(cfg.classes or {}) do<br />
self:addClass(class)<br />
end<br />
if self.isSmall then<br />
self:addClass(cfg.smallClass or 'mbox-small')<br />
end<br />
self:addClass(self.typeClass)<br />
self:addClass(args.class)<br />
self.style = args.style<br />
self.attrs = args.attrs<br />
<br />
-- Set text style.<br />
self.textstyle = args.textstyle<br />
<br />
-- Find if we are on the template page or not. This functionality is only<br />
-- used if useCollapsibleTextFields is set, or if both cfg.templateCategory<br />
-- and cfg.templateCategoryRequireName are set.<br />
self.useCollapsibleTextFields = cfg.useCollapsibleTextFields<br />
if self.useCollapsibleTextFields<br />
or cfg.templateCategory<br />
and cfg.templateCategoryRequireName<br />
then<br />
self.name = args.name<br />
if self.name then<br />
local templateName = mw.ustring.match(<br />
self.name,<br />
'^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$'<br />
) or self.name<br />
templateName = 'Template:' .. templateName<br />
self.templateTitle = getTitleObject(templateName)<br />
end<br />
self.isTemplatePage = self.templateTitle<br />
and mw.title.equals(self.title, self.templateTitle)<br />
or false<br />
end<br />
<br />
-- Process data for collapsible text fields. At the moment these are only<br />
-- used in {{ambox}}.<br />
if self.useCollapsibleTextFields then<br />
-- Get the self.issue value.<br />
if self.isSmall and args.smalltext then<br />
self.issue = args.smalltext<br />
else<br />
local sect<br />
if args.sect == '' then<br />
sect = 'This ' .. (cfg.sectionDefault or 'page')<br />
elseif type(args.sect) == 'string' then<br />
sect = 'This ' .. args.sect<br />
end<br />
local issue = args.issue<br />
issue = type(issue) == 'string' and issue ~= '' and issue or nil<br />
local text = args.text<br />
text = type(text) == 'string' and text or nil<br />
local issues = {}<br />
tinsert(issues, sect)<br />
tinsert(issues, issue)<br />
tinsert(issues, text)<br />
self.issue = tconcat(issues, ' ')<br />
end<br />
<br />
-- Get the self.talk value.<br />
local talk = args.talk<br />
-- Show talk links on the template page or template subpages if the talk<br />
-- parameter is blank.<br />
if talk == ''<br />
and self.templateTitle <br />
and (<br />
mw.title.equals(self.templateTitle, self.title)<br />
or self.title:isSubpageOf(self.templateTitle)<br />
)<br />
then<br />
talk = '#'<br />
elseif talk == '' then<br />
talk = nil<br />
end<br />
if talk then<br />
-- If the talk value is a talk page, make a link to that page. Else<br />
-- assume that it's a section heading, and make a link to the talk<br />
-- page of the current page with that section heading.<br />
local talkTitle = getTitleObject(talk)<br />
local talkArgIsTalkPage = true<br />
if not talkTitle or not talkTitle.isTalkPage then<br />
talkArgIsTalkPage = false<br />
talkTitle = getTitleObject(<br />
self.title.text,<br />
mw.site.namespaces[self.title.namespace].talk.id<br />
)<br />
end<br />
if talkTitle and talkTitle.exists then<br />
local talkText = 'Relevant discussion may be found on'<br />
if talkArgIsTalkPage then<br />
talkText = format(<br />
'%s [[%s|%s]].',<br />
talkText,<br />
talk,<br />
talkTitle.prefixedText<br />
)<br />
else<br />
talkText = format(<br />
'%s the [[%s#%s|talk page]].',<br />
talkText,<br />
talkTitle.prefixedText,<br />
talk<br />
)<br />
end<br />
self.talk = talkText<br />
end<br />
end<br />
<br />
-- Get other values.<br />
self.fix = args.fix ~= '' and args.fix or nil<br />
local date<br />
if args.date and args.date ~= '' then<br />
date = args.date<br />
elseif args.date == '' and self.isTemplatePage then<br />
date = lang:formatDate('F Y')<br />
end<br />
if date then<br />
self.date = format(" <small>''(%s)''</small>", date)<br />
end<br />
self.info = args.info<br />
end<br />
<br />
-- Set the non-collapsible text field. At the moment this is used by all box<br />
-- types other than ambox, and also by ambox when small=yes.<br />
if self.isSmall then<br />
self.text = args.smalltext or args.text<br />
else<br />
self.text = args.text<br />
end<br />
<br />
-- Set the below row.<br />
self.below = cfg.below and args.below<br />
<br />
-- General image settings.<br />
self.imageCellDiv = not self.isSmall and cfg.imageCellDiv and true or false<br />
self.imageEmptyCell = cfg.imageEmptyCell<br />
if cfg.imageEmptyCellStyle then<br />
self.imageEmptyCellStyle = 'border:none;padding:0px;width:1px'<br />
end<br />
<br />
-- Left image settings.<br />
local imageLeft = self.isSmall and args.smallimage or args.image<br />
if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none'<br />
or not cfg.imageCheckBlank and imageLeft ~= 'none'<br />
then<br />
self.imageLeft = imageLeft<br />
if not imageLeft then<br />
local imageSize = self.isSmall<br />
and (cfg.imageSmallSize or '30x30px')<br />
or '40x40px'<br />
self.imageLeft = format('[[File:%s|%s|link=|alt=]]', self.typeImage<br />
or 'Imbox notice.png', imageSize)<br />
end<br />
end<br />
<br />
-- Right image settings.<br />
local imageRight = self.isSmall and args.smallimageright or args.imageright<br />
if not (cfg.imageRightNone and imageRight == 'none') then<br />
self.imageRight = imageRight<br />
end<br />
<br />
-- Add mainspace categories. At the moment these are only used in {{ambox}}.<br />
if cfg.allowMainspaceCategories then<br />
if args.cat then<br />
args.cat1 = args.cat<br />
end<br />
self.catNums = getArgNums(args, 'cat')<br />
if args.category then<br />
args.category1 = args.category<br />
end<br />
self.categoryNums = getArgNums(args, 'category')<br />
if args.all then<br />
args.all1 = args.all<br />
end<br />
self.allNums = getArgNums(args, 'all')<br />
self.categoryParamNums = union(self.catNums, self.categoryNums)<br />
self.categoryParamNums = union(self.categoryParamNums, self.allNums)<br />
-- The following is roughly equivalent to the old {{Ambox/category}}.<br />
local date = args.date<br />
date = type(date) == 'string' and date<br />
local preposition = 'from'<br />
for _, num in ipairs(self.categoryParamNums) do<br />
local mainCat = args['cat' .. tostring(num)]<br />
or args['category' .. tostring(num)]<br />
local allCat = args['all' .. tostring(num)]<br />
mainCat = type(mainCat) == 'string' and mainCat<br />
allCat = type(allCat) == 'string' and allCat<br />
if mainCat and date and date ~= '' then<br />
local catTitle = format('%s %s %s', mainCat, preposition, date)<br />
self:addCat('main', catTitle)<br />
catTitle = getTitleObject('Category:' .. catTitle)<br />
if not catTitle or not catTitle.exists then<br />
self:addCat(<br />
'main',<br />
'Articles with invalid date parameter in template'<br />
)<br />
end<br />
elseif mainCat and (not date or date == '') then<br />
self:addCat('main', mainCat)<br />
end<br />
if allCat then<br />
self:addCat('main', allCat)<br />
end<br />
end<br />
end<br />
<br />
-- Add template-namespace categories.<br />
if cfg.templateCategory then<br />
if cfg.templateCategoryRequireName then<br />
if self.isTemplatePage then<br />
self:addCat('template', cfg.templateCategory)<br />
end<br />
elseif not self.title.isSubpage then<br />
self:addCat('template', cfg.templateCategory)<br />
end<br />
end<br />
<br />
-- Add template error category.<br />
if cfg.templateErrorCategory then<br />
local templateErrorCategory = cfg.templateErrorCategory<br />
local templateCat, templateSort<br />
if not self.name and not self.title.isSubpage then<br />
templateCat = templateErrorCategory<br />
elseif self.isTemplatePage then<br />
local paramsToCheck = cfg.templateErrorParamsToCheck or {}<br />
local count = 0<br />
for i, param in ipairs(paramsToCheck) do<br />
if not args[param] then<br />
count = count + 1<br />
end<br />
end<br />
if count > 0 then<br />
templateCat = templateErrorCategory<br />
templateSort = tostring(count)<br />
end<br />
if self.categoryNums and #self.categoryNums > 0 then<br />
templateCat = templateErrorCategory<br />
templateSort = 'C'<br />
end<br />
end<br />
self:addCat('template', templateCat, templateSort)<br />
end<br />
<br />
-- Categories for all namespaces.<br />
if self.invalidTypeError then<br />
local allSort = (self.nsid == 0 and 'Main:' or '') .. self.title.prefixedText<br />
self:addCat('all', 'Wikipedia message box parameter needs fixing', allSort)<br />
end<br />
if self.isSubstituted then<br />
self:addCat('all', 'Pages with incorrectly substituted templates')<br />
end<br />
<br />
-- Convert category tables to strings and pass them through<br />
-- [[Module:Category handler]].<br />
self.categories = categoryHandler{<br />
main = tconcat(self.mainCats or {}),<br />
template = tconcat(self.templateCats or {}),<br />
all = tconcat(self.allCats or {}),<br />
nocat = args.nocat,<br />
demospace = self.demospace,<br />
page = self.pageTitle and self.pageTitle.prefixedText or nil<br />
}<br />
end<br />
<br />
function box:export()<br />
local root = htmlBuilder.create()<br />
<br />
-- Add the subst check error.<br />
if self.isSubstituted and self.name then<br />
root<br />
.tag('b')<br />
.addClass('error')<br />
.wikitext(format(<br />
'Template <code>%s[[Template:%s|%s]]%s</code> has been incorrectly substituted.',<br />
mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}')<br />
))<br />
end<br />
<br />
-- Create the box table.<br />
local boxTable = root.tag('table')<br />
boxTable<br />
.attr('id', self.id)<br />
for i, class in ipairs(self.classes or {}) do<br />
boxTable<br />
.addClass(class)<br />
end<br />
boxTable<br />
.cssText(self.style)<br />
.attr('role', 'presentation')<br />
for attr, val in pairs(self.attrs or {}) do<br />
boxTable<br />
.attr(attr, val)<br />
end<br />
<br />
-- Add the left-hand image.<br />
local row = boxTable.tag('tr')<br />
if self.imageLeft then<br />
local imageLeftCell = row.tag('td').addClass('mbox-image')<br />
if self.imageCellDiv then<br />
-- If we are using a div, redefine imageLeftCell so that the image<br />
-- is inside it. Divs use style="width: 52px;", which limits the<br />
-- image width to 52px. If any images in a div are wider than that,<br />
-- they may overlap with the text or cause other display problems.<br />
imageLeftCell = imageLeftCell.tag('div').css('width', '52px') <br />
end<br />
imageLeftCell<br />
.wikitext(self.imageLeft)<br />
elseif self.imageEmptyCell then<br />
-- Some message boxes define an empty cell if no image is specified, and<br />
-- some don't. The old template code in templates where empty cells are<br />
-- specified gives the following hint: "No image. Cell with some width<br />
-- or padding necessary for text cell to have 100% width."<br />
row.tag('td')<br />
.addClass('mbox-empty-cell') <br />
.cssText(self.imageEmptyCellStyle)<br />
end<br />
<br />
-- Add the text.<br />
local textCell = row.tag('td').addClass('mbox-text')<br />
if self.useCollapsibleTextFields then<br />
-- The message box uses advanced text parameters that allow things to be<br />
-- collapsible. At the moment, only ambox uses this.<br />
textCell<br />
.cssText(self.textstyle)<br />
local textCellSpan = textCell.tag('span')<br />
textCellSpan<br />
.addClass('mbox-text-span')<br />
.wikitext(self.issue)<br />
if not self.isSmall then<br />
textCellSpan<br />
.tag('span')<br />
.addClass('hide-when-compact')<br />
.wikitext(self.talk and ' ' .. self.talk)<br />
.wikitext(self.fix and ' ' .. self.fix)<br />
end<br />
textCellSpan<br />
.wikitext(self.date and ' ' .. self.date)<br />
if not self.isSmall then<br />
textCellSpan<br />
.tag('span')<br />
.addClass('hide-when-compact')<br />
.wikitext(self.info and ' ' .. self.info)<br />
end<br />
else<br />
-- Default text formatting - anything goes.<br />
textCell<br />
.cssText(self.textstyle)<br />
.wikitext(self.text)<br />
end<br />
<br />
-- Add the right-hand image.<br />
if self.imageRight then<br />
local imageRightCell = row.tag('td').addClass('mbox-imageright')<br />
if self.imageCellDiv then<br />
-- If we are using a div, redefine imageRightCell so that the image<br />
-- is inside it.<br />
imageRightCell = imageRightCell.tag('div').css('width', '52px')<br />
end<br />
imageRightCell<br />
.wikitext(self.imageRight)<br />
end<br />
<br />
-- Add the below row.<br />
if self.below then<br />
boxTable.tag('tr')<br />
.tag('td')<br />
.attr('colspan', self.imageRight and '3' or '2')<br />
.addClass('mbox-text')<br />
.cssText(self.textstyle)<br />
.wikitext(self.below)<br />
end<br />
<br />
-- Add error message for invalid type parameters.<br />
if self.invalidTypeError then<br />
root<br />
.tag('div')<br />
.css('text-align', 'center')<br />
.wikitext(format(<br />
'This message box is using an invalid "type=%s" parameter and needs fixing.',<br />
self.type or ''<br />
))<br />
end<br />
<br />
-- Add categories.<br />
root<br />
.wikitext(self.categories)<br />
<br />
return tostring(root)<br />
end<br />
<br />
local function main(boxType, args)<br />
local outputBox = box.new()<br />
outputBox:setTitle(args)<br />
local cfg = outputBox:getConfig(boxType)<br />
args = outputBox:removeBlankArgs(cfg, args)<br />
outputBox:setBoxParameters(cfg, args)<br />
return outputBox:export()<br />
end<br />
<br />
local function makeWrapper(boxType)<br />
return function (frame)<br />
local args = getArgs(frame, {trim = false, removeBlanks = false})<br />
return main(boxType, args)<br />
end<br />
end<br />
<br />
local p = {<br />
main = main,<br />
mbox = makeWrapper('mbox')<br />
}<br />
<br />
for boxType in pairs(cfgTables) do<br />
p[boxType] = makeWrapper(boxType)<br />
end<br />
<br />
return p</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Namespace_detect/configModule:Namespace detect/config2014-04-05T17:03:49Z<p>Mr. Stradivarius : use cfg.demopage rather than cfg.page now that the default parameter has been changed on the /data page</p>
<hr />
<div>--------------------------------------------------------------------------------<br />
-- Namespace detect configuration data --<br />
-- --<br />
-- This module stores configuration data for Module:Namespace detect. Here --<br />
-- you can localise the module to your wiki's language. --<br />
-- --<br />
-- To activate a configuration item, you need to uncomment it. This means --<br />
-- that you need to remove the text "-- " at the start of the line. --<br />
--------------------------------------------------------------------------------<br />
<br />
local cfg = {} -- Don't edit this line.<br />
<br />
--------------------------------------------------------------------------------<br />
-- Parameter names --<br />
-- These configuration items specify custom parameter names. Values added --<br />
-- here will work in addition to the default English parameter names. --<br />
-- To add one extra name, you can use this format: --<br />
-- --<br />
-- cfg.foo = 'parameter name' --<br />
-- --<br />
-- To add multiple names, you can use this format: --<br />
-- --<br />
-- cfg.foo = {'parameter name 1', 'parameter name 2', 'parameter name 3'} --<br />
--------------------------------------------------------------------------------<br />
<br />
---- This parameter displays content for the main namespace:<br />
-- cfg.main = 'main'<br />
<br />
---- This parameter displays in talk namespaces:<br />
-- cfg.talk = 'talk'<br />
<br />
---- This parameter displays content for "other" namespaces (namespaces for which<br />
---- parameters have not been specified):<br />
-- cfg.other = 'other'<br />
<br />
---- This parameter makes talk pages behave as though they are the corresponding<br />
---- subject namespace. Note that this parameter is used with [[Module:Yesno]].<br />
---- Edit that module to change the default values of "yes", "no", etc.<br />
-- cfg.subjectns = 'subjectns'<br />
<br />
---- This parameter sets a demonstration namespace:<br />
-- cfg.demospace = 'demospace'<br />
<br />
---- This parameter sets a specific page to compare:<br />
cfg.demopage = 'page'<br />
<br />
--------------------------------------------------------------------------------<br />
-- Table configuration --<br />
-- These configuration items allow customisation of the "table" function, --<br />
-- used to generate a table of possible parameters in the module --<br />
-- documentation. --<br />
--------------------------------------------------------------------------------<br />
<br />
---- The header for the namespace column in the wikitable containing the list of<br />
---- possible subject-space parameters.<br />
-- cfg.wikitableNamespaceHeader = 'Namespace'<br />
<br />
---- The header for the wikitable containing the list of possible subject-space<br />
---- parameters.<br />
-- cfg.wikitableAliasesHeader = 'Aliases'<br />
<br />
--------------------------------------------------------------------------------<br />
-- End of configuration data --<br />
--------------------------------------------------------------------------------<br />
<br />
return cfg -- Don't edit this line.</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Namespace_detect/dataModule:Namespace detect/data2014-04-05T17:02:16Z<p>Mr. Stradivarius : bug fix - use the demospace parameter as both key and value in the argKeys table</p>
<hr />
<div>--------------------------------------------------------------------------------<br />
-- Namespace detect data --<br />
-- This module holds data for [[Module:Namespace detect]] to be loaded per --<br />
-- page, rather than per #invoke, for performance reasons. --<br />
--------------------------------------------------------------------------------<br />
<br />
local cfg = require('Module:Namespace detect/config')<br />
<br />
local function addKey(t, key, defaultKey)<br />
if key ~= defaultKey then<br />
t[#t + 1] = key<br />
end<br />
end<br />
<br />
-- Get a table of parameters to query for each default parameter name.<br />
-- This allows wikis to customise parameter names in the cfg table while<br />
-- ensuring that default parameter names will always work. The cfg table<br />
-- values can be added as a string, or as an array of strings.<br />
<br />
local defaultKeys = {<br />
'main',<br />
'talk',<br />
'other',<br />
'subjectns',<br />
'demospace',<br />
'demopage'<br />
}<br />
<br />
local argKeys = {}<br />
for i, defaultKey in ipairs(defaultKeys) do<br />
argKeys[defaultKey] = {defaultKey}<br />
end<br />
<br />
for defaultKey, t in pairs(argKeys) do<br />
local cfgValue = cfg[defaultKey]<br />
local cfgValueType = type(cfgValue)<br />
if cfgValueType == 'string' then<br />
addKey(t, cfgValue, defaultKey)<br />
elseif cfgValueType == 'table' then<br />
for i, key in ipairs(cfgValue) do<br />
addKey(t, key, defaultKey)<br />
end<br />
end<br />
cfg[defaultKey] = nil -- Free the cfg value as we don't need it any more.<br />
end<br />
<br />
local function getParamMappings()<br />
--[[<br />
-- Returns a table of how parameter names map to namespace names. The keys<br />
-- are the actual namespace names, in lower case, and the values are the<br />
-- possible parameter names for that namespace, also in lower case. The<br />
-- table entries are structured like this:<br />
-- {<br />
-- [''] = {'main'},<br />
-- ['wikipedia'] = {'wikipedia', 'project', 'wp'},<br />
-- ...<br />
-- }<br />
--]]<br />
local mappings = {}<br />
local mainNsName = mw.site.subjectNamespaces[0].name<br />
mainNsName = mw.ustring.lower(mainNsName)<br />
mappings[mainNsName] = mw.clone(argKeys.main)<br />
mappings['talk'] = mw.clone(argKeys.talk)<br />
for nsid, ns in pairs(mw.site.subjectNamespaces) do<br />
if nsid ~= 0 then -- Exclude main namespace.<br />
local nsname = mw.ustring.lower(ns.name)<br />
local canonicalName = mw.ustring.lower(ns.canonicalName)<br />
mappings[nsname] = {nsname}<br />
if canonicalName ~= nsname then<br />
table.insert(mappings[nsname], canonicalName)<br />
end<br />
for _, alias in ipairs(ns.aliases) do<br />
table.insert(mappings[nsname], mw.ustring.lower(alias))<br />
end<br />
end<br />
end<br />
return mappings<br />
end<br />
<br />
return {<br />
argKeys = argKeys,<br />
cfg = cfg,<br />
mappings = getParamMappings()<br />
}</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:Message_box/configurationModule:Message box/configuration2014-01-27T01:07:32Z<p>Mr. Stradivarius : allow the hidden arg to be blank, and allow tmbox to have an id attribute, per protected edit request by User:Technical 13</p>
<hr />
<div>local ambox = {<br />
types = {<br />
speedy = {<br />
class = 'ambox-speedy',<br />
image = 'Ambox speedy deletion.png'<br />
},<br />
delete = {<br />
class = 'ambox-delete',<br />
image = 'Ambox deletion.png'<br />
},<br />
content = {<br />
class = 'ambox-content',<br />
image = 'Ambox content.png'<br />
},<br />
style = {<br />
class = 'ambox-style',<br />
image = 'Edit-clear.svg'<br />
},<br />
move = {<br />
class = 'ambox-move',<br />
image = 'Ambox move.png'<br />
},<br />
protection = {<br />
class = 'ambox-protection',<br />
image = 'Ambox protection.png'<br />
},<br />
notice = {<br />
class = 'ambox-notice',<br />
image = 'Ambox notice.png'<br />
}<br />
},<br />
default = 'notice',<br />
allowBlankParams = {'talk', 'sect', 'date', 'issue', 'fix', 'subst', 'hidden'},<br />
allowSmall = true,<br />
smallParam = 'left',<br />
smallClass = 'mbox-small-left',<br />
substCheck = true,<br />
classes = {'metadata', 'plainlinks', 'ambox'},<br />
imageEmptyCell = true,<br />
imageCheckBlank = true,<br />
imageSmallSize = '20x20px',<br />
imageCellDiv = true,<br />
useCollapsibleTextFields = true,<br />
imageRightNone = true,<br />
sectionDefault = 'article',<br />
allowMainspaceCategories = true,<br />
templateCategory = 'Article message templates',<br />
templateCategoryRequireName = true,<br />
templateErrorCategory = 'Article message templates with missing parameters',<br />
templateErrorParamsToCheck = {'issue', 'fix', 'subst'}<br />
}<br />
<br />
local cmbox = {<br />
types = {<br />
speedy = {<br />
class = 'cmbox-speedy',<br />
image = 'Cmbox deletion.png'<br />
},<br />
delete = {<br />
class = 'cmbox-delete',<br />
image = 'Cmbox deletion.png'<br />
},<br />
content = {<br />
class = 'cmbox-content',<br />
image = 'Cmbox content.png'<br />
},<br />
style = {<br />
class = 'cmbox-style',<br />
image = 'Edit-clear.svg'<br />
},<br />
move = {<br />
class = 'cmbox-move',<br />
image = 'Cmbox move.png'<br />
},<br />
protection = {<br />
class = 'cmbox-protection',<br />
image = 'Cmbox protection.png'<br />
},<br />
notice = {<br />
class = 'cmbox-notice',<br />
image = 'Cmbox notice.png'<br />
}<br />
},<br />
default = 'notice',<br />
showInvalidTypeError = true,<br />
classes = {'plainlinks', 'cmbox'},<br />
imageEmptyCell = true<br />
}<br />
<br />
local fmbox = {<br />
types = {<br />
warning = {<br />
class = 'fmbox-warning',<br />
image = 'Cmbox deletion.png'<br />
},<br />
editnotice = {<br />
class = 'fmbox-editnotice',<br />
image = 'Imbox notice.png'<br />
},<br />
system = {<br />
class = 'fmbox-system',<br />
image = 'Imbox notice.png'<br />
}<br />
},<br />
default = 'system',<br />
showInvalidTypeError = true,<br />
allowId = true,<br />
classes = {'plainlinks', 'fmbox'},<br />
imageEmptyCell = false,<br />
imageRightNone = false<br />
}<br />
<br />
local imbox = {<br />
types = {<br />
speedy = {<br />
class = 'imbox-speedy',<br />
image = 'Imbox speedy deletion.png'<br />
},<br />
delete = {<br />
class = 'imbox-delete',<br />
image = 'Imbox deletion.png'<br />
},<br />
content = {<br />
class = 'imbox-content',<br />
image = 'Imbox content.png'<br />
},<br />
style = {<br />
class = 'imbox-style',<br />
image = 'Edit-clear.svg'<br />
},<br />
move = {<br />
class = 'imbox-move',<br />
image = 'Imbox move.png'<br />
},<br />
protection = {<br />
class = 'imbox-protection',<br />
image = 'Imbox protection.png'<br />
},<br />
license = {<br />
class = 'imbox-license',<br />
image = 'Imbox license.png'<br />
},<br />
featured = {<br />
class = 'imbox-featured',<br />
image = 'Imbox featured.png'<br />
},<br />
notice = {<br />
class = 'imbox-notice',<br />
image = 'Imbox notice.png'<br />
}<br />
},<br />
default = 'notice',<br />
showInvalidTypeError = true,<br />
classes = {'imbox'},<br />
usePlainlinksParam = true,<br />
imageEmptyCell = true,<br />
below = true,<br />
templateCategory = 'File message boxes'<br />
}<br />
<br />
local ombox = {<br />
types = {<br />
speedy = {<br />
class = 'ombox-speedy',<br />
image = 'Imbox speedy deletion.png'<br />
},<br />
delete = {<br />
class = 'ombox-delete',<br />
image = 'Imbox deletion.png'<br />
},<br />
content = {<br />
class = 'ombox-content',<br />
image = 'Imbox content.png'<br />
},<br />
style = {<br />
class = 'ombox-style',<br />
image = 'Edit-clear.svg'<br />
},<br />
move = {<br />
class = 'ombox-move',<br />
image = 'Imbox move.png'<br />
},<br />
protection = {<br />
class = 'ombox-protection',<br />
image = 'Imbox protection.png'<br />
},<br />
notice = {<br />
class = 'ombox-notice',<br />
image = 'Imbox notice.png'<br />
}<br />
},<br />
default = 'notice',<br />
showInvalidTypeError = true,<br />
classes = {'plainlinks', 'ombox'},<br />
allowSmall = true,<br />
imageEmptyCell = true,<br />
imageRightNone = true<br />
}<br />
<br />
local tmbox = {<br />
types = {<br />
speedy = {<br />
class = 'tmbox-speedy',<br />
image = 'Imbox speedy deletion.png'<br />
},<br />
delete = {<br />
class = 'tmbox-delete',<br />
image = 'Imbox deletion.png'<br />
},<br />
content = {<br />
class = 'tmbox-content',<br />
image = 'Imbox content.png'<br />
},<br />
style = {<br />
class = 'tmbox-style',<br />
image = 'Edit-clear.svg '<br />
},<br />
move = {<br />
class = 'tmbox-move',<br />
image = 'Imbox move.png'<br />
},<br />
protection = {<br />
class = 'tmbox-protection',<br />
image = 'Imbox protection.png'<br />
},<br />
notice = {<br />
class = 'tmbox-notice',<br />
image = 'Imbox notice.png'<br />
}<br />
},<br />
default = 'notice',<br />
showInvalidTypeError = true,<br />
classes = {'plainlinks', 'tmbox'},<br />
allowId = true,<br />
allowSmall = true,<br />
imageRightNone = true,<br />
imageEmptyCell = true,<br />
imageEmptyCellStyle = true,<br />
templateCategory = 'Talk message boxes'<br />
}<br />
<br />
return {<br />
ambox = ambox,<br />
cmbox = cmbox,<br />
fmbox = fmbox,<br />
imbox = imbox,<br />
ombox = ombox,<br />
tmbox = tmbox<br />
}</div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Mod%C3%A8le:OmboxModèle:Ombox2013-10-20T12:45:08Z<p>Mr. Stradivarius : switch to Lua version</p>
<hr />
<div>{{#invoke:Message box|ombox}}<noinclude><br />
{{documentation}}<br />
<!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --><br />
</noinclude></div>Mr. Stradivariushttp://centenaire.boulognebillancourt.com/mediawiki/index.php?title=Module:HtmlBuilderModule:HtmlBuilder2013-09-18T15:22:39Z<p>Mr. Stradivarius : don't add attributes with no values, plus a couple of minor tweaks</p>
<hr />
<div>-- Module for building complex HTML (e.g. infoboxes, navboxes) using a fluent interface.<br />
<br />
local HtmlBuilder = {}<br />
<br />
local metatable = {}<br />
<br />
metatable.__index = function(t, key)<br />
local ret = rawget(t, key)<br />
if ret then<br />
return ret<br />
end<br />
<br />
ret = metatable[key]<br />
if type(ret) == 'function' then<br />
return function(...) <br />
return ret(t, ...) <br />
end <br />
else<br />
return ret<br />
end<br />
end<br />
<br />
metatable.__tostring = function(t)<br />
local ret = {}<br />
t._build(ret)<br />
return table.concat(ret)<br />
end<br />
<br />
metatable._build = function(t, ret)<br />
if t.tagName then <br />
table.insert(ret, '<' .. t.tagName)<br />
for i, attr in ipairs(t.attributes) do<br />
table.insert(ret, ' ' .. attr.name .. '="' .. attr.val .. '"') <br />
end<br />
if #t.styles > 0 then<br />
table.insert(ret, ' style="')<br />
for i, prop in ipairs(t.styles) do<br />
if type(prop) == 'string' then -- added with cssText()<br />
table.insert(ret, prop .. ';')<br />
else -- added with css()<br />
table.insert(ret, prop.name .. ':' .. prop.val .. ';')<br />
end<br />
end<br />
table.insert(ret, '"')<br />
end<br />
if t.selfClosing then<br />
table.insert(ret, ' /')<br />
end<br />
table.insert(ret, '>') <br />
end<br />
for i, node in ipairs(t.nodes) do<br />
if node then<br />
if type(node) == 'table' then<br />
node._build(ret)<br />
else<br />
table.insert(ret, tostring(node))<br />
end<br />
end<br />
end<br />
if t.tagName and not t.unclosed and not t.selfClosing then<br />
table.insert(ret, '</' .. t.tagName .. '>')<br />
end<br />
end<br />
<br />
metatable.node = function(t, builder)<br />
if builder then<br />
table.insert(t.nodes, builder)<br />
end<br />
return t<br />
end<br />
<br />
metatable.wikitext = function(t, ...) <br />
local vals = {...}<br />
for i = 1, #vals do<br />
if vals[i] then<br />
table.insert(t.nodes, vals[i])<br />
end<br />
end<br />
return t<br />
end<br />
<br />
metatable.newline = function(t)<br />
table.insert(t.nodes, '\n')<br />
return t<br />
end<br />
<br />
metatable.tag = function(t, tagName, args)<br />
args = args or {}<br />
args.parent = t<br />
local builder = HtmlBuilder.create(tagName, args)<br />
table.insert(t.nodes, builder)<br />
return builder<br />
end<br />
<br />
local function getAttr(t, name)<br />
for i, attr in ipairs(t.attributes) do<br />
if attr.name == name then<br />
return attr<br />
end<br />
end<br />
end<br />
<br />
metatable.attr = function(t, name, val)<br />
if type(val) == 'string' or type(val) == 'number' then<br />
-- if caller sets the style attribute explicitly, then replace all styles previously added with css() and cssText()<br />
if name == 'style' then<br />
t.styles = {val}<br />
return t<br />
end<br />
<br />
local attr = getAttr(t, name)<br />
if attr then<br />
attr.val = val<br />
else<br />
table.insert(t.attributes, {name = name, val = val})<br />
end<br />
end<br />
<br />
return t<br />
end<br />
<br />
metatable.addClass = function(t, class)<br />
if class then<br />
local attr = getAttr(t, 'class')<br />
if attr then<br />
attr.val = attr.val .. ' ' .. class<br />
else<br />
t.attr('class', class)<br />
end<br />
end<br />
<br />
return t<br />
end<br />
<br />
metatable.css = function(t, name, val)<br />
if type(val) == 'string' or type(val) == 'number' then<br />
for i, prop in ipairs(t.styles) do<br />
if prop.name == name then<br />
prop.val = val<br />
return t<br />
end<br />
end<br />
<br />
table.insert(t.styles, {name = name, val = val})<br />
end<br />
<br />
return t<br />
end<br />
<br />
metatable.cssText = function(t, css)<br />
if css then<br />
table.insert(t.styles, css)<br />
end<br />
return t<br />
end<br />
<br />
metatable.done = function(t)<br />
return t.parent or t<br />
end<br />
<br />
metatable.allDone = function(t)<br />
while t.parent do<br />
t = t.parent<br />
end<br />
return t<br />
end<br />
<br />
function HtmlBuilder.create(tagName, args)<br />
args = args or {}<br />
local builder = {}<br />
setmetatable(builder, metatable)<br />
builder.nodes = {}<br />
builder.attributes = {}<br />
builder.styles = {}<br />
builder.tagName = tagName<br />
builder.parent = args.parent<br />
builder.unclosed = args.unclosed or false<br />
builder.selfClosing = args.selfClosing or false<br />
return builder<br />
end<br />
<br />
return HtmlBuilder</div>Mr. Stradivarius