Module:Author

require('strict')

local p = {}

-- Local variables local getArgs = require('Module:Arguments').getArgs local yesno = require('Module:Yesno') local construct_header = require('Module:Header structure').construct_header

local getWDValue = require('Module:WikidataIB')._getValue local dateModule = require("Module:Era") local tableToolsModule = require("Module:TableTools") local categories = {} -- List of categories to add page to.

local function preprocess(args) return mw.getCurrentFrame:preprocess(args) end

local year_properties = { birth = 'P569', death = 'P570', flourit = 'P1317' }

local function get_wd_entity(args) -- Fetch entity object for Wikidata item connected to the current page -- Let manually-specified Wikidata ID override if given and valid args.wikidata = args.wikidata or args.wikidata_id -- alias if args.wikidata and mw.wikibase.isValidEntityId(args.wikidata) then return mw.wikibase.getEntity(args.wikidata) else return mw.wikibase.getEntity end end

-- Get the actual parentheses-enclosed HTML string that shows the dates. local function getFormattedDates(birthyear, deathyear) if not birthyear and not deathyear then return nil end local dates = ' ('	if birthyear then		dates = dates .. birthyear	end	if birthyear ~= deathyear then		-- Add spaces if there are spaces in either of the dates.		local spaces = 		if string.match((birthyear or ) .. (deathyear or ''), ' ') then			spaces = ' '		end		dates = dates .. spaces .. '–' .. spaces	end	if deathyear and birthyear ~= deathyear then		dates = dates .. deathyear	end	if birthyear or deathyear then		dates = dates .. ')' end return dates end

-- Add a category to the current list of categories. Do not include the Category prefix. local function addCategory(category) table.insert(categories, category) end

-- Remove a category. Do not include the Category prefix. local function removeCategory(category) for catPos, cat in pairs(categories) do   	if cat == category then table.remove(categories, catPos) end end end

-- Get wikitext for all categories added using addCategory. local function getCategories categories = tableToolsModule.removeDuplicates(categories) table.sort(categories) local out = '' for _, cat in pairs(categories) do out = out .. ''	end return out end

-- Take a statement of a given property and make a human-readable year string -- out of it, adding the relevant categories as we go. -- @param table statement The statement. -- @param string type One of 'birth' or 'death'. local function getYearStringFromSingleStatement(statement, year_type) local snak = statement.mainsnak -- We're not using mw.wikibase.formatValue because we only want years. -- No value. This is invalid for birth dates (should be 'somevalue'	-- instead), and indicates 'still alive' for death dates. if snak.snaktype == 'novalue' and year_type == 'birth' then addCategory('Authors with missing birth dates') return nil end if snak.snaktype == 'novalue' and year_type == 'death' then addCategory('Living authors') return nil end -- Unknown value. if snak.snaktype == 'somevalue' then addCategory('Authors with unknown ' .. year_type .. ' dates') return '?' end -- Extract year from the time value. local _,_, extractedYear = string.find(snak.datavalue.value.time, '([%+%-]%d%d%d+)%-') local year = math.abs(tonumber(extractedYear)) addCategory(dateModule.era(extractedYear) .. ' authors') -- Century & millennium precision. if snak.datavalue.value.precision == 6 or snak.datavalue.value.precision == 7 then local ceilfactor = 100 local precisionName = 'century' if snak.datavalue.value.precision == 6 then ceilfactor = 1000 precisionName = 'millennium' end local cent = math.max(math.ceil(year / ceilfactor), 1) -- @TODO: extract this to use something like en:wikipedia:Module:Ordinal local suffix = 'th' if string.sub(cent, -1) == '1' and string.sub(cent, -2) ~= '11' then suffix = 'st' elseif string.sub(cent, -1) == '2' and string.sub(cent, -2) ~= '12' then suffix = 'nd' elseif string.sub(cent, -1) == '3' and string.sub(cent, -2) ~= '13' then suffix = 'rd' end year = cent .. suffix .. ' ' .. precisionName addCategory('Authors with approximate ' .. year_type .. ' dates') end if snak.datavalue.value.precision == 8 then -- decade precision year = math.floor(tonumber(year) / 10) * 10 .. 's' addCategory('Authors with approximate ' .. year_type .. ' dates') end if tonumber(extractedYear) < 0 then year = year .. ' BCE' end -- Remove from 'Living authors' if that's not possible. if tonumber(extractedYear) < tonumber(os.date('%Y') - 110) then removeCategory('Living authors') end -- Add to e.g. 'YYYY births' category (before we add 'c.' or 'fl.' prefixes). if year_type == 'birth' or year_type == 'death' then -- mw.logObject('Wikidata cat') -- mw.logObject(year .. ' ' .. year_type .. 's') addCategory(year .. ' ' .. year_type .. 's') end -- Extract circa (P1480 = sourcing circumstances, Q5727902 = circa) if statement.qualifiers and statement.qualifiers.P1480 then for _,qualifier in pairs(statement.qualifiers.P1480) do			if qualifier.datavalue and qualifier.datavalue.value.id == 'Q5727902' then addCategory('Authors with approximate ' .. year_type .. ' dates') year = 'c. ' .. year end end end -- Add floruit abbreviation. if year_type == 'floruit' then year = 'fl. ' .. year end return year end

