local getArgs = require('Module:Arguments').getArgs
local _INFOBOX = require('Module:Infobox').infobox
local _CFG = mw.loadData('Modul:Article/config')
local _TT = require('Module:TableTools')
local _SMWUTIL = require('Module:SmwUtil')
local p = {}
local errors = {}
local os2osfamily = {}
function getPagesInCategory(category)
if category and #category > 0 then
local result = _SMWUTIL.ask({select = '[[Category:' .. category .. ']]', fields = {}}, { mainlabel = 'pageName' } )
local ret = {}
for _, v in pairs(result) do
if v.pageName and #v.pageName > 0 then
ret[v.pageName] = v.pageName
end
end
return ret
else
return nil
end
end
function getOs2osfamily()
if #os2osfamily == 0 then
local result = _SMWUTIL.ask({ select = '[[Category:Betriebssysteme]]', fields = 'is member of os family#-=family' }, { mainlabel = 'pageName' })
if not result or _TT.size(result) == 0 then
os2osfamily = _CFG.os2osfamily
else
for _, osdata in pairs(result) do
if osdata.pageName and osdata.family then
os2osfamily[mw.ustring.lower(osdata.pageName)] = mw.ustring.lower(osdata.family)
os2osfamily[osdata.pageName] = mw.ustring.lower(osdata.family)
end
end
end
end
return os2osfamily
end
function getArticleInfoboxType(uservalues_os)
if #os2osfamily == 0 then
if _CFG.control.useAutomatedOsFamilyDetection then
os2osfamily = getOs2osfamily()
else
os2osfamily = _CFG.os2osfamily
end
end
local families = {}
local ibType
for _, os in pairs(uservalues_os) do
if os2osfamily[mw.ustring.lower(os)] then
families[os2osfamily[mw.ustring.lower(os)]] = 1
ibType = os2osfamily[mw.ustring.lower(os)]
else
families[_CFG.control.fallbackIBType] = 1
ibType = _CFG.control.fallbackIBType
end
end
if _TT.size(families) ~= 1 then
return _CFG.control.fallbackIBType
else
return ibType
end
end
function getPortalListFor( services )
local validTechnicalServices = {}
for _, s in pairs( services ) do
if not _CFG.fallBack2default[s] then
table.insert( validTechnicalServices, s )
end
end
if #validTechnicalServices == 0 then
return {}
end
-- now build the query string and execute it
local query = {
select = '[[' .. table.concat( validTechnicalServices, ']] OR [[' ) .. ']]',
fields = { 'Shows on service landing page#-=portal' }
}
local result = _SMWUTIL.ask( query, { mainlabel = 'service' } )
-- build the lookup
local portalsLookup = {}
for _, row in pairs( result ) do
if type( row.portal ) ~= 'table' then
row.portal = { row.portal }
end
portalsLookup[row.service] = row.portal
portalsLookup[mw.ustring.lower(row.service)] = row.portal
end
-- why use this loop instead of processing the query result directly?
-- for the breadcrumb I want to prioritize services, so the parent
-- is derived from the first entered service.
-- Therefore I must base all my calculations on the original order
-- of services instead of the query order
local alreadyProcessed = {}
local portalList = {}
for _, service in pairs( services ) do
if portalsLookup[service] then
for _, v in pairs( portalsLookup[service] ) do
if not alreadyProcessed[v] then
table.insert( portalList, v )
alreadyProcessed[v] = true
end
end
elseif _CFG.fallBack2default[service]
and not alreadyProcessed[_CFG.control.displayDefaultAs]
then
table.insert( portalList, _CFG.control.displayDefaultAs )
alreadyProcessed[_CFG.control.displayDefaultAs] = true
end
end
return portalList
end
function printList( list, link, default )
if not list or _TT.size( list ) == 0 then
return default or ''
end
if not link then
return mw.text.listToText( list, ', ', ' und ' )
end
local linkedList = {}
local insertDefault = true
for _, v in pairs( list ) do
if not _CFG.fallBack2default[v] then
table.insert( linkedList, '[[' .. v .. ']]' )
elseif insertDefault then
table.insert( linkedList, _CFG.control.displayDefaultAs )
insertDefault = false
end
end
return mw.text.listToText( linkedList, ', ', ' und ' )
end
function split(str, sep)
if type(str) == 'table' then
return str
end
local r = {}
if not sep then
sep = ','
end
if str then
for v in mw.text.gsplit(str, sep, true) do
s = mw.text.trim(v)
if mw.ustring.len(s) > 0 then
table.insert(r, s)
end
end
end
if #r == 0 then
r = nil
end
return r
end
function p.setDisplaytitle(uservalues)
if not uservalues.displaytitle or #uservalues.displaytitle == 0 then
-- return line is debug
return '<span style="display:none">no displaytitle found: ' .. mw.title.getCurrentTitle().text .. '</span>'
end
mw.ext.displaytitle.set(uservalues.displaytitle)
-- return line is debug
return '<span style="display:none">set displaytitle to ' .. uservalues.displaytitle .. '</span>'
end
function p.processArgs(args)
--[[
This method takes the user provided arguments and
* checks all mandatory requirements
* converts all multivalue parameters to tables
* checks all provided valid entries
* sets default values for non mandatory and not provided parameters
* adds a preceeding 'Service:' to service if omitted
f
************** NOTE:
The following parameters will optionally be checked for validity:
* os
* service
see Modul:Article/config table control for more information
--]]
local uservalues = {}
for _, arg in pairs(_CFG.allParams) do
if _TT.inTable(_CFG.mandatory, arg) and (not args[arg] or #args[arg] == 0) then
table.insert(errors, "Pflichtfeld ''" .. arg .. "'' fehlt!")
elseif _TT.inTable(_CFG.multivalues, arg) then -- process multivalue parameters (can assume that mandatory fields are set)
if args[arg] and #args[arg] > 0 then -- multivalue parameter is set, so proceed
local valid = {}
local invalid = {}
if _CFG.validValues[arg] then
-- we only have valid values for type and targetgroup
for _, v in pairs(split(args[arg])) do -- for every entry, check if it is valid
if _TT.inTable(_CFG.validValues[arg], v) then
table.insert(valid, v)
else
table.insert(invalid, v)
end
end
else -- of if _CFG.validValues[arg] then
-- os and service can be validated later, if requested
valid = split(args[arg])
end -- of if _CFG.validValues[arg] then .. else
uservalues[arg] = valid
if #invalid > 0 then
table.insert(errors, "Parameter ''" .. arg .. "'' hat einen ungültigen Wert: " .. mw.text.listToText(invalid) .. "!")
end
else -- multivalue parameter is not set, take default (which can be nil)
uservalues[arg] = _TT.shallowClone(_CFG.defaultValues[arg])
end
else -- process singlevalue parameters (can assume that mandatory fields are set)
if not args[arg] or #args[arg] == 0 then -- if not set, take default (which can be nil)
uservalues[arg] = mw.clone(_CFG.defaultValues[arg])
elseif _CFG.validValues[arg] and not _TT.inTable(_CFG.validValues[arg], args[arg]) then
table.insert(errors, "Parameter ''" .. arg .. "'' hat einen ungültigen Wert: " .. args[arg] .. "!")
else
uservalues[arg] = args[arg]
end -- of if not args[arg] or #args[arg] == 0 then .. elseif .. else
end -- of if _TT.inTable(_CFG.mandatory, arg) and (not args[arg] or #args[arg] == 0) then .. elseif .. else
end -- of for _, arg in pairs(_CFG.allParams) do
-- special processing of service field. see to it, that all entries have a preceeding 'Service:'
for k, v in pairs(uservalues.service) do
-- the following is for backwards compatibilty when we migrated services from ns meta to ns service
if mw.ustring.find(v, 'Meta:', 1, true) then
-- this is due to legacy reasons. service entites were once stored in namespace meta
uservalues.service[k] = mw.ustring.gsub(v, 'Meta', 'Service', 1)
elseif not mw.ustring.find(v, 'Service:', 1, true) then
uservalues.service[k] = 'Service:' .. v
end
end
-- if requested, do some plausibility checks
if _CFG.control.validateParamOs then
-- valid_od is an associative list os -> osfamily. here, os is present as queried and in lowercase
local valid_os = getOs2osfamily()
local newvalues = {}
local fallingBack2default = nil
for _, os in pairs(uservalues.os) do
if not valid_os[mw.ustring.lower(os)] then
-- this could happen, if user manually added one of the fallback defaults
if _CFG.fallBack2default[os] then
if not fallingBack2default then
table.insert(newvalues, _CFG.defaultValues.os[1])
fallingBack2default = true
end
else
table.insert(errors, "Parameter ''os'' hat einen ungültigen Wert: '" .. os .. "'!")
end
else
table.insert(newvalues, os)
end -- of if not valid_os[os] then
end -- of for _, os in pairs(uservalues.os) do
uservalues.os = newvalues
end -- of if _CFG.control.validateParamOs then
if _CFG.control.validateParamService then
local valid_service = getPagesInCategory('Services')
local inactive_service = getPagesInCategory('Inactive services')
-- this is an associative list service -> service, whereas service is a pagename in ns service, e.g. Service:Mail
local all_services = valid_service
for k,v in pairs(inactive_service) do
all_services[k] = v
end
local newvalues = {}
local fallingBack2default = nil
for _, service in pairs(uservalues.service) do
if not all_services[service] then
if _CFG.fallBack2default[service] then
if not fallingBack2default then
table.insert(newvalues, _CFG.defaultValues.service[1])
fallingBack2default = true
end
else
table.insert(errors, "Parameter ''service'' hat einen ungültigen Wert: '" .. service .. "'!")
end
else
table.insert(newvalues, service)
end -- of if not valid_service[service] then
end -- of for _, service in pairs(uservalues.service) do
uservalues.service = newvalues
end -- end of if _CFG.control.validateParamService then
-- derive linked portal pages from technical services
uservalues._portal = getPortalListFor( uservalues.service )
return uservalues
end
function p.storeSementicData(uservalues)
local smwData = {}
for arg, prop in pairs(_CFG.arg2prop) do
smwData[prop] = uservalues[arg]
end
-- handling of special property "has sortkey"
if uservalues.disambiguation and #uservalues.disambiguation > 0 then
smwData['has sortkey'] = uservalues.disambiguation .. ' ' .. mw.title.getCurrentTitle().text
else
smwData['has sortkey'] = mw.title.getCurrentTitle().text
end
if uservalues.pagetype and #uservalues.pagetype > 0 then
smwData['is disambiguation page'] = 1
else
smwData['is disambiguation page'] = 0
end
_SMWUTIL.set(smwData)
-- debug:
--return '<pre>\nuservalues:\n' .. _TT.printTable(uservalues) .. '\n\nsmwData:' .. _TT.printTable(smwData) .. '</pre>'
end
function p.infobox(uservalues)
-- "calculate" type of infobox
local ib_type = getArticleInfoboxType(uservalues.os)
-- copy uservalues into local data tables
local os = mw.clone( uservalues.os )
local portal = mw.clone( uservalues._portal )
table.sort(os)
table.sort(portal)
local ib_args = {
bodyclass = '',
aboveclass = 'objtitle titletext',
above = uservalues.type,
subheader = _CFG.ibCoreData[ib_type].image .. ' ' .. _CFG.ibCoreData[ib_type].label,
headerclass = 'headertext',
labelstyle = 'width: 30%;',
datastyle = 'width: 70%;',
header1 = "Informationen",
label2 = 'Betriebssystem',
data2 = printList( os, true ),
label3 = 'Service',
data3 = printList( portal, true, 'keine' ),
label4 = 'Interessant für',
data4 = printList( uservalues.targetgroup ),
header6 = _CFG.ibCoreData[ib_type].portal
}
return _INFOBOX(ib_args)
end
function p.addBreadcrumbData( uservalues )
if not _CFG.control.idForParentField then
return ''
end
local findNonDefault = function( dataList )
if type( dataList ) ~= 'table' then
return false
end
for _, v in pairs( dataList ) do
if not _CFG.fallBack2default[v] then
return v
end
end
end
local portal = uservalues._portal
local parent = findNonDefault( portal )
if not parent then
parent = findNonDefault( uservalues.os )
end
if not parent and uservalues.disambiguation and #uservalues.disambiguation > 0 then
parent = uservalues.disambiguation
end
if not parent then
return ''
else
return '<span id="' .. _CFG.control.idForParentField .. '" style="display:none">' .. parent .. '</span>'
end
end
function p.addCategory(uservalues)
if mw.title.getCurrentTitle().namespace ~= 0 then
return ''
end
local cat = _CFG.type2category[uservalues.type] or _CFG.control.fallBackCategory
local category = '[[Category:' .. cat .. ']]'
return category
end
function p.addErrors()
local errorBoxes = mw.html.create('')
if #errors > 0 then
local messageBox = require('Module:Message box')
for _, errortext in pairs(errors) do
errorBoxes:wikitext(messageBox.main( 'ambox', {
type = 'delete',
text = errortext
-- More parameters...
})
)
end
if mw.title.getCurrentTitle().namespace == 0 then
errorBoxes:wikitext('[[Category:' .. _CFG.control.errorCategory .. ']]')
end
end
return errorBoxes
end
function p.addLinkToDisambiguation(uservalues)
local disambigLink = mw.html.create('')
if uservalues.disambiguation and #uservalues.disambiguation > 0 then
local hatnote = require('Module:Hatnote')._hatnote
disambigLink:wikitext(hatnote(_CFG.hatnote .. ' [[' .. uservalues.disambiguation .. ']]', {}))
end
return disambigLink
end
function p._main(args)
local uservalues = p.processArgs(args)
local output = mw.html.create('')
if #errors == 0 then
result = p.storeSementicData(uservalues)
if result then
-- only used in debug context
output:wikitext(result)
end
end
output:wikitext( p.addBreadcrumbData( uservalues ) )
:wikitext( p.infobox( uservalues ) )
:wikitext( p.setDisplaytitle( uservalues ) )
:wikitext( p.addCategory( uservalues ) )
:node( p.addLinkToDisambiguation( uservalues ) )
:node( p.addErrors() )
if mw.title.getCurrentTitle().text == "tttNetzlaufwerk einbinden (iPadOS)" then
ibtype = getArticleInfoboxType(uservalues.os);
output:wikitext('<pre>' .. _TT.printTable(args) .. '</pre>')
:wikitext( 'ib_Type is ' .. ( ibtype and ibtype or 'nil' ) .. '<br>' )
end
-- when {{article}} is called by {{disambiguation}} you need to: {{#ifeq:{{#var:disambiguate_isdisambiguation|0}}|1||{{#set:Is disambiguation=0}}}}
-- --> maybe its enough, to not have it set at all... {{#set:Is disambiguation=1}} is done by {{disambiguate}}
return tostring(output)
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
function p.debugLocal(uservalues_os)
--return _TT.printTable(getOs2osfamily())
return getArticleInfoboxType(uservalues_os)
end
return p