Module:Wd: Difference between revisions

4,748 bytes added ,  7 August 2023
Trying to fix some issues.
(Created page with "-- Original module located at en:Module:Wd and en:Module:Wd/i18n. local p = {} local arg = ... local i18n local function loadI18n(aliasesP, frame) local title i...")
 
(Trying to fix some issues.)
 
(7 intermediate revisions by 3 users not shown)
Line 1: Line 1:
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].


require("strict")
local p = {}
local p = {}
local arg = ...
local arg = ...
Line 72: Line 73:
image                  = "P18",
image                  = "P18",
author                  = "P50",
author                  = "P50",
authorNameString        = "P2093",
publisher              = "P123",
publisher              = "P123",
importedFrom            = "P143",
importedFrom            = "P143",
wikimediaImportURL      = "P4656",
statedIn                = "P248",
statedIn                = "P248",
pages                  = "P304",
pages                  = "P304",
Line 94: Line 97:
inferredFrom            = "P3452",
inferredFrom            = "P3452",
typeOfReference        = "P3865",
typeOfReference        = "P3865",
column                  = "P3903"
column                  = "P3903",
subjectNamedAs          = "P1810",
wikidataProperty        = "P1687",
publishedIn            = "P1433"
}
}


Line 148: Line 154:
}
}


local Config = {}
local function replaceAlias(id)
if aliasesP[id] then
id = aliasesP[id]
end
 
return id
end


-- allows for recursive calls
local function errorText(code, param)
function Config:new()
local text = i18n["errors"][code]
local cfg = {}
if param then text = mw.ustring.gsub(text, "$1", param) end
setmetatable(cfg, self)
return text
self.__index = self
end


cfg.separators = {
local function throwError(errorMessage, param)
-- single value objects wrapped in arrays so that we can pass by reference
error(errorText(errorMessage, param))
["sep"]  = {copyTable(defaultSeparators["sep"])},
end
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"]  = {copyTable(defaultSeparators["punc"])}
}


cfg.entity = nil
local function replaceDecimalMark(num)
cfg.entityID = nil
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
cfg.propertyID = nil
end
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}


cfg.bestRank = true
local function padZeros(num, numDigits)
cfg.ranks = {true, true, false}  -- preferred = true, normal = true, deprecated = false
local numZeros
cfg.foundRank = #cfg.ranks
local negative = false
cfg.flagBest = false
cfg.flagRank = false


cfg.periods = {true, true, true}  -- future = true, current = true, former = true
if num < 0 then
cfg.flagPeriod = false
negative = true
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))}  -- today as {year, month, day}
num = num * -1
end


cfg.mdyDate = false
num = tostring(num)
cfg.singleClaim = false
numZeros = numDigits - num:len()
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false


cfg.inSitelinks = false
for _ = 1, numZeros do
num = "0"..num
end


cfg.langCode = mw.language.getContentLanguage().code
if negative then
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
num = "-"..num
cfg.langObj = mw.language.new(cfg.langCode)
end


cfg.siteID = mw.wikibase.getGlobalSiteId()
return num
end


cfg.states = {}
local function replaceSpecialChar(chr)
cfg.states.qualifiersCount = 0
if chr == '_' then
cfg.curState = nil
-- replace underscores with spaces
 
return ' '
cfg.prefetchedRefs = nil
else
 
return chr
return cfg
end
end
end


local State = {}
local function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""


function State:new(cfg, type)
for i = 1, #str do
local stt = {}
chr = str:sub(i,i)
setmetatable(stt, self)
self.__index = self


stt.conf = cfg
if not esc then
stt.type = type
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end


stt.results = {}
return strOut
end


stt.parsedFormat = {}
local function buildWikilink(target, label)
stt.separator = {}
if not label or target == label then
stt.movSeparator = {}
return "[[" .. target .. "]]"
stt.puncMark = {}
else
return "[[" .. target .. "|" .. label .. "]]"
end
end