-- Get a given or family name property. This concatenates (with spaces) all -- statements of the given property in order of the series ordinal (P1545) -- qualifier. @TODO fix this. local function getNameFromWikidata(item, property) local statements = item:getBestStatements(property) local out = {} if statements[1] and statements[1].mainsnak.datavalue then local itemId = statements[1].mainsnak.datavalue.value.id		table.insert(out, mw.wikibase.label(itemId) or '') end return table.concat(out, ' ') end

local function getPropertyValue(item, property) local statements = item:getBestStatements(property) if statements[1] and statements[1].mainsnak.datavalue then return statements[1].mainsnak.datavalue.value end end

-- The 'Wikisource' format for a birth or death year is as follows: --    "?" or empty for unknown (or still alive) --    Use BCE for years before year 1 --    Approximate dates: --        Decades or centuries: "1930s" or "20th century" --        Circa: "c/1930" or "c. 1930" or "ca 1930" or "circa 1930" --        Tenuous year: "1932/?" --        Choice of two or more years: "1932/1933" -- This is a slightly overly-complicated function, but one day will be able to be deleted. -- @param string type Either 'birth' or 'death' -- @return string The year to display local function formatWikisourceYear(year, year_type) if not year then return nil end -- mw.logObject('formatWikisourceYear') local yearParts = mw.text.split(year, '/', true) -- mw.logObject('yearParts') -- mw.logObject(yearParts) -- Ends in a question mark. if yearParts[2] == '?' then addCategory('Authors with unknown ' .. year_type .. ' dates') if tonumber(yearParts[1]) then -- mw.logObject('unknown') -- mw.logObject(yearParts[1] .. ' ' .. year_type .. 's') addCategory(dateModule.era(yearParts[1]) .. ' authors') addCategory(yearParts[1] .. ' ' .. year_type .. 's') else addCategory('Authors with non-numeric ' .. year_type .. ' dates') end return yearParts[1] .. '?'	end -- Starts with one of the 'circa' abbreviations local circaNames = {'ca.', 'c.', 'ca', 'c', 'circa'} for _, circaName in pairs(circaNames) do		local yearNumber local isCirca = false if yearParts[1] == circaName then yearNumber = mw.text.trim(yearParts[2]) isCirca = true elseif string.match(yearParts[1], '^' .. circaName) then yearNumber = string.gsub(yearParts[1], '^' .. circaName, '') isCirca = (tonumber(yearNumber) ~= nil) end if isCirca then addCategory('Authors with approximate ' .. year_type .. ' dates') if tonumber(yearNumber) then yearNumber = tonumber(yearNumber) -- mw.logObject(yearNumber) addCategory(dateModule.era(tostring(yearNumber)) .. ' authors') addCategory(yearNumber .. ' ' .. year_type .. 's') else addCategory('Authors with non-numeric ' .. year_type .. ' dates') end return 'c. ' .. yearNumber end end -- If there is more than one year part, and they're all numbers, add categories. local allPartsAreNumeric = true if #yearParts > 1 then for _, yearPart in pairs(yearParts) do			if tonumber(yearPart) then -- mw.logObject('numeric') -- mw.logObject(yearPart .. ' ' .. year_type .. 's') addCategory(yearPart .. ' ' .. year_type .. 's') addCategory(dateModule.era(yearPart) .. ' authors') else allPartsAreNumeric = false end end if allPartsAreNumeric then addCategory('Authors with approximate birth dates') end elseif #yearParts == 1 and not tonumber(year) then allPartsAreNumeric = false end -- Otherwise, just use whatever's been given if not allPartsAreNumeric then addCategory('Authors with non-numeric ' .. year_type .. ' dates') end if #yearParts == 1 or allPartsAreNumeric == false then -- mw.logObject('not numeric') -- mw.logObject(year .. ' ' .. year_type .. 's') addCategory(year .. ' ' .. year_type .. 's') end return year end

