wikipedia-module-citeq/module_citeq.lua

1031 lines
38 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

require('strict')
local wdib = require('Module:WikidataIB')
local getValue = wdib._getValue
local _getPropOfProp = wdib._getPropOfProp
local followQid = wdib._followQid
--local getPropertyIDs = wdib._getPropertyIDs
local cite_cfg = mw.loadData ('Module:Cite/config')
local cfg = mw.loadData ('Module:Cite_Q/config')
local cs1_module = 'Module:Citation/CS1' -- don't load the module yet, may not be needed
local cs1_cfg = mw.loadData('Module:Citation/CS1/Configuration')
--------------------------------------------------------------------------------
-- makeOrdinal needs to be internationalised along with cfg.i18n_t
-- takes cardinal number as a numeric and returns the ordinal as a string
-- we need three exceptions in English for 1st, 2nd, 3rd, 21st, .. 31st, etc.
--------------------------------------------------------------------------------
local makeOrdinal = function(cardinal)
local card = tonumber(cardinal)
if not card then return cardinal end
local ordsuffix = cfg.i18n_t.ordinal.default
if card % 10 == 1 then
ordsuffix = cfg.i18n_t.ordinal[1]
elseif card % 10 == 2 then
ordsuffix = cfg.i18n_t.ordinal[2]
elseif card % 10 == 3 then
ordsuffix = cfg.i18n_t.ordinal[3]
end
-- In English, 1, 21, 31, etc. use 'st', but 11, 111, etc. use 'th'
-- similarly for 12 and 13, etc.
if (card % 100 == 11) or (card % 100 == 12) or (card % 100 == 13) then
ordsuffix = cfg.i18n_t.ordinal.default
end
return card .. ordsuffix
end
--[[--------------------------< I S _ S E T >------------------------------------------------------------------
Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.
]]
local function is_set( var )
return not (var == nil or var == '')
end
--[[--------------------------< I N _ A R R A Y >--------------------------------------------------------------
Whether needle is in haystack (taken from Module:Citation/CS1/Utilities)
]]
local function in_array( needle, haystack )
if needle == nil then
return false
end
for n, v in ipairs( haystack ) do
if v == needle then
return n
end
end
return false
end
--[[--------------------------< A C C E P T _ V A L U E >-------------------------------------------------------
Accept WD value by framing in ((...)) if param_val is equal to keyword; else pass-through WD value as is.
]]
local function accept_value( param_val, wd_val )
local val = param_val
if val then
if in_array (val, {'accept', '))((', ':d:'}) then
val = '((' .. wd_val .. '))'
elseif '((accept))' == val then
val = 'accept'
elseif '(())(())' == val then
val = '))(('
elseif '((:d:))' == val then
val = ':d:'
else
val = wd_val
end
end
return val
end
-- function to fetch a value to display
local function makelink(v, out, link, maxpos, wdl)
local label
if v.mainsnak.snaktype == "value" then
if v.mainsnak.datatype == "wikibase-item" then
local qnumber = v.mainsnak.datavalue.value.id
local sitelink = mw.wikibase.getSitelink(qnumber)
if qnumber == "Q2818964" then sitelink = nil end -- suppress link to "Various authors"
-- Last name, first name(s)
local last_name = getValue({"P734", qid=qnumber, maxvals=1, ps=2})
local first_names = getValue({"P735", qid=qnumber, maxvals=0, ps=2})
if first_names then
first_names = mw.text.trim(first_names:gsub(', ', ' '))
end
if v.qualifiers and v.qualifiers.P1932 then
label = v.qualifiers.P1932[1].datavalue.value -- "object named as"
else
label = mw.wikibase.getLabel(qnumber)
if label then
label = mw.text.nowiki(label)
else
label = qnumber -- should add tracking category
end
end
local position = maxpos + 1 -- Default to 'next' author.
-- use P1545 (series ordinal) instead of default position.
if v.qualifiers and v.qualifiers.P1545 and v.qualifiers.P1545[1] then
position = tonumber(v.qualifiers.P1545[1].datavalue.value)
end
maxpos = math.max(maxpos, position)
if sitelink then
-- just the plain name,
-- but keep a record of the links, using the same index
if last_name and first_names then
out[position] = {
label = label,
last = last_name or nil,
first = first_names or nil,
}
else
out[position] = label
end
link[position] = sitelink
else
if wdl then
-- show that there's a Wikidata entry available
out[position] = string.format(
"[[:d:Q%s|%s]]&nbsp;<span title='%s'>[[File:Wikidata-logo.svg|16px|alt=|link=]]</span>",
v.mainsnak.datavalue.value["numeric-id"],
label,
cfg.i18n_t["errors"]["local-article-not-found"]
)
else
-- no Wikidata links wanted, so just give the plain label
if last_name and first_names then
out[position] = {
label = label,
last = last_name or nil,
first = first_names or nil,
}
else
out[position] = label
end
end
end
elseif v.mainsnak.datatype == "string" then
local position = maxpos + 1 -- Default to 'next' author.
-- use P1545 (series ordinal) instead of default position.
if v.qualifiers and v.qualifiers.P1545 and v.qualifiers.P1545[1] then
position = tonumber(v.qualifiers.P1545[1].datavalue.value)
end
maxpos = math.max(maxpos, position)
-- Names from "author given names" and "author last names" qualifiers
local last_name
local first_names
if v.qualifiers and v.qualifiers.P9688 and v.qualifiers.P9688[1] then
last_name = v.qualifiers.P9688[1].datavalue.value
end
if v.qualifiers and v.qualifiers.P9687 and v.qualifiers.P9687[1] then
first_names = mw.text.trim(v.qualifiers.P9687[1].datavalue.value:gsub(', ', ' '))
end
if last_name and first_names then
out[position] = {
label = v.mainsnak.datavalue.value,
last = last_name or nil,
first = first_names or nil,
}
else
out[position] = v.mainsnak.datavalue.value
end
else
-- not a wikibase-item or a string!
end
else
-- try the "object named as" qualifier, otherwise use the unknown author item
if v.qualifiers and v.qualifiers.P1932 then
label = v.qualifiers.P1932[1].datavalue.value -- "object named as"
-- Names from "author given names" and "author last names" qualifiers
local last_name
local first_names
if v.qualifiers and v.qualifiers.P9688 and v.qualifiers.P9688[1] then
last_name = v.qualifiers.P9688[1].datavalue.value
end
if v.qualifiers and v.qualifiers.P9687 and v.qualifiers.P9687[1] then
first_names = mw.text.trim(v.qualifiers.P9687[1].datavalue.value:gsub(', ', ' '))
end
if last_name and first_names then
out[position] = {
label = label,
last = last_name or nil,
first = first_names or nil,
}
else
out[position] = label
end
else
label = mw.wikibase.getLabel("Q4233718"):gsub("^%l", mw.ustring.upper) .. (cfg.i18n_t.trackingcats["unknown-author"] or "")
end
maxpos = maxpos + 1
out[maxpos] = label
end
return maxpos
end
--[=[-------------------------< G E T _ N A M E _ L I S T >----------------------------------------------------
get_name_list -- adapted from getAuthors code taken from Module:RexxS
arguments:
nl_type - type of name list to fetch: nl_type = 'author' for authors; 'editor' for editors; 'translator' for translators
args - pointer to the parameter arguments table from the template call
qid - value from |qid= parameter; the Q-id of the source (book, etc.) in qid
wdl - value from the |wdl= parameter; a Boolean passed to enable links to Wikidata when no article exists
returns nothing; modifies the args table
]=]
local function get_name_list (nl_type, args, qid, wdl)
local propertyID
local fallbackID
if nl_type =="author" then
propertyID = 'P50' -- for authors
fallbackID = 'P2093' -- author-string
elseif nl_type =="editor" then
propertyID = 'P5769' -- "editor-in-chief"
fallbackID = 'P98' -- for editors - So-called "fallbacks" are actually a second set of properties processed
-- TBD. Take book series editors into account as well (if they have a separate P code as well)?
elseif nl_type == "translator" then
propertyID = 'P655' -- for translators
fallbackID = nil
-- elseif 'contributor' == nl_type then
-- f.e. author of forewords (P2679) and afterwords (P2680); requires |contribution=, |title= and |author=
-- propertyID = 'P' -- for contributors
-- fallbackID = nil
else
return -- not specified so return
end
-- wdl is a Boolean passed to enable links to Wikidata when no article exists
-- if "false" or "no" or "0" is passed set it false
-- if nothing or an empty string is passed set it false
if wdl and (#wdl > 0) then
wdl = wdl:lower()
wdl = in_array (wdl, {"false", "no", "0"})
else
-- wdl is empty, so
wdl = false
end
local props = nil
local fallback = nil
if mw.wikibase.entityExists(qid) then
props = mw.wikibase.getAllStatements(qid, propertyID)
if props and fallbackID then
fallback = mw.wikibase.getAllStatements(qid, fallbackID)
end
end
-- Make sure it actually has at least one of the properties requested
if not (props and props[1]) and not (fallback and fallback[1]) then
return nil
end
-- So now we have something to return:
-- table 'out' is going to store the names(s):
-- and table 'link' will store any links to the name's article
local out = {}
local link = {}
local maxpos = 0
if props and props[1] then
for k, v in pairs(props) do
if 'deprecated' ~= v.rank then -- ignore deprecated names
maxpos = makelink(v, out, link, maxpos, wdl)
end
end
end
if fallback and fallback[1] then
-- second properties
for k, v in pairs(fallback) do
if 'deprecated' ~= v.rank then -- ignore deprecated names
maxpos = makelink(v, out, link, maxpos, wdl)
end
end
end
-- if there's anything to return, then insert the additions in the template arguments table
-- in the form |author1=firstname secondname |author2= ...
-- Renumber, in case we have inconsistent numbering
local keys = {}
for k, v in pairs(out) do
keys[#keys + 1] = k
end
table.sort(keys) -- as they might be out of order
for i, k in ipairs(keys) do
local label
if out[k].label then
label = out[k].label
else
label = out[k]
end
label = label:gsub('&#39;', '\''); -- prevent cs1|2 multiple names categorization; replace html entity with the actual character
mw.log(i .. " " .. k .. " " .. label)
if args[nl_type .. i] then -- name gets overwritten
-- pull corresponding -link only if overwritten name is same as WD name
if link[k] and (args[nl_type .. i] == label) then
args[nl_type .. '-link' .. i] = args[nl_type .. '-link' .. i] or link[k] -- author-linkn or editor-linkn
end
else -- name does not get overwritten, so pull name from WD
if out[k].first and out[k].last then
-- First/last names were found, so add them
args[nl_type .. '-last' .. i] = args[nl_type .. '-last' .. i] or out[k].last
args[nl_type .. '-first' .. i] = args[nl_type .. '-first' .. i] or out[k].first
else
args[nl_type .. i] = label
end
if link[k] then
args[nl_type .. '-link' .. i] = args[nl_type .. '-link' .. i] or link[k] -- author-linkn or editor-linkn
end
end
end
end
-- gets language codes used for a monolingual text property as a table
local function _getLangOfProp(qid, pid)
if not pid then return {} end
local out = {}
local props = mw.wikibase.getAllStatements(qid, pid)
for i, v in ipairs(props) do
if v.mainsnak.datatype == "monolingualtext" and v.mainsnak.datavalue then
out[#out + 1] = v.mainsnak.datavalue.value.language
end
end
return out
end
local function getLangOfProp(frame)
local pid = frame.args.pid or mw.text.trim(frame.args[1] or "")
if pid == "" then return end
local qid = frame.args.qid
if qid == "" then qid = nil end
return table.concat(_getLangOfProp(qid, pid), ", ")
end
-- gets the language codes of a Wikidata entry as a table
local function _lang_code(qid)
-- P407 "language of work or name"
local lc = _getPropOfProp( {qid = qid, prop1 = "P407", prop2 = "P424", ps = 1} ) -- "Wikimedia language code"
if lc then return mw.text.split( lc, "[, ]+" ) end
lc = _getPropOfProp( {qid = qid, prop1 = "P407", prop2 = "P218", ps = 1} ) -- "ISO 639-1 code"
if lc then return mw.text.split( lc, "[, ]+" ) end
return _getLangOfProp(qid, "P1476") -- "title"
end
local function lang_code(frame)
return table.concat(_lang_code(frame.args.qid or mw.text.trim(frame.args[1] or "")), ", ")
end
-- export for debug
local function getPropOfProp(frame)
return _getPropOfProp(frame.args)
end
-- wraps a string in nowiki unless disable flag is set
local function wrap_nowiki(str, disable)
if disable then return str or '' end
return mw.text.nowiki(str or '')
end
-- returns the first string sequence of 3 or more digits in a date string, or nil
local function yearFromDate(datestr)
if datestr then
return string.match(datestr, '%d%d%d+')
end
return datestr
end
-- Attempt to determine heuristically if we're citing a news article
local function isNewsArticle(qid, args)
local newsitems = {
'Q5707594', -- news article
'Q30070590', -- magazine article
-- TODO: somehow use WHERE {?item (wdt:P31|wdt:P31/wdt:P279*) wd:Q5707594.}
-- main subclasses of news article:
'Q87849013', -- AP article
'Q2144639', -- feature story
'Q139172839', -- top story
'Q18339884', -- cover story
'Q3326038', -- critical review
'Q4202018', -- interview
'Q309481', -- obituary
'Q108970988', -- column
'Q2602337', -- op-ed
'Q59908', -- opinion piece
}
-- most of the time a cheap lookup to a single value will work
if args['instance-of'] and in_array(args['instance-of'], newsitems) then
return true
end
-- otherwise check multiple non-deprecated values
local instance_of = mw.wikibase.getBestStatements(qid, 'P31')
if instance_of and instance_of[1] then
for k, v in pairs(instance_of) do
if v.mainsnak.snaktype == "value" and v.mainsnak.datatype == "wikibase-item" then
if in_array(v.mainsnak.datavalue.value.id, newsitems) then
return true
end
end
end
end
return false
end
-- Figure out a URL access value, from the "online access status" (P6954) qualifier of an identifier
-- The [[Template:Cite journal]] docs state these should only allow "free"
-- e.g. DOI = P356
local function getIdentifierAccess(qid, args, identifier)
if not args[identifier] then
return nil
end
-- check DOI registrant is known free, using CS1 config
-- TODO: this should also be modelled in Wikidata
if identifier == 'doi' then
local registrant, suffix = mw.ustring.match(args.doi:lower(), '^10%.([^/]+)/([^%s–]+)$');
if registrant and suffix and cs1_cfg.known_free_doi_registrants_t[registrant] then
return 'free'
elseif registrant and suffix and cs1_cfg.extended_registrants_t[registrant] then
for _, incipit in ipairs(cs1_cfg.extended_registrants_t[registrant]) do
if mw.ustring.find (suffix, '^' .. incipit:lower()) then
return 'free'
end
end
end
end
-- fetch statement values to examine qualifiers
local ids = mw.wikibase.getBestStatements(qid, cfg.simple_properties_t[identifier]['id'])
if not ids or not ids[1] then
return nil
end
if not ids[1].qualifiers or not ids[1].qualifiers.P6954 then
return nil
end
-- Check property constraints (P2302) in definition of online access status (P6954)
-- Q66439598 = 'limited', -- partial open access
-- Q107459441 = 'registration', -- registration required
-- Q910845 = 'subscription', -- paywall
-- Q16572632 = 'subscription', -- login (see note on P6954)
local free_status = {
'Q24707952', -- free to read
'Q232932', -- open access
}
local access = ids[1].qualifiers.P6954[1]
if access.datatype == "wikibase-item" then
local accessQID = access.datavalue.value.id
if in_array(accessQID, free_status) then
return 'free'
end
end
return nil
end
-- sort sequence table whose values are key-value pairs by key
local function comp_key(a, b)
local param_a, enum_a = a[1]:match ('(%D+)(%d+)$'); -- get param name and enumerator from <a>
local param_b, enum_b = b[1]:match ('(%D+)(%d+)$'); -- get param name and enumerator from <b>
if enum_a and enum_b then -- if both parameters have neumerators
if param_a == param_b then -- are parameter names the same?
return tonumber (enum_a) < tonumber (enum_b); -- yes: compare enumerators
end
end
return a[1] < b[1]; -- alpha sort if here
end
-- sort sequence table whose values are key-value pairs by value
local function comp_val(a, b)
return a[2] < b[2]
end
--[[-------------------------< C I T E _ Q >------------------------------------------------------------------
Takes standard CS1|2 template parameters and passes all to {{citation}}. If neither of |author= and |author1=
are set, calls get_authors() to try to get an author name-list from Wikidata. The result is passed to
{{citation}} for rendering.
--]]
local function _cite_q (citeq_args)
local frame = mw.getCurrentFrame()
-- parameters that don't get passed to Citation
local expand = citeq_args.expand -- when set to anything, causes {{cite q}} to render <code><nowiki>{{citation|...}}</nowiki></code>
local qid = citeq_args.qid or citeq_args[1]
local wdl = citeq_args.wdl
local template = citeq_args.template
citeq_args.expand = nil
citeq_args[1] = nil
citeq_args.qid = nil
citeq_args.wdl = nil
citeq_args.template = nil
-- if title supplied, flag to not read html title
local titleforced = (citeq_args.title ~= nil)
local oth = {}
-- put the language codes into a sequential table langcodes[]
local langcodes = {}
if citeq_args.language then
-- check these are a supported language codes
for lc in mw.text.gsplit( citeq_args.language, "[, ]+", false ) do
langcodes[#langcodes+1] = mw.language.isSupportedLanguage(citeq_args.language) and citeq_args.language
end
end
if not langcodes[1] then
-- try to find language of work
langcodes = _lang_code(qid)
end
if not langcodes[1] then
-- try fallback to journal's language
local journal_qid = followQid({qid = qid, props = "P1433"})
langcodes = journal_qid and _lang_code(journal_qid)
end
citeq_args.language = citeq_args.language or table.concat(langcodes, ", ")
-- loop through list of simple properties and get their values in citeq_args
for name, data in pairs(cfg.simple_properties_t) do
if data.qidonly then
citeq_args[name] = followQid({qid = qid, props = data.id})
if citeq_args[name] == qid then
citeq_args[name] = nil
end
end
if not citeq_args[name] then
citeq_args[name] = getValue( {data.id, fwd = "ALL", osd = "no", noicon = "true", qid = qid, maxvals = data.maxvals, linked = data.linked, rank = data.rank or "best", citeq_args[name] } )
end
if data.populate_from_journal then
local publishedin = getValue( {"P1433", ps = 1, qid = qid, maxvals = 0, citeq_args[name], qual = data.id, qualsonly = 'yes'} )
citeq_args[name] = publishedin or _getPropOfProp({qid = qid, prop1 = "P1433", prop2 = data.id, maxvals = data.maxvals, ps = 1})
end
if citeq_args[name] and citeq_args[name]:find (cfg.i18n_t.trackingcats["missing-wikidata"], 1, true) then
-- try fallback to work's native language
citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals = data.maxvals, linked = "no", lang = langcodes[1] } )
if citeq_args[name]:find('^Q%d+$') then -- qid was returned
-- try fallback to qid's native language
local qid_languages = _lang_code(citeq_args[name])
citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals = data.maxvals, linked = "no", lang = qid_languages[1] } )
if citeq_args[name]:find('^Q%d+$') then -- qid was returned again
citeq_args[name] = nil
else
-- record the language found if no lang specified
citeq_args.language = citeq_args.language or qid_languages[1]
end
end
end
if data.others then
oth[#oth + 1] = citeq_args[name] and (name:gsub("^%l", string.upper) .. ": " .. citeq_args[name])
citeq_args[name] = nil
end
end
citeq_args.doi = citeq_args.doi and citeq_args.doi:lower() or nil; -- force |doi= value to be lower case
-- URL access for identifiers, from P6954 online access status
-- The [[Template:Cite journal]] docs state these should only allow "free"
citeq_args['bibcode-access'] = citeq_args['bibcode-access'] or getIdentifierAccess(qid, citeq_args, 'bibcode');
citeq_args['doi-access'] = citeq_args['doi-access'] or getIdentifierAccess(qid, citeq_args, 'doi');
citeq_args['hdl-access'] = citeq_args['hdl-access'] or getIdentifierAccess(qid, citeq_args, 'hdl');
citeq_args['jstor-access'] = citeq_args['jstor-access'] or getIdentifierAccess(qid, citeq_args, 'jstor');
-- citeq_args['ol-access'] = citeq_args['ol-access'] or getIdentifierAccess(qid, citeq_args, 'ol');
citeq_args['osti-access'] = citeq_args['osti-access'] or getIdentifierAccess(qid, citeq_args, 'osti');
citeq_args['ssrn-access'] = citeq_args['ssrn-access'] or getIdentifierAccess(qid, citeq_args, 'ssrn');
citeq_args['s2cid-access'] = citeq_args['s2cid-access'] or getIdentifierAccess(qid, citeq_args, 's2cid');
citeq_args.others = citeq_args.others or table.concat(oth, ". ")
if citeq_args.others == "" then
citeq_args.others = nil
end
citeq_args.journal = citeq_args.journal and citeq_args.journal:gsub("^''", ""):gsub("''$", ""):gsub("|''", "|"):gsub("'']]", "]]")
citeq_args.ol = (getValue( {"P648", ps = 1, qid = qid, maxvals = 1, citeq_args.ol } ) or ''):gsub("^OL(.+)$", "%1")
if citeq_args.ol == "" then
citeq_args.ol = nil
end
-- TBD. Take care of |ol-access=?
citeq_args.biorxiv = citeq_args.biorxiv and ("10.1101/" .. citeq_args.biorxiv)
citeq_args.isbn = getValue( {"P957", ps = 1, qid = qid, maxvals = 1, rank="best", citeq_args.isbn } ) -- try ISBN 10 (only one value accepted)
-- if url then see if there's an archive: citeq_args.url
local url
if not citeq_args.url then
for i, pr in ipairs( {"P953", "P856", "P2699"} ) do
url = getValue( {pr, ps = 1, qid = qid, maxvals = 1, qual="P1065" } )
if url then
citeq_args.url = mw.text.split( url, " (", true )[1]
local arcurl = mw.ustring.match( url, " %((.*)%)" ) -- when there is an archive url, <url> holds: url<space>(archive url); here extract the archive url if present
if arcurl then
local arcy, arcm, arcd = arcurl:match("(20%d%d)%p?(%d%d)%p?(%d%d)")
if arcy and arcm and arcd then
citeq_args["archive-url"] = arcurl
citeq_args["archive-date"] = tonumber(arcd) .. " " .. cfg.i18n_t.months[tonumber(arcm)] .. " " .. arcy
end
end
break
end
end
end
if citeq_args.publisher == "Unknown" then -- look for "stated as" (P1932)
local stated_as = getValue( {"P123", ps = 1, qid = qid, maxvals = 1, qual="P1932", qo="y"} )
if stated_as then citeq_args.publisher = stated_as end
end
-- Handle thesis args - degree, supervising institution
local is_thesis = false
if citeq_args.thesis_degree then
is_thesis = true
citeq_args.degree = citeq_args.thesis_degree
citeq_args.thesis_degree = nil
end
if citeq_args.thesis_submitted_to then
is_thesis = true
citeq_args.publisher = citeq_args.thesis_submitted_to
citeq_args.thesis_submitted_to = nil
end
if not titleforced then
-- Handle subtitle.
if citeq_args.title then
local subtitle = mw.wikibase.getBestStatements (qid, 'P1680');
if 0 ~= #subtitle then
subtitle = subtitle[1].mainsnak.datavalue.value.text;
citeq_args.title = citeq_args.title .. ": " .. subtitle
end
end
local htmltitle = getValue( {"P1476", qual = "P6833", ps = 1, qid = qid, maxvals = 1, qo = "y"} )
if htmltitle then
citeq_args.title = htmltitle:gsub("</?i>", "''")
else
local title_display = citeq_args.title
or mw.wikibase.getLabel(qid)
or (langcodes[1] and mw.wikibase.getLabelByLang(qid, langcodes[1]))
if citeq_args.url then
citeq_args.title = wrap_nowiki(title_display)
else
local slink = mw.wikibase.getSitelink(qid)
local slink_flag = false
local wrap_title = ''
local wslink = false
if not slink then
-- See if we have wikisource
if not citeq_args.url then
local wikisource_sitelink = mw.wikibase.getSitelink(qid, cfg.i18n_t.wikisource) or nil
if wikisource_sitelink then
slink = ':s:'..wikisource_sitelink
wslink = true
end
end
end
if citeq_args.title then
if slink then
wrap_title = wrap_nowiki(citeq_args.title)
slink_flag = true
else
citeq_args.title = wrap_nowiki(citeq_args.title)
end
else
if slink and not wslink then
if slink:lower() == title_display:lower() then
citeq_args.title = '[[' .. slink .. ']]'
else
wrap_title = wrap_nowiki(slink:gsub("%s%(.+%)$", ""):gsub(",.+$", ""))
slink_flag = true
end
elseif wslink then
wrap_title = wrap_nowiki(title_display)
slink_flag = true
else
citeq_args.title = wrap_nowiki(title_display)
end
end
if slink_flag then
if slink == wrap_title and not wslink then -- direct link
citeq_args.title = '[[' .. slink .. ']]'
else -- piped link
citeq_args.title = '[[' .. slink .. '|' .. wrap_title .. ']]'
end
end
end
end
end
-- TBD: incorporate |at, |sheets= and |sheet= here as well
-- Sort out what should happen if several of them are given at the same time
if citeq_args.page or citeq_args.p then -- let single take precedence over multiple
citeq_args.pages = nil
citeq_args.pp = nil
end
if citeq_args.pages then
local _, count = string.gsub(citeq_args.pages, "[,;%s]%d+", "")
if count == 1 then
citeq_args.page = citeq_args.pages
citeq_args.pages = nil
end
end
if is_set (qid) then
if not is_set (citeq_args.author) and not is_set (citeq_args.author1)
and not is_set (citeq_args.subject) and not is_set (citeq_args.subject1)
and not is_set (citeq_args.host) and not is_set (citeq_args.host1)
and not is_set (citeq_args.last) and not is_set (citeq_args.last1)
and not is_set (citeq_args.surname) and not is_set (citeq_args.surname1)
and not is_set (citeq_args['author-last']) and not is_set (citeq_args['author-last1']) and not is_set (citeq_args['author1-last'])
and not is_set (citeq_args['author-surname']) and not is_set (citeq_args['author-surname1']) and not is_set (citeq_args['author1-surname1']) then -- if neither are set, try to get authors from Wikidata
get_name_list ('author', citeq_args, qid, wdl) -- modify citeq_args table with authors from Wikidata
end
if not is_set (citeq_args.editor) and not is_set (citeq_args.editor1)
and not is_set (citeq_args['editor-last']) and not is_set (citeq_args['editor-last1']) and not is_set (citeq_args['editor1-last'])
and not is_set (citeq_args['editor-surname']) and not is_set (citeq_args['editor-surname1']) and not is_set (citeq_args['editor1-surname']) then -- if neither are set, try to get editors from Wikidata
get_name_list ('editor', citeq_args, qid, wdl) -- modify citeq_args table with editors from Wikidata
end
if not is_set (citeq_args.translator) and not is_set (citeq_args.translator1)
and not is_set (citeq_args['translator-last']) and not is_set (citeq_args['translator-last1']) and not is_set (citeq_args['translator1-last'])
and not is_set (citeq_args['translator-surname']) and not is_set (citeq_args['translator-surname1']) and not is_set (citeq_args['translator1-surname']) then -- if neither are set, try to get translators from Wikidata
get_name_list ('translator', citeq_args, qid, wdl) -- modify citeq_args table with translators from Wikidata
end
end
for k, v in pairs(citeq_args) do
if in_array (v, {'(())', 'unset', 'ignore'}) or 'string' ~= type(k) then -- empty accept-as-is-written (()) markup to indicate an empty/unused parameter value, other ((...)) markups are deliberately passed down to {{citation}}
citeq_args[k] = nil
elseif in_array (v, {'((unset))', '((ignore))'}) then -- strip off markup for free-text values clashing with local keywords
citeq_args[k] = 'unset'
end
end
local author_count = 0
for k, v in pairs(citeq_args) do
if k:find("^author%d+$") then
author_count = author_count + 1
end
end
if author_count > 8 then -- convention in astronomy journals, optional mode for this?
if 'all' == citeq_args['display-authors'] then
citeq_args['display-authors'] = nil; -- unset because no longer needed
else
citeq_args['display-authors'] = citeq_args['display-authors'] or 3 -- limit to three displayed names
end
end
local editor_count = 0
for k, v in pairs(citeq_args) do
if k:find("^editor%d+$") then
editor_count = editor_count + 1
end
end
if editor_count > 8 then -- convention in astronomy journals, optional mode for this?
if 'all' == citeq_args['display-editors'] then
citeq_args['display-editors'] = nil; -- unset because no longer needed
else
citeq_args['display-editors'] = citeq_args['display-editors'] or 3 -- limit to three displayed names
end
end
-- change edition to ordinal if it's set and numeric
citeq_args.edition = citeq_args.edition and makeOrdinal(citeq_args.edition)
-- code to make a guess what template to use from the supplied parameters
-- (first draft for proof-of-concept)
local pubyear = yearFromDate(citeq_args['publication-date'])
local is_news = isNewsArticle(qid, citeq_args)
if is_news then
template = template or "news"
-- some items use P123 (publisher) as the newspaper instead of P1433 (published in)
if citeq_args.publisher and not citeq_args.journal then
citeq_args.journal = citeq_args.publisher
citeq_args.publisher = nil
end
elseif citeq_args.journal then
template = template or "journal"
citeq_args['publication-date'] = pubyear or citeq_args['publication-date']
-- articles can be published in books/proceedings, otherwise suppress publisher
if not citeq_args.isbn then
citeq_args.publisher = nil
end
-- only display ISSN if there are no article identifiers
if citeq_args.issn then
if citeq_args.doi or citeq_args.pmid or citeq_args.pmc or citeq_args.arxiv or
citeq_args.bibcode or citeq_args.jstor or citeq_args.ssrn or citeq_args.s2cid or
citeq_args.hdl or citeq_args.biorxiv or citeq_args.osti or citeq_args.zbl or
citeq_args.jfm or citeq_args.mr then
citeq_args.issn = nil
end
end
elseif is_thesis then
template = template or "thesis"
elseif citeq_args.isbn then
template = template or "book"
citeq_args.asin = nil -- suppress ASIN if ISBN exists
citeq_args['publication-date'] = pubyear or citeq_args['publication-date']
elseif citeq_args.website then
template = template or "web"
end
-- |id= could hold more than one identifier pulled from Wikidata not supported by {{citation}}, right now only add our qid to the list
local list_sep = '. '
if citeq_args.mode ~= 'cs1' then
list_sep = ', '
end
local id = cfg.i18n_t.wdq.. '&nbsp;[[:d:' .. qid .. '|' .. qid .. ']]'
local old_id = citeq_args.id
if wdl then -- show WD logo
id = id .. '[[File:Wikidata-logo.svg|16px|alt=|link=]]' -- possibly replace by WD edit icon?
end
if is_set (old_id) then
citeq_args.id = old_id .. list_sep .. id -- append to user-specified contents
else
citeq_args.id = id
end
local opt_cat = ''
-- Indicator if item has been retracted
local retract = ''
if citeq_args.retracted_by then
opt_cat = cfg.i18n_t.trackingcats.retracted
local retract_id = string.gsub(citeq_args.retracted_by, 'Q', '')
local retr_params = {
citeq = 'yes',
id = frame:expandTemplate{
title = 'QID', args = {qid = retract_id}
}
}
retr_params.doi = getValue({"P356", ps = 1, qid = citeq_args.retracted_by}) or nil
retr_params.doi = retr_params.doi and retr_params.doi:lower() or nil
retr_params.bibcode = getValue({"P819", ps = 1, qid = citeq_args.retracted_by}) or nil
retr_params.pmc = getValue({"P932", ps = 1, qid = citeq_args.retracted_by}) or nil
retr_params.pmid = getValue({"P698", ps = 1, qid = citeq_args.retracted_by}) or nil
-- check qualifiers for access=free
retr_params['doi-access'] = getIdentifierAccess(citeq_args.retracted_by, retr_params, 'doi')
retr_params['bibcode-access'] = getIdentifierAccess(citeq_args.retracted_by, retr_params, 'bibcode')
if 'yes' == citeq_args['retracted-intentional'] then
retr_params.intentional = 'yes'
end
retract = ' ' .. frame:expandTemplate{ title = "Retracted", args = retr_params }
end
-- remove non-CS1 retraction properties
citeq_args.retracted_by = nil
citeq_args['retracted-intentional'] = nil
-- clean up any blank parameters
for k, v in pairs(citeq_args) do
if v == "" then citeq_args[k] = nil end
end
-- if |expand=<anything>, write a nowiki'd version to see what the {{citation}} template call looks like
if expand then
local expand_args = { "{{" .. (template and ("Cite "..template) or "Citation") } -- init with citation template
if expand == "self" then
citeq_args.id = old_id -- restore original |id= parameter
expand_args = { cfg.i18n_t.cite_q .. qid } -- expand to itself
end
-- make a sortable table and sort it by param name
local sorttable = {}
for param, val in pairs (citeq_args) do
table.insert(sorttable, {param, val})
end
table.sort(sorttable, comp_key)
-- add contents to expand_args
for idx, val in ipairs(sorttable) do
table.insert(expand_args, val[1] .. '=' .. val[2])
end
-- make the nowiki'd string and done
return frame:preprocess (table.concat ({'<syntaxhighlight lang="wikitext" inline="1">', table.concat (expand_args, ' |') .. '}}', '</syntaxhighlight>'}));
end
-- Indicator if item has corrigendum/erratum
local erratumid = ""
if citeq_args.erratum then
opt_cat = opt_cat .. cfg.i18n_t.trackingcats.erratum
local erratum_params = {
citeq = 'yes',
id = frame:expandTemplate{
title = 'QID', args = {qid = string.gsub(citeq_args.erratum, 'Q', '')}
}
}
erratum_params.doi = getValue({"P356", ps = 1, qid = citeq_args.erratum}) or nil
erratum_params.doi = erratum_params.doi and erratum_params.doi:lower() or nil
erratum_params.bibcode = getValue({"P819", ps = 1, qid = citeq_args.erratum}) or nil
erratum_params.pmc = getValue({"P932", ps = 1, qid = citeq_args.erratum}) or nil
erratum_params.pmid = getValue({"P698", ps = 1, qid = citeq_args.erratum}) or nil
-- check qualifiers for access=free
erratum_params['doi-access'] = getIdentifierAccess(citeq_args.erratum, erratum_params, 'doi')
erratum_params['bibcode-access'] = getIdentifierAccess(citeq_args.erratum, erratum_params, 'bibcode')
if 'yes' == citeq_args['erratum-checked'] then
erratum_params.checked = 'yes'
end
erratumid = ' ' .. frame:expandTemplate{ title = "Erratum", args = erratum_params }
end
citeq_args.erratum = nil
citeq_args['erratum-checked'] = nil
-- Indicator if item has an expression of concern notice (P14891)
local eoc = ""
if citeq_args['expression-of-concern'] then
opt_cat = opt_cat .. cfg.i18n_t.trackingcats['expression-of-concern']
local eoc_params = {
citeq = 'yes',
id = frame:expandTemplate{
title = 'QID', args = {qid = string.gsub(citeq_args['expression-of-concern'], 'Q', '')}
}
}
eoc_params.doi = getValue({"P356", ps = 1, qid = citeq_args['expression-of-concern']}) or nil
eoc_params.doi = eoc_params.doi and eoc_params.doi:lower() or nil
eoc_params.bibcode = getValue({"P819", ps = 1, qid = citeq_args['expression-of-concern']}) or nil
eoc_params.pmc = getValue({"P932", ps = 1, qid = citeq_args['expression-of-concern']}) or nil
eoc_params.pmid = getValue({"P698", ps = 1, qid = citeq_args['expression-of-concern']}) or nil
-- check qualifiers for access=free
eoc_params['doi-access'] = getIdentifierAccess(citeq_args['expression-of-concern'], eoc_params, 'doi')
eoc_params['bibcode-access'] = getIdentifierAccess(citeq_args['expression-of-concern'], eoc_params, 'bibcode')
if 'yes' == citeq_args['eoc-intentional'] then
eoc_params.intentional = 'yes'
end
eoc = ' ' .. frame:expandTemplate{ title = "Expression of concern", args = eoc_params }
end
citeq_args['expression-of-concern'] = nil
citeq_args['eoc-intentional'] = nil
if citeq_args.replaced_by then
opt_cat = opt_cat .. cfg.i18n_t.trackingcats.replaced
citeq_args.replaced_by = nil
end
if not citeq_args.title then -- emit category link if missing title
opt_cat = opt_cat .. cfg.i18n_t.trackingcats['no-label-or-title'];
end
-- Disable CiteSeerX IDs for now
citeq_args.citeseerx = nil
-- remove any remaining non-CS1 fields
citeq_args['instance-of'] = nil
-- render the template
local lc_template = template and template:lower() or "citation"
local output
if cite_cfg.known_templates_t[lc_template] then
local config_t = {['CitationClass'] = cite_cfg.citation_classes_t[lc_template] or lc_template}; -- set CitationClass value
output = require (cs1_module)._citation (nil, citeq_args, config_t) -- go render the citation
else
output = frame:expandTemplate{ -- render the template
title = "Cite "..template, args = citeq_args
} .. cfg.i18n_t.trackingcats["unknown-template"]
end
return output .. erratumid .. eoc .. retract .. opt_cat
end
local function cite_q (frame)
local args = {}
for k, v in pairs(frame:getParent().args) do
if v ~= "" then args[k] = v end
end
for k, v in pairs(frame.args) do
if v ~= "" then args[k] = v end
end
args.qid = args.qid or args[1] or ""
if args.qid == "" then return nil end
args[1] = nil
local citesep = (args.citesep or "")
if citesep == "" then citesep = ", " end
citesep = citesep:gsub('"', '') -- strip double quotes after setting default to allow |citesep="" as a blank separator
args.citesep = nil
local tag = args.tag or ""
if tag == "" then tag = nil end
args.tag = nil
local list = args.list or ""
if list == "" then list = nil end
args.list = nil
args.language = args.language or args.lang
args.lang = nil
local cites = {}
for q in args.qid:gmatch("Q%d+") do
-- make a new copy of the arguments
local newargs = {}
for k, v in pairs(args) do
if k ~= "qid" then
newargs[k] = v
end
end
newargs.qid = q
if tag == "ref" then
cites[#cites + 1] = frame:callParserFunction{ name = "#tag:ref", args = { _cite_q(newargs), name = q } }
-- expand like this: args = { _cite_q(newargs), name = 'foo', group = 'bar' }
else
cites[#cites + 1] = _cite_q(newargs)
end
end
if list then
return frame:expandTemplate{ title = list, args = cites }
else
return table.concat(cites, citesep)
end
end
--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]
return {
makeOrdinal = makeOrdinal,
_getLangOfProp = _getLangOfProp,
getLangOfProp = getLangOfProp,
lang_code = lang_code,
getPropOfProp = getPropOfProp,
_cite_q = _cite_q,
cite_q = cite_q
}