Aller au contenu

Module:Cite

De Wiki Undertale FR

Generates citations across the wiki to ensure standard formatting. The module accepts several different types of citations and processes each differently, based on the f table's functions.

Documentation

Package items

f.twitter(args) (function)
Handles Twitter citations.
Parameter: args Citation type arguments (table)
Returns: Citation text (string)
See also:
f.bluesky(args) (function)
Handles Bluesky citations.
Parameter: args Citation type arguments (table)
Returns: Citation text (string)
See also: Template:Cite bluesky
f.game(frame_args) (function)
Handles citations of text in the game.
Parameter: frame_args Citation type arguments (table)
Returns: Citation text (string)
See also: Template:Cite game
f.youtube(args) (function)
Handles citations of YouTube videos.
Parameter: args Citation type arguments (table)
Returns: Citation text (string)
See also: Template:Cite youtube
f.tumblr(args) (function)
Handles Tumblr citations.
Parameter: args Citation type arguments (table)
Returns: Citation text (string)
See also: Template:Cite tumblr
f.code(args) (function)
Handles citations of the game's code.
Parameter: args Citation type arguments (table)
Returns: Citation text (string)
See also:
f.news(args) (function)
Handles citations from news sources.
Parameter: args Citation type arguments (table)
Returns: Citation text (string)
See also: Template:Cite news
f.twitch(args) (function)
Handles citations from Twitch clips.
Parameter: args Citation type arguments (table)
Returns: Citation text (string)
See also: uty:Template:Cite twitch

Private items

err(text) (function • local)
Wrapper for userError that places the page under the Pages with user errors category.
Parameter: text Text to display as an error (string)
Returns: Wikitext with the error and category (string)
valid_date(d) (function • local)
Checks whether a given date string is a valid date.
Parameter: d Date string to check (string)
Returns: Whether the string is a valid date (boolean)
f (table • local)
Table of possible citation types and the way they are processed. Each type has a function associated with it that gets passed the arguments after the type, does validation of these arguments and returns the whole citation text.

See also

Category:Citation templates

--- Generates citations across the wiki to ensure standard formatting.
--  The module accepts several different types of citations and processes each
--  differently, based on the <code>f</code> table's functions.
--  @module             cite
--  @alias              p
--  @require            Module:Date
--  @require            Module:User error
--  @require            Module:Yesno
--  @require            Module:Tags
--  @require            Module:Cite/data
--  @author             [[User:KockaAdmiralac|KockaAdmiralac]]
--  @author             [[User:Ceruleanwarbler2|Ceruleanwarbler2]]
--  @author             [[User:Jacky720|Jacky720]]
--  @see                [[:Category:Citation templates]]
--  <nowiki>
local p = {}

require('strict')

-- Module dependencies
local Date = require('Module:Date')
local yesno = require('Module:Yesno')
local tags = require('Module:Tags')
local data = mw.loadData('Module:Cite/data')

--  Private logic.

local DATE_FORMAT = '%B %d, %Y.'

--- Wrapper for <code>userError</code> that places the page under the Pages with
--  user errors category.
--  @function           err
--  @param              {string} text Text to display as an error
--  @return             {string} Wikitext with the error and category
--  @local
local function err(text)
	return require('Module:User error')(text, 'Pages with user errors')
end

--- Checks whether a given date string is a valid date.
--  @function           valid_date
--  @param              {string} d Date string to check
--  @return             {bool} Whether the string is a valid date
--  @local
local function valid_date(d)
	return pcall(function()
		Date(d)
	end)
end

--- Table of possible citation types and the way they are processed.
--  Each type has a function associated with it that gets passed the arguments
--  after the type, does validation of these arguments and returns the whole
--  citation text.
--  @table              cite_functions
--  @alias              f
local f = {}