--[=[ Get a formatted year of the given property and add to the relevant categories ]=] local function formatWikidataYear(item, year_type) -- Check sanity of inputs if not item or not year_type or not year_properties[year_type] then return nil end local property = year_properties[year_type] -- Get this property's statements. local statements = item:getBestStatements(property) if #statements == 0 then -- If there are no statements of this type, add to 'missing' category. if year_type == 'birth' or year_type == 'death' then addCategory('Authors with missing ' .. year_type .. ' dates') end local isHuman = item:formatPropertyValues('P31').value == 'human' if year_type == 'death' and isHuman then -- If no statements about death, assume to be alive. addCategory('Living authors') end end -- Compile a list of years, one from each statement. local years = {} for _, statement in pairs(statements) do		local year = getYearStringFromSingleStatement(statement, year_type) if year then table.insert(years, year) end end years = tableToolsModule.removeDuplicates(years) -- If no year found yet, try for a floruit date. if #years == 0 or table.concat(years, '/') == '?' then local floruitStatements = item:getBestStatements(year_properties['flourit']) for _, statement in pairs(floruitStatements) do -- If all we've got so far is 'unknown', replace it. if table.concat(years, '/') == '?' then years = {} end addCategory('Authors with floruit dates') local year = getYearStringFromSingleStatement(statement, 'floruit') if year then table.insert(years, year) end end end years = tableToolsModule.removeDuplicates(years) if #years == 0 then return nil end return table.concat(years, '/') end

-- Get a single formatted date, with no categories. -- args.year, args.year_type, args.wd_entity local function date(args) -- mw.logObject('date') if args.year then return formatWikisourceYear(args.year, args.year_type) else return formatWikidataYear(args.wd_entity, args.year_type) end end

-- Get a formatted string of the years that this author lived, -- and categorise in the appropriate categories. -- The returned string starts with a line break. local function dates(args) local item = args.wd_entity local outHtml = mw.html.create -- Check a given title as having the appropriate dates as a disambiguating suffix. local function checkTitleDatesAgainstWikidata(title, wikidata) -- All disambiguated author pages have parentheses in their titles. local titleHasParentheses = string.find(tostring(title), '%d%)')		if not titleHasParentheses then			return		end		-- The title should end with years in the same format as is used in the page header		-- but with a normal hyphen instead of an en-dash.		local dates = '(' .. (date({year_type = 'birth', wikidata = wikidata}) or '') .. '-' .. (date({year_type = 'death', wikidata = wikidata}) or '') .. ')'		if string.sub(tostring(title), -string.len(dates)) ~= dates then 			addCategory('Authors with title-date mismatches')		end	end	-- Check disambiguated page titles for accuracy.	checkTitleDatesAgainstWikidata(args.pagetitle or mw.title.getCurrentTitle, args.wikidata)

