Module:Commons link: Difference between revisions
From Zoophilia Wiki
Jump to navigationJump to search
meta>Hike395 (factor out redundant code) |
Quantumhusky (talk | contribs) m (1 revision imported) |
||
(12 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
-- Module to find commons galleries and categories based on wikidata entries | -- Module to find commons galleries and categories based on wikidata entries | ||
local getArgs = require('Module:Arguments').getArgs | local getArgs = require('Module:Arguments').getArgs | ||
local yesNo = require('Module:Yesno') | |||
local generateWarning = require('Module:If preview')._warning | |||
local p = {} | local p = {} | ||
local function _getTitleQID(qid) | -- Check if string is a valid QID | ||
-- Argument: QID to check | |||
-- Returns: valid (bool) | |||
local function _validQID(qid) | |||
return qid and mw.ustring.find(qid,"^[Qq]%d+$") | |||
end | |||
-- Check if string is a valid wikidata property string | |||
-- Argument: property string to check | |||
-- Returns: valid (bool) | |||
local function _validProp(prop) | |||
return prop and mw.ustring.find(prop,"^[Pp]%d+$") | |||
end | |||
local function _lcfirst(doit,s) | |||
if doit then | |||
return mw.ustring.lower(mw.ustring.sub(s,1,1))..mw.ustring.sub(s,2) | |||
end | |||
return s | |||
end | |||
-- Format displayed linktext | |||
-- Arguments: | |||
-- s = string to display | |||
-- formatting = formatting table: | |||
-- formatting.linktext = if defined, override s | |||
-- formatting.lcfirst = lower case the first letter in display | |||
-- formatting.bold = whether to bold the display | |||
-- formatting.italic = whether to italicize the display | |||
-- formatting.nowrap = set nowrapping | |||
-- Returns: | |||
-- formatted string | |||
local function _formatResult(s, formatting) | |||
local resultVal = formatting.linktext or s | |||
resultVal = _lcfirst(formatting.lcfirst,resultVal) | |||
if formatting.italic then resultVal = "<i>" .. resultVal .. "</i>" end | |||
if formatting.bold then resultVal = "<b>" .. resultVal .. "</b>" end | |||
if formatting.nowrap then | |||
resultVal = '<span style="white-space:nowrap;">' .. resultVal .. "</span>" | |||
end | |||
return resultVal | |||
end | |||
-- Get title, namespace, and QID for current page | |||
-- Arguments: | |||
-- qid = testing only: get title of alternative page with QID=qid | |||
-- nsQid = whether to return the ns of the qid page or current | |||
-- Returns: | |||
-- title, namespace (string), qid of current page (or test page) | |||
local function _getTitleQID(qid,nsQid) | |||
local titleObject = mw.title.getCurrentTitle() | local titleObject = mw.title.getCurrentTitle() | ||
-- look up qid for current page (if not testing) | -- look up qid for current page (if not testing) | ||
local nsText = mw.ustring.gsub(titleObject.nsText,"_"," ") | |||
if not _validQID(qid) then | |||
qid = mw.wikibase.getEntityIdForCurrentPage() | qid = mw.wikibase.getEntityIdForCurrentPage() | ||
return titleObject.text, | return titleObject.text, nsText, qid | ||
end | end | ||
-- testing-only path: given a qid, determine title | -- testing-only path: given a qid, determine title | ||
-- use namespace from current page (to suppress tracking cat) | -- always use namespace from current page (to suppress tracking cat) | ||
local title = mw.wikibase. | qid = qid:upper() | ||
-- | local title = mw.wikibase.getSitelink(qid) or "" | ||
-- strip any namespace from sitelink | |||
local firstColon = mw.ustring.find(title,':',1,true) | local firstColon = mw.ustring.find(title,':',1,true) | ||
local qidNsText = "" | |||
if firstColon then | if firstColon then | ||
qidNsText = mw.ustring.sub(title,1,firstColon-1) | |||
title = mw.ustring.sub(title,firstColon+1) | title = mw.ustring.sub(title,firstColon+1) | ||
end | end | ||
return title, | if nsQid then | ||
return title, qidNsText, qid | |||
end | |||
return title, nsText, qid | |||
end | end | ||
Line 32: | Line 89: | ||
-- commonsSitelink = commons sitelink for current article | -- commonsSitelink = commons sitelink for current article | ||
local function _lookupGallery(qid,fetch,commonsSitelink) | local function _lookupGallery(qid,fetch,commonsSitelink) | ||
if not _validQID(qid) then | |||
return nil, true, nil | |||
end | |||
qid = qid:upper() | |||
local galleryLink = nil | local galleryLink = nil | ||
local consistent = true | local consistent = true | ||
Line 60: | Line 121: | ||
-- Returns: either category-stripped name of article, or nil | -- Returns: either category-stripped name of article, or nil | ||
local function _lookupFallback(qid,property) | local function _lookupFallback(qid,property) | ||
if not qid then | if not _validQID(qid) or not _validProp(property) then | ||
return nil | return nil | ||
end | end | ||
qid = qid:upper() | |||
property = property:upper() | |||
-- If property exists on current article, get value (other article qid) | -- If property exists on current article, get value (other article qid) | ||
local value = mw.wikibase.getBestStatements(qid, property)[1] | local value = mw.wikibase.getBestStatements(qid, property)[1] | ||
Line 86: | Line 149: | ||
-- commonsSitelink = commons sitelink for current article | -- commonsSitelink = commons sitelink for current article | ||
local function _lookupCategory(qid, fetch, commonsSitelink) | local function _lookupCategory(qid, fetch, commonsSitelink) | ||
if not _validQID(qid) then | |||
return nil, true, nil | |||
end | |||
qid = qid:upper() | |||
local categoryLink = nil | local categoryLink = nil | ||
local consistent = true | local consistent = true | ||
Line 95: | Line 162: | ||
categoryLink = mw.ustring.sub(commonsSitelink,10) | categoryLink = mw.ustring.sub(commonsSitelink,10) | ||
end | end | ||
-- P373 | -- purposefully do not look in P373 ("commons category" property) | ||
-- if P373 is desired, then restore search code here | |||
if P373 | |||
-- P910 is the "topic's main category". Look for commons sitelink there | -- P910 is the "topic's main category". Look for commons sitelink there | ||
local fallback = _lookupFallback(qid,"P910") | local fallback = _lookupFallback(qid,"P910") | ||
Line 128: | Line 187: | ||
end | end | ||
-- Does the article have a Commons gallery, and is it consistent? | |||
-- Arguments: | |||
-- qid = QID to lookup in wikidata (for testing only) | |||
-- Returns: | |||
-- filename at Commons, bool: is wikidata consistent for this article? | |||
function p._hasGalleryConsistent(qid) | |||
local wp_title, wp_ns | |||
wp_title, wp_ns, qid = _getTitleQID(qid) | |||
return _lookupGallery(qid,true) | |||
end | |||
-- Does the article have a corresponding Commons gallery? | |||
-- Arguments: | |||
-- qid = QID to lookup in wikidata (for testing only) | |||
-- Returns: | |||
-- filename at Commons if so, false if not | |||
function p._hasGallery(qid) | |||
local galleryLink, consistent = p._hasGalleryConsistent(qid) | |||
return consistent and galleryLink | |||
end | |||
-- Does the article have a Commons category? Is wikidata consistent for that? | |||
-- Arguments: | |||
-- qid = QID to lookup in wikidata (for testing only) | |||
-- prefix = whether to add "Category:" to return string (default true) | |||
-- Returns: | |||
-- filename at Commons, bool: consistent | |||
function p._hasCategoryConsistent(qid,prefix) | |||
if prefix == nil then | |||
prefix = true | |||
end | |||
local wp_title, wp_ns | |||
wp_title, wp_ns, qid = _getTitleQID(qid) | |||
local categoryLink, consistent = _lookupCategory(qid,true) | |||
if categoryLink and prefix then | |||
categoryLink = "Category:"..categoryLink | |||
end | |||
return categoryLink, consistent | |||
end | |||
-- Does the article have a corresponding Commons category? | |||
-- Arguments: | |||
-- qid = QID to lookup in wikidata (for testing only) | |||
-- prefix = whether to add "Category:" to return string (default true) | |||
-- Returns: | |||
-- filename at Commons if so, blank if not | |||
function p._hasCategory(qid,prefix) | |||
local categoryLink, consistent = p._hasCategoryConsistent(qid,prefix) | |||
return consistent and categoryLink | |||
end | |||
-- Create Commons link corresponding to current article | -- Create Commons link corresponding to current article | ||
Line 133: | Line 242: | ||
-- namespace = namespace in Commons ("" for galleries) | -- namespace = namespace in Commons ("" for galleries) | ||
-- default = use as Commons link, don't access wikidata | -- default = use as Commons link, don't access wikidata | ||
-- search = string to search for | -- search = string to search for | ||
-- fallback = string to search for if wikidata fails | |||
-- formatting = formatting parameters | |||
-- qid = QID to lookup in wikidata (for testing only) | -- qid = QID to lookup in wikidata (for testing only) | ||
-- Returns: | -- Returns: | ||
-- formatted wikilink to Commons | -- formatted wikilink to Commons in specified namespace | ||
function p._getCommons(namespace,default, | function p._getCommons(namespace,default,search,fallback,formatting,qid) | ||
local nsColon | local nsColon | ||
if not namespace or namespace == "" then | if not namespace or namespace == "" then | ||
Line 146: | Line 256: | ||
end | end | ||
if default then | if default then | ||
return "[[Commons:"..nsColon..default.."|"..( | return "[[Commons:"..nsColon..default.."|".._formatResult(default,formatting).."]]" | ||
end | end | ||
if search then | if search then | ||
return "[[Commons:Special:Search/"..nsColon..search.."|"..( | return "[[Commons:Special:Search/"..nsColon..search.."|".._formatResult(search,formatting).."]]" | ||
end | end | ||
local | local wp_title, wp_ns | ||
wp_title, wp_ns, qid = _getTitleQID(qid) | |||
local commonsLink = nil | |||
local consistent = true | |||
if nsColon == "" then | |||
commonsLink, consistent = _lookupGallery(qid,true) | |||
elseif namespace:lower() == "category" then | |||
commonsLink, consistent = _lookupCategory(qid,true) | |||
end | |||
-- use wikidata if consistent | |||
if commonsLink and consistent then | |||
return "[[Commons:"..nsColon..commonsLink.."|".._formatResult(commonsLink,formatting).."]]" | |||
end | |||
-- if not consistent, fall back to search and add to tracking cat | |||
-- construct default result (which searches for title) | -- construct default result (which searches for title) | ||
local searchResult = "[[Commons:Special:Search/"..nsColon.. | local searchResult = "[[Commons:Special:Search/"..nsColon..(fallback or wp_title) | ||
if | .."|".._formatResult(fallback or wp_title,formatting).."]]" | ||
local | if not consistent and wp_ns == "" then | ||
local friendlyNS | |||
if nsColon == "" then | if nsColon == "" then | ||
friendlyNS = "gallery" | |||
else | |||
friendlyNS = namespace:lower() | |||
end | end | ||
searchResult = searchResult.."[[Category:Inconsistent wikidata for Commons "..friendlyNS.."]]" | |||
end | end | ||
return searchResult | return searchResult | ||
Line 184: | Line 293: | ||
-- Arguments: | -- Arguments: | ||
-- default = use as Commons link, don't access wikidata | -- default = use as Commons link, don't access wikidata | ||
-- search = string to search for | -- search = string to search for | ||
-- fallback = string to search for if wikidata lookup fails | |||
-- formatting = formatting parameters | |||
-- qid = QID to lookup in wikidata (for testing only) | -- qid = QID to lookup in wikidata (for testing only) | ||
-- Returns: | -- Returns: | ||
-- formatted wikilink to Commons "best" landing page | -- formatted wikilink to Commons "best" landing page | ||
function p._getGalleryOrCategory(default, | function p._getGalleryOrCategory(default, search, fallback, formatting, qid) | ||
if default then | if default then | ||
return "[[Commons:"..default.."|"..( | return "[[Commons:"..default.."|".._formatResult(default,formatting).."]]" | ||
end | end | ||
if search then | if search then | ||
return "[[Commons:Special:Search/"..search.."|"..( | return "[[Commons:Special:Search/"..search.."|".._formatResult(search,formatting).."]]" | ||
end | |||
local wp_title, wp_ns | |||
wp_title, wp_ns, qid = _getTitleQID(qid) | |||
local trackingCats = "" | |||
local galleryLink, consistent, commonsSitelink = _lookupGallery(qid,true) | |||
-- use wikidata if either sitelink or P935 exist, and they both agree | |||
if galleryLink and consistent then | |||
return "[[Commons:"..galleryLink.."|".._formatResult(galleryLink,formatting).."]]" | |||
end | |||
if not consistent and wp_ns == "" then | |||
trackingCats = "[[Category:Inconsistent wikidata for Commons gallery]]" | |||
end | |||
-- if gallery is not good, fall back looking for category | |||
local categoryLink | |||
categoryLink, consistent = _lookupCategory(qid,false,commonsSitelink) | |||
if categoryLink and consistent then | |||
return "[[Commons:Category:"..categoryLink.."|".._formatResult(categoryLink,formatting).."]]"..trackingCats | |||
end | |||
if not consistent and wp_ns == "" then | |||
trackingCats = trackingCats.."[[Category:Inconsistent wikidata for Commons category]]" | |||
end | end | ||
local | -- return search result looking for title as last attempt | ||
return "[[Commons:Special:Search/" .. (fallback or wp_title) .. | |||
"|" .. _formatResult(fallback or wp_title,formatting) .. "]]" .. trackingCats | |||
end | |||
-- Return link(s) Commons gallery, or category, or both from wikidata | |||
-- Arguments: | |||
-- defaultGallery = default gallery link to use, instead of wikidata | |||
-- defaultCategory = default category link to use, instead of wikidata | |||
-- categoryText = if both gallery and category, text to use in category link ("category" by default) | |||
-- oneSearch = only emit one search result | |||
-- formatting = formatting parameters | |||
-- qid = qid of page to lookup in wikidata (testing only) | |||
function p._getGalleryAndCategory(defaultGallery, defaultCategory, | |||
categoryText, oneSearch, formatting, qid | |||
) | |||
local wp_title, wp_ns | |||
wp_title, wp_ns, qid = _getTitleQID(qid) | |||
categoryText = categoryText or "category" | |||
local trackingCats = "" | |||
local galleryLink, galleryConsistent | |||
local commonsSitelink = nil | |||
if defaultGallery then | |||
galleryLink = defaultGallery | |||
galleryConsistent = true | |||
else | |||
galleryLink, galleryConsistent, commonsSitelink = _lookupGallery(qid,true) | |||
end | |||
local galleryGood = galleryLink and galleryConsistent | |||
if not galleryConsistent and wp_ns == "" then | |||
trackingCats = "[[Category:Inconsistent wikidata for Commons gallery]]" | |||
end | |||
local categoryLink, categoryConsistent | |||
if defaultCategory then | |||
categoryLink = defaultCategory | |||
categoryConsistent = true | |||
else | |||
categoryLink, categoryConsistent = _lookupCategory(qid,defaultGallery,commonsSitelink) | |||
end | |||
local categoryGood = categoryLink and categoryConsistent | |||
if not categoryConsistent and wp_ns == "" then | |||
trackingCats = trackingCats.."[[Category:Inconsistent wikidata for Commons category]]" | |||
end | |||
local firstLink | |||
-- construct default result (which searches for title) | -- construct default result (which searches for title) | ||
local searchResult = "[[Commons:Special:Search/".. | local searchResult = "[[Commons:Special:Search/"..wp_title.."|".._formatResult(wp_title,formatting).."]]" | ||
if not oneSearch then | |||
if | searchResult = searchResult.." ([[Commons:Special:Search/Category:"..wp_title.."|"..categoryText.."]])" | ||
local | end | ||
local linkText = nil | |||
if galleryGood then | |||
firstLink = galleryLink | |||
linkText = galleryLink | |||
elseif categoryGood then | |||
firstLink = "Category:"..categoryLink | |||
linkText = categoryLink | |||
else | |||
return searchResult..trackingCats | |||
end | |||
local resultVal = "[[Commons:"..firstLink.."|".._formatResult(linkText,formatting).."]]" | |||
if galleryGood and categoryGood then | |||
resultVal = resultVal.." ([[Commons:Category:"..categoryLink.."|"..categoryText.."]])" | |||
end | |||
return resultVal..trackingCats | |||
end | |||
-- Compare two titles with their namespaces stripped | |||
local function titleMatch(s1,s2) | |||
s1 = s1 or "" | |||
s2 = s2 or "" | |||
s1 = mw.ustring.gsub(s1,"^[^:]+:","") | |||
s2 = mw.ustring.gsub(s2,"^[^:]+:","") | |||
return s1 == s2 | |||
end | |||
local galleryTrackingCats = { | |||
commons_link_on_wikidata = '[[Category:Commons link is on Wikidata]]', | |||
commons_link_defined_as_pagename = '[[Category:Commons link is defined as the pagename]]', | |||
commons_link_locally_defined = '[[Category:Commons link is locally defined]]', | |||
commons_link_from_wikidata = '[[Category:Commons link from Wikidata]]', | |||
commons_link_is_pagename = '[[Category:Commons link is the pagename]]', | |||
inconsistent = '[[Category:Inconsistent wikidata for Commons gallery]]' | |||
} | |||
local categoryTrackingCats = { | |||
commons_link_on_wikidata = '[[Category:Commons category link is on Wikidata]]', | |||
commons_link_defined_as_pagename = '[[Category:Commons category link is defined as the pagename]]', | |||
commons_link_locally_defined = '[[Category:Commons category link is locally defined]]', | |||
commons_link_from_wikidata = '[[Category:Commons category link from Wikidata]]', | |||
commons_link_is_pagename = '[[Category:Commons category link is the pagename]]', | |||
inconsistent = '[[Category:Inconsistent wikidata for Commons category]]' | |||
} | |||
local function selectTrackingCat(trackingCats,wikidata,consistent,default,title) | |||
if not consistent then | |||
return trackingCats.inconsistent | |||
end | |||
if default then | |||
-- construct warning message | |||
if default == wikidata then | |||
return trackingCats.commons_link_on_wikidata | |||
end | end | ||
if | local warning = "" | ||
if wikidata then | |||
warning = generateWarning({ | |||
"Commons link does not match Wikidata – [[Template:Commons_category#Resolving_discrepancies|please check]]" | |||
}) | |||
end | end | ||
if titleMatch(default,title) then | |||
return trackingCats.commons_link_defined_as_pagename .. warning | |||
return | |||
end | end | ||
if not | return trackingCats.commons_link_locally_defined .. warning | ||
end | |||
if wikidata then | |||
return trackingCats.commons_link_from_wikidata | |||
end | |||
return trackingCats.commons_link_is_pagename | |||
end | |||
-- Figure out tracking categories and editor warnings | |||
-- Arguments: | |||
-- default = Commons link argument passed to template | |||
-- fetchGallery = whether to fetch a gallery from Wikidata | |||
-- fetchCategory = whether to fetch a category from Wikidata | |||
-- qid = force a qid for testing | |||
-- Returns: | |||
-- tracking category and possible user warning | |||
-- | |||
-- Note: the logic for the tracking is quite different than the logic | |||
-- for generating Commons links (above). Thus, it is separated into another | |||
-- function for code clarity and maintainability. This should not seriously | |||
-- affect performance: server time is dominated by fetching wikidata entities, | |||
-- and those entities should be cached and shared between the Commons generating | |||
-- code and this tracking code. | |||
function p._tracking(default, fetchGallery, fetchCategory, qid) | |||
local title, wp_ns, wp_qid = _getTitleQID(qid,true) | |||
if wp_ns ~= "" then | |||
title = wp_ns..":"..title | |||
end | |||
-- only track if test or namespace=article or namespace=category | |||
if not (qid or wp_ns == "" or wp_ns == "Category") then | |||
return "" | |||
end | |||
-- determine title and namespace of wikidata and wp article | |||
local wikidata = nil | |||
local consistent = nil | |||
-- Tracking code works for all 4 cases of states of fetchGallery/Category | |||
-- fetchGallery takes precedence | |||
if fetchGallery then | |||
wikidata, consistent = p._hasGalleryConsistent(qid) | |||
if default or not fetchCategory or (consistent and wikidata) then | |||
return selectTrackingCat(galleryTrackingCats,wikidata,consistent, | |||
default,title) | |||
end | end | ||
end | end | ||
return | if fetchCategory then | ||
cat_wikidata, cat_consistent = p._hasCategoryConsistent(qid,true) | |||
if not fetchGallery or (cat_consistent and cat_wikidata) then | |||
return selectTrackingCat(categoryTrackingCats,cat_wikidata, | |||
cat_consistent,default,title) | |||
end | |||
return selectTrackingCat(galleryTrackingCats,wikidata,consistent, | |||
default,title) | |||
end | |||
return "" -- nothing fetched, nothing tracked | |||
end | end | ||
local function _createFormatting(args) | |||
formatting = {} | |||
formatting.linktext = args.linktext | |||
formatting.lcfirst = yesNo(args.lcfirst) | |||
formatting.bold = yesNo(args.bold) | |||
formatting.italic = yesNo(args.italic) | |||
formatting.nowrap = yesNo(args.nowrap) | |||
return formatting | |||
end | |||
-- Testing-only entry point for _getTitleQID | -- Testing-only entry point for _getTitleQID | ||
function p.getTitleQID(frame) | function p.getTitleQID(frame) | ||
local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | ||
local text, ns, qid = _getTitleQID(args[1]) | local text, ns, qid = _getTitleQID(args[1],args[2]) | ||
return text..","..ns..","..(qid or "nil") | return text..","..ns..","..(qid or "nil") | ||
end | end | ||
Line 241: | Line 521: | ||
function p.getGallery(frame) | function p.getGallery(frame) | ||
local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | ||
return p._getCommons("",args[1],args. | return p._getCommons("",args[1],args.search,args.fallback,_createFormatting(args),args.qid) | ||
end | end | ||
Line 247: | Line 527: | ||
function p.getCategory(frame) | function p.getCategory(frame) | ||
local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | ||
local retval = p._getCommons("Category", args[1], | |||
args.search, args.fallback, _createFormatting(args), args.qid | |||
) | |||
if args.tracking then | |||
local default = nil | |||
if args[1] then | |||
default = "Category:"..args[1] | |||
end | |||
retval = retval..p._tracking(default, false, true, args.qid) | |||
end | |||
return retval | |||
end | end | ||
function p.getGalleryOrCategory(frame) | function p.getGalleryOrCategory(frame) | ||
local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | ||
local retval = p._getGalleryOrCategory( | |||
args[1], args.search, args.fallback, _createFormatting(args), args.qid | |||
) | |||
if args.tracking then | |||
retval = retval..p._tracking(args[1],true,true,args.qid) | |||
end | |||
return retval | |||
end | |||
function p.hasGallery(frame) | |||
local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | |||
return p._hasGallery(args.qid) or "" | |||
end | end | ||
function p.hasCategory(frame) | |||
local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | |||
return p._hasCategory(args.qid) or "" | |||
end | |||
function p.hasGalleryOrCategory(frame) | |||
local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | |||
return p._hasGallery(args.qid) or p._hasCategory(args.qid) or "" | |||
end | |||
function p.getGalleryAndCategory(frame) | |||
local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | |||
return p._getGalleryAndCategory(args[1], args[2], | |||
args.categoryText, args.oneSearch, _createFormatting(args), args.qid) | |||
end | |||
function p.tracking(frame) | |||
local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) | |||
return p._tracking(args[1], args.fetchGallery, args.fetchCategory, args.qid) | |||
end | |||
return p | return p |
Latest revision as of 01:02, 7 June 2021
Documentation for this module may be created at Module:Commons link/doc
-- Module to find commons galleries and categories based on wikidata entries local getArgs = require('Module:Arguments').getArgs local yesNo = require('Module:Yesno') local generateWarning = require('Module:If preview')._warning local p = {} -- Check if string is a valid QID -- Argument: QID to check -- Returns: valid (bool) local function _validQID(qid) return qid and mw.ustring.find(qid,"^[Qq]%d+$") end -- Check if string is a valid wikidata property string -- Argument: property string to check -- Returns: valid (bool) local function _validProp(prop) return prop and mw.ustring.find(prop,"^[Pp]%d+$") end local function _lcfirst(doit,s) if doit then return mw.ustring.lower(mw.ustring.sub(s,1,1))..mw.ustring.sub(s,2) end return s end -- Format displayed linktext -- Arguments: -- s = string to display -- formatting = formatting table: -- formatting.linktext = if defined, override s -- formatting.lcfirst = lower case the first letter in display -- formatting.bold = whether to bold the display -- formatting.italic = whether to italicize the display -- formatting.nowrap = set nowrapping -- Returns: -- formatted string local function _formatResult(s, formatting) local resultVal = formatting.linktext or s resultVal = _lcfirst(formatting.lcfirst,resultVal) if formatting.italic then resultVal = "<i>" .. resultVal .. "</i>" end if formatting.bold then resultVal = "<b>" .. resultVal .. "</b>" end if formatting.nowrap then resultVal = '<span style="white-space:nowrap;">' .. resultVal .. "</span>" end return resultVal end -- Get title, namespace, and QID for current page -- Arguments: -- qid = testing only: get title of alternative page with QID=qid -- nsQid = whether to return the ns of the qid page or current -- Returns: -- title, namespace (string), qid of current page (or test page) local function _getTitleQID(qid,nsQid) local titleObject = mw.title.getCurrentTitle() -- look up qid for current page (if not testing) local nsText = mw.ustring.gsub(titleObject.nsText,"_"," ") if not _validQID(qid) then qid = mw.wikibase.getEntityIdForCurrentPage() return titleObject.text, nsText, qid end -- testing-only path: given a qid, determine title -- always use namespace from current page (to suppress tracking cat) qid = qid:upper() local title = mw.wikibase.getSitelink(qid) or "" -- strip any namespace from sitelink local firstColon = mw.ustring.find(title,':',1,true) local qidNsText = "" if firstColon then qidNsText = mw.ustring.sub(title,1,firstColon-1) title = mw.ustring.sub(title,firstColon+1) end if nsQid then return title, qidNsText, qid end return title, nsText, qid end -- Lookup Commons gallery in Wikidata -- Arguments: -- qid = QID of current article -- fetch = whether to lookup Commons sitelink (bool) -- commonsSitelink = default value for Commons sitelink -- Returns: -- categoryLink = name of Commons category, nil if nothing is found -- consistent = multiple wikidata fields are examined: are they consistent? -- commonsSitelink = commons sitelink for current article local function _lookupGallery(qid,fetch,commonsSitelink) if not _validQID(qid) then return nil, true, nil end qid = qid:upper() local galleryLink = nil local consistent = true -- look up commons sitelink for article, use if not category if fetch then commonsSitelink = mw.wikibase.getSitelink(qid,"commonswiki") or commonsSitelink end if commonsSitelink and mw.ustring.sub(commonsSitelink,1,9) ~= "Category:" then galleryLink = commonsSitelink end -- P935 is the "commons gallery" property for this article local P935 = mw.wikibase.getBestStatements(qid, "P935")[1] if P935 and P935.mainsnak.datavalue then local gallery = P935.mainsnak.datavalue.value if galleryLink and galleryLink ~= gallery then consistent = false else galleryLink = gallery end end return galleryLink, consistent, commonsSitelink end -- Find fallback category by looking up Commons sitelink of different page -- Arguments: -- qid = QID for current article -- property = property that refers to other article whose sitelink to return -- Returns: either category-stripped name of article, or nil local function _lookupFallback(qid,property) if not _validQID(qid) or not _validProp(property) then return nil end qid = qid:upper() property = property:upper() -- If property exists on current article, get value (other article qid) local value = mw.wikibase.getBestStatements(qid, property)[1] if value and value.mainsnak.datavalue and value.mainsnak.datavalue.value.id then -- Look up Commons sitelink of other article local sitelink = mw.wikibase.getSitelink(value.mainsnak.datavalue.value.id,"commonswiki") -- Check to see if it starts with "Category:". If so, strip it and return if sitelink and mw.ustring.sub(sitelink,1,9) == "Category:" then return mw.ustring.sub(sitelink,10) end end return nil end -- Find Commons category by looking in wikidata -- Arguments: -- qid = QID of current article -- fetch = whether to lookup Commons sitelink (bool) -- commonsSitelink = default value for Commons sitelink -- Returns: -- categoryLink = name of Commons category, nil if nothing is found -- consistent = multiple wikidata fields are examined: are they consistent? -- commonsSitelink = commons sitelink for current article local function _lookupCategory(qid, fetch, commonsSitelink) if not _validQID(qid) then return nil, true, nil end qid = qid:upper() local categoryLink = nil local consistent = true -- look up commons sitelink for article, use if starts with "Category:" if fetch then commonsSitelink = mw.wikibase.getSitelink(qid,"commonswiki") or commonsSitelink end if commonsSitelink and mw.ustring.sub(commonsSitelink,1,9) == "Category:" then categoryLink = mw.ustring.sub(commonsSitelink,10) end -- purposefully do not look in P373 ("commons category" property) -- if P373 is desired, then restore search code here -- P910 is the "topic's main category". Look for commons sitelink there local fallback = _lookupFallback(qid,"P910") if fallback then if categoryLink and categoryLink ~= fallback then consistent = false qid = nil else categoryLink = fallback end end -- P1754 is the "list's main category". Look for commons sitelink there fallback = _lookupFallback(qid,"P1754") if fallback then if categoryLink and categoryLink ~= fallback then consistent = false else categoryLink = fallback end end return categoryLink, consistent, commonsSitelink end -- Does the article have a Commons gallery, and is it consistent? -- Arguments: -- qid = QID to lookup in wikidata (for testing only) -- Returns: -- filename at Commons, bool: is wikidata consistent for this article? function p._hasGalleryConsistent(qid) local wp_title, wp_ns wp_title, wp_ns, qid = _getTitleQID(qid) return _lookupGallery(qid,true) end -- Does the article have a corresponding Commons gallery? -- Arguments: -- qid = QID to lookup in wikidata (for testing only) -- Returns: -- filename at Commons if so, false if not function p._hasGallery(qid) local galleryLink, consistent = p._hasGalleryConsistent(qid) return consistent and galleryLink end -- Does the article have a Commons category? Is wikidata consistent for that? -- Arguments: -- qid = QID to lookup in wikidata (for testing only) -- prefix = whether to add "Category:" to return string (default true) -- Returns: -- filename at Commons, bool: consistent function p._hasCategoryConsistent(qid,prefix) if prefix == nil then prefix = true end local wp_title, wp_ns wp_title, wp_ns, qid = _getTitleQID(qid) local categoryLink, consistent = _lookupCategory(qid,true) if categoryLink and prefix then categoryLink = "Category:"..categoryLink end return categoryLink, consistent end -- Does the article have a corresponding Commons category? -- Arguments: -- qid = QID to lookup in wikidata (for testing only) -- prefix = whether to add "Category:" to return string (default true) -- Returns: -- filename at Commons if so, blank if not function p._hasCategory(qid,prefix) local categoryLink, consistent = p._hasCategoryConsistent(qid,prefix) return consistent and categoryLink end -- Create Commons link corresponding to current article -- Arguments: -- namespace = namespace in Commons ("" for galleries) -- default = use as Commons link, don't access wikidata -- search = string to search for -- fallback = string to search for if wikidata fails -- formatting = formatting parameters -- qid = QID to lookup in wikidata (for testing only) -- Returns: -- formatted wikilink to Commons in specified namespace function p._getCommons(namespace,default,search,fallback,formatting,qid) local nsColon if not namespace or namespace == "" then nsColon = "" else nsColon = namespace..":" end if default then return "[[Commons:"..nsColon..default.."|".._formatResult(default,formatting).."]]" end if search then return "[[Commons:Special:Search/"..nsColon..search.."|".._formatResult(search,formatting).."]]" end local wp_title, wp_ns wp_title, wp_ns, qid = _getTitleQID(qid) local commonsLink = nil local consistent = true if nsColon == "" then commonsLink, consistent = _lookupGallery(qid,true) elseif namespace:lower() == "category" then commonsLink, consistent = _lookupCategory(qid,true) end -- use wikidata if consistent if commonsLink and consistent then return "[[Commons:"..nsColon..commonsLink.."|".._formatResult(commonsLink,formatting).."]]" end -- if not consistent, fall back to search and add to tracking cat -- construct default result (which searches for title) local searchResult = "[[Commons:Special:Search/"..nsColon..(fallback or wp_title) .."|".._formatResult(fallback or wp_title,formatting).."]]" if not consistent and wp_ns == "" then local friendlyNS if nsColon == "" then friendlyNS = "gallery" else friendlyNS = namespace:lower() end searchResult = searchResult.."[[Category:Inconsistent wikidata for Commons "..friendlyNS.."]]" end return searchResult end -- Returns "best" Commons link: first look for gallery, then try category -- Arguments: -- default = use as Commons link, don't access wikidata -- search = string to search for -- fallback = string to search for if wikidata lookup fails -- formatting = formatting parameters -- qid = QID to lookup in wikidata (for testing only) -- Returns: -- formatted wikilink to Commons "best" landing page function p._getGalleryOrCategory(default, search, fallback, formatting, qid) if default then return "[[Commons:"..default.."|".._formatResult(default,formatting).."]]" end if search then return "[[Commons:Special:Search/"..search.."|".._formatResult(search,formatting).."]]" end local wp_title, wp_ns wp_title, wp_ns, qid = _getTitleQID(qid) local trackingCats = "" local galleryLink, consistent, commonsSitelink = _lookupGallery(qid,true) -- use wikidata if either sitelink or P935 exist, and they both agree if galleryLink and consistent then return "[[Commons:"..galleryLink.."|".._formatResult(galleryLink,formatting).."]]" end if not consistent and wp_ns == "" then trackingCats = "[[Category:Inconsistent wikidata for Commons gallery]]" end -- if gallery is not good, fall back looking for category local categoryLink categoryLink, consistent = _lookupCategory(qid,false,commonsSitelink) if categoryLink and consistent then return "[[Commons:Category:"..categoryLink.."|".._formatResult(categoryLink,formatting).."]]"..trackingCats end if not consistent and wp_ns == "" then trackingCats = trackingCats.."[[Category:Inconsistent wikidata for Commons category]]" end -- return search result looking for title as last attempt return "[[Commons:Special:Search/" .. (fallback or wp_title) .. "|" .. _formatResult(fallback or wp_title,formatting) .. "]]" .. trackingCats end -- Return link(s) Commons gallery, or category, or both from wikidata -- Arguments: -- defaultGallery = default gallery link to use, instead of wikidata -- defaultCategory = default category link to use, instead of wikidata -- categoryText = if both gallery and category, text to use in category link ("category" by default) -- oneSearch = only emit one search result -- formatting = formatting parameters -- qid = qid of page to lookup in wikidata (testing only) function p._getGalleryAndCategory(defaultGallery, defaultCategory, categoryText, oneSearch, formatting, qid ) local wp_title, wp_ns wp_title, wp_ns, qid = _getTitleQID(qid) categoryText = categoryText or "category" local trackingCats = "" local galleryLink, galleryConsistent local commonsSitelink = nil if defaultGallery then galleryLink = defaultGallery galleryConsistent = true else galleryLink, galleryConsistent, commonsSitelink = _lookupGallery(qid,true) end local galleryGood = galleryLink and galleryConsistent if not galleryConsistent and wp_ns == "" then trackingCats = "[[Category:Inconsistent wikidata for Commons gallery]]" end local categoryLink, categoryConsistent if defaultCategory then categoryLink = defaultCategory categoryConsistent = true else categoryLink, categoryConsistent = _lookupCategory(qid,defaultGallery,commonsSitelink) end local categoryGood = categoryLink and categoryConsistent if not categoryConsistent and wp_ns == "" then trackingCats = trackingCats.."[[Category:Inconsistent wikidata for Commons category]]" end local firstLink -- construct default result (which searches for title) local searchResult = "[[Commons:Special:Search/"..wp_title.."|".._formatResult(wp_title,formatting).."]]" if not oneSearch then searchResult = searchResult.." ([[Commons:Special:Search/Category:"..wp_title.."|"..categoryText.."]])" end local linkText = nil if galleryGood then firstLink = galleryLink linkText = galleryLink elseif categoryGood then firstLink = "Category:"..categoryLink linkText = categoryLink else return searchResult..trackingCats end local resultVal = "[[Commons:"..firstLink.."|".._formatResult(linkText,formatting).."]]" if galleryGood and categoryGood then resultVal = resultVal.." ([[Commons:Category:"..categoryLink.."|"..categoryText.."]])" end return resultVal..trackingCats end -- Compare two titles with their namespaces stripped local function titleMatch(s1,s2) s1 = s1 or "" s2 = s2 or "" s1 = mw.ustring.gsub(s1,"^[^:]+:","") s2 = mw.ustring.gsub(s2,"^[^:]+:","") return s1 == s2 end local galleryTrackingCats = { commons_link_on_wikidata = '[[Category:Commons link is on Wikidata]]', commons_link_defined_as_pagename = '[[Category:Commons link is defined as the pagename]]', commons_link_locally_defined = '[[Category:Commons link is locally defined]]', commons_link_from_wikidata = '[[Category:Commons link from Wikidata]]', commons_link_is_pagename = '[[Category:Commons link is the pagename]]', inconsistent = '[[Category:Inconsistent wikidata for Commons gallery]]' } local categoryTrackingCats = { commons_link_on_wikidata = '[[Category:Commons category link is on Wikidata]]', commons_link_defined_as_pagename = '[[Category:Commons category link is defined as the pagename]]', commons_link_locally_defined = '[[Category:Commons category link is locally defined]]', commons_link_from_wikidata = '[[Category:Commons category link from Wikidata]]', commons_link_is_pagename = '[[Category:Commons category link is the pagename]]', inconsistent = '[[Category:Inconsistent wikidata for Commons category]]' } local function selectTrackingCat(trackingCats,wikidata,consistent,default,title) if not consistent then return trackingCats.inconsistent end if default then -- construct warning message if default == wikidata then return trackingCats.commons_link_on_wikidata end local warning = "" if wikidata then warning = generateWarning({ "Commons link does not match Wikidata – [[Template:Commons_category#Resolving_discrepancies|please check]]" }) end if titleMatch(default,title) then return trackingCats.commons_link_defined_as_pagename .. warning end return trackingCats.commons_link_locally_defined .. warning end if wikidata then return trackingCats.commons_link_from_wikidata end return trackingCats.commons_link_is_pagename end -- Figure out tracking categories and editor warnings -- Arguments: -- default = Commons link argument passed to template -- fetchGallery = whether to fetch a gallery from Wikidata -- fetchCategory = whether to fetch a category from Wikidata -- qid = force a qid for testing -- Returns: -- tracking category and possible user warning -- -- Note: the logic for the tracking is quite different than the logic -- for generating Commons links (above). Thus, it is separated into another -- function for code clarity and maintainability. This should not seriously -- affect performance: server time is dominated by fetching wikidata entities, -- and those entities should be cached and shared between the Commons generating -- code and this tracking code. function p._tracking(default, fetchGallery, fetchCategory, qid) local title, wp_ns, wp_qid = _getTitleQID(qid,true) if wp_ns ~= "" then title = wp_ns..":"..title end -- only track if test or namespace=article or namespace=category if not (qid or wp_ns == "" or wp_ns == "Category") then return "" end -- determine title and namespace of wikidata and wp article local wikidata = nil local consistent = nil -- Tracking code works for all 4 cases of states of fetchGallery/Category -- fetchGallery takes precedence if fetchGallery then wikidata, consistent = p._hasGalleryConsistent(qid) if default or not fetchCategory or (consistent and wikidata) then return selectTrackingCat(galleryTrackingCats,wikidata,consistent, default,title) end end if fetchCategory then cat_wikidata, cat_consistent = p._hasCategoryConsistent(qid,true) if not fetchGallery or (cat_consistent and cat_wikidata) then return selectTrackingCat(categoryTrackingCats,cat_wikidata, cat_consistent,default,title) end return selectTrackingCat(galleryTrackingCats,wikidata,consistent, default,title) end return "" -- nothing fetched, nothing tracked end local function _createFormatting(args) formatting = {} formatting.linktext = args.linktext formatting.lcfirst = yesNo(args.lcfirst) formatting.bold = yesNo(args.bold) formatting.italic = yesNo(args.italic) formatting.nowrap = yesNo(args.nowrap) return formatting end -- Testing-only entry point for _getTitleQID function p.getTitleQID(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) local text, ns, qid = _getTitleQID(args[1],args[2]) return text..","..ns..","..(qid or "nil") end -- Testing-only entry point for _lookupFallback function p.lookupFallback(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) local fallback = _lookupFallback(args[1],args[2]) return fallback or "nil" end -- Find the Commons gallery page associated with article function p.getGallery(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) return p._getCommons("",args[1],args.search,args.fallback,_createFormatting(args),args.qid) end -- Find the Commons category page associated with article function p.getCategory(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) local retval = p._getCommons("Category", args[1], args.search, args.fallback, _createFormatting(args), args.qid ) if args.tracking then local default = nil if args[1] then default = "Category:"..args[1] end retval = retval..p._tracking(default, false, true, args.qid) end return retval end function p.getGalleryOrCategory(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) local retval = p._getGalleryOrCategory( args[1], args.search, args.fallback, _createFormatting(args), args.qid ) if args.tracking then retval = retval..p._tracking(args[1],true,true,args.qid) end return retval end function p.hasGallery(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) return p._hasGallery(args.qid) or "" end function p.hasCategory(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) return p._hasCategory(args.qid) or "" end function p.hasGalleryOrCategory(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) return p._hasGallery(args.qid) or p._hasCategory(args.qid) or "" end function p.getGalleryAndCategory(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) return p._getGalleryAndCategory(args[1], args[2], args.categoryText, args.oneSearch, _createFormatting(args), args.qid) end function p.tracking(frame) local args = getArgs(frame,{frameOnly=true,parentOnly=false,parentFirst=false}) return p._tracking(args[1], args.fetchGallery, args.fetchCategory, args.qid) end return p