--- Handles Twitter citations.
--  @function           f.twitter
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
--  @see                [[Template:Cite twitter]]
--  @see                [[github:client9/snowflake2time]]
function f.twitter(args)
	-- Check validity of arguments
	if not args.id or not tonumber(args.id) then
		return err('Tweet snowflake invalid or not specified')
	end
	if not args.author or not args['text'] then
		return err('Tweet author or citation not specified')
	end
	if args.wayback and not tonumber(args.wayback) then
		return err('Archive ID invalid')
	end
	if args.archivetoday and (not args.archivetimestamp or not valid_date(args.archivetimestamp)) then
		return err('Date of archivation not specified')
	end
	-- Twitter snowflake date extraction
	local snowflake = tonumber(args.id)
	local epoch = math.floor(snowflake / 4194304 + 1288834974657)
	local date1 = Date(math.floor(epoch / 1000))
	-- Format citation
	local date2
	if args.archivetimestamp then
		date2 = Date(args.archivetimestamp):fmt(DATE_FORMAT)
	elseif args.wayback then
		date2 = Date(args.wayback):fmt(DATE_FORMAT)
	end
	local deadurl = yesno(args['deleted'], false)
	local url
	if deadurl and args.archivetoday then
		url = string.format('https://archive.today/%s/', args.archivetoday)
	elseif deadurl and args.wayback then
		url = string.format(
			'https://web.archive.org/web/%s/https://twitter.com/%s/status/%s',
			args.wayback,
			args.author,
			args.id
		)
	else
		url = string.format('https://twitter.com/%s/status/%s', args.author, args.id)
	end
	local str = {
		'<q cite="',
		url,
		'">',
		args.text,
		'</q> — <cite class="twitter">[',
		url
	}
	if data.twitter[args.author] then
		table.insert(str, ' ')
		table.insert(str, data.twitter[args.author])
		table.insert(str, ' (@')
		table.insert(str, args.author)
		table.insert(str, ')')
	else
		table.insert(str, ' @')
		table.insert(str, args.author)
	end
	table.insert(str, ' on Twitter,]</cite> ')
	table.insert(str, date1:fmt(DATE_FORMAT))
	if deadurl then
		if args.wayback or args.archivetoday then
			table.insert(str, ' Archived on ')
			table.insert(str, date2)
		else
			table.insert(str, ' \'\'\'[deleted]\'\'\'')
		end
	end
	return table.concat(str)
end

--- Handles Bluesky citations.
--  @function           f.bluesky
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
--  @see                [[Template:Cite bluesky]]
function f.bluesky(args)
	if not args.author or not args.text then
		return err('Author or quote not specified')
	end
	if not args.id then
		return err('Post ID not specified')
	end
	if not args.timestamp or not valid_date(args.timestamp) then
		return err('Date invalid or not specified')
	end
	if args.wayback and not tonumber(args.wayback) then
		return err('Archive ID invalid')
	end
	if args.archivetoday and (not args.archivetimestamp or not valid_date(args.archivetimestamp)) then
		return err('Date of archivation not specified')
	end
	local archivedate
	if args.archivetimestamp then
		archivedate = Date(args.archivetimestamp):fmt(DATE_FORMAT)
	elseif args.wayback then
		archivedate = Date(args.wayback):fmt(DATE_FORMAT)
	end
	local deadurl = yesno(args['deleted'], false)
	local url
	if deadurl and args.archivetoday then
		url = string.format('https://archive.today/%s/', args.archivetoday)
	elseif deadurl and args.wayback then
		url = string.format(
			'https://web.archive.org/web/%s/https://bsky.app/profile/%s/post/%s',
			args.wayback,
			args.author,
			args.id
		)
	else
		url = string.format('https://bsky.app/profile/%s/post/%s', args.author, args.id)
	end
	
	local str = string.format(
		'<q cite="%s">%s</q> — <cite class="bluesky">[%s @%s on Bluesky,]</cite> %s',
		url,
		tags.replace(args.text),
		url,
		args.author,
		Date(args.timestamp):fmt(DATE_FORMAT)
	)
	if deadurl then
		if args.wayback or args.archivetoday then
			str = string.format(
				'%s Archived on %s',
				str,
				archivedate
			)
		else
			str = string.format(
				'%s \'\'\'[deleted]\'\'\'',
				str
			)
		end
	end
	return str
end

--- Handles citations of text in the game.
--  @function           f.game
--  @param              {table} frame_args Citation type arguments
--  @return             {string} Citation text
--  @see                [[Template:Cite game]]
function f.game(frame_args)
	local args = {}
	for i, v in ipairs(frame_args) do
		-- frame.args is a metatable, so we cannot use #args or table.concat
		-- on it before we convert it to a real table.
		args[i] = v
	end
	local num_args = #args
	if num_args == 0 then
		return err('Quote not specified')
	end
	if num_args == 1 then
		return err('Quote author not specified')
	end
	local author = args[num_args]
	args[num_args] = nil
	return string.format(
		'<q>%s</q> — <cite class="game">%s</cite>',
		tags.replace(table.concat(args, '<br />')),
		author
	)
end

--- Handles citations of YouTube videos.
--  @function           f.youtube
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
--  @see                [[Template:Cite youtube]]
function f.youtube(args)
	if not args.id or not args.title then
		return err('Video ID or title not specified')
	end
	local url
	if args.timestamp then
		url = string.format('https://youtu.be/%s?t=%s', args.id, args.timestamp)
	else
		url = string.format('https://youtu.be/%s', args.id)
	end
	local duration
	local seconds = tonumber(args.timestamp)
	if seconds ~= nil then
		local minutes = math.floor(seconds / 60)
		seconds = seconds - minutes * 60
		local hours = math.floor(minutes / 60)
		minutes = minutes - hours * 60
		if hours > 0 then
			duration = string.format(
				' (%02d:%02d:%02d)',
				hours,
				minutes,
				seconds
			)
		else
			duration = string.format(
				' (%02d:%02d)',
				minutes,
				seconds
			)
		end
	else
		duration = ''
	end
	return string.format(
		'<cite class="youtube">[%s %s]</cite>%s on YouTube',
		url,
		args.title,
		duration
	)