-- Get the dates (do death first, so birth can override categories if required): -- Death local deathyear local wikidataDeathyear = formatWikidataYear(item, 'death') local wikisourceDeathyear = formatWikisourceYear(args.deathyear, 'death') if args.deathyear then -- For Wikisource-supplied death dates. deathyear = wikisourceDeathyear addCategory('Authors with override death dates') if item and wikisourceDeathyear ~= wikidataDeathyear then addCategory('Authors with death dates differing from Wikidata') end if tonumber(deathyear) then addCategory(dateModule.era(deathyear) .. ' authors') end else deathyear = wikidataDeathyear end if not deathyear then addCategory('Authors with missing death dates') end -- Birth local birthyear local wikidataBirthyear = formatWikidataYear(item, 'birth') local wikisourceBirthyear = formatWikisourceYear(args.birthyear, 'birth') if args.birthyear then -- For Wikisource-supplied birth dates. birthyear = wikisourceBirthyear addCategory('Authors with override birth dates') if item and wikisourceBirthyear ~= wikidataBirthyear then addCategory('Authors with birth dates differing from Wikidata') end if tonumber(birthyear) then addCategory(dateModule.era(birthyear) .. ' authors') end else birthyear = wikidataBirthyear end if not birthyear then addCategory('Authors with missing birth dates') end -- Put all the output together, including manual override of the dates. local dates = '' if args.dates then -- The parentheses are repeated here and in getFormattedDates addCategory('Authors with override dates') dates = ' (' .. args.dates .. ')' else dates = getFormattedDates(birthyear, deathyear) end if dates then outHtml:wikitext(dates) return tostring(outHtml) end return nil end

--[=[ Match claims to configured categories. Utility function for constructCategories.

Modifies the provided table to add categories configured in /data. ]=] local function addCategoriesFromClaims(entity, pId, knownCategories) -- Abort if the provided category mappings are missing or undefined if not knownCategories then error("Category mappings are not defined. Check Module:Author/data.") end -- Get statements for the property provided (ignore deprecated statements) local statements = entity:getBestStatements(pId) -- Get the category for each statement's value if a mapping exists for _, v in pairs(statements) do		-- Sometimes the property exists on the item but has no value, -- or it has an unknown value, -- so in the output from mw.wikibase.getEntity -- .mainsnak's .datavalue will be nil. if v.mainsnak.snaktype == "value" then local valueId = v.mainsnak.datavalue.value.id			-- Add the category if we have a mapping for this statement local knownCat = knownCategories[valueId] if knownCat then addCategory(knownCat) end end end end

--[=[ Get categories for nationality, occupations, etc.

Returns categories as a string of wikicode ]=] local function constructCategories(entity) if not entity then return nil end -- Load the property to category mappings local DATA = mw.loadData('Module:Author/data') -- Add categories from properties for which we have a configured mapping addCategoriesFromClaims(entity, 'P27',  DATA.categories.nationalities) addCategoriesFromClaims(entity, 'P106', DATA.categories.occupations) addCategoriesFromClaims(entity, 'P140', DATA.categories.religions) addCategoriesFromClaims(entity, 'P135', DATA.categories.movements) addCategoriesFromClaims(entity, 'P1142', DATA.categories.ideologies) addCategoriesFromClaims(entity, 'P108', DATA.categories.employer) addCategoriesFromClaims(entity, 'P39',  DATA.categories.positionheld) addCategoriesFromClaims(entity, 'P166', DATA.categories.awardreceived) addCategoriesFromClaims(entity, 'P463', DATA.categories.memberof) addCategoriesFromClaims(entity, 'P411', DATA.categories.canonizationstatus) addCategoriesFromClaims(entity, 'P3919', DATA.categories.contributedto) addCategoriesFromClaims(entity, 'P3716', DATA.categories.socialclassification) addCategoriesFromClaims(entity, 'P1303', DATA.categories.instrument) addCategoriesFromClaims(entity, 'P1343', DATA.categories.describedby) end