stt.linked = false
-- used to make frame.args mutable, to replace #frame.args (which is always 0)
stt.rawValue = false
-- with the actual amount and to simply copy tables
stt.shortName = false
local function copyTable(tIn)
stt.anyLanguage = false
if not tIn then
stt.unitOnly = false
return nil
stt.singleValue = false
end


return stt
local tOut = {}
end


local function replaceAlias(id)
for i, v in pairs(tIn) do
if aliasesP[id] then
tOut[i] = v
id = aliasesP[id]
end
end


return id
return tOut
end
end


local function errorText(code, param)
-- used to merge output arrays together;
local text = i18n["errors"][code]
-- note that it currently mutates the first input array
if param then text = mw.ustring.gsub(text, "$1", param) end
local function mergeArrays(a1, a2)
return text
for i = 1, #a2 do
end
a1[#a1 + 1] = a2[i]
end


local function throwError(errorMessage, param)
return a1
error(errorText(errorMessage, param))
end
end


local function replaceDecimalMark(num)
local function split(str, del)
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
local out = {}
end
local i, j = str:find(del)


local function padZeros(num, numDigits)
if i and j then
local numZeros
out[1] = str:sub(1, i - 1)
local negative = false
out[2] = str:sub(j + 1)
else
out[1] = str
end


if num < 0 then
return out
negative = true
end
num = num * -1
end


num = tostring(num)
local function parseWikidataURL(url)
numZeros = numDigits - num:len()
local id


for _ = 1, numZeros do
if url:match('^http[s]?://') then
num = "0"..num
id = split(url, "Q")
end


if negative then
if id[2] then
num = "-"..num
return "Q" .. id[2]
end
end
end


return num
return nil
end
end


local function replaceSpecialChar(chr)
local function parseDate(dateStr, precision)
if chr == '_' then
precision = precision or "d"
-- replace underscores with spaces
return ' '
else
return chr
end
end


local function replaceSpecialChars(str)
local i, j, index, ptr
local chr
local parts = {nil, nil, nil}
local esc = false
local strOut = ""


for i = 1, #str do
if dateStr == nil then
chr = str:sub(i,i)
return parts[1], parts[2], parts[3]  -- year, month, day
 
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
end


return strOut
-- 'T' for snak values, '/' for outputs with '/Julian' attached
end
i, j = dateStr:find("[T/]")


local function buildWikilink(target, label)
if i then
if not label or target == label then
dateStr = dateStr:sub(1, i-1)
return "[[" .. target .. "]]"
else
return "[[" .. target .. "|" .. label .. "]]"
end
end
end


-- used to make frame.args mutable, to replace #frame.args (which is always 0)
local from = 1
-- with the actual amount and to simply copy tables
 
function copyTable(tIn)
if dateStr:sub(1,1) == "-" then
if not tIn then
-- this is a negative number, look further ahead
return nil
from = 2
end
end


local tOut = {}
index = 1
ptr = 1


for i, v in pairs(tIn) do
i, j = dateStr:find("-", from)
tOut[i] = v
end


return tOut
if i then
end
-- year
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)  -- explicitly give base 10 to prevent error


-- used to merge output arrays together;
if parts[index] == -0 then
-- note that it currently mutates the first input array
parts[index] = tonumber("0")  -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
local function mergeArrays(a1, a2)
end
for i = 1, #a2 do
a1[#a1 + 1] = a2[i]
end


return a1
if precision == "y" then
end
-- we're done
return parts[1], parts[2], parts[3]  -- year, month, day
end


local function split(str, del)
index = index + 1
local out = {}
ptr = i + 1
local i, j = str:find(del)


if i and j then
i, j = dateStr:find("-", ptr)
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
out[1] = str
end


return out
if i then
end
-- month
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)


local function parseWikidataURL(url)
if precision == "m" then
local id
-- we're done
return parts[1], parts[2], parts[3]  -- year, month, day
end


if url:match('^http[s]?://') then
index = index + 1
id = split(url, "Q")
ptr = i + 1
end
end


if id[2] then
if dateStr:sub(ptr) ~= "" then
return "Q" .. id[2]
-- day if we have month, month if we have year, or year
end
parts[index] = tonumber(dateStr:sub(ptr), 10)
end
end


