Kamis, 29 Oktober 2026
05:23 WIB
TERKINI
Lampung Barat Pesan Bupati Lambar: Patriotik, Gigih, dan Berempati Kunci Masa Depan Bangsa Bandar Lampung 343 Polisi Disiagakan Kawal Aksi Sumpah Pemuda 2026 di Bandar Lampung Lampung Selatan Jaringan Penyelundup Solar Subsidi Terbongkar di Lampung Selatan, Tiga Tersangka Diamankan Bandar Lampung Perpanjangan SIM A/C Kini Lebih Praktis: Cek Jadwal dan Syarat di Bandar Lampung Lampung Selatan Gunung Anak Krakatau: Asap Hitam Viral Bukan Erupsi, Status Tetap Siaga Lampung Selatan Jaringan Penyelundup Rokok Ilegal Senilai Rp14,85 Miliar Terbongkar di Bakauheni Bandar Lampung Harga Singkong Lampung Meroket, Petani Nikmati Keuntungan Rp45 Juta per Hektare Bandar Lampung BSU 2026 Segera Disalurkan: Rp900 Ribu dan Analisis Pakar Terkait Tantangan Pekerja Bandar Lampung Optimisme Harga Singkong Lampung 2027: Bibit dan Pupuk Kunci Utama Bandar Lampung Krisis Asap dan Ribuan Titik Panas: WALHI Desak Audit Ekologis TNWK Lampung Barat Pesan Bupati Lambar: Patriotik, Gigih, dan Berempati Kunci Masa Depan Bangsa Bandar Lampung 343 Polisi Disiagakan Kawal Aksi Sumpah Pemuda 2026 di Bandar Lampung Lampung Selatan Jaringan Penyelundup Solar Subsidi Terbongkar di Lampung Selatan, Tiga Tersangka Diamankan Bandar Lampung Perpanjangan SIM A/C Kini Lebih Praktis: Cek Jadwal dan Syarat di Bandar Lampung Lampung Selatan Gunung Anak Krakatau: Asap Hitam Viral Bukan Erupsi, Status Tetap Siaga Lampung Selatan Jaringan Penyelundup Rokok Ilegal Senilai Rp14,85 Miliar Terbongkar di Bakauheni Bandar Lampung Harga Singkong Lampung Meroket, Petani Nikmati Keuntungan Rp45 Juta per Hektare Bandar Lampung BSU 2026 Segera Disalurkan: Rp900 Ribu dan Analisis Pakar Terkait Tantangan Pekerja Bandar Lampung Optimisme Harga Singkong Lampung 2027: Bibit dan Pupuk Kunci Utama Bandar Lampung Krisis Asap dan Ribuan Titik Panas: WALHI Desak Audit Ekologis TNWK

Modul:Article history

Wikimedia module

Bagikan:
Dokumentasi modullihatsuntingriwayatsegarkan
DiproteksiModul ini dilindungi. Modul ini sangat mencolok yang digunakan oleh banyak halaman, atau sangat sering disubstitusikan. Karena vandalisme atau kesalahan akan mempengaruhi banyak halaman, dan suntingan kecil dapat memberi beban besar pada server, modul ini dilindungi dari penyuntingan.
WarningModul Lua ini digunakan pada banyak halaman dan perubahannya kemungkinan memicu perubahan massal pada semua halaman yang menggunakannya. Uji cobalah di subhalaman /bak pasir atau /kasus uji Modul:Article history, atau bak pasir modul Anda. Pertimbangkan untuk mendiskusikan perubahan di halaman pembicaraan sebelum mengimplementasikannya.
Modul ini menggunakan Lua:
CSSModul ini menggunakan TemplateStyles:

Modul ini mengimplementasikan Templat:Article history (sunting | bicara | riwayat | pranala | pantau | log). Silakan lihat halaman templat untuk dokumentasi mengenai cara menggunakannya.

Detail teknisDetail teknis

Modul mempunyai sebuah konfigurasi di Modul:Article history/config, yang dapat digunakan untuk menerjemahkan/memodifikasi templat untuk penggunaan di wiki lainnya. Lihat modul konfigurasi untuk petunjuk mengenai cara memodifikasinya. Konfigurasi untuk Wikipedia bahasa Inggris sangat rumit, namun modul ini juga dapat digunakan dengan cara yang jauh lebih sederhana. Jika Anda ingin menggunakan fitur-fitur lebih lanjut yang disediakan oleh modul ini, anda dapat merujuk pada dokumentasi kelas di bawah ini.

Terdapat juga kelas Kategori di Modul:Article history/Category, yang dapat digunakan di modul konfigurasi dan utama.