-- Output link and category for initial letters of family name. -- --[=[ local function lastInitial(args) local initials = nil -- Allow manual override of initials. if args.last_initial then initials = args.last_initial end -- Handle special override, used by the Template:Disambiguation. if initials == '!NO_INITIALS' then return nil end -- If a lastname is provided, get the initials from that. if not initials and args.lastname then initials = mw.ustring.sub(args.lastname, 1, 2) end -- Fetch from Wikidata. if not initials then local item = args.wd_entity if item then -- Get the first family name statement. local familyNames = item:getBestStatements('P734') if #familyNames > 0 then local familyNameId = familyNames[1].mainsnak.datavalue.value.id				local familyName = mw.wikibase.getEntity(familyNameId) if familyName.labels and familyName.labels.en then -- Take the first two characters of the English label -- (this avoids issues with 'navive label P1705' and is fine for English Wikisource). initials = mw.ustring.sub(familyName.labels.en.value, 1, 2) end end end end

-- Put it all together and output. local out = '' if initials then local authorIndex = 'Author Index: ' .. initials .. '' local authorCategory = mw.title.new('Authors-' .. initials, 'Category') out = authorIndex ..  .. authorCategory.prefixedText ..  if authorCategory.exists ~= true then local missingAuthorCat = mw.title.new('Author pages with missing initials category', 'Category') out = out ..  .. missingAuthorCat.prefixedText ..  end else addCategory('Authors without initials') out = 'Authors without initials' end return out end ]=]

--[=[ Debugging: function p.lastInitial(args) return lastInitial(args) end ]=] -- Debugging 1: mw.log(p.lastInitial({last_initial = 'Qx'})) -- Debugging 2: mw.log(p.lastInitial({wikidata = 'Q1107985'})) -- Debugging 1: mw.log(p.lastInitial({lastname = 'Qqxxx'})) -- Debugging 3: mw.log(p.lastInitial({last_initial = 'Qx', wikidata_id='Q1107985'}))

-- Header assembly local function ucfirst(s) return string.sub(s, 1, 1) .. string.sub(s, 2) end

local function gender_from_wd(wd_entity) if not wd_entity then return nil end local statements = wd_entity:getBestStatements('P21') if #statements == 0 then return nil end local genders = {} for _, statement in pairs(statements) do		local snak = statement.mainsnak if snak.snaktype ~= 'value' or not snak or snak.datatype ~= 'wikibase-item' or not snak.datavalue or not snak.datavalue.value or not snak.datavalue.value.id then break end local gender_item = mw.wikibase.getEntity(snak.datavalue.value.id) if not gender_item or not gender_item.labels or not gender_item.labels.en or not gender_item.labels.en.value then break end table.insert(genders, gender_item.labels.en.value) end return genders[1] end

local function get_image(args) local image = args.image local template_ns = args.template_ns or 'Author' if image then addCategory(template_ns .. ' pages with image') else image = getWDValue({			[1] = 'P18',			['qid'] = args.wikidata,			['fetchwikidata'] = 'ALL',			['onlysourced'] = 'no',			['noicon'] = 'true',			['maxvals'] = '1',			['rank'] = 'best'		}) if image then addCategory(template_ns .. ' pages with Wikidata image') else addCategory(template_ns .. ' pages without image') end end local image_display = '' if image then local upright = '' if yesno(args.upright) then upright = '|upright=0.6' end local caption_div = '' if args.image_caption then caption_div = tostring(mw.html.create('div'):css({['text-align'] = 'center'}):wikitext(args.image_caption)) end image_display = '' end return image_display end