return nil
return parts[1], parts[2], parts[3]  -- year, month, day
end
end


function parseDate(dateStr, precision)
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
precision = precision or "d"
if aY == nil or bY == nil then
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1


local i, j, index, ptr
if aY < bY then
local parts = {nil, nil, nil}
return true
end


if dateStr == nil then
if aY > bY then
return parts[1], parts[2], parts[3]  -- year, month, day
return false
end
end


-- 'T' for snak values, '/' for outputs with '/Julian' attached
if aM < bM then
i, j = dateStr:find("[T/]")
return true
end


if i then
if aM > bM then
dateStr = dateStr:sub(1, i-1)
return false
end
end


local from = 1
if aD < bD then
 
return true
if dateStr:sub(1,1) == "-" then
-- this is a negative number, look further ahead
from = 2
end
end


index = 1
return false
ptr = 1
end


i, j = dateStr:find("-", from)
local function getHookName(param, index)
if hookNames[param] then
return hookNames[param][index]
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end


if i then
local function alwaysTrue()
-- year
return true
parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10)  -- remove '+' sign (explicitly give base 10 to prevent error)
end


if parts[index] == -0 then
-- The following function parses a format string.
parts[index] = tonumber("0"-- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
--
end
-- The example below shows how a parsed string is structured in memory.
 
-- Variables other than 'str' and 'child' are left out for clarity's sake.
if precision == "y" then
--
-- we're done
-- Example:
return parts[1], parts[2], parts[3]  -- year, month, day
-- "A %p B [%s[%q1]] C [%r] D"
end
--
 
-- Structure:
index = index + 1
-- [
ptr = i + 1
--  {
 
--    str = "A "
i, j = dateStr:find("-", ptr)
--   },
 
--  {
if i then
--    str = "%p"
-- month
--   },
parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
--  {
--    str = " B ",
--    child =
--    [
--      {
--        str = "%s",
--         child =
--        [
--          {
--            str = "%q1"
--          }
--        ]
--      }
--    ]
--  },
--  {
--     str = " C ",
--    child =
--    [
--      {
--        str = "%r"
--      }
--    ]
--  },
--  {
--    str = " D"
--  }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}


if precision == "m" then
local function newObject(array)
-- we're done
local obj = {}  -- new object
return parts[1], parts[2], parts[3] -- year, month, day
obj.str = ""
end
 
array[#array + 1] = obj -- array{object}
obj.parent = array


index = index + 1
return obj
ptr = i + 1
end
end
end


if dateStr:sub(ptr) ~= "" then
local function endParam()
-- day if we have month, month if we have year, or year
if param > 0 then
parts[index] = tonumber(dateStr:sub(ptr), 10)
if cur.str ~= "" then
cur.str = "%"..cur.str
cur.param = true
params[cur.str] = true
cur.parent.req[cur.str] = true
prev = cur
cur = newObject(cur.parent)
end
param = 0
end
end
end


return parts[1], parts[2], parts[3] -- year, month, day
root = {} -- array
end
root.req = {}
cur = newObject(root)
prev = nil


local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
esc = false
if aY == nil or bY == nil then
param = 0
return nil
end
aM = aM or 1
aD = aD or 1
bM = bM or 1
bD = bD or 1


if aY < bY then
for i = 1, #str do
return true
chr = str:sub(i,i)
end


if aY > bY then
if not esc then
return false
if chr == '\\' then
end
endParam()
 
esc = true
if aM < bM then
elseif chr == '%' then
return true
endParam()
end
if cur.str ~= "" then
 
cur = newObject(cur.parent)
if aM > bM then
end
return false
param = 2
end
elseif chr == '[' then
endParam()
if prev and cur.str == "" then
table.remove(cur.parent)
cur = prev
end
cur.child = {}  -- new array
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end


if aD < bD then
cur.str = cur.str .. replaceSpecialChar(chr)
return true
end
else
cur.str = cur.str .. chr
esc = false
end
 
prev = nil
end
end


return false
endParam()
end
 
-- make sure that at least one required parameter has been defined
if not next(root.req) then
throwError("missing-required-parameter")
end


local function getHookName(param, index)
-- make sure that the separator parameter "%s" is not amongst the required parameters
if hookNames[param] then
if root.req[parameters.separator] then
return hookNames[param][index]
throwError("extra-required-parameter", parameters.separator)
elseif param:len() > 2 then
return hookNames[param:sub(1, 2).."\\d"][index]
else
return nil
end
end
end


local function alwaysTrue()
return root, params
return true
end
end


-- The following function parses a format string.
local function sortOnRank(claims)
--
local rankPos
-- The example below shows how a parsed string is structured in memory.
local ranks = {{}, {}, {}, {}}  -- preferred, normal, deprecated, (default)
-- Variables other than 'str' and 'child' are left out for clarity's sake.
local sorted = {}
--
 
-- Example:
for _, v in ipairs(claims) do
-- "A %p B [%s[%q1]] C [%r] D"
rankPos = rankTable[v.rank] or 4
--
ranks[rankPos][#ranks[rankPos] + 1] = v
-- Structure:
end
-- [
 
--  {
sorted = ranks[1]
--    str = "A "
sorted = mergeArrays(sorted, ranks[2])
--  },
sorted = mergeArrays(sorted, ranks[3])
--  {
 
--    str = "%p"
return sorted
--  },
end
--  {
 
--    str = " B ",
local Config = {}
--    child =
--    [
--      {
--         str = "%s",
--        child =
--        [
--          {
--            str = "%q1"
--          }
--        ]
--      }
--    ]
--  },
--  {
--    str = " C ",
--    child =
--    [
--      {
--        str = "%r"
--      }
--    ]
--  },
--  {
--    str = " D"
--  }
-- ]
--
local function parseFormat(str)
local chr, esc, param, root, cur, prev, new
local params = {}


local function newObject(array)
-- allows for recursive calls
local obj = {} -- new object
function Config:new()
obj.str = ""
local cfg = {}
setmetatable(cfg, self)
self.__index = self


array[#array + 1] = obj  -- array{object}
cfg.separators = {
obj.parent = array
-- single value objects wrapped in arrays so that we can pass by reference
["sep"]  = {copyTable(defaultSeparators["sep"])},
["sep%s"] = {copyTable(defaultSeparators["sep%s"])},
["sep%q"] = {copyTable(defaultSeparators["sep%q"])},
["sep%r"] = {copyTable(defaultSeparators["sep%r"])},
["punc"]  = {copyTable(defaultSeparators["punc"])}
}


return obj
cfg.entity = nil
end
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}


local function endParam()
cfg.bestRank = true
if param > 0 then
cfg.ranks = {true, true, false}  -- preferred = true, normal = true, deprecated = false
if cur.str ~= "" then
cfg.foundRank = #cfg.ranks
cur.str = "%"..cur.str
cfg.flagBest = false
cur.param = true
cfg.flagRank = false
params[cur.str] = true
 
cur.parent.req[cur.str] = true
cfg.periods = {true, true, true}  -- future = true, current = true, former = true
prev = cur
cfg.flagPeriod = false
cur = newObject(cur.parent)
cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))}  -- today as {year, month, day}
end
param = 0
end
end


