Mô đun:Protection banner
Mô-đun này là loại cần được khóa. Nó là một mô-đun nhìn thấy rõ được sử dụng bởi một số lượng lớn các trang, hoặc là các thay thế rất thường xuyên. Bởi vì phá hoại hoặc lỗi sai khi sửa sẽ ảnh hưởng đến nhiều trang và thậm chí chỉnh sửa tầm thường có thể gây ra tải đáng kể cho các máy chủ, nó được khóa bảo vệ hạn chế sửa đổi. |
Mô-đun này tạo biểu ngữ bảo vệ và biểu tượng ổ khóa được đặt ở đầu trang được khóa.
Sử dụng
Hầu hết người dùng sẽ không cần sử dụng mô-đun này trực tiếp. Để thêm các bản mẫu khóa vào các trang, bạn có thể sử dụng bản mẫu {{Khóa}} hoặc bạn có thể thấy thuận tiện hơn khi sử dụng một trong các bản mẫu khóa cụ thể hơn trong bảng bên dưới.
Từ wikitext
{{#invoke:Protection banner|main | 1 = lý do | small = yes/no | action = hành động | date = ngày khóa | user = tên thành viên | section = tên đề mục trang thảo luận | category = no }}
Cú pháp #invoke có thể được sử dụng để tạo các bản mẫu khóa cụ thể hơn {{ khóa}}. Ví dụ: có thể tạo một bản mẫu khóa luôn hiển thị biểu tượng ổ khóa bằng cách sử dụng mã {{#invoke:Protection banner|main|small=yes}}
. Các trang gọi bản mẫu này vẫn có thể sử dụng các đối số khác, như action. Tuy nhiên, điều này chỉ hoạt động sâu một cấp; một trang gọi một bản mẫu gọi một bản mẫu khác chứa mã ở trên sẽ không thể tự động sử dụng các tham số như action.
Note: Bạn không còn có thể chỉ định hết hạn, vì nó được tự động truy xuất trong mọi trường hợp.
Từ Lua
Đầu tiên, gọi mô đun:
<syntaxhightlight lang="lua"> local mProtectionBanner = require('Module:Protection banner') </syntaxhightlight>
Sau đó, bạn có thể tạo các biểu ngữ bảo vệ bằng cách sử dụng chức năng _main.
<syntaxhightlight lang="lua"> mProtectionBanner._main(args, cfg, titleObj) </syntaxhightlight>
args là một bảng các đối số để truyền cho mô-đun. Để biết các khóa và giá trị có thể cho bảng này, hãy xem phần tham số. Biến cfg và titleObj chỉ dành cho thử nghiệm; cfg chỉ định một bảng cấu hình tùy chỉnh để sử dụng thay vì Module:Protection banner/config, and titleObj chỉ định một đối tượng mw.title để sử dụng thay vì tiêu đề hiện tại. args, cfg và titleObj đều là tùy chọn.
Tham số
Tất cả tham số đều là tùy chọn:
- 1 – Lý do mà trang được khóa. Nếu được đặt, đây phải là một trong các giá trị được liệt kê trong bảng lý do.
- small – Nếu đặt là "yes", "y", "1", hoặc "true", một biểu tượng ổ khóa được tạo ra thay vì biểu ngữ khóa đầy đủ.
- action – Các hành động khóa. Phải là một trong số các dạng sau đây: "edit" (đối với khóa thông thường), "move" (đối với khóa di chuyển). Giá trị mặc định là "edit".
- date – Ngày khóa. Đây phải là đầu vào hợp lệ cho tham số thứ hai của #time parser function. Đối số này có hiệu lực vì những lý do sử dụng tham số PROTECTIONDATE trong cấu hình của chúng. Đối số này chỉ có hiệu lực cho các lý do "office" và "reset".
- user – Tên thành viên của thành viên để tạo liên kết cho. Kể từ tháng 7 năm 2014, điều này chỉ có hiệu lực khi lý do "usertalk" được chỉ định.
- section – Tên phần của trang thảo luận của trang được khóa nơi diễn ra cuộc thảo luận. Tham số này hầu hết đều hoạt động, nhưng không phải tất cả, các giá trị của reason.
- category – Không thêm thể loại nếu tham số này được đặt thành "no", "n", "0", hoặc "false".
Lý do
Bảng sau đây chứa các lý do có sẵn, cộng với các hành động có sẵn.
Lý do | Hành động | Miêu tả |
---|---|---|
blp | edit | Đối với các trang được khóa để tuân thủ quy định về Tiểu sử người đang sống |
dispute | edit | Đối với các trang được khóa do tranh chấp sửa đổi |
dmca | edit | Đối với các trang được khóa bởi Wikimedia Foundation do yêu cầu gỡ xuống từ en:Digital Millennium Copyright Act |
ecp | edit | Đối với các bài viết trong lĩnh vực chủ đề được ủy quyền bởi ArbCom hoặc đáp ứng tiêu chí sử dụng của cộng đồng |
mainpage | edit | Đối với các trang được khóa để được hiển thị trên Trang chính |
office | edit | Đối với các trang được khóa bởi Wikimedia Foundation |
reset | edit | Đối với các trang được khóa bởi Wikimedia Foundation và "đặt lại" đến phiên bản thường |
sock | edit | Đối với các trang được khóa do rối phá hoại |
template | edit | Đối với các bản mẫu và mô-đun Lua nguy hiểm cao |
usertalk | edit | Đối với các trang được bảo vệ chống lại các sửa đổi gây rối bởi thành viên cụ thể |
vandalism | edit | Đối với các trang được khóa để ngăn chặn phá hoại |
dispute | move | Đối với các trang được khóa chống lại việc di chuyển trang do tranh chấp về tiêu đề trang |
vandalism | move | Đối với các trang được khóa chống lại phá hoại trang di chuyển |
Lỗi
Dưới đây là danh sách một số lỗi phổ biến mà mô-đun này có thể tạo ra và cách khắc phục chúng.
Sai ngày khóa trang
Lỗi: sai ngày khóa ("abc")
Lỗi này được tạo ra nếu bạn cung cấp một tham số |date=
mà giá trị của nó không được công nhận là ngày hợp lệ bởi chức năng phân tích cú pháp #time. Nếu nghi ngờ, bạn chỉ có thể sử dụng một ngày ở định dạng "dd Month YYYY", ví dụ "8 tháng 1 2025". Để xem đầy đủ các đầu vào hợp lệ, xem #time documentation (chỉ tham số đầu tiên, chuỗi định dạng, có thể được chỉ định).
Sai hành động
Lỗi: sai hành động ("abc")
Lỗi này được tạo ra nếu bạn chỉ định một hành động khóa không hợp lệ. Chỉ có ba hành động hợp lệ: edit
(mặc định, cho khóa thường) và move
(cho khóa di chuyển). Điều này chỉ có thể nếu bạn đang sử dụng một bản mẫu hỗ trợ chỉ định thủ công hành động khóa, chẳng hạn như {{Khóa}} hoặc nếu bạn đang sử dụng trực tiếp #invoke. Nếu đây không phải là trường hợp, vui lòng để lại tin nhắn trên Thảo luận Mô đun:Protection banner.
Lý do không thể chứa ký tự cây đứng
Lỗi: lý do không thể chứa ký tự cây đứng ("|")
Lỗi này được tạo ra nếu bạn chỉ định một lý do sử dụng tham số |1=
bao gồm ký tự cây đứng ("|"). Vui lòng kiểm tra xem bạn có đang nhập sai bản mẫu {{!}} vào tham số này không. Ký tự cây đứng không được phép vì mô-đun sử dụng nó trong nội bộ. Có thể xem danh sách các lý do hợp lệ trong phần lý do.
Một số lỗi khác
Nếu bạn thấy một lỗi khác ngoài các lỗi ở trên, đó có thể là lỗi trong mô-đun hoặc lỗi trong cấu hình. Xin vui lòng gửi một tin nhắn về nó tại Thảo luận Mô đun:Protection banner.
Thông tin kỹ thuật
Mô-đun này sử dụng dữ liệu cấu hình từ Module:Protection banner/config. Hầu hết các hành vi của mô-đun có thể được cấu hình ở đó, làm cho nó dễ dàng di chuyển qua các wiki khác nhau và các ngôn ngữ khác nhau.
Các trường hợp thử nghiệm chung cho mô-đun có thể được tìm thấy tại Module:Protection banner/testcases, và các trường hợp thử nghiệm cụ thể cho cấu hình của enwiki có thể được tìm thấy tại Module:Protection banner/config/testcases.
Báo cáo lỗi và yêu cầu tính năng nên được thực hiện trên the module's talk page.
-- This module implements {{pp-meta}} and its daughter templates such as
-- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.
-- Initialise necessary modules.
require('strict')
local makeFileLink = require('Mô đun:File link')._main
local effectiveProtectionLevel = require('Mô đun:Effective protection level')._main
local effectiveProtectionExpiry = require('Mô đun:Effective protection expiry')._main
local yesno = require('Mô đun:Yesno')
-- Lazily initialise modules and objects we don't always need.
local getArgs, makeMessageBox, lang
-- Set constants.
local CONFIG_MODULE = 'Mô đun:Protection banner/config'
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function makeCategoryLink(cat, sort)
if cat then
return string.format(
'[[%s:%s|%s]]',
mw.site.namespaces[14].name,
cat,
sort
)
end
end
-- Validation function for the expiry and the protection date
local function validateDate(dateString, dateType)
if not lang then
lang = mw.language.getContentLanguage()
end
local success, result = pcall(lang.formatDate, lang, 'U', dateString)
if success then
result = tonumber(result)
if result then
return result
end
end
error(string.format(
'sai %s: %s',
dateType,
tostring(dateString)
), 4)
end
local function makeFullUrl(page, query, display)
return string.format(
'[%s %s]',
tostring(mw.uri.fullUrl(page, query)),
display
)
end
-- Given a directed graph formatted as node -> table of direct successors,
-- get a table of all nodes reachable from a given node (though always
-- including the given node).
local function getReachableNodes(graph, start)
local toWalk, retval = {[start] = true}, {}
while true do
-- Can't use pairs() since we're adding and removing things as we're iterating
local k = next(toWalk) -- This always gets the "first" key
if k == nil then
return retval
end
toWalk[k] = nil
retval[k] = true
for _,v in ipairs(graph[k]) do
if not retval[v] then
toWalk[v] = true
end
end
end
end
--------------------------------------------------------------------------------
-- Protection class
--------------------------------------------------------------------------------
local Protection = {}
Protection.__index = Protection
Protection.supportedActions = {
edit = true,
move = true,
autoreview = true,
upload = true
}
Protection.bannerConfigFields = {
'text',
'explanation',
'tooltip',
'alt',
'link',
'image'
}
function Protection.new(args, cfg, title)
local obj = {}
obj._cfg = cfg
obj.title = title or mw.title.getCurrentTitle()
-- Set action
if not args.action then
obj.action = 'edit'
elseif Protection.supportedActions[args.action] then
obj.action = args.action
else
error(string.format(
'sai hành động: %s',
tostring(args.action)
), 3)
end
-- Set level
obj.level = args.demolevel or effectiveProtectionLevel(obj.action, obj.title)
if not obj.level or (obj.action == 'move' and obj.level == 'autoconfirmed') then
-- Users need to be autoconfirmed to move pages anyway, so treat
-- semi-move-protected pages as unprotected.
obj.level = '*'
end
-- Set expiry
local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title)
if effectiveExpiry == 'infinity' then
obj.expiry = 'indef'
elseif effectiveExpiry ~= 'unknown' then
obj.expiry = validateDate(effectiveExpiry, 'expiry date')
end
-- Set reason
if args[1] then
obj.reason = mw.ustring.lower(args[1])
if obj.reason:find('|') then
error('lý do không thể chứa ký tự cây đứng ("|")', 3)
end
end
-- Set protection date
if args.date then
obj.protectionDate = validateDate(args.date, 'protection date')
end
-- Set banner config
do
obj.bannerConfig = {}
local configTables = {}
if cfg.banners[obj.action] then
configTables[#configTables + 1] = cfg.banners[obj.action][obj.reason]
end
if cfg.defaultBanners[obj.action] then
configTables[#configTables + 1] = cfg.defaultBanners[obj.action][obj.level]
configTables[#configTables + 1] = cfg.defaultBanners[obj.action].default
end
configTables[#configTables + 1] = cfg.masterBanner
for i, field in ipairs(Protection.bannerConfigFields) do
for j, t in ipairs(configTables) do
if t[field] then
obj.bannerConfig[field] = t[field]
break
end
end
end
end
return setmetatable(obj, Protection)
end
function Protection:isProtected()
return self.level ~= '*'
end
function Protection:isTemporary()
return type(self.expiry) == 'number'
end
function Protection:makeProtectionCategory()
local cfg = self._cfg
local title = self.title
-- Exit if the page is not protected.
if not self:isProtected() then
return ''
end
-- Get the expiry key fragment.
local expiryFragment
if self.expiry == 'indef' then
expiryFragment = self.expiry
elseif type(self.expiry) == 'number' then
expiryFragment = 'temp'
end
-- Get the namespace key fragment.
local namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]
if not namespaceFragment and title.namespace % 2 == 1 then
namespaceFragment = 'talk'
end
-- Define the order that key fragments are tested in. This is done with an
-- array of tables containing the value to be tested, along with its
-- position in the cfg.protectionCategories table.
local order = {
{val = expiryFragment, keypos = 1},
{val = namespaceFragment, keypos = 2},
{val = self.reason, keypos = 3},
{val = self.level, keypos = 4},
{val = self.action, keypos = 5}
}
--[[
-- The old protection templates used an ad-hoc protection category system,
-- with some templates prioritising namespaces in their categories, and
-- others prioritising the protection reason. To emulate this in this module
-- we use the config table cfg.reasonsWithNamespacePriority to set the
-- reasons for which namespaces have priority over protection reason.
-- If we are dealing with one of those reasons, move the namespace table to
-- the end of the order table, i.e. give it highest priority. If not, the
-- reason should have highest priority, so move that to the end of the table
-- instead.
--]]
table.insert(order, table.remove(order, self.reason and cfg.reasonsWithNamespacePriority[self.reason] and 2 or 3))
--[[
-- Define the attempt order. Inactive subtables (subtables with nil "value"
-- fields) are moved to the end, where they will later be given the key
-- "all". This is to cut down on the number of table lookups in
-- cfg.protectionCategories, which grows exponentially with the number of
-- non-nil keys. We keep track of the number of active subtables with the
-- noActive parameter.
--]]
local noActive, attemptOrder
do
local active, inactive = {}, {}
for i, t in ipairs(order) do
if t.val then
active[#active + 1] = t
else
inactive[#inactive + 1] = t
end
end
noActive = #active
attemptOrder = active
for i, t in ipairs(inactive) do
attemptOrder[#attemptOrder + 1] = t
end
end
--[[
-- Check increasingly generic key combinations until we find a match. If a
-- specific category exists for the combination of key fragments we are
-- given, that match will be found first. If not, we keep trying different
-- key fragment combinations until we match using the key
-- "all-all-all-all-all".
--
-- To generate the keys, we index the key subtables using a binary matrix
-- with indexes i and j. j is only calculated up to the number of active
-- subtables. For example, if there were three active subtables, the matrix
-- would look like this, with 0 corresponding to the key fragment "all", and
-- 1 corresponding to other key fragments.
--
-- j 1 2 3
-- i
-- 1 1 1 1
-- 2 0 1 1
-- 3 1 0 1
-- 4 0 0 1
-- 5 1 1 0
-- 6 0 1 0
-- 7 1 0 0
-- 8 0 0 0
--
-- Values of j higher than the number of active subtables are set
-- to the string "all".
--
-- A key for cfg.protectionCategories is constructed for each value of i.
-- The position of the value in the key is determined by the keypos field in
-- each subtable.
--]]
local cats = cfg.protectionCategories
for i = 1, 2^noActive do
local key = {}
for j, t in ipairs(attemptOrder) do
if j > noActive then
key[t.keypos] = 'all'
else
local quotient = i / 2 ^ (j - 1)
quotient = math.ceil(quotient)
if quotient % 2 == 1 then
key[t.keypos] = t.val
else
key[t.keypos] = 'all'
end
end
end
key = table.concat(key, '|')
local attempt = cats[key]
if attempt then
return makeCategoryLink(attempt, title.text)
end
end
return ''
end
function Protection:isIncorrect()
local expiry = self.expiry
return not self:isProtected()
or type(expiry) == 'number' and expiry < os.time()
end
function Protection:isTemplateProtectedNonTemplate()
local action, namespace = self.action, self.title.namespace
return self.level == 'templateeditor'
and (
(action ~= 'edit' and action ~= 'move')
or (namespace ~= 10 and namespace ~= 828)
)
end
function Protection:makeCategoryLinks()
local msg = self._cfg.msg
local ret = { self:makeProtectionCategory() }
if self:isIncorrect() then
ret[#ret + 1] = makeCategoryLink(
msg['tracking-category-incorrect'],
self.title.text
)
end
if self:isTemplateProtectedNonTemplate() then
ret[#ret + 1] = makeCategoryLink(
msg['tracking-category-template'],
self.title.text
)
end
return table.concat(ret)
end
--------------------------------------------------------------------------------
-- Blurb class
--------------------------------------------------------------------------------
local Blurb = {}
Blurb.__index = Blurb
Blurb.bannerTextFields = {
text = true,
explanation = true,
tooltip = true,
alt = true,
link = true
}
function Blurb.new(protectionObj, args, cfg)
return setmetatable({
_cfg = cfg,
_protectionObj = protectionObj,
_args = args
}, Blurb)
end
-- Private methods --
function Blurb:_formatDate(num)
-- Formats a Unix timestamp into dd Month, YYYY format.
lang = lang or mw.language.getContentLanguage()
local success, date = pcall(
lang.formatDate,
lang,
self._cfg.msg['expiry-date-format'] or 'F j Y',
'@' .. tostring(num)
)
if success then
return date
end
end
function Blurb:_getExpandedMessage(msgKey)
return self:_substituteParameters(self._cfg.msg[msgKey])
end
function Blurb:_substituteParameters(msg)
if not self._params then
local parameterFuncs = {}
parameterFuncs.CURRENTVERSION = self._makeCurrentVersionParameter
parameterFuncs.EDITREQUEST = self._makeEditRequestParameter
parameterFuncs.EXPIRY = self._makeExpiryParameter
parameterFuncs.EXPLANATIONBLURB = self._makeExplanationBlurbParameter
parameterFuncs.IMAGELINK = self._makeImageLinkParameter
parameterFuncs.INTROBLURB = self._makeIntroBlurbParameter
parameterFuncs.INTROFRAGMENT = self._makeIntroFragmentParameter
parameterFuncs.PAGETYPE = self._makePagetypeParameter
parameterFuncs.PROTECTIONBLURB = self._makeProtectionBlurbParameter
parameterFuncs.PROTECTIONDATE = self._makeProtectionDateParameter
parameterFuncs.PROTECTIONLEVEL = self._makeProtectionLevelParameter
parameterFuncs.PROTECTIONLOG = self._makeProtectionLogParameter
parameterFuncs.TALKPAGE = self._makeTalkPageParameter
parameterFuncs.TOOLTIPBLURB = self._makeTooltipBlurbParameter
parameterFuncs.TOOLTIPFRAGMENT = self._makeTooltipFragmentParameter
parameterFuncs.VANDAL = self._makeVandalTemplateParameter
self._params = setmetatable({}, {
__index = function (t, k)
local param
if parameterFuncs[k] then
param = parameterFuncs[k](self)
end
param = param or ''
t[k] = param
return param
end
})
end
msg = msg:gsub('${(%u+)}', self._params)
return msg
end
function Blurb:_makeCurrentVersionParameter()
-- A link to the page history or the move log, depending on the kind of
-- protection.
local pagename = self._protectionObj.title.prefixedText
if self._protectionObj.action == 'move' then
-- We need the move log link.
return makeFullUrl(
'Special:Log',
{type = 'move', page = pagename},
self:_getExpandedMessage('current-version-move-display')
)
else
-- We need the history link.
return makeFullUrl(
pagename,
{action = 'history'},
self:_getExpandedMessage('current-version-edit-display')
)
end
end
function Blurb:_makeEditRequestParameter()
local mEditRequest = require('Module:Submit an edit request')
local action = self._protectionObj.action
local level = self._protectionObj.level
-- Get the edit request type.
local requestType
if action == 'edit' then
if level == 'autoconfirmed' then
requestType = 'semi'
elseif level == 'extendedconfirmed' then
requestType = 'extended'
elseif level == 'templateeditor' then
requestType = 'template'
end
end
requestType = requestType or 'full'
-- Get the display value.
local display = self:_getExpandedMessage('edit-request-display')
return mEditRequest._link{type = requestType, display = display}
end
function Blurb:_makeExpiryParameter()
local expiry = self._protectionObj.expiry
if type(expiry) == 'number' then
return self:_formatDate(expiry)
else
return expiry
end
end
function Blurb:_makeExplanationBlurbParameter()
-- Cover special cases first.
if self._protectionObj.title.namespace == 8 then
-- MediaWiki namespace
return self:_getExpandedMessage('explanation-blurb-nounprotect')
end
-- Get explanation blurb table keys
local action = self._protectionObj.action
local level = self._protectionObj.level
local talkKey = self._protectionObj.title.isTalkPage and 'talk' or 'subject'
-- Find the message in the explanation blurb table and substitute any
-- parameters.
local explanations = self._cfg.explanationBlurbs
local msg
if explanations[action][level] and explanations[action][level][talkKey] then
msg = explanations[action][level][talkKey]
elseif explanations[action][level] and explanations[action][level].default then
msg = explanations[action][level].default
elseif explanations[action].default and explanations[action].default[talkKey] then
msg = explanations[action].default[talkKey]
elseif explanations[action].default and explanations[action].default.default then
msg = explanations[action].default.default
else
error(string.format(
'không thể tìm thấy lời giải thích cho hành động "%s", cấp độ "%s" và key thảo luận "%s"',
action,
level,
talkKey
), 8)
end
return self:_substituteParameters(msg)
end
function Blurb:_makeImageLinkParameter()
local imageLinks = self._cfg.imageLinks
local action = self._protectionObj.action
local level = self._protectionObj.level
local msg
if imageLinks[action][level] then
msg = imageLinks[action][level]
elseif imageLinks[action].default then
msg = imageLinks[action].default
else
msg = imageLinks.edit.default
end
return self:_substituteParameters(msg)
end
function Blurb:_makeIntroBlurbParameter()
if self._protectionObj:isTemporary() then
return self:_getExpandedMessage('intro-blurb-expiry')
else
return self:_getExpandedMessage('intro-blurb-noexpiry')
end
end
function Blurb:_makeIntroFragmentParameter()
if self._protectionObj:isTemporary() then
return self:_getExpandedMessage('intro-fragment-expiry')
else
return self:_getExpandedMessage('intro-fragment-noexpiry')
end
end
function Blurb:_makePagetypeParameter()
local pagetypes = self._cfg.pagetypes
return pagetypes[self._protectionObj.title.namespace]
or pagetypes.default
or error('không có kiểu trang mặc định được định nghĩa', 8)
end
function Blurb:_makeProtectionBlurbParameter()
local protectionBlurbs = self._cfg.protectionBlurbs
local action = self._protectionObj.action
local level = self._protectionObj.level
local msg
if protectionBlurbs[action][level] then
msg = protectionBlurbs[action][level]
elseif protectionBlurbs[action].default then
msg = protectionBlurbs[action].default
elseif protectionBlurbs.edit.default then
msg = protectionBlurbs.edit.default
else
error('không có blurb khóa được định nghĩa cho protectionBlurbs.edit.default', 8)
end
return self:_substituteParameters(msg)
end
function Blurb:_makeProtectionDateParameter()
local protectionDate = self._protectionObj.protectionDate
if type(protectionDate) == 'number' then
return self:_formatDate(protectionDate)
else
return protectionDate
end
end
function Blurb:_makeProtectionLevelParameter()
local protectionLevels = self._cfg.protectionLevels
local action = self._protectionObj.action
local level = self._protectionObj.level
local msg
if protectionLevels[action][level] then
msg = protectionLevels[action][level]
elseif protectionLevels[action].default then
msg = protectionLevels[action].default
elseif protectionLevels.edit.default then
msg = protectionLevels.edit.default
else
error('không có cấp độ khóa được định nghĩa cho protectionLevels.edit.default', 8)
end
return self:_substituteParameters(msg)
end
function Blurb:_makeProtectionLogParameter()
local pagename = self._protectionObj.title.prefixedText
if self._protectionObj.action == 'autoreview' then
-- We need the pending changes log.
return makeFullUrl(
'Đặc biệt:Nhật trình',
{type = 'stable', page = pagename},
self:_getExpandedMessage('pc-log-display')
)
else
-- We need the protection log.
return makeFullUrl(
'Đặc biệt:Nhật trình',
{type = 'protect', page = pagename},
self:_getExpandedMessage('protection-log-display')
)
end
end
function Blurb:_makeTalkPageParameter()
return string.format(
'[[%s:%s#%s|%s]]',
mw.site.namespaces[self._protectionObj.title.namespace].talk.name,
self._protectionObj.title.text,
self._args.section or 'top',
self:_getExpandedMessage('talk-page-link-display')
)
end
function Blurb:_makeTooltipBlurbParameter()
if self._protectionObj:isTemporary() then
return self:_getExpandedMessage('tooltip-blurb-expiry')
else
return self:_getExpandedMessage('tooltip-blurb-noexpiry')
end
end
function Blurb:_makeTooltipFragmentParameter()
if self._protectionObj:isTemporary() then
return self:_getExpandedMessage('tooltip-fragment-expiry')
else
return self:_getExpandedMessage('tooltip-fragment-noexpiry')
end
end
function Blurb:_makeVandalTemplateParameter()
return require('Mô đun:Vandal-m')._main{
self._args.user or self._protectionObj.title.baseText
}
end
-- Public methods --
function Blurb:makeBannerText(key)
-- Validate input.
if not key or not Blurb.bannerTextFields[key] then
error(string.format(
'"%s" không phải là trường cấu hình biểu ngữ hợp lệ',
tostring(key)
), 2)
end
-- Generate the text.
local msg = self._protectionObj.bannerConfig[key]
if type(msg) == 'string' then
return self:_substituteParameters(msg)
elseif type(msg) == 'function' then
msg = msg(self._protectionObj, self._args)
if type(msg) ~= 'string' then
error(string.format(
'đầu ra xấu từ chức năng cấu hình banner với key "%s"'
.. ' (chuỗi thiếu, có %s)',
tostring(key),
type(msg)
), 4)
end
return self:_substituteParameters(msg)
end
end
--------------------------------------------------------------------------------
-- BannerTemplate class
--------------------------------------------------------------------------------
local BannerTemplate = {}
BannerTemplate.__index = BannerTemplate
function BannerTemplate.new(protectionObj, cfg)
local obj = {}
obj._cfg = cfg
-- Set the image filename.
local imageFilename = protectionObj.bannerConfig.image
if imageFilename then
obj._imageFilename = imageFilename
else
-- If an image filename isn't specified explicitly in the banner config,
-- generate it from the protection status and the namespace.
local action = protectionObj.action
local level = protectionObj.level
local namespace = protectionObj.title.namespace
local reason = protectionObj.reason
-- Deal with special cases first.
if (
namespace == 10
or namespace == 828
or reason and obj._cfg.indefImageReasons[reason]
)
and action == 'edit'
and level == 'sysop'
and not protectionObj:isTemporary()
then
-- Fully protected modules and templates get the special red "indef"
-- padlock.
obj._imageFilename = obj._cfg.msg['image-filename-indef']
else
-- Deal with regular protection types.
local images = obj._cfg.images
if images[action] then
if images[action][level] then
obj._imageFilename = images[action][level]
elseif images[action].default then
obj._imageFilename = images[action].default
end
end
end
end
return setmetatable(obj, BannerTemplate)
end
function BannerTemplate:renderImage()
local filename = self._imageFilename
or self._cfg.msg['image-filename-default']
or 'Transparent.gif'
return makeFileLink{
file = filename,
size = (self.imageWidth or 20) .. 'px',
alt = self._imageAlt,
link = self._imageLink,
caption = self.imageCaption
}
end
--------------------------------------------------------------------------------
-- Banner class
--------------------------------------------------------------------------------
local Banner = setmetatable({}, BannerTemplate)
Banner.__index = Banner
function Banner.new(protectionObj, blurbObj, cfg)
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.
obj.imageWidth = 40
obj.imageCaption = blurbObj:makeBannerText('alt') -- Large banners use the alt text for the tooltip.
obj._reasonText = blurbObj:makeBannerText('text')
obj._explanationText = blurbObj:makeBannerText('explanation')
obj._page = protectionObj.title.prefixedText -- Only makes a difference in testing.
return setmetatable(obj, Banner)
end
function Banner:__tostring()
-- Renders the banner.
makeMessageBox = makeMessageBox or require('Module:Message box').main
local reasonText = self._reasonText or error('không có đặt văn bản lý do', 2)
local explanationText = self._explanationText
local mbargs = {
page = self._page,
type = 'protection',
image = self:renderImage(),
text = string.format(
"'''%s'''%s",
reasonText,
explanationText and '<br />' .. explanationText or ''
)
}
return makeMessageBox('mbox', mbargs)
end
--------------------------------------------------------------------------------
-- Padlock class
--------------------------------------------------------------------------------
local Padlock = setmetatable({}, BannerTemplate)
Padlock.__index = Padlock
function Padlock.new(protectionObj, blurbObj, cfg)
local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.
obj.imageWidth = 20
obj.imageCaption = blurbObj:makeBannerText('tooltip')
obj._imageAlt = blurbObj:makeBannerText('alt')
obj._imageLink = blurbObj:makeBannerText('link')
obj._indicatorName = cfg.padlockIndicatorNames[protectionObj.action]
or cfg.padlockIndicatorNames.default
or 'pp-default'
return setmetatable(obj, Padlock)
end
function Padlock:__tostring()
local frame = mw.getCurrentFrame()
-- The nowiki tag helps prevent whitespace at the top of articles.
return frame:extensionTag{name = 'nowiki'} .. frame:extensionTag{
name = 'indicator',
args = {name = self._indicatorName},
content = self:renderImage()
}
end
--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------
local p = {}
function p._exportClasses()
-- This is used for testing purposes.
return {
Protection = Protection,
Blurb = Blurb,
BannerTemplate = BannerTemplate,
Banner = Banner,
Padlock = Padlock,
}
end
function p._main(args, cfg, title)
args = args or {}
cfg = cfg or require(CONFIG_MODULE)
local protectionObj = Protection.new(args, cfg, title)
local ret = {}
-- If a page's edit protection is equally or more restrictive than its
-- protection from some other action, then don't bother displaying anything
-- for the other action (except categories).
if protectionObj.action == 'edit' or
args.demolevel or
not getReachableNodes(
cfg.hierarchy,
protectionObj.level
)[effectiveProtectionLevel('edit', protectionObj.title)]
then
-- Initialise the blurb object
local blurbObj = Blurb.new(protectionObj, args, cfg)
-- Render the banner
if protectionObj:isProtected() then
ret[#ret + 1] = tostring(
(yesno(args.small) and Padlock or Banner)
.new(protectionObj, blurbObj, cfg)
)
end
end
-- Render the categories
if yesno(args.category) ~= false then
ret[#ret + 1] = protectionObj:makeCategoryLinks()
end
return table.concat(ret)
end
function p.main(frame, cfg)
cfg = cfg or require(CONFIG_MODULE)
-- Find default args, if any.
local parent = frame.getParent and frame:getParent()
local defaultArgs = parent and cfg.wrappers[parent:getTitle():gsub('/sandbox$', '')]
-- Find user args, and use the parent frame if we are being called from a
-- wrapper template.
getArgs = getArgs or require('Mô đun:Arguments').getArgs
local userArgs = getArgs(frame, {
parentOnly = defaultArgs,
frameOnly = not defaultArgs
})
-- Build the args table. User-specified args overwrite default args.
local args = {}
for k, v in pairs(defaultArgs or {}) do
args[k] = v
end
for k, v in pairs(userArgs) do
args[k] = v
end
return p._main(args, cfg)
end
return p