Module:Edition

From OODA WIKI

The Edition module is responsible for retrieving and displaying various bits of data about editions on Wikisource.

For all of the methods below, the wikidata parameter can be left out and the item ID of the page on which it is invoked will be used.

Method: author_list

Get a list of authors. These will be linked to their author pages where they exist.

Used by {{author-list}}.

Wikitext Output
{{#invoke:Edition|author_list|wikidata=Q28913867}} Lua error at line 123: attempt to index field 'wikibase' (a nil value).

Method: badge

Display Wikidata badges for an edition.

Edition Wikitext Output
A Camp in the Adirondacks {{#invoke:Edition|badge|wikidata=Q16325556}} Lua error at line 123: attempt to index field 'wikibase' (a nil value).
The Great Secret {{#invoke:Edition|badge|wikidata=Q75047127}} Lua error at line 123: attempt to index field 'wikibase' (a nil value).
The Riverside song book/The Open Window {{#invoke:Edition|badge|wikidata=Q59364480}} Lua error at line 123: attempt to index field 'wikibase' (a nil value).
Douglas Adams (no badge) {{#invoke:Edition|badge|wikidata=Q42}} Lua error at line 123: attempt to index field 'wikibase' (a nil value).

Method: inline

The 'inline summary' of an edition can be used on an author's page, for example, to show the basic details about the edition and work.

Wikitext Output
{{#invoke:Edition|inline|wikidata_id=Q28913867}} Lua error at line 123: attempt to index field 'wikibase' (a nil value).

Method: authority_control

For editions, you can list all available authority controlled identifiers with:

Wikitext Output
{{#invoke:Edition|authority_control|wikidata=Q28020002}} Lua error at line 123: attempt to index field 'wikibase' (a nil value).

And if you want to list all for the parent work of the edition, use:

Wikitext Output
{{#invoke:Edition|authority_control|wikidata=Q28020002|show_work=yes}} Lua error at line 123: attempt to index field 'wikibase' (a nil value).

See also


--------------------------------------------------------------------------------
-- Organisation of this module: the exports are listed at the bottom; their
-- direct functions are just above there, and supporting functions are higher
-- up.
--
-- Please add testcases before modifying anything.
--
--------------------------------------------------------------------------------
require('strict')

local instanceOfProp = 'P31'
local authorProp = 'P50'
local editionOrTranslationOfProp = 'P629'
local equivalentClassProp = 'P1709'
local topicsMainWikimediaPortalProp = 'P1151';

--------------------------------------------------------------------------------
-- Insert a value into a table, but not if it's already there.
local function tableInsertUnique( theTable, value )
	for _, item in pairs( theTable ) do
		if item == value then
			-- Already present
			return
		end
	end
	-- Otherwise, add the new value.
	table.insert( theTable, value )
end

--------------------------------------------------------------------------------
-- For the given author item, find the Wikisource sitelink. If there isn't one,
-- check to see if there the author has a topicsMainWikimediaPortal statement,
-- and then see if that portal has a sitelink instead.
local function get_author_sitelink( authorItem )
	local siteLink = authorItem:getSitelink( mw.language.getContentLanguage().code .. 'wikisource' )
	if ( siteLink ~= nil ) then
		return siteLink
	end
	local portalStatements = authorItem:getBestStatements( topicsMainWikimediaPortalProp )
	for _, mainPortal in pairs( portalStatements ) do
		local mainPortalId = mainPortal['mainsnak']['datavalue']['value']['id']
		local mainPortalItem = mw.wikibase.getEntity( mainPortalId )
		siteLink = mainPortalItem:getSitelink( mw.language.getContentLanguage().code .. 'wikisource' )
	end
	return siteLink
end

--------------------------------------------------------------------------------
-- Get the schema.org URL for the itemtype of the given item.
local function getSchemaorgItemtype( item )
	local schemaPrefix = 'http://schema.org/'
	for _, instanceOf in pairs( item:getBestStatements( instanceOfProp ) ) do
		local instanceOfId = instanceOf['mainsnak']['datavalue']['value']['id']
		local instanceOfItem = mw.wikibase.getEntity( instanceOfId )
		-- Now go through each of the instance-of item's class statements,
		-- seeing if we can find a matching schema.org URL.
		for _, equivClass in pairs( instanceOfItem:getBestStatements( equivalentClassProp ) ) do
			local val = equivClass['mainsnak']['datavalue']['value']
			if string.sub( val, 1, #schemaPrefix ) == schemaPrefix then
				-- This is a schema.org URL.
				return val
			end
		end
	end
	-- If we've not figured it out by now, give up with the default.
	return schemaPrefix .. 'Thing'
end

--------------------------------------------------------------------------------
-- Create the HTML (including wikitext link) for all provided authors,
-- and add it to the given authorLinks table (for later concatenation).
local function process_authors( authorLinks, authors )
	if authors == nil or #authors == 0 then
		return
	end
	for _, author in pairs( authors ) do
		local authorId = author['mainsnak']['datavalue']['value']['id']
		local authorItem =  mw.wikibase.getEntity( authorId )
		local siteLink = get_author_sitelink( authorItem )
		local authorName = authorItem:getLabel()
		if siteLink ~= nil then
			authorName = mw.title.new( siteLink ).text
		end
		local authorHtml = mw.html.create('span')
			:attr('itemprop', 'author')
			:attr('itemscope', '')
			:attr('itemtype', getSchemaorgItemtype( authorItem ) )
		local authorNameHtml = authorHtml:tag( 'span' )
		authorNameHtml:attr( 'itemprop', 'name' )
			:wikitext( authorName )
		local authorHtmlStr = tostring( authorHtml )
		if siteLink == nil then
			tableInsertUnique( authorLinks, authorHtmlStr )
		else
			tableInsertUnique( authorLinks, '[[' .. siteLink .. '|' .. authorHtmlStr .. ']]' )
		end
	end
end

--------------------------------------------------------------------------------
-- Get an Item based on what's passed in the 'wikidata' or 'page' parameters of
-- the args, or the current page's ID otherwise.
local function getItem( args )
	local id = nil
	-- If args is a table with an appropriate element, use it.
	if type( args ) == 'table' then
		if args.wikidata ~= '' and args.wikidata ~= nil then
			id = args.wikidata
		elseif args.wikidata_id ~= '' and args.wikidata_id ~= nil then
			id = args.wikidata_id
		elseif args.page ~= '' and args.page ~= nil then
			local title = mw.title.new( args.page )
			id = mw.wikibase.getEntityIdForTitle( title.nsText .. title.text )
			-- If no entity for this page, maybe it's a subpage and we should look for the root page's entity.
			if id == nil then
				id = mw.wikibase.getEntityIdForTitle( title.nsText .. title.rootText )
			end
		end
	end
	if type( args ) == 'string' and args ~= '' then
		id = args
	end
	return mw.wikibase.getEntity( id )
end

--------------------------------------------------------------------------------
-- Exported method.
--
local function author_list( args )
	local item = getItem( args )
	if item == nil then
		return ''
	end
	local authorLinks = {}

	-- Collect the authors of this item.
	local authors = item:getBestStatements( authorProp )
	process_authors( authorLinks, authors )

	-- Also collect the authors of the parent work.
	local works = item:getBestStatements( editionOrTranslationOfProp )
	for _, work in pairs( works ) do
		local workId = work['mainsnak']['datavalue']['value']['id']
		local workItem = mw.wikibase.getEntity( workId )
		authors = workItem:getBestStatements( authorProp )
		if #authors > 0 then
			process_authors( authorLinks, authors )
		end
	end

	-- Output the final list of links.
	local outHtml = mw.html.create()
	local separator = args.separator or ', '
	local last_separator = args.last_separator or ', and '
	local i = 1
	for _, link in pairs( authorLinks ) do
		outHtml:wikitext( link )
		if i == ( #authorLinks - 1 ) then
			outHtml:wikitext( last_separator )
		elseif #authorLinks > 1 and i ~= #authorLinks then
			outHtml:wikitext( separator )
		end
		i = i + 1
	end
	return tostring( outHtml )
end

--------------------------------------------------------------------------------
-- Exported method.
--
local function inline( args )
	local item = getItem( args )
	local outHtml = mw.html.create()
	if ( item == nil ) then
		outHtml:wikitext( '<span class="error">Unable to determine the Wikidata item to use</span>' )
		return tostring( outHtml )
	end

	-- Make sure it's an edition.
	local editionOrTranslationOfStmts = item:getBestStatements( editionOrTranslationOfProp )
	if #editionOrTranslationOfStmts == 0 then
		outHtml:wikitext( '<span class="error">' .. item.id .. ' is not an edition or translation of a work (missing P629)</span>' )
		return tostring( outHtml )
	end


	-- Title/label.
	local title = item:getSitelink( 'enwikisource' )
	local label = item:getLabel( 'en' )
	local hasWikisourcePage = false
	if title == nil or title == '' then
		title = label
	else
		hasWikisourcePage = true
		title = '[[' .. title .. '|' .. label .. ']]'
	end
	outHtml:wikitext( title .. ' ' );


	-- Publication date
	local publicationDate = item:formatPropertyValues( 'P577' )
	outHtml:wikitext( '(' .. publicationDate.value .. ') ' )


	-- Authors
	if args.hideauthors == nil or args.hideauthors == '' then
		local authorList = author_list( args )
		if authorList ~= '' then
			outHtml:wikitext( ' by ' .. authorList .. ' ' )
		end
	end


	-- Scanned file on Wikimedia Commons.
	if not hasWikisourcePage then
		-- Add links to Index page or Commons file.
		local hasIndexOrCommonsLink = false
		local scannedFileOnWikimediaCommons = 'P996'
		local scannedFileOnWikimediaCommonsStmts = item:getBestStatements( scannedFileOnWikimediaCommons )
		for _, stmt in pairs( scannedFileOnWikimediaCommonsStmts ) do
			local commonsFilename = stmt['mainsnak']['datavalue']['value']
			outHtml:wikitext( ' ' .. mw.getCurrentFrame():expandTemplate{ title = 'Small scan link', args = { commonsFilename } } )
			hasIndexOrCommonsLink = true;
		end
		-- Add link to the IA item if no links were added above.
		if not hasIndexOrCommonsLink then
			local internetArchiveIdProp = 'P724'
			local internetArchiveIdStmts = item:getBestStatements( internetArchiveIdProp )
			for _, stmt in pairs( internetArchiveIdStmts ) do
				local internetArchiveId = stmt['mainsnak']['datavalue']['value']
				outHtml:wikitext( ' ' .. mw.getCurrentFrame():expandTemplate{ title = 'IA small link', args = { internetArchiveId } } )
			end
		end
	end


	-- Wikidata and Wikipedia links.
	local img = '[[File:Wikidata-books-task-force-logo.svg|20px|alt=Wikidata books task force logo|link=d:' .. item.id .. '|View on Wikidata]]'
	outHtml:wikitext( img )

	return tostring( outHtml )
end

--------------------------------------------------------------------------------
-- Exported method. Get wikitext for displaying an edition's badges from Wikidata.
-- To get an indicator, pass args.indicator = true
-- Testing: =p.badge({args={wikidata='Q75043199'}})
--
local function badge( args )
	local item = getItem( args )
	if not ( item and item.sitelinks and item.sitelinks.enwikisource and item.sitelinks.enwikisource.badges ) then
		return ''
	end
	local badges = item.sitelinks.enwikisource.badges
	local out = ''
	for _, badge in pairs( badges ) do
		local badgeOut = ''
		local badgeItem = mw.wikibase.getEntity( badge )
		local badgeName = ''
		local wikisourceBadgeClass = 'Q75042035'
		if badgeItem.claims.P31[1].mainsnak.datavalue.value.id == wikisourceBadgeClass and badgeItem.claims.P18 ~= nil then
			local imageName = badgeItem.claims.P18[1].mainsnak.datavalue.value
			if mw.wikibase.getLabel( badge ) ~= nil then
				badgeName = mw.wikibase.getLabel( badge )
			end
			badgeOut = '<span class="indicator-badge">[[File:' .. imageName .. '|16px|link=Help:Text status|' .. badgeName .. ']]</span>'
			if args.indicator ~= nil then
				badgeOut = '<indicator name="wikisource-badge-' .. badgeName .. '">' .. badgeOut .. '</indicator>'
			end
			if args.category ~= nil and badgeItem.claims.P910 ~= nil then
				local categoryQid = badgeItem.claims.P910[1].mainsnak.datavalue.value.id
				local category = mw.wikibase.getEntity( categoryQid )
				badgeOut = badgeOut .. '[[' .. category.sitelinks.enwikisource.title .. ']]'
			end
			out = out .. badgeOut
		end
	end
	return mw.getCurrentFrame():preprocess( out )
end

--------------------------------------------------------------------------------
-- Exported method.
local function authority_control( args )
	local item = getItem( args )
	-- Gather every 'external-id' statement.
	local out = mw.html.create( '' )
	for propertyId,claims in pairs( item.claims) do
		local propItem = getItem( propertyId )
		for _,claim in pairs( claims ) do
			if claim.mainsnak.datatype == 'external-id' then
				local propLabel = propItem:getLabel()
				if not propLabel then
					if propItem.aliases.en and propItem.aliases.en[1].value then
						propLabel = propItem.aliases.en[1].value
					else
						propLabel = propertyId
					end
				end
				out:wikitext( '* [[d:Special:EntityPage/'.. propertyId .. '|' .. propLabel .. ']]: ' .. claim.mainsnak.datavalue.value .. '\n' )
			end
		end
	end
	return tostring( out )
end

--------------------------------------------------------------------------------
-- Export all public functions.
return {
	-- =p.author_list({args={wikidata='Q28913867'}})
	author_list = function( frame ) return author_list( frame.args ) end;
	-- =p.inline({args={wikidata_id='Q28913867'}})
	inline = function( frame ) return inline( frame.args ) end;
	-- =p.badge({args={wikidata='Q28020002'}})
	badge = function( frame ) return badge( frame.args ) end;
	-- =p.authority_control({args={wikidata='Q19035838'}})
	authority_control = function( frame ) return authority_control( frame.args ) end;
}