local function construct_defaultsort(args) local defaultsort = args.defaultsort if not defaultsort then if args.firstname and args.lastname then defaultsort = ucfirst(args.lastname .. ', ' .. args.firstname) elseif args.firstname or args.lastname then defaultsort = ucfirst((args.lastname or ) .. (args.firstname or )) end end if defaultsort then return mw.getCurrentFrame:callParserFunction('DEFAULTSORT', {defaultsort}) end return '' end

local function author(args) -- Always tell Header structure that we're -- TODO: Is there a use case for letting our clients set this? args.template = 'author'

local current_title = mw.title.getCurrentTitle local firstname = args.firstname local lastname = args.lastname local last_initial = args.last_initial if firstname then firstname = preprocess(firstname) end if lastname then lastname = preprocess(lastname) end if last_initial then last_initial = preprocess(last_initial) end args.header_class = 'wst-author ws-header' args.wd_entity = get_wd_entity(args) -- main block args.main_class = 'authortemplate' if last_initial ~= '!NO_INITIALS' then -- TODO: use code from lastInitial? if last_initial then args.previous =  .. 'Author Index: ' .. last_initial ..  else args.previous = 'Authors without initials' end end local name_text if firstname and lastname then if yesno(args.invert_names) then name_text = lastname .. ' ' .. firstname else name_text = firstname .. ' ' .. lastname end else name_text = firstname or lastname end args.image_caption = args.image_caption or name_text local name_text_span = '' if name_text then name_text_span = tostring(mw.html.create('span'):css({['font-weight'] = 'bold'}):wikitext(name_text)) end local dates_text = '' if not yesno(args.disambiguation) then dates_text = dates(args) or '' end args.main_title = name_text_span .. dates_text -- notes block args.notes_class = 'author_notes' args.commonscat = args.commonscat or (		args.wd_entity		and args.wd_entity.claims		and args.wd_entity.claims.P373		and args.wd_entity.claims.P373[1]		and args.wd_entity.claims.P373[1].mainsnak		and args.wd_entity.claims.P373[1].mainsnak.datatype == 'string'		and args.wd_entity.claims.P373[1].mainsnak.datavalue		and args.wd_entity.claims.P373[1].mainsnak.datavalue.value	) args.notes = args.description -- image local image_display = get_image(args) -- defaultsort args.defaultsort = args.defaultsort or args.sortkey local defaultsort_magicword = construct_defaultsort(args) -- categories if not args.categories then -- Author index category if last_initial and last_initial ~= '!NO_INITIALS' then addCategory('Authors-' .. last_initial) elseif not last_initial then addCategory('Authors without initials') end -- Categorisation of author pages by gender, based on Wikidata sex or gender Property (P21) -- The main are: male (Q6581097), female (Q6581072), transgender female (Q1052281), transgender male (Q2449503) if args.wd_entity then local gender = gender_from_wd(args.wd_entity) if gender == 'transgender female' or gender == 'female' then addCategory('Women authors') addCategory('Author pages with gender in Wikidata') elseif gender == 'transgender male' or gender == 'male' then addCategory('Male authors') addCategory('Author pages with gender in Wikidata') elseif gender then addCategory('Author pages with unknown gender in Wikidata') else addCategory('Author pages with no gender in Wikidata') end if gender == 'transgender female' or gender == 'transgender male' then addCategory('Transgender and transsexual authors') end else addCategory('Author pages with gender manually categorised') end constructCategories(args.wd_entity) -- Categorisation of author pages with interwiki links (used for maintenance view only, links come from WD) if args.wikipedia then addCategory('Author pages linking to Wikipedia') end if args.wikiquote then addCategory('Author pages linking to Wikiquote') end if args.commons then addCategory('Author pages linking to Wikimedia Commons') end if args.commonscat then addCategory('Author pages linking to Wikimedia Commons categories') end -- Whether page is connected to Wikidata if (args.namespace or current_title.nsText) == 'Author' then if args.wd_entity then addCategory('Author pages connected to Wikidata') else addCategory('Author pages not connected to Wikidata') end end end -- microformat local microformat = mw.html.create('div') :attr('id', 'ws-data') :addClass('vcard ws-noexport') :css({['display'] = 'none', ['speak'] = 'none'}) local microformat_wikitext = { tostring(mw.html.create('span'):attr('id', 'ws-article-id'):wikitext(current_title.id)) }	if args.wd_entity then table.insert(			microformat_wikitext,			tostring(mw.html.create('span') :attr('id', 'ws-article-id') :wikitext(args.wd_entity.id) )		)	end if name_text then table.insert(			microformat_wikitext,			tostring(mw.html.create('span') :attr('id', 'ws-name') :addClass('fn') :wikitext(name_text) )		)		table.insert(			microformat_wikitext,			tostring(mw.html.create('span') :addClass('n') :wikitext(table.concat({ tostring(mw.html.create('span'):addClass('given-name'):wikitext(firstname or '')), tostring(mw.html.create('span'):addClass('family-name'):wikitext(lastname or '')) }))			)		)	end if args.defaultsort then table.insert(microformat_wikitext, tostring(mw.html.create('span'):attr('id', 'ws-key'):wikitext(args.defaultsort))) end if args.image then table.insert(microformat_wikitext, tostring(mw.html.create('span'):attr('id', 'ws-image'):wikitext(args.image))) end if args.birthyear then table.insert(			microformat_wikitext,			tostring(mw.html.create('span') :attr('id', 'ws-birthdate') :addClass('bday') :wikitext(date({['year_type'] = 'birth', ['year'] = args.birthyear})) )		)	end if args.deathyear then table.insert(			microformat_wikitext,			tostring(mw.html.create('span') :attr('id', 'ws-deathdate') :addClass('dday') :wikitext(date({['year_type'] = 'death', ['year'] = args.deathyear})) )		)	end if args.wikipedia then table.insert(microformat_wikitext, tostring(mw.html.create('span'):attr('id', 'ws-wikipedia'):wikitext(args.wikipedia))) end if args.wikiquote then table.insert(microformat_wikitext, tostring(mw.html.create('span'):attr('id', 'ws-wikiquote'):wikitext(args.wikiquote))) end if args.commonscat then table.insert(microformat_wikitext, tostring(mw.html.create('span'):attr('id', 'ws-commons'):wikitext(args.commonscat))) elseif args.commons then table.insert(microformat_wikitext, tostring(mw.html.create('span'):attr('id', 'ws-commons'):wikitext('Category' .. args.commons))) end if args.description then table.insert(microformat_wikitext, tostring(mw.html.create('span'):attr('id', 'ws-description'):addClass('note'):wikitext(args.description))) end microformat:wikitext(table.concat(microformat_wikitext)) -- assemble local cats = '' if not yesno(args.nocat) then cats = (args.categories or '') .. getCategories end args.post_notes = image_display .. defaultsort_magicword .. cats .. tostring(microformat) args.wikidataswitch = true return construct_header(args) end

-- for testing

function p._getCategories(args) p._author(args) return getCategories end function p.getCategories(frame) return p._getCategories(getArgs(frame)) end

function p._date(args) args.wd_entity = get_wd_entity(args) args.year_type = args.year_type or args['type'] or 'birth' return date(args) end function p.date(frame) return p._date(getArgs(frame)) end

-- used by Module:Person

function p._dates(args) args.wd_entity = get_wd_entity(args) return dates(args) end function p.dates(frame) return p._dates(getArgs(frame)) end

function p._get_image(args) args.wikidata = args.wikidata or args.wikidata_id -- alias if yesno(args['nocat']) then return get_image(args) else return get_image(args) .. getCategories end end function p.get_image(frame) return p._get_image(getArgs(frame)) end

function p._construct_defaultsort(args) return construct_defaultsort(args) end function p.construct_defaultsort(frame) return p._construct_defaultsort(getArgs(frame)) end

-- Used by Module:Disambiguation and Template:Author function p._author(args) return author(args) end function p.author(frame) return p._author(getArgs(frame)) end

return p