root = {}  -- array
cfg.mdyDate = false
root.req = {}
cfg.singleClaim = false
cur = newObject(root)
cfg.sourcedOnly = false
prev = nil
cfg.editable = false
cfg.editAtEnd = false


esc = false
cfg.inSitelinks = false
param = 0


for i = 1, #str do
cfg.langCode = mw.language.getContentLanguage().code
chr = str:sub(i,i)
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)


if not esc then
cfg.siteID = mw.wikibase.getGlobalSiteId()
if chr == '\\' then
 
endParam()
cfg.states = {}
esc = true
cfg.states.qualifiersCount = 0
elseif chr == '%' then
cfg.curState = nil
endParam()
 
if cur.str ~= "" then
cfg.prefetchedRefs = nil
cur = newObject(cur.parent)
 
end
return cfg
param = 2
end
elseif chr == '[' then
 
endParam()
local State = {}
if prev and cur.str == "" then
 
table.remove(cur.parent)
function State:new(cfg, type)
cur = prev
local stt = {}
end
setmetatable(stt, self)
cur.child = {} -- new array
self.__index = self
cur.child.req = {}
cur.child.parent = cur
cur = newObject(cur.child)
elseif chr == ']' then
endParam()
if cur.parent.parent then
new = newObject(cur.parent.parent.parent)
if cur.str == "" then
table.remove(cur.parent)
end
cur = new
end
else
if param > 1 then
param = param - 1
elseif param == 1 then
if not chr:match('%d') then
endParam()
end
end


