Roovet Articles

Module:Official website

This module implements {{Official website}}. Please see the template page for documentation.

Tracking categories

See also


-- Module:Official_website
-- Robust version that works WITH or WITHOUT Wikibase.
-- If Wikibase (mw.wikibase) is unavailable, the module will gracefully
-- skip all Wikidata lookups and only use the provided |url= / positional args.

local makeUrl = require('Module:URL')._url

local p = {}

-- -------- Utility helpers --------

-- Wrapper for pcall which returns nil on failure.
local function quickPcall(func)
	local ok, res = pcall(func)
	if ok then
		return res
	end
end

-- Detect whether Wikibase is available on this wiki.
local hasWikibase = (mw and mw.wikibase)
	and type(mw.wikibase.getEntityIdForCurrentPage) == 'function'
	and type(mw.wikibase.getAllStatements) == 'function'

local function getCurrentQid()
	if not hasWikibase then return nil end
	return quickPcall(function()
		return mw.wikibase.getEntityIdForCurrentPage()
	end)
end

-- Gets the rank for a Wikidata property table. Returns 1, 0 or -1.
local function getRank(prop)
	local rank = prop and prop.rank
	if rank == 'preferred'   then return 1 end
	if rank == 'normal'      then return 0 end
	if rank == 'deprecated'  then return -1 end
	return 0
end

-- Finds whether a Wikidata property is qualified as being in English (P407 = Q1860).
local function isEnglish(prop)
	local ret = quickPcall(function ()
		if not prop or not prop.qualifiers or not prop.qualifiers.P407 then
			return false
		end
		for _, lang in ipairs(prop.qualifiers.P407) do
			local v = lang and lang.datavalue and lang.datavalue.value
			if v and v['numeric-id'] == 1860 then
				return true
			end
		end
		return false
	end)
	return ret == true
end

-- Normalize URL for comparison (trim spaces and trailing slashes).
local function normalizeUrl(u)
	if type(u) ~= 'string' then return u end
	u = mw.text.trim(u)
	-- remove trailing slashes (one or more)
	u = u:gsub('/+$', '')
	return u
end

-- -------- Wikidata fetch (guarded) --------

local fetchWikidataUrl
fetchWikidataUrl = function()
	-- If Wikibase isn't available, short-circuit to nil and memoize.
	if not hasWikibase then
		fetchWikidataUrl = function() return nil end
		return nil
	end

	-- Get objects for all official sites (P856) on Wikidata.
	local websites = quickPcall(function ()
		return mw.wikibase.getAllStatements(getCurrentQid(), 'P856')
	end) or {}

	-- Clone for safe sorting.
	websites = mw.clone(websites)

	-- Add original index to stabilize sort.
	for i, website in ipairs(websites) do
		website._index = i
	end

	-- Sort by (1) rank, (2) English qualifier, (3) original index.
	table.sort(websites, function(ws1, ws2)
		local r1, r2 = getRank(ws1), getRank(ws2)
		if r1 ~= r2 then
			return r1 > r2
		end
		local e1, e2 = isEnglish(ws1), isEnglish(ws2)
		if e1 ~= e2 then
			return e1
		end
		return (ws1._index or 0) < (ws2._index or 0)
	end)

	-- Extract URL string from top candidate.
	local url = quickPcall(function ()
		local snak = websites[1] and websites[1].mainsnak
		return snak and snak.datavalue and snak.datavalue.value or nil
	end)

	-- Memoize result.
	fetchWikidataUrl = function ()
		return url
	end

	return url
end

-- -------- Rendering --------

-- Render the URL link + optional adornments.
local function renderUrl(options)
	if not options.url and not options.wikidataurl then
		-- Nothing to show; offer a helpful message.
		local qid = hasWikibase and getCurrentQid() or nil
		local result = '<strong class="error">No URL found. ' ..
			'Please specify a URL' ..
			(hasWikibase and ' here or add one to Wikidata.' or ' here.') ..
			'</strong>'
		if hasWikibase and qid then
			result = result ..
				' [[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px' ..
				'|alt=Edit this at Wikidata' ..
				'|link=https://www.wikidata.org/wiki/' .. qid .. '#P856' ..
				'|Edit this at Wikidata]]'
		end
		return result
	end

	local ret = {}
	ret[#ret + 1] = string.format(
		'<span class="official-website">%s</span>',
		makeUrl(options.url or options.wikidataurl, options.display)
	)

	-- If pulling from Wikidata and a local URL wasn't provided, show edit badge (only if Wikibase exists).
	if options.wikidataurl and not options.url and hasWikibase then
		local qid = getCurrentQid()
		if qid then
			ret[#ret + 1] =
				'[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px' ..
				'|alt=Edit this at Wikidata' ..
				'|link=https://www.wikidata.org/wiki/' .. qid .. '#P856' ..
				'|Edit this at Wikidata]]'
		end
	end

	if options.format == 'flash' then
		ret[#ret + 1] = mw.getCurrentFrame():expandTemplate{
			title = 'Color',
			args = {'#505050', '(Requires [[Adobe Flash Player]])'}
		}
	end

	if options.mobile then
		ret[#ret + 1] = '(' .. makeUrl(options.mobile, 'Mobile') .. ')'
	end

	return table.concat(ret, ' ')
end

-- Render the tracking category (only when it makes sense).
local function renderTrackingCategory(url, wikidataurl)
	-- Only in article namespace.
	if mw.title.getCurrentTitle().namespace ~= 0 then
		return ''
	end

	-- If Wikibase isn't available, we cannot track Wikidata-related categories.
	if not hasWikibase then
		-- If no URL at all, optionally add a local maintenance category.
		if not url then
			return '[[Category:Official website missing URL]]'
		end
		return ''
	end

	local category
	if not url and not wikidataurl then
		category = 'Official website missing URL'
	elseif not url and wikidataurl then
		-- OK: URL solely from Wikidata -> no category
		return ''
	elseif url and wikidataurl then
		if normalizeUrl(url) ~= normalizeUrl(wikidataurl) then
			category = 'Official website different in Wikidata and Wikipedia'
		end
	else
		category = 'Official website not in Wikidata'
	end
	return category and string.format('[[Category:%s]]', category) or ''
end

-- -------- Public interface --------

function p._main(args)
	local url = args[1] or args.URL or args.url
	local wikidataurl = fetchWikidataUrl()

	local formattedUrl = renderUrl{
		url = url,
		wikidataurl = wikidataurl,
		display = args[2] or args.name or 'Official website',
		format = args.format,
		mobile = args.mobile
	}

	return formattedUrl .. renderTrackingCategory(url, wikidataurl)
end

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = 'Template:Official website'
	})
	return p._main(args)
end

return p