Modul:GetShortDescription

TemplateStyles' src attribute must not be empty.

TemplateStyles' src attribute must not be empty.

TemplateStyles' src attribute must not be empty.

TemplateStyles' src attribute must not be empty.

TemplateStyles' src attribute must not be empty.

{{#invoke:GetShortDescription|main |name= |only= |prefer= |fallback= |lang_italic= |lang_nocat= |lang_size= |lang_cat= |lang_rtl= |lang_no= }}

By providing only the required page name (including namespace), the module will look for an explicit {{short description}} in that page, but if not found, will try to fetch the wikidata description. If neither are found, the result will be an empty string.

If the result is a wikidata description, and it is declared (at the source) as being of a foreign language (i.e. not en), the result will be appropriately formatted by Module:Lang (the module powering {{lang}}), in accordance with ; see #Foreign language wikidata descriptions (below).

Markup: {{#invoke:GetShortDescription|main |name=Wikipedia}}
Result: table

This is equivalent to stating |prefer=explicit; see #prefer (below).

Providing a value for |only= will limit the search to being only for the stated description. If no description is found, the result will be an empty string, unless a fallback is provided; see #fallback (below).

explicit

beccè'
Markup: {{#invoke:GetShortDescription|main |name=Wikipedia |only=explicit }}
Result:

wikidata

beccè'
Markup: {{#invoke:GetShortDescription|main |name=Wikipedia |only=wikidata }}
Result: table

prefer

beccè'

Providing a value for |prefer= will initiate the search for the stated description, but try for the alternative if none is found. If no description is found, the result will be an empty string, unless a fallback is provided; see #fallback (below).

explicit or wikidata

beccè'
Markup: {{#invoke:GetShortDescription|main |name=Wikipedia |prefer=explicit }}
Result: table

wikidata or explicit

beccè'
Markup: {{#invoke:GetShortDescription|main |name=Wikipedia |prefer=wikidata }}
Result: table

fallback

beccè'

If a value for |fallback= is provided, and no description is found by the expressed route, the result will be the stated fallback.

only or fallback

beccè'
Markup: {{#invoke:GetShortDescription|main |name=Example |only=explicit |fallback=This is a fallback }}
Result: table

prefer or fallback

beccè'
Markup: {{#invoke:GetShortDescription|main |name=Example |prefer=wikidata |fallback=This is a fallback }}
Result: table

Foreign language wikidata descriptions

beccè'

Should a wikidata description be retrieved, which is declared (at the source) as being of a foreign language (i.e. not en), per , the return will be formatted as appropriate by Module:Lang by default. This may be disabled with |lang_no=yes or adjusted via the parameters for {{lang}}: |lang_italic=, |lang_nocat=, |lang_size=, |lang_cat= and |lang_rtl=; see lang's documentation for details.

Requiring this module

beccè'

Instances when a table is returned

beccè'

Providing a value for |objectify_alarm= will cause alarming messages (red informational messages) to be returned as tables.

Providing a value for |report_redlinks= will cause the return of a report instead of nothing in the event that the page named is nonexistent (i.e. a WP:REDLINK) and a search for a {{short description}} template is processed.

A table will also be returned in the event that the module is instructed to |prefer=explicit (its default), and returns a Wikidata description. If the reason for there being no explicit short description is because it was set to none; the table will include a value for table.none

See below for examples of these behaviors:

local getShortDescription = require( 'Module:GetShortDescription' ).main

local short_description = getShortDescription( {
  -- required
    name = 'page name',

  -- optional
    prefer = 'explicit' or 'wikidata',
    only = 'explicit' or 'wikidata',
    fallback = 'fallback',

    objectify_alarm = true,
    report_redlinks = true,

    lang_no = 'yes',

    -- {{lang}} options
    lang_italic = <yes, no, unset, invert, default>,
    lang_nocat = <yes, y, true, t, on, 1>,
    lang_size = <CSS font-size e.g. '1.5em'>,
    lang_cat = <no, n, false, f, off, 0>,
    lang_rtl = <no (default), yes>
} )

-- If we've requested to report_redlinks or to objectify_alarm then
if type( short_description ) == 'table' then
  if short_description.alarm then
    -- An alarming message has been returned
    local alarming_message = short_description.alarm
  elseif short_description.redlink then
    -- Do something about that
  elseif short_description.wikidata then
    -- A Wikidata description was returned without being explicitly requested
    local wikidata_description = short_description.wikidata
    if short_description.none then
      -- Because the explicit short desc was 'none'
    end
  end
end



local function isEmpty(value) return value == nil or value == '' end

local function notEmpty(value) return not isEmpty(value) end

local function isNone(value) return value:lower() == 'none' end

local function alarmingMessage(message, preview)
	message = '<span style="color:#d33">[[Module:GetShortDescription]] '..message..'.</span>'
	if not preview then
		message = message..'[[Category:Pages displaying alarming messages about Module:GetShortDescription]]'
	end
	return message
end

-- Grammatically reasonable concatenation of possible issues into one message per problematic link target.
local function previewWarning(args_name, quantity_of_things)
	local message = ''
	if quantity_of_things.params > 3 then
		message = message..' with extraneous parameters'
	end
	if quantity_of_things.descriptions > 1 then
		message = message..', declaring '..quantity_of_things.descriptions..' short descriptions'
	end
	if quantity_of_things.templates > 1 or notEmpty(message) then
		message = 'has detected that [[:'..args_name..'|'..args_name..']] has '..
			quantity_of_things.templates..' {{tlx|short description}}'..message
		mw.addWarning(alarmingMessage(message, true))
	end
end

local function getWikidataDescription(title, args, fallback)
	local wikidata_id = mw.wikibase.getEntityIdForTitle(title)
	if isEmpty(wikidata_id) then
		return nil
	end
	local wikidata_description, wikidata_description_lang = mw.wikibase.getDescriptionWithLang(wikidata_id)
	if isEmpty(wikidata_description) then
		return nil
	end
	local result = {wikidata = wikidata_description}
	if isEmpty(args.lang_no) and notEmpty(wikidata_description_lang) and wikidata_description_lang ~= 'en' then
		-- According to the docs this is a possibility...
		result.wikidata = require('Module:Lang')._lang{
			wikidata_description_lang,
			wikidata_description,
			italic = args.lang_italic,
			nocat = args.lang_nocat,
			size = args.lang_size,
			cat = args.lang_cat,
			rtl = args.lang_rtl
		}
	end
	result.fellback = fallback
	return result
end

local function getShortDescriptionTemplates(title_table)
	local page_content = title_table:getContent()
	
	-- Assume no content means a nonexistent title because it's cheaper than testing if it exists.
	if isEmpty(page_content) then
		return {redlink = true}
	end
	
	local contents_of_all_short_description_templates = {}
	
	-- Because there could be any number of short description templates, and not all where there should be; get all the templates.
	for template in page_content:gmatch('{%b{}}') do
		local short_description_content = mw.ustring.match(template, '^{{%s*[Ss]hort description%s*|%s*(.-)%s*}}')
		if notEmpty(short_description_content) then
			-- Collect the contents of short description templates.
			contents_of_all_short_description_templates[#contents_of_all_short_description_templates+1] = short_description_content
		end
		-- An opportunity for efficiency gain exists - to break if another type of template is found e.g. citation templates,
		-- but on an appallingly formatted page, a short description template down by the categories would likely be missed.
	end
	return contents_of_all_short_description_templates
end

local function getShortDescription(args_name, args_name_title_table, title, title_table, fallback)
	local contents_of_all_short_description_templates = {}
	local redirected
	
	-- Check for short description templates on redirect pages.
	if title ~= args_name then
		contents_of_all_short_description_templates = getShortDescriptionTemplates(args_name_title_table)
		if contents_of_all_short_description_templates.redlink then
			return contents_of_all_short_description_templates
		end
		redirected = false
	end
	
	if #contents_of_all_short_description_templates < 1 then
		contents_of_all_short_description_templates = getShortDescriptionTemplates(title_table)
		if notEmpty(redirected) then
			redirected = true
		end
	end
	
	if contents_of_all_short_description_templates.redlink then
		return contents_of_all_short_description_templates
	end
	
	if #contents_of_all_short_description_templates < 1 then
		return nil
	end
	
	local quantity_of_things = {
		templates = #contents_of_all_short_description_templates,
		descriptions = 0,
		params = 0
	}
	
	local possible_short_descriptions = {}
	
	-- Look through the short description templates:
	for template_content_index, short_description_template_contents in ipairs(contents_of_all_short_description_templates) do
		-- Split the contents at pipes and trim.
		local short_description_template_params = mw.text.split(short_description_template_contents, '%s*|%s*')
		if #short_description_template_params > quantity_of_things.params then
			quantity_of_things.params = #short_description_template_params
		end
		possible_short_descriptions[template_content_index] = {}
		-- Look through the params:
		for i, param in ipairs(short_description_template_params) do
			if param == 'noreplace' or mw.ustring.match(param, '^2%s*=%s*noreplace$') then
				-- Take note of 'noreplace'-ing for establishment of hierarchy later.
				possible_short_descriptions[template_content_index].noreplace = true
			else
				local has_equals = param:match('=')
				if not has_equals or param:match('^1') then
					-- Grab the short description.
					if has_equals then
						param = mw.ustring.gsub(param, '^1%s*=%s*', '')
					end
					-- If the template has both a numbered and an unnumbered short description;
					-- whichever comes last (ltr) will be used by that template, so overwriting works out great.
					possible_short_descriptions[template_content_index].description = param
					-- And we want to know the total quantity of descriptions being declared.
					quantity_of_things.descriptions = quantity_of_things.descriptions + 1
				end
			end
		end
	end
	
	local short_descriptions = {}
	
	-- Look through the possible short descriptions for definite short descriptions,
	-- and prepare for working out which of possibly multiple short descriptions is actually being applied for the page:
	for i, possible_short_description in ipairs(possible_short_descriptions) do
		if possible_short_description.description then
			-- If a description is 'noreplace'-ing or 'none'; demote it.
			if (possible_short_description.noreplace or isNone(possible_short_description.description)) and
				#possible_short_descriptions > 1 then
				-- But don't demote it if it's already at the bottom.
				if i > 1 then
					table.insert(short_descriptions, #short_descriptions, possible_short_description)
				else
					short_descriptions[#short_descriptions+1] = possible_short_description
				end
			else
				short_descriptions[#short_descriptions+1] = possible_short_description
			end
		end
	end
	
	-- Let previewWarning() work out if these numbers are bad.
	previewWarning(args_name, quantity_of_things)
	
	if #short_descriptions >= 1 then
		-- Pop!
		local short_description = short_descriptions[#short_descriptions].description
		if notEmpty(short_description) then
			return {explicit = short_description, fellback = fallback, redirected = redirected}
		end
	end
	return nil
end

local function isSisterProjectLink(title)
	local sister_project_prefixes = {
		'wiktionary', 'wikt',
		'wikinews', 'n',
		'wikibooks', 'b',
		'wikiquote', 'q',
		'wikisource', 's',
		'wikispecies', 'species',
		'wikiversity', 'v',
		'wikivoyage', 'voy',
		'commons', 'c',
		'wikidata', 'd',
		'mediawikiwiki', 'mw',
		'wikimedia', 'foundation', 'wmf',
		'meta', 'm',
		'incubator',
		'phabricator', 'phab'
	}
	local pre_colon = title:match('^(%a+):')
	if pre_colon then
		for i, sister in ipairs(sister_project_prefixes) do
			if pre_colon == sister then
				return true
			end
		end
	end
	return false
end

-- Literally testing if title_table.isRedirect can be expensive;
-- processing this way resolves (multiple) redirects without the possibly expensive check.
local function getTitleAndTable(orig_name)
	local title_table = mw.title.new(orig_name)
	title_table = title_table.redirectTarget or title_table
	local title = title_table.prefixedText
	if title == orig_name then
		return title, title_table
	end
	return getTitleAndTable(title)
end

local function getDescription(args)
	local args_name = args.name
	if isEmpty(args_name) then
		return {alarm = 'requires a page name (including namespace)'}
	end
	
	-- Keep the orginal name, cleaned up, and its title_table for later.
	local args_name_title_table = mw.title.new(args_name)
	args_name = args_name_title_table.prefixedText
	
	if isSisterProjectLink(args_name) then
		return nil
	end
	
	local title, title_table = getTitleAndTable(args_name)
	
	if title ~= args_name then
		if isSisterProjectLink(title) then
			return nil
		end
	end
	
	local only = args.only
	local prefer = args.prefer or 'explicit'
	
	-- Pass args_name to getShortDescription() so previewWarning()s won't be confusing for redirects.
	
	if notEmpty(only) then
		if only == 'explicit' then
			return getShortDescription(args_name, args_name_title_table, title, title_table)
		end
		if only == 'wikidata' then
			return getWikidataDescription(title, args)
		end
		return {alarm = 'accepts either "explicit" or "wikidata" as the value of |only='}
	end
	
	if notEmpty(prefer) then
		if prefer == 'explicit' then
			local short_description = getShortDescription(args_name, args_name_title_table, title, title_table)
			if notEmpty(short_description) then
				-- Assume a Wikidata search would be a bad idea for an assumed nonexistent title.
				if short_description.redlink or (not isNone(short_description.explicit) or args.none_is_valid) then
					return short_description
				end
			end
			return getWikidataDescription(title, args, true)
		end
		if prefer == 'wikidata' then
			return getWikidataDescription(title, args) or getShortDescription(args_name, args_name_title_table, title, title_table, true)
		end
		return {alarm = 'accepts either "explicit" or "wikidata" as the value of |prefer='}
	end
end

local function main(args)
	local result = getDescription(args)
	if notEmpty(result) then
		if result.alarm then
			result.alarm = alarmingMessage(result.alarm)
		end
		if args.stringify then
			if result.alarm then
				result = result.alarm
			else
				result = result.explicit or result.wikidata
				if args.none_is_nil and isNone(result) then
					result = nil
				end
			end
		elseif not result.alarm and args.none_is_nil then
			local description = result.explicit or result.wikidata
			if description and args.none_is_nil and isNone(description) then
				result = nil
			end
		end
	end
	return result
end

local p = {}

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame)
	if isEmpty(args) then
		return alarmingMessage('could not getArgs') -- This really would be alarming.
	end
	return main(args)
end

return p