cur.str = cur.str .. replaceSpecialChar(chr)
stt.conf = cfg
end
stt.type = type
else
cur.str = cur.str .. chr
esc = false
end


prev = nil
stt.results = {}
end


endParam()
stt.parsedFormat = {}
 
stt.separator = {}
-- make sure that at least one required parameter has been defined
stt.movSeparator = {}
if not next(root.req) then
stt.puncMark = {}
throwError("missing-required-parameter")
end


-- make sure that the separator parameter "%s" is not amongst the required parameters
stt.linked = false
if root.req[parameters.separator] then
stt.rawValue = false
throwError("extra-required-parameter", parameters.separator)
stt.shortName = false
end
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false


return root, params
return stt
end
end


local function sortOnRank(claims)
-- if id == nil then item connected to current page is used
local rankPos
function Config:getLabel(id, raw, link, short)
local ranks = {{}, {}, {}, {}}  -- preferred, normal, deprecated, (default)
local label = nil
local sorted = {}
local prefix, title= "", nil


for _, v in ipairs(claims) do
if not id then
rankPos = rankTable[v.rank] or 4
id = mw.wikibase.getEntityIdForCurrentPage()
ranks[rankPos][#ranks[rankPos] + 1] = v
end


sorted = ranks[1]
if not id then
sorted = mergeArrays(sorted, ranks[2])
sorted = mergeArrays(sorted, ranks[3])
 
return sorted
end
 
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
 
if not id then
id = mw.wikibase.getEntityIdForCurrentPage()
 
if not id then
return ""
return ""
end
end
Line 670: Line 675:
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
label = id
label = id
if id:sub(1,1) == "P" then
prefix = "Property:"
end
end
end


prefix = "d:" .. prefix
prefix, title = "d:Special:EntityPage/", label -- may be nil
 
title = label -- may be nil
else
else
-- try short name first if requested
-- try short name first if requested
Line 691: Line 690:
-- get label
-- get label
if not label then
if not label then
label = mw.wikibase.getLabelByLang(id, self.langCode)
label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
end
end
end
end
Line 704: Line 703:
elseif id:sub(1,1) == "P" then
elseif id:sub(1,1) == "P" then
-- properties have no sitelink, link to Wikidata instead
-- properties have no sitelink, link to Wikidata instead
title = id
prefix, title = "d:Special:EntityPage/", id
prefix = "d:Property:"
end
end
end
end


label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
if title then
if title then
label = buildWikilink(prefix .. title, label)
label = buildWikilink(prefix .. title, label)
Line 745: Line 744:
value = value .. "#" .. self.propertyID
value = value .. "#" .. self.propertyID
elseif self.inSitelinks then
elseif self.inSitelinks then
value = value .. "#sitelinks-wikipedia"
value = value .. "#sitelinks-bharatpedia"
end
end


Line 877: Line 876:
if not unitOnly then
if not unitOnly then
-- get value and strip + signs from front
-- get value and strip + signs from front
value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")
value = mw.ustring.gsub(datavalue['amount'], "^%+(.+)$", "%1")


if raw then
if raw then
Line 1,289: Line 1,288:
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")
lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")


value = "[https://tools.wmflabs.org/geohack/geohack.php?language="..self.langCode.."&params="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
value = "[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."&params="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"
end
end


Line 1,811: Line 1,810:
local value = ""
local value = ""
local ref = {}
local ref = {}
local referenceEmpty = true  -- will be set to false if at least one parameter is left unremoved
    local numAuthorParameters = 0
    local numAuthorNameStringParameters = 0
    local tempLink
    local additionalRefProperties = {}  -- will hold properties of the reference which are not in statement.snaks, namely backup title from "subject named as" and URL from an external ID
    local wikidataPropertiesOfSource  -- will contain "Wikidata property" properties of the item in stated in, if any


local version = 1 -- increment this each time the below logic is changed to avoid conflict errors
local version = 4 -- increment this each time the below logic is changed to avoid conflict errors


if statement.snaks then
if statement.snaks then
Line 1,818: Line 1,823:
if statement.snaks[aliasesP.importedFrom] then
if statement.snaks[aliasesP.importedFrom] then
statement.snaks[aliasesP.importedFrom] = nil
statement.snaks[aliasesP.importedFrom] = nil
end
-- don't include "Wikimedia import URL"
if statement.snaks[aliasesP.wikimediaImportURL] then
statement.snaks[aliasesP.wikimediaImportURL] = nil
-- don't include "retrieved" if no "referenceURL" is present,
-- as "retrieved" probably belongs to "Wikimedia import URL"
if statement.snaks[aliasesP.retrieved] and not statement.snaks[aliasesP.referenceURL] then
statement.snaks[aliasesP.retrieved] = nil
end
end
end


Line 1,839: Line 1,854:
statement.snaks[aliasesP.language] = nil
statement.snaks[aliasesP.language] = nil
end
end
       
        if statement.snaks[aliasesP.statedIn] and not statement.snaks[aliasesP.referenceURL] then
        -- "stated in" was given but "reference URL" was not.
        -- get "Wikidata property" properties from the item in "stated in"
        -- if any of the returned properties of the external-id datatype is in statement.snaks, generate a URL from it and use the URL in the reference
       
        -- find the "Wikidata property" properties in the item from "stated in"
        wikidataPropertiesOfSource = mw.text.split(p._properties{p.flags.raw, aliasesP.wikidataProperty, [p.args.eid] = self.conf:getValue(statement.snaks[aliasesP.statedIn][1], true, false)}, ", ", true)
        for i, wikidataPropertyOfSource in pairs(wikidataPropertiesOfSource) do
        if statement.snaks[wikidataPropertyOfSource] and statement.snaks[wikidataPropertyOfSource][1].datatype == "external-id" then
        tempLink = self.conf:getValue(statement.snaks[wikidataPropertyOfSource][1], false, true)  -- not raw, linked
        if mw.ustring.match(tempLink, "^%[%Z- %Z+%]$") then  -- getValue returned a URL.
            additionalRefProperties[aliasesP.referenceURL] = mw.ustring.gsub(tempLink, "^%[(%Z-) %Z+%]$", "%1")  -- the URL is in wiki markup, so strip the square brackets and the display text
            statement.snaks[wikidataPropertyOfSource] = nil
            break
        end
        end
        end
        end
       
        -- don't include "subject named as", but use it as the title when "title" is not present but a URL is
        if statement.snaks[aliasesP.subjectNamedAs] then
        if not statement.snaks[aliasesP.title] and (statement.snaks[aliasesP.referenceURL] or additionalRefProperties[aliasesP.referenceURL]) then
        additionalRefProperties[aliasesP.title] = statement.snaks[aliasesP.subjectNamedAs][1].datavalue.value
        end
        statement.snaks[aliasesP.subjectNamedAs] = nil
        end


-- retrieve all the parameters
-- retrieve all the parameters
Line 1,845: Line 1,887:


-- multiple authors may be given
-- multiple authors may be given
if i == aliasesP.author then
if i == aliasesP.author or i == aliasesP.authorNameString then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true)  -- link = true/false, anyLang = true
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true)  -- link = true/false, anyLang = true
else
else
Line 1,854: Line 1,896:
params[i] = nil
params[i] = nil
else
else
referenceEmpty = false
if statement.snaks[i][1].datatype == 'external-id' then
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
key = "external-id"
Line 1,873: Line 1,917:
-- continue if an option is available in the corresponding cite template
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
                                -- handle non-author properties (and author properties ("author" and "author name string"), if they don't use the same template parameter)
                                if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
    citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
                                    -- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
    for k=2, #params[i] do
                                        citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
    end
                                -- handle "author" and "author name string" specially if they use the same template parameter
                                elseif i == aliasesP.author or i == aliasesP.authorNameString then
                                    if params[aliasesP.author] ~= nil then
                                        numAuthorParameters = #params[aliasesP.author]
                                    else
                                        numAuthorParameters = 0
                                    end
 
                                    if params[aliasesP.authorNameString] ~= nil then
                                        numAuthorNameStringParameters = #params[aliasesP.authorNameString]
                                    else
                                        numAuthorNameStringParameters = 0
                                    end


-- if there are multiple parameter values (authors), add those too
                                    -- execute only if both "author" and "author name string" satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
for k=2, #params[i] do
                                    -- reason: parameters are added to params each iteration of the loop, not before the loop
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
                                    if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
end
                                        for k=1, numAuthorParameters + numAuthorNameStringParameters do
                                            if k <= numAuthorParameters then  -- now handling the authors from the "author" property
                                                citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
                                            else  -- now handling the authors from "author name string"
                                                citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
                                            end
        end
                                    end
                                end
end
end
else
else
Line 1,884: Line 1,955:
end
end
end
end
end
end
end
-- use additional properties
for i in pairs(additionalRefProperties) do
for j in pairs(citeParams) do
if not citeMismatch[j] and i18n["cite"][j][i] then
citeParams[j][i18n["cite"][j][i]] = additionalRefProperties[i]
else
citeMismatch[j] = true
end
end
end
end
Line 1,920: Line 2,002:
end
end


-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then
elseif not referenceEmpty then
citeParams['default'] = {}
value = "<span style=\"color: crimson\">" .. errorText("malformed-reference") .. "</span>"
end
if value ~= "" then
value = {value} -- create one value object


-- start by adding authors up front
if not self.rawValue then
if params[aliasesP.author] and #params[aliasesP.author] > 0 then
-- this should become a <ref> tag, so save the reference's hash for later
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ")
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
end


-- combine "reference URL" and "title" into one link if both are present
ref = {value}  -- wrap the value object in an array
if params[aliasesP.referenceURL] and params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]'
elseif params[aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1]
elseif params[aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"'
end
 
-- then add "stated in"
if params[aliasesP.statedIn] then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[aliasesP.statedIn][1] .. "''"
end
 
-- remove previously added parameters so that they won't be added a second time
params[aliasesP.author] = nil
params[aliasesP.referenceURL] = nil
params[aliasesP.title] = nil
params[aliasesP.statedIn] = nil
 
-- add the rest of the parameters
for i, v in pairs(params) do
i = self.conf:getLabel(i)
 
if i ~= "" then
citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1]
end
end
 
value = table.concat(citeParams['default'], "; ")
 
if value ~= "" then
value = value .. "."
end
end
 
if value ~= "" then
value = {value}  -- create one value object
 
if not self.rawValue then
-- this should become a <ref> tag, so save the reference's hash for later
value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version)
end
 
ref = {value}  -- wrap the value object in an array
end
end
end
end
Line 2,507: Line 2,549:
value = value or ""
value = value or ""


if cfg.editable and value ~= "" then
if cfg.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. cfg:getEditIcon()
value = value .. cfg:getEditIcon()
end
end
 
return value
end
 
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
 
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
 
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
 
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
if not mw.wikibase then return nil end


return value
end
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
local function establishCommands(commandList, commandFunc)
for _, commandName in pairs(commandList) do
local function wikitextWrapper(frame)
local args = copyTable(frame.args)
args.pointer = 1
loadI18n(aliasesP, frame)
return commandFunc(args, commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
args = copyTable(args)
args.pointer = 1
loadI18n(aliasesP)
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
establishCommands(p.claimCommands, claimCommand)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
function p.main(frame)
local f, args
local f, args