Modul:Article

IMT:HilfeWiki - das Wiki
Wechseln zu:Navigation, Suche
Documentation icon Module documentation[view] [edit] [history] [purge]

This module implements template {{Article}}. Configuration is stored in Module:Article/config.

Usage

{{#invoke:Article|main}}

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 article = {}
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 article.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 article.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 article.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',
		title = uservalues.type,
		above = _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 article.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 article.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 article.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 article.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 article._main(args)
	local uservalues = article.processArgs(args)
	local output = mw.html.create('')
	if #errors == 0 then
		result = article.storeSementicData(uservalues)
		if result then
			-- only used in debug context
			output:wikitext(result)
		end
	end

	output:wikitext( article.addBreadcrumbData( uservalues ) )
		:wikitext( article.infobox( uservalues ) )
		:wikitext( article.addCategory( uservalues ) )
		:node( article.addLinkToDisambiguation( uservalues ) )
		:node( article.addErrors() )

	-- 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 article.main(frame)
	local args = getArgs(frame)
	return article._main(args)
end

return article