Dokumentasi di atas ditransklusikan dari Modul:Article history/doc. (sunting | riwayat)
Penyunting dapat melakukan uji coba pada halaman bak pasir (buat | cermin) dan kasus uji (buat) modul ini.
Subhalaman modul ini.
-------------------------------------------------------------------------------
--                            Article history
--
-- This module allows editors to link to all the significant events in an
-- article's history, such as good article nominations and featured article
-- nominations. It also displays its current status, as well as other
-- information, such as the date it was featured on the main page.
-------------------------------------------------------------------------------
localCONFIG_PAGE='Modul:Article history/config'
localWRAPPER_TEMPLATE='Templat:Article history'
localDEBUG_MODE=false-- If true, errors are not caught.
-- Load required modules.
require('strict')
localCategory=require('Modul:Article history/Category')
localyesno=require('Modul:Yesno')
locallang=mw.language.getContentLanguage()
-------------------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------------------
localfunctionisPositiveInteger(num)
returntype(num)=='number'
andmath.floor(num)==num
andnum>0
andnum<math.huge
end
localfunctionsubstituteParams(msg,...)
returnmw.message.newRawMessage(msg,...):plain()
end
localfunctionmakeUrlLink(url,display)
returnstring.format('[%s %s]',url,display)
end
localfunctionmaybeCallFunc(val,...)
-- Checks whether val is a function, and if so calls it with the specified
-- arguments. Otherwise val is returned as-is.
iftype(val)=='function'then
returnval(...)
else
returnval
end
end
localfunctionrenderImage(image,caption,size)
ifcaptionthen
caption='|'..caption
else
caption=''
end
returnstring.format('[[Berkas:%s|%s%s]]',image,size,caption)
end
localfunctionaddMixin(class,mixin)
-- Add a mixin to a class. The functions will be shared across classes, so
-- don't use it for functions that keep state.
forname,methodinpairs(mixin)do
class[name]=method
end
end
-------------------------------------------------------------------------------
-- Message mixin
-- This mixin is used by all classes to add message-related methods.
-------------------------------------------------------------------------------
localMessage={}
functionMessage:message(key,...)
-- This fetches the message from the config with the specified key, and
-- substitutes parameters $1, $2 etc. with the subsequent values it is
-- passed.
localmsg=self.cfg.msg[key]
ifselect('#',...)>0then
returnsubstituteParams(msg,...)
else
returnmsg
end
end
functionMessage:raiseError(msg,help)
-- Raises an error with the specified message and help link. Execution
-- stops unless the error is caught. This is used for errors where
-- subsequent processing becomes impossible.
localerrorText
ifhelpthen
errorText=self:message('error-message-help',msg,help)
else
errorText=self:message('error-message-nohelp',msg)
end
error(errorText,0)
end
functionMessage:addWarning(msg,help)
-- Adds a warning to the object's warnings table. Execution continues as
-- normal. This is used for errors that should be fixed but that do not
-- prevent the module from outputting something useful.
self.warnings=self.warningsor{}
localwarningText
ifhelpthen
warningText=self:message('warning-help',msg,help)
else
warningText=self:message('warning-nohelp',msg)
end
table.insert(self.warnings,warningText)
end
functionMessage:getWarnings()
returnself.warningsor{}
end
-------------------------------------------------------------------------------
-- Row class
-- This class represents one row in the template.
-------------------------------------------------------------------------------
localRow={}
Row.__index=Row
addMixin(Row,Message)
functionRow.new(data)
localobj=setmetatable({},Row)
obj.cfg=data.cfg
obj.currentTitle=data.currentTitle
obj.makeData=data.makeData-- used by Row:getData
returnobj
end
functionRow:_cachedTry(cacheKey,errorCacheKey,func)
-- This method is for use in Row object methods that are called more than
-- once. The results of such methods should be cached to avoid unnecessary
-- processing. We also cache any errors found and abort if an error was
-- raised previously, otherwise error messages could be displayed multiple
-- times.
--
-- We use false as a key to cache nil results, so func cannot return false.
--
-- @param cacheKey The key to cache successful results with
-- @param errorCacheKey The key to cache errors with
-- @param func an anonymous function that returns the method result
ifself[errorCacheKey]then
returnnil
end
localret=self[cacheKey]
ifretthen
returnret
elseifret==falsethen
returnnil
end
localsuccess
ifDEBUG_MODEthen
success=true
ret=func()
else
success,ret=pcall(func)
end
ifsuccessthen
ifretthen
self[cacheKey]=ret
returnret
else
self[cacheKey]=false
returnnil
end
else
self[errorCacheKey]=true
-- We have already formatted the error message, so no need to format it
-- again.
error(ret,0)
end
end
functionRow:getData(articleHistoryObj)
returnself:_cachedTry('_dataCache','_isDataError',function()
returnself.makeData(articleHistoryObj)
end)
end
functionRow:setIconValues(icon,caption,size)
self.icon=icon
self.iconCaption=caption
self.iconSize=size
end
functionRow:getIcon(articleHistoryObj)
returnmaybeCallFunc(self.icon,articleHistoryObj,self)
end
functionRow:getIconCaption(articleHistoryObj)
returnmaybeCallFunc(self.iconCaption,articleHistoryObj,self)
end
functionRow:getIconSize()
returnself.iconSizeorself.cfg.defaultIconSizeor'30px'
end
functionRow:renderIcon(articleHistoryObj)
localicon=self:getIcon(articleHistoryObj)
ifnoticonthen
returnnil
end
returnrenderImage(
icon,
self:getIconCaption(articleHistoryObj),
self:getIconSize()
)
end
functionRow:setNoticeBarIconValues(icon,caption,size)
self.noticeBarIcon=icon
self.noticeBarIconCaption=caption
self.noticeBarIconSize=size
end
functionRow:getNoticeBarIcon(articleHistoryObj)
localicon=maybeCallFunc(self.noticeBarIcon,articleHistoryObj,self)
ificon==truethen
icon=self:getIcon(articleHistoryObj)
ifnoticonthen
self:raiseError(
self:message('row-error-missing-icon'),
self:message('row-error-missing-icon-help')
)
end
end
returnicon
end
functionRow:getNoticeBarIconCaption(articleHistoryObj)
localcaption=maybeCallFunc(
self.noticeBarIconCaption,
articleHistoryObj,
self
)
ifnotcaptionthen
caption=self:getIconCaption(articleHistoryObj)
end
returncaption
end
functionRow:getNoticeBarIconSize()
returnself.noticeBarIconSizeorself.cfg.defaultNoticeBarIconSizeor'15px'
end
functionRow:exportNoticeBarIcon(articleHistoryObj)
localicon=self:getNoticeBarIcon(articleHistoryObj)
ifnoticonthen
returnnil
end
returnrenderImage(
icon,
self:getNoticeBarIconCaption(articleHistoryObj),
self:getNoticeBarIconSize()
)
end
functionRow:setText(text)
self.text=text
end
functionRow:getText(articleHistoryObj)
returnmaybeCallFunc(self.text,articleHistoryObj,self)
end
functionRow:exportHtml(articleHistoryObj)
ifself._htmlthen
returnself._html
end
localtext=self:getText(articleHistoryObj)
ifnottextthen
returnnil
end
localhtml=mw.html.create('tr')
html
:tag('td')
:addClass('mbox-image')
:wikitext(self:renderIcon(articleHistoryObj))
:done()
:tag('td')
:addClass('mbox-text')
:wikitext(text)
self._html=html
returnhtml
end
functionRow:setCategories(val)
-- Set the categories from the object's config. val can be either an array
-- of strings or a function returning an array of category objects.
self.categories=val
end
functionRow:getCategories(articleHistoryObj)
localret={}
iftype(self.categories)=='table'then
for_,catinipairs(self.categories)do
ret[#ret+1]=Category.new(cat)
end
elseiftype(self.categories)=='function'then
localt=self.categories(articleHistoryObj,self)or{}
for_,categoryObjinipairs(t)do
ret[#ret+1]=categoryObj
end
end
returnret
end
-------------------------------------------------------------------------------
-- Status class
-- Status objects deal with possible current statuses of the article.
-------------------------------------------------------------------------------
localStatus=setmetatable({},Row)
Status.__index=Status
functionStatus.new(data)
localobj=Row.new(data)
setmetatable(obj,Status)
obj.id=data.id
obj.statusCfg=obj.cfg.statuses[obj.id]
obj.name=obj.statusCfg.name
obj:setIconValues(
obj.statusCfg.icon,
obj.statusCfg.iconCaptionorobj.name,
data.iconSize
)
obj:setNoticeBarIconValues(
obj.statusCfg.noticeBarIcon,
obj.statusCfg.noticeBarIconCaptionorobj.name,
obj.statusCfg.noticeBarIconSize
)
obj:setText(obj.statusCfg.text)
obj:setCategories(obj.statusCfg.categories)
returnobj
end
functionStatus:getIconSize()
returnself.iconSize
orself.statusCfg.iconSize
orself.cfg.defaultStatusIconSize
or'50px'
end
functionStatus:getText(articleHistoryObj)
localtext=Row.getText(self,articleHistoryObj)
iftextthen
returnsubstituteParams(
text,
self.currentTitle.subjectPageTitle.prefixedText,
self.currentTitle.text
)
end
end
-------------------------------------------------------------------------------
-- MultiStatus class
-- For when an article can have multiple distinct statuses, e.g. former
-- featured article status and good article status.
-------------------------------------------------------------------------------
localMultiStatus=setmetatable({},Row)
MultiStatus.__index=MultiStatus
functionMultiStatus.new(data)
localobj=Row.new(data)
setmetatable(obj,MultiStatus)
obj.id=data.id
obj.statusCfg=obj.cfg.statuses[data.id]
obj.name=obj.statusCfg.name
-- Set child status objects
localfunctiongetChildStatusData(data,id,iconSize)
localret={}
fork,vinpairs(data)do
ret[k]=v
end
ret.id=id
ret.iconSize=iconSize
returnret
end
obj.statuses={}
localdefaultIconSize=obj.cfg.defaultMultiStatusIconSizeor'30px'
for_,idinipairs(obj.statusCfg.statuses)do
table.insert(obj.statuses,Status.new(getChildStatusData(
data,
id,
obj.cfg.statuses[id].iconMultiSizeordefaultIconSize
)))
end
returnobj
end
functionMultiStatus:exportHtml(articleHistoryObj)
localret=mw.html.create()
for_,objinipairs(self.statuses)do
ret:node(obj:exportHtml(articleHistoryObj))
end
returnret
end
functionMultiStatus:getCategories(articleHistoryObj)
localret={}
for_,objinipairs(self.statuses)do
for_,categoryObjinipairs(obj:getCategories(articleHistoryObj))do
ret[#ret+1]=categoryObj
end
end
returnret
end
functionMultiStatus:exportNoticeBarIcon()
localret={}
for_,objinipairs(self.statuses)do
ret[#ret+1]=obj:exportNoticeBarIcon()
end
returntable.concat(ret)
end
functionMultiStatus:getWarnings()
localret={}
for_,objinipairs(self.statuses)do
for_,msginipairs(obj:getWarnings())do
ret[#ret+1]=msg
end
end
returnret
end
-------------------------------------------------------------------------------
-- Notice class
-- Notice objects contain notices about an article that aren't part of its
-- current status, e.g. the date an article was featured on the main page.
-------------------------------------------------------------------------------
localNotice=setmetatable({},Row)
Notice.__index=Notice
functionNotice.new(data)
localobj=Row.new(data)
setmetatable(obj,Notice)
obj:setIconValues(
data.icon,
data.iconCaption,
data.iconSize
)
obj:setNoticeBarIconValues(
data.noticeBarIcon,
data.noticeBarIconCaption,
data.noticeBarIconSize
)
obj:setText(data.text)
obj:setCategories(data.categories)
returnobj
end
-------------------------------------------------------------------------------
-- Action class
-- Action objects deal with a single action in the history of the article. We
-- use getter methods rather than properties for the name and result, etc., as
-- their processing needs to be delayed until after the status object has been
-- initialised. The status object needs to parse the action objects when it is
-- initialised, and the value of some names, etc., in the action objects depend
-- on the status object, so this is necessary to avoid errors/infinite loops.
-------------------------------------------------------------------------------
localAction=setmetatable({},Row)
Action.__index=Action
functionAction.new(data)
localobj=Row.new(data)
setmetatable(obj,Action)
obj.paramNum=data.paramNum
-- Set the ID
do
ifnotdata.codethen
obj:raiseError(
obj:message('action-error-no-code',obj:getParameter('code')),
obj:message('action-error-no-code-help')
)
end
localcode=mw.ustring.upper(data.code)
obj.id=obj.cfg.actions[code]andobj.cfg.actions[code].id
ifnotobj.idthen
obj:raiseError(
obj:message(
'action-error-invalid-code',
data.code,
obj:getParameter('code')
),
obj:message('action-error-invalid-code-help')
)
end
end
-- Add a shortcut for this action's config.
obj.actionCfg=obj.cfg.actions[obj.id]
-- Set the link
obj.link=data.linkorobj.currentTitle.talkPageTitle.prefixedText
-- Set the result ID
do
localresultCode=data.resultCode
andmw.ustring.lower(data.resultCode)
or'_BLANK'
ifobj.actionCfg.results[resultCode]then
obj.resultId=obj.actionCfg.results[resultCode].id
elseifresultCode=='_BLANK'then
obj:raiseError(
obj:message(
'action-error-blank-result',
obj.id,
obj:getParameter('resultCode')
),
obj:message('action-error-blank-result-help')
)
else
obj:raiseError(
obj:message(
'action-error-invalid-result',
data.resultCode,
obj.id,
obj:getParameter('resultCode')
),
obj:message('action-error-invalid-result-help')
)
end
end
-- Set the date
ifdata.datethen
localsuccess,date=pcall(
lang.formatDate,
lang,
obj:message('action-date-format'),
data.date
)
ifsuccessanddatethen
obj.date=date
else
obj:addWarning(
obj:message(
'action-warning-invalid-date',
data.date,
obj:getParameter('date')
),
obj:message('action-warning-invalid-date-help')
)
end
else
obj:addWarning(
obj:message(
'action-warning-no-date',
obj.paramNum,
obj:getParameter('date'),
obj:getParameter('code')
),
obj:message('action-warning-no-date-help')
)
end
obj.date=obj.dateorobj:message('action-date-missing')
-- Set the oldid
obj.oldid=tonumber(data.oldid)
ifdata.oldidand(notobj.oldidornotisPositiveInteger(obj.oldid))then
obj.oldid=nil
obj:addWarning(
obj:message(
'action-warning-invalid-oldid',
data.oldid,
obj:getParameter('oldid')
),
obj:message('action-warning-invalid-oldid-help')
)
end
-- Set the notice bar icon values
obj:setNoticeBarIconValues(
data.noticeBarIcon,
data.noticeBarIconCaption,
data.noticeBarIconSize
)
-- Set the categories
obj:setCategories(obj.actionCfg.categories)
returnobj
end
functionAction:getParameter(key)
-- Finds the original parameter name for the given key that was passed to
-- Action.new.
localprefix=self.cfg.actionParamPrefix
localsuffix
fork,vinpairs(self.cfg.actionParamSuffixes)do
ifv==keythen
suffix=k
break
end
end
ifnotsuffixthen
error('invalid key "'..tostring(key)..'" passed to Action:getParameter',2)
end
returnprefix..tostring(self.paramNum)..suffix
end
functionAction:getName(articleHistoryObj)
returnmaybeCallFunc(self.actionCfg.name,articleHistoryObj,self)
end
functionAction:getResult(articleHistoryObj)
returnmaybeCallFunc(
self.actionCfg.results[self.resultId].text,
articleHistoryObj,
self
)
end
functionAction:exportHtml(articleHistoryObj)
ifself._htmlthen
returnself._html
end
localrow=mw.html.create('tr')
-- Date cell
localdateCell=row:tag('td')
ifself.oldidthen
dateCell
:tag('span')
:addClass('plainlinks')
:wikitext(makeUrlLink(
self.currentTitle.subjectPageTitle:fullUrl{oldid=self.oldid},
self.date
))
else
dateCell:wikitext(self.date)
end
-- Process cell
row
:tag('td')
:wikitext(string.format(
"'''[[%s|%s]]'''",
self.link,
self:getName(articleHistoryObj)
))
-- Result cell
row
:tag('td')
:wikitext(self:getResult(articleHistoryObj))
self._html=row
returnrow
end
-------------------------------------------------------------------------------
-- CollapsibleNotice class
-- This class makes notices that go in the collapsible part of the template,
-- underneath the list of actions.
-------------------------------------------------------------------------------
localCollapsibleNotice=setmetatable({},Row)
CollapsibleNotice.__index=CollapsibleNotice
functionCollapsibleNotice.new(data)
localobj=Row.new(data)
setmetatable(obj,CollapsibleNotice)
obj:setIconValues(
data.icon,
data.iconCaption,
data.iconSize
)
obj:setNoticeBarIconValues(
data.noticeBarIcon,
data.noticeBarIconCaption,
data.noticeBarIconSize
)
obj:setText(data.text)
obj:setCollapsibleText(data.collapsibleText)
obj:setCategories(data.categories)
returnobj
end
functionCollapsibleNotice:setCollapsibleText(s)
self.collapsibleText=s
end
functionCollapsibleNotice:getCollapsibleText(articleHistoryObj)
returnmaybeCallFunc(self.collapsibleText,articleHistoryObj,self)
end
functionCollapsibleNotice:getIconSize()
returnself.iconSize
orself.cfg.defaultCollapsibleNoticeIconSize
or'20px'
end
functionCollapsibleNotice:exportHtml(articleHistoryObj,isInCollapsibleTable)
localcacheKey=isInCollapsibleTable
and'_htmlCacheCollapsible'
or'_htmlCacheDefault'
returnself:_cachedTry(cacheKey,'_isHtmlError',function()
localtext=self:getText(articleHistoryObj)
ifnottextthen
returnnil
end
localfunctionmaybeMakeCollapsibleTable(cell,text,collapsibleText)
-- If collapsible text is specified, makes a collapsible table
-- inside the cell with two rows, a header row with one cell and a
-- collapsed row with one cell. These are filled with text and
-- collapsedText, respectively. If no collapsible text is
-- specified, the text is added to the cell as-is.
ifcollapsibleTextthen
cell
:tag('div')
:addClass('mw-collapsible mw-collapsed')
:tag('div')
:wikitext(text)
:done()
:tag('div')
:addClass('mw-collapsible-content')
:css('border','1px silver solid')
:wikitext(collapsibleText)
else
cell:wikitext(text)
end
end
localhtml=mw.html.create('tr')
localicon=self:renderIcon(articleHistoryObj)
localcollapsibleText=self:getCollapsibleText(articleHistoryObj)
ifisInCollapsibleTablethen
localtextCell=html:tag('td')
:attr('colspan',3)
:css('width','100%')
localrowText
ificonthen
rowText=icon..' '..text
else
rowText=text
end
maybeMakeCollapsibleTable(textCell,rowText,collapsibleText)
else
localtextCell=html
:tag('td')
:addClass('mbox-image')
:wikitext(icon)
:done()
:tag('td')
:addClass('mbox-text')
maybeMakeCollapsibleTable(textCell,text,collapsibleText)
end
returnhtml
end)
end
-------------------------------------------------------------------------------
-- ArticleHistory class
-- This class represents the whole template.
-------------------------------------------------------------------------------
localArticleHistory={}
ArticleHistory.__index=ArticleHistory
addMixin(ArticleHistory,Message)
functionArticleHistory.new(args,cfg,currentTitle)
localobj=setmetatable({},ArticleHistory)
-- Set input
obj.args=argsor{}
obj.currentTitle=currentTitleormw.title.getCurrentTitle()
-- Define object structure.
obj._errors={}
obj._allObjectsCache={}
-- Format the config
localfunctionsubstituteAliases(t,ret)
-- This function substitutes strings found in an "aliases" subtable
-- as keys in the parent table. It works recursively, so "aliases"
-- subtables can be placed at any level. It assumes that tables will
-- not be nested recursively, which should be true in the case of our
-- config file.
ret=retor{}
fork,vinpairs(t)do
ifk~='aliases'then
iftype(v)=='table'then
localnewRet={}
ret[k]=newRet
ifv.aliasesthen
for_,aliasinipairs(v.aliases)do
ret[alias]=newRet
end
end
substituteAliases(v,newRet)
else
ret[k]=v
end
end
end
returnret
end
obj.cfg=substituteAliases(cfgorrequire(CONFIG_PAGE))
--[[
	-- Get a table of the arguments sorted by prefix and number. Non-string
	-- keys and keys that don't contain a number are ignored. (This means that
	-- positional parameters are ignored, as they are numbers, not strings.)
	-- The parameter numbers are stored in the first positional parameter of
	-- the subtables, and any gaps are removed so that the tables can be
	-- iterated over with ipairs.
	--
	-- For example, these arguments:
	--   {a1x = 'eggs', a1y = 'spam', a2x = 'chips', b1z = 'beans', b3x = 'bacon'}
	-- would translate into this prefixArgs table.
	--   {
	--     a = {
	--       {1, x = 'eggs', y = 'spam'},
	--       {2, x = 'chips'}
	--     },
	--     b = {
	--       {1, z = 'beans'},
	--       {3, x = 'bacon'}
	--     }
	--   }
	--]]
do
localprefixArgs={}
fork,vinpairs(obj.args)do
iftype(k)=='string'then
localprefix,num,suffix=k:match('^(.-)([1-9][0-9]*)(.*)$')
ifprefixthen
num=tonumber(num)
prefixArgs[prefix]=prefixArgs[prefix]or{}
prefixArgs[prefix][num]=prefixArgs[prefix][num]or{}
prefixArgs[prefix][num][suffix]=v
prefixArgs[prefix][num][1]=num
end
end
end
-- Remove the gaps
localprefixArrays={}
forprefix,prefixTableinpairs(prefixArgs)do
prefixArrays[prefix]={}
localnumKeys={}
fornuminpairs(prefixTable)do
numKeys[#numKeys+1]=num
end
table.sort(numKeys)
for_,numinipairs(numKeys)do
table.insert(prefixArrays[prefix],prefixTable[num])
end
end
obj.prefixArgs=prefixArrays
end
returnobj
end
functionArticleHistory:try(func,...)
ifDEBUG_MODEthen
localval=func(...)
returnval
else
localsuccess,val=pcall(func,...)
ifsuccessthen
returnval
else
table.insert(self._errors,val)
returnnil
end
end
end
functionArticleHistory:getActionObjects()
-- Gets an array of action objects for the parameters specified by the
-- user. We memoise this so that the parameters only have to be processed
-- once.
ifself.actionsthen
returnself.actions
end
-- Get the action args, and exit if they don't exist.
localactionArgs=self.prefixArgs[self.cfg.actionParamPrefix]
ifnotactionArgsthen
self.actions={}
returnself.actions
end
-- Make the objects.
localactions={}
localsuffixes=self.cfg.actionParamSuffixes
for_,tinipairs(actionArgs)do
localobjArgs={}
fork,vinpairs(t)do
localnewK=suffixes[k]
ifnewKthen
objArgs[newK]=v
end
end
objArgs.paramNum=t[1]
objArgs.cfg=self.cfg
objArgs.currentTitle=self.currentTitle
localactionObj=self:try(Action.new,objArgs)
table.insert(actions,actionObj)
end
self.actions=actions
returnactions
end
functionArticleHistory:getStatusIdForCode(code)
-- Gets a status ID given a status code. If no code is specified, returns
-- nil, and if the code is invalid, raises an error.
ifnotcodethen
returnnil
end
localstatuses=self.cfg.statuses
localcodeUpper=mw.ustring.upper(code)
ifstatuses[codeUpper]then
returnstatuses[codeUpper].id
else
self:addWarning(
self:message('articlehistory-warning-invalid-status',code),
self:message('articlehistory-warning-invalid-status-help')
)
returnnil
end
end
functionArticleHistory:getStatusObj()
-- Get the status object for the current status.
ifself.statusObj==falsethen
returnnil
elseifself.statusObj~=nilthen
returnself.statusObj
end
localstatusId
ifself.cfg.getStatusIdFunctionthen
statusId=self:try(self.cfg.getStatusIdFunction,self)
else
statusId=self:try(
self.getStatusIdForCode,self,
self.args[self.cfg.currentStatusParam]
)
end
ifnotstatusIdthen
self.statusObj=false
returnnil
end
-- Check that some actions were specified, and if not add a warning.
localactions=self:getActionObjects()
if#actions<1then
self:addWarning(
self:message('articlehistory-warning-status-no-actions'),
self:message('articlehistory-warning-status-no-actions-help')
)
end
-- Make a new status object.
localstatusObjData={
id=statusId,
currentTitle=self.currentTitle,
cfg=self.cfg
}
localisMulti=self.cfg.statuses[statusId].isMulti
localinitFunc=isMultiandMultiStatus.neworStatus.new
localstatusObj=self:try(initFunc,statusObjData)
self.statusObj=statusObjorfalse
returnself.statusObjornil
end
functionArticleHistory:getStatusId()
localstatusObj=self:getStatusObj()
returnstatusObjandstatusObj.id
end
functionArticleHistory:_noticeFactory(memoizeKey,configKey,class)
-- This holds the logic for fetching tables of Notice and CollapsibleNotice
-- objects.
ifself[memoizeKey]then
returnself[memoizeKey]
end
localret={}
for_,tinipairs(self.cfg[configKey]or{})do
ift.isActive(self)then
localdata={}
fork,vinpairs(t)do
ifk~='isActive'then
data[k]=v
end
end
data.cfg=self.cfg
data.currentTitle=self.currentTitle
ret[#ret+1]=class.new(data)
end
end
self[memoizeKey]=ret
returnret
end
functionArticleHistory:getNoticeObjects()
returnself:_noticeFactory('notices','notices',Notice)
end
functionArticleHistory:getCollapsibleNoticeObjects()
returnself:_noticeFactory(
'collapsibleNotices',
'collapsibleNotices',
CollapsibleNotice
)
end
functionArticleHistory:getAllObjects(addSelf)
localcacheKey=addSelfand'addSelf'or'default'
localret=self._allObjectsCache[cacheKey]
ifnotretthen
ret={}
localstatusObj=self:getStatusObj()
ifstatusObjthen
ret[#ret+1]=statusObj
end
localobjTables={
self:getNoticeObjects(),
self:getActionObjects(),
self:getCollapsibleNoticeObjects()
}
for_,tinipairs(objTables)do
for_,objinipairs(t)do
ret[#ret+1]=obj
end
end
ifaddSelfthen
ret[#ret+1]=self
end
self._allObjectsCache[cacheKey]=ret
end
returnret
end
functionArticleHistory:getNoticeBarIcons()
localret={}
-- Icons that aren't part of a row.
ifself.cfg.noticeBarIconsthen
for_,datainipairs(self.cfg.noticeBarIcons)do
ifdata.isActive(self)then
ret[#ret+1]=renderImage(
data.icon,
nil,
data.sizeorself.cfg.defaultNoticeBarIconSize
)
end
end
end
-- Icons in row objects.
for_,objinipairs(self:getAllObjects())do
ret[#ret+1]=obj:exportNoticeBarIcon(self)
end
returnret
end
functionArticleHistory:getErrorMessages()
-- Returns an array of error/warning strings. Error strings come first.
localret={}
for_,msginipairs(self._errors)do
ret[#ret+1]=msg
end
for_,objinipairs(self:getAllObjects(true))do
for_,msginipairs(obj:getWarnings())do
ret[#ret+1]=msg
end
end
returnret
end
functionArticleHistory:categoriesAreActive()
-- Returns a boolean indicating whether categories should be output or not.
localtitle=self.currentTitle
localns=title.namespace
returntitle.isTalkPage
andns~=3-- not user talk
andns~=119-- not draft talk
end
functionArticleHistory:renderCategories()
localret={}
ifself:categoriesAreActive()then
-- Child object categories
for_,objinipairs(self:getAllObjects())do
localcategories=self:try(obj.getCategories,obj,self)
for_,categoryObjinipairs(categoriesor{})do
ret[#ret+1]=tostring(categoryObj)
end
end
-- Extra categories
for_,funcinipairs(self.cfg.extraCategoriesor{})do
localcats=func(self)or{}
for_,categoryObjinipairs(cats)do
ret[#ret+1]=tostring(categoryObj)
end
end
end
returntable.concat(ret)
end
functionArticleHistory:__tostring()
localroot=mw.html.create()
-- Table root
localtableRoot=root:tag('table')
tableRoot:addClass('article-history tmbox tmbox-notice')
-- Status
localstatusObj=self:getStatusObj()
ifstatusObjthen
tableRoot:node(self:try(statusObj.exportHtml,statusObj,self))
end
-- Notices
localnotices=self:getNoticeObjects()
for_,noticeObjinipairs(notices)do
tableRoot:node(self:try(noticeObj.exportHtml,noticeObj,self))
end
-- Get action objects and the collapsible notice objects, and generate the
-- HTML objects for the action objects. We need the action HTML objects so
-- that we can accurately calculate the number of collapsible rows, as some
-- action objects may generate errors when the HTML is generated.
localactions=self:getActionObjects()or{}
localcollapsibleNotices=self:getCollapsibleNoticeObjects()or{}
localcollapsibleNoticeHtmlObjects,actionHtmlObjects={},{}
for_,objinipairs(actions)do
table.insert(
actionHtmlObjects,
self:try(obj.exportHtml,obj,self)
)
end
for_,objinipairs(collapsibleNotices)do
table.insert(
collapsibleNoticeHtmlObjects,
self:try(obj.exportHtml,obj,self,true)-- Render the collapsed version
)
end
localnActionRows=#actionHtmlObjects
localnCollapsibleRows=nActionRows+#collapsibleNoticeHtmlObjects
-- Find out if we are collapsed or not.
localisCollapsed=yesno(self.args.collapse)
ifisCollapsed==nilthen
ifself.cfg.uncollapsedRows=='all'then
isCollapsed=false
elseifnCollapsibleRows==1then
isCollapsed=false
else
isCollapsed=nCollapsibleRows>(tonumber(self.cfg.uncollapsedRows)or3)
end
end
-- If we are not collapsed, re-render the collapsible notices in the
-- non-collapsed version.
ifnotisCollapsedthen
collapsibleNoticeHtmlObjects={}
for_,objinipairs(collapsibleNotices)do
table.insert(
collapsibleNoticeHtmlObjects,
self:try(obj.exportHtml,obj,self,false)
)
end
end
-- Collapsible table for actions and collapsible notices. Collapsible
-- notices are only included in the table if it is collapsed. Action rows
-- are always included.
localcollapsibleTable
ifisCollapsedornActionRows>0then
-- Collapsible table base
collapsibleTable=tableRoot
:tag('tr')
:tag('td')
:attr('colspan',2)
:css('width','100%')
:tag('table')
:addClass('article-history-milestones')
:addClass(isCollapsedand'mw-collapsible mw-collapsed'ornil)
:css('width','100%')
:css('font-size','90%')
-- Header row
localctHeader=collapsibleTable
:tag('tr')
:tag('th')
:attr('colspan',3)
:css('font-size','110%')
-- Notice bar
ifisCollapsedthen
localnoticeBarIcons=self:getNoticeBarIcons()
if#noticeBarIcons>0then
localnoticeBar=ctHeader:tag('span'):css('float','left')
for_,iconinipairs(noticeBarIcons)do
noticeBar:wikitext(icon)
end
ctHeader:wikitext(' ')
end
end
-- Header text
ifmw.site.namespaces[self.currentTitle.namespace].subject.id==0then
ctHeader:wikitext(self:message('milestones-header'))
else
ctHeader:wikitext(self:message(
'milestones-header-other-ns',
self.currentTitle.subjectNsText
))
end
-- Subheadings
ifnActionRows>0then
collapsibleTable
:tag('tr')
:css('text-align','left')
:tag('th')
:wikitext(self:message('milestones-date-header'))
:done()
:tag('th')
:wikitext(self:message('milestones-process-header'))
:done()
:tag('th')
:wikitext(self:message('milestones-result-header'))
end
-- Actions
for_,htmlObjinipairs(actionHtmlObjects)do
collapsibleTable:node(htmlObj)
end
end
-- Collapsible notices and current status
-- These are only included in the collapsible table if it is collapsed.
-- Otherwise, they are added afterwards, so that they align with the
-- notices.
do
localtableNode,statusColspan
ifisCollapsedthen
tableNode=collapsibleTable
statusColspan=3
else
tableNode=tableRoot
statusColspan=2
end
-- Collapsible notices
for_,objinipairs(collapsibleNotices)do
tableNode:node(self:try(obj.exportHtml,obj,self,isCollapsed))
end
-- Current status
ifstatusObjandnActionRows>1then
tableNode
:tag('tr')
:tag('td')
:attr('colspan',statusColspan)
:wikitext(self:message('status-blurb',statusObj.name))
end
end
-- Get the categories. We have to do this before the error row, so that
-- category errors display.
localcategories=self:renderCategories()
-- Error row and error category
localerrors=self:getErrorMessages()
localerrorCategory
if#errors>0then
localerrorList=tableRoot
:tag('tr')
:tag('td')
:attr('colspan',2)
:addClass('mbox-text')
:tag('ul')
:addClass('error')
:css('font-weight','bold')
for_,msginipairs(errors)do
errorList:tag('li'):wikitext(msg)
end
ifself:categoriesAreActive()then
errorCategory=tostring(Category.new(self:message(
'error-category'
)))
end
-- If there are no errors and no active objects, then exit. We can't make
-- this check earlier as we don't know where the errors may be until we
-- have finished rendering the banner.
elseif#self:getAllObjects()<1then
return''
end
-- Add the categories
root:wikitext(categories)
root:wikitext(errorCategory)
localframe=mw.getCurrentFrame()
returnframe:extensionTag{
name='templatestyles',args={src='Modul:Message box/tmbox.css'}
}..frame:extensionTag{
name='templatestyles',args={src='Modul:Article history/styles.css'}
}..tostring(root)
end
-------------------------------------------------------------------------------
-- Exports
-- These functions are called from Lua and from wikitext.
-------------------------------------------------------------------------------
localp={}
functionp._main(args,cfg,currentTitle)
localarticleHistoryObj=ArticleHistory.new(args,cfg,currentTitle)
returntostring(articleHistoryObj)
end
functionp.main(frame)
localargs=require('Modul:Arguments').getArgs(frame,{
wrappers=WRAPPER_TEMPLATE
})
ifframe:getTitle():find('sandbox',1,true)then
CONFIG_PAGE=CONFIG_PAGE..'/bak pasir'
end
returnp._main(args)
end
functionp._exportClasses()
return{
Message=Message,
Row=Row,
Status=Status,
MultiStatus=MultiStatus,
Notice=Notice,
Action=Action,
CollapsibleNotice=CollapsibleNotice,
ArticleHistory=ArticleHistory
}
end
returnp
Konten disalin dari Wikipedia Bahasa Indonesia (lisensi CC BY-SA) Lihat versi asli di Wikipedia

Rekomendasi Pilihan