end

--- Handles Tumblr citations.
--  @function           f.tumblr
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
--  @see                [[Template:Cite tumblr]]
function f.tumblr(args)
	if not args.author or not args.text then
		return err('Author or quote not specified')
	end
	if not args.id or not tonumber(args.id) then
		return err('Post ID invalid or not specified')
	end
	if not args.timestamp or not valid_date(args.timestamp) then
		return err('Date invalid or not specified')
	end
	local url = string.format(
		'https://%s.tumblr.com/post/%s',
		-- Temporary solution to avoid showing reblogger's name as author name
		-- on inaccessible posts.
		-- To be replaced by proper archival parameters.
		args.reblogger or args.author,
		args.id
	)
	return string.format(
		'<q cite="%s">%s</q> — <cite class="tumblr">[%s %s on Tumblr,]</cite> %s',
		url,
		tags.replace(args.text),
		url,
		args.author,
		Date(args.timestamp):fmt(DATE_FORMAT)
	)
end

--- Handles citations of the game's code.
--  @function           f.code
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
--  @see                [[Template:Cite code]]
--  @see                [[code:|Code repository]]
function f.code(args)
	local hasLink = args.chapter == nil or tonumber(args.chapter) < 5
	if not args.script then
		return err('Script name not specified')
	end
	if args.line and not tonumber(args.line) then
		return err('Starting line number is not a number')
	end
	if args.line2 and not tonumber(args.line2) then
		return err('Ending line number is not a number')
	end
	local str = {'<cite class="code">'}
	if hasLink then
		table.insert(str, '[[code:')
		if args.chapter ~= nil then
			table.insert(str, 'ch')
			table.insert(str, args.chapter)
			table.insert(str, '/')
		end
		table.insert(str, args.script)
		if args.line then
			table.insert(str, '#L')
			table.insert(str, args.line)
		end
		table.insert(str, '|')
	end
	table.insert(str, args.script)
	table.insert(str, ' script')
	if hasLink then
		table.insert(str, ']]')
	end
	table.insert(str, '</cite>')
	if args.line then
		table.insert(str, ', line')
		if args.line2 then
			table.insert(str, 's ')
			table.insert(str, args.line)
			table.insert(str, '–')
			table.insert(str, args.line2)
		else
			table.insert(str, ' ')
			table.insert(str, args.line)
		end
	end
	if args.chapter ~= nil then
		table.insert(str, ', Chapter ')
		table.insert(str, args.chapter)
	end
	return table.concat(str)
end

--- Handles citations from news sources.
--  @function           f.news
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
--  @see                [[Template:Cite news]]
function f.news(args)
	if not args.text then
		return err('Relevant news post excerpt not specified')
	end
	if not args.title then
		return err('News post title not specified')
	end
	if not args.site then
		return err('News site name not specified')
	end
	if not args.url then
		return err('News post URL not specified')
	end
	if not args.timestamp or not valid_date(args.timestamp) then
		return err('News post date invalid or not specified')
	end
	local author = {}
	if args.firstname then
		if args.lastname then
			table.insert(author, args.lastname)
			table.insert(author, ', ')
		end
		table.insert(author, args.firstname)
		table.insert(author, ', ')
	end
	return string.format(
		'<q cite="%s">%s</q> — [%s %s] (%s%s) <cite class="news">%s</cite>.',
		args.url,
		tags.replace(args.text),
		args.url,
		args.title,
		table.concat(author),
		Date(args.timestamp):fmt(DATE_FORMAT),
		args.site
	)
end

--- Handles citations from Twitch clips.
--  @function           f.twitch
--  @param              {table} args Citation type arguments
--  @return             {string} Citation text
--  @see                [[uty:Template:Cite twitch]]
function f.twitch(args)
	if not args.id then
		return err('Clip URL segment not specified')
	end
	if not args.title then
		return err('Clip title not specified')
	end
	if not args.author then
		return err('Streamer username not specified')
	end
	if not args.timestamp or not valid_date(args.timestamp) then
		return err('Stream date invalid or not specified')
	end
	return string.format(
		'<cite class="twitch">[https://clips.twitch.tv/%s %s,]</cite> a clip from [https://twitch.tv/%s @%s\'s] Twitch stream on %s.',
		args.id,
		args.title,
		mw.ustring.lower(args.author),
		args.author,
		Date(args.timestamp):fmt(DATE_FORMAT)
	)
end

-- Package items.

for mname, method in pairs(f) do
	p[mname] = function(frame)
		return method(frame:getParent().args)
	end
end

return p