Jump to content

Module:Excerpt: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Update to 1.2
Update to 1.2.3, fixes lint errors (see talk page), global scope issue and code style changes
Line 2: Line 2:
-- Documentation https://www.mediawiki.org/wiki/Module:Excerpt
-- Documentation https://www.mediawiki.org/wiki/Module:Excerpt
-- By User:Sophivorus, User:Certes & others
-- By User:Sophivorus, User:Certes & others
-- Version 1.2
-- Version 1.2.3
-- License CC-BY-SA-4.0
-- License CC-BY-SA-4.0


local Transcluder = require('Module:Transcluder')
local Transcluder = require( 'Module:Transcluder' )


local yesno = require('Module:Yesno')
local yesno = require( 'Module:Yesno' )


local ok, config = pcall(require, 'Module:Excerpt/config')
local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end
if not ok then config = {} end


Line 16: Line 16:
-- Helper function to get arguments
-- Helper function to get arguments
local args
local args
function getArg(key, default)
function getArg( key, default )
value = args[key]
local value = args[ key ]
if value and mw.text.trim(value) ~= '' then
if value and mw.text.trim( value ) ~= '' then
return value
return value
end
end
Line 25: Line 25:


-- Helper function to handle errors
-- Helper function to handle errors
function getError(message, value)
function getError( message, value )
if type(message) == 'string' then
if type( message ) == 'string' then
message = Transcluder.getError(message, value)
message = Transcluder.getError( message, value )
end
end
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
message:node('[[Category:' .. config.categories.errors .. ']]')
message:node( '[[Category:' .. config.categories.errors .. ']]' )
end
end
return message
return message
Line 36: Line 36:


-- Helper function to get localized messages
-- Helper function to get localized messages
function getMessage(key)
function getMessage( key )
local ok, TNT = pcall(require, 'Module:TNT')
local ok, TNT = pcall( require, 'Module:TNT' )
if not ok then return key end
if not ok then return key end
return TNT.format('I18n/Module:Excerpt.tab', key)
return TNT.format( 'I18n/Module:Excerpt.tab', key )
end
end


function p.main(frame)
function p.main( frame )
args = Transcluder.parseArgs(frame)
args = Transcluder.parseArgs( frame )


-- Make sure the requested page exists
-- Make sure the requested page exists
local page = getArg(1)
local page = getArg( 1 )
if not page then return getError('no-page') end
if not page then return getError( 'no-page' ) end
local title = mw.title.new(page)
local title = mw.title.new(page)
if not title then return getError('no-page') end
if not title then return getError( 'no-page' ) end
if title.isRedirect then title = title.redirectTarget end
if title.isRedirect then title = title.redirectTarget end
if not title.exists then return getError('page-not-found', page) end
if not title.exists then return getError( 'page-not-found', page ) end
page = title.prefixedText
page = title.prefixedText


-- Set variables from the template parameters
-- Set variables from the template parameters
local section = getArg(2, mw.ustring.match( getArg(1), '[^#]+#(.+)') )
local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
local hat = yesno( getArg('hat', true) )
local hat = yesno( getArg( 'hat', true ) )
local edit = yesno( getArg('edit', true) )
local edit = yesno( getArg( 'edit', true ) )
local this = getArg('this')
local this = getArg( 'this' )
local only = getArg('only')
local only = getArg( 'only' )
local files = getArg('files', getArg('file', ( only == 'file' and 1 ) ) )
local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
local lists = getArg('lists', getArg('list', ( only == 'list' and 1 ) ) )
local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
local tables = getArg('tables', getArg('table', ( only == 'table' and 1 ) ) )
local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
local templates = getArg('templates', getArg('template', ( only == 'template' and 1 ) ) )
local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
local paragraphs = getArg('paragraphs', getArg('paragraph', ( only == 'paragraph' and 1 ) ) )
local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
local references = getArg('references')
local references = getArg( 'references' )
local subsections = not yesno( getArg('subsections') )
local subsections = not yesno( getArg( 'subsections' ) )
local noBold = not yesno( getArg('bold') )
local noBold = not yesno( getArg( 'bold' ) )
local freefiles = yesno( getArg('freefiles') )
local freefiles = yesno( getArg( 'freefiles' ) )
local inline = yesno( getArg('inline') )
local inline = yesno( getArg( 'inline' ) )
local quote = yesno( getArg('quote') )
local quote = yesno( getArg( 'quote' ) )
local more = yesno( getArg('more') )
local more = yesno( getArg( 'more' ) )
local class = getArg('class')
local class = getArg( 'class' )


-- Build the hatnote
-- Build the hatnote
Line 79: Line 79:
hat = this
hat = this
elseif quote then
elseif quote then
hat = getMessage('this')
hat = getMessage( 'this' )
elseif only then
elseif only then
hat = getMessage(only)
hat = getMessage( only )
else
else
hat = getMessage('section')
hat = getMessage( 'section' )
end
end
hat = hat .. ' ' .. getMessage('excerpt') .. ' '
hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
if section and not fragment then
if section and not fragment then
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode(section) .. '|' .. page
hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. page
.. ' § ' .. mw.ustring.gsub(section, '%[%[([^]|]+)|?[^]]*%]%]', '%1') .. ']].' -- remove nested links
.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
else
else
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
Line 94: Line 94:
if edit then
if edit then
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
hat = hat .. title:fullUrl('action=edit') .. ' ' .. mw.message.new('editsection'):plain()
hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
end
end
if config.hat then
if config.hat then
hat = config.hat .. hat .. '}}'
hat = config.hat .. hat .. '}}'
hat = frame:preprocess(hat)
hat = frame:preprocess( hat )
else
else
hat = mw.html.create('div'):addClass('dablink excerpt-hat'):wikitext(hat)
hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
end
end
else
else
Line 109: Line 109:
-- Build the "Read more" link
-- Build the "Read more" link
if more and not inline then
if more and not inline then
more = "'''[[" .. page .. '#' .. (section or '') .. "|" .. getMessage('more') .. "]]'''"
more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
more = mw.html.create('div'):addClass('noprint excerpt-more'):wikitext(more)
more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
else
else
more = nil
more = nil
Line 124: Line 124:
categories = 0,
categories = 0,
references = references,
references = references,
only = only and mw.text.trim(only, 's') .. 's',
only = only and mw.text.trim( only, 's' ) .. 's',
noBold = noBold,
noBold = noBold,
noSelfLinks = true,
noSelfLinks = true,
Line 134: Line 134:


-- Get the excerpt itself
-- Get the excerpt itself
local title = page .. '#' .. (section or '')
local title = page .. '#' .. ( section or '' )
local ok, excerpt = pcall(Transcluder.get, title, options)
local ok, excerpt = pcall( Transcluder.get, title, options )
if not ok then return getError(excerpt) end
if not ok then return getError( excerpt ) end
if mw.text.trim(excerpt) == '' and not only then
if mw.text.trim( excerpt ) == '' and not only then
if section then return getError('section-empty', section) else return getError('lead-empty') end
if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
end
end


-- If no file was found, try to get one from the infobox
-- If no file was found, try to get one from the infobox
local fileNamespaces = Transcluder.getNamespaces('File')
local fileNamespaces = Transcluder.getNamespaces( 'File' )
if ((only == 'file' or only == 'files') or (not only and (files ~= '0' or not files))) and -- caller asked for files
if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
not Transcluder.matchAny(excerpt, '%[%[', fileNamespaces, ':') and -- and there are no files in Transcluder's output
not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
config.captions -- and we have the config option required to try finding files in templates
config.captions -- and we have the config option required to try finding files in templates
then
then
-- We cannot distinguish the infobox from the other templates so we search them all
-- We cannot distinguish the infobox from the other templates so we search them all
local infobox = Transcluder.getTemplates(excerpt);
local infobox = Transcluder.getTemplates( excerpt );
infobox = table.concat(infobox)
infobox = table.concat( infobox )
local parameters = Transcluder.getParameters(infobox)
local parameters = Transcluder.getParameters( infobox )
local file, captions, caption
local file, captions, caption
for _, pair in pairs(config.captions) do
for _, pair in pairs( config.captions ) do
file = pair[1]
file = pair[1]
file = parameters[file]
file = parameters[file]
if file and Transcluder.matchAny(file, '^.*%.', {'[Jj][Pp][Ee]?[Gg]','[Pp][Nn][Gg]','[Gg][Ii][Ff]','[Ss][Vv][Gg]'}, '.*') then
if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
file = mw.ustring.match(file, '%[?%[?.-:([^{|]+)%]?%]?') or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
captions = pair[2]
captions = pair[2]
for _, p in pairs(captions) do
for _, p in pairs( captions ) do
if parameters[p] then caption = parameters[p] break end
if parameters[ p ] then caption = parameters[ p ] break end
end
end
excerpt = '[[File:' .. file .. '|thumb|' .. (caption or '') .. ']]' .. excerpt
excerpt = '[[File:' .. file .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
if ( freefiles ) then
if ( freefiles ) then
excerpt = Transcluder.removeNonFreeFiles(excerpt)
excerpt = Transcluder.removeNonFreeFiles( excerpt )
end
end
break
break
Line 173: Line 173:
-- because we had to search the infoboxes for files
-- because we had to search the infoboxes for files
local trash
local trash
if only and (only == 'template' or only == 'templates') then
if only and ( only == 'template' or only == 'templates' ) then
trash, excerpt = Transcluder.getTemplates(excerpt, templates);
trash, excerpt = Transcluder.getTemplates( excerpt, templates );
else -- Remove blacklisted templates
else -- Remove blacklisted templates
local blacklist = config.blacklist and table.concat(config.blacklist, ',') or ''
local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
if templates then
if templates then
if string.sub(templates, 1, 1) == '-' then --Unwanted templates. Append to blacklist
if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
blacklist = templates .. ',' .. blacklist
blacklist = templates .. ',' .. blacklist
else --Wanted templates. Replaces blacklist and acts as whitelist
else --Wanted templates. Replaces blacklist and acts as whitelist
Line 186: Line 186:
blacklist = '-' .. blacklist
blacklist = '-' .. blacklist
end
end
trash, excerpt = Transcluder.getTemplates(excerpt, blacklist);
trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
end
end


-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
excerpt = mw.text.trim(excerpt)
excerpt = mw.text.trim( excerpt )
excerpt = string.gsub(excerpt, '\n\n\n+', '\n\n')
excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
excerpt = '\n' .. excerpt .. '\n'
excerpt = '\n' .. excerpt .. '\n'


-- Remove nested categories
-- Remove nested categories
excerpt = frame:preprocess(excerpt)
excerpt = frame:preprocess( excerpt )
local categories, excerpt = Transcluder.getCategories(excerpt, options.categories)
local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )


-- Add tracking categories
-- Add tracking categories
Line 219: Line 219:
local tag1 = 'div'
local tag1 = 'div'
local tag2 = 'div'
local tag2 = 'div'
if inline then
if quote then
tag1 = 'span'
tag2 = 'span'
elseif quote then
tag2 = 'blockquote'
tag2 = 'blockquote'
end
end
excerpt = mw.html.create(tag1):addClass('excerpt'):wikitext(excerpt)
excerpt = mw.html.create( tag1 ):addClass( 'excerpt' ):wikitext( excerpt )
if inline then
local block = mw.html.create(tag2):addClass('excerpt-block'):addClass(class)
excerpt = excerpt:css( 'display', 'inline' )
return block:node(styles):node(hat):node(excerpt):node(more)
end
local block = mw.html.create( tag2 ):addClass( 'excerpt-block' ):addClass( class )
if inline then
block = block:css( 'display', 'inline' )
end
return block:node( styles ):node( hat ):node( excerpt ):node( more )
end
end


-- Entry points for backwards compatibility
-- Entry points for backwards compatibility
function p.lead(frame) return p.main(frame) end
function p.lead( frame ) return p.main( frame ) end
function p.excerpt(frame) return p.main(frame) end
function p.excerpt( frame ) return p.main( frame ) end


return p
return p

Revision as of 12:50, 5 August 2022

-- Module:Excerpt implements the Excerpt template
-- Documentation https://www.mediawiki.org/wiki/Module:Excerpt
-- By User:Sophivorus, User:Certes & others
-- Version 1.2.3
-- License CC-BY-SA-4.0

local Transcluder = require( 'Module:Transcluder' )

local yesno = require( 'Module:Yesno' )

local ok, config = pcall( require, 'Module:Excerpt/config' )
if not ok then config = {} end

local p = {}

-- Helper function to get arguments
local args
function getArg( key, default )
	local value = args[ key ]
	if value and mw.text.trim( value ) ~= '' then
		return value
	end
	return default
end

-- Helper function to handle errors
function getError( message, value )
	if type( message ) == 'string' then
		message = Transcluder.getError( message, value )
	end
	if config.categories and config.categories.errors and mw.title.getCurrentTitle().isContentPage then
		message:node( '[[Category:' .. config.categories.errors .. ']]' )
	end
	return message
end

-- Helper function to get localized messages
function getMessage( key )
	local ok, TNT = pcall( require, 'Module:TNT' )
	if not ok then return key end
	return TNT.format( 'I18n/Module:Excerpt.tab', key )
end

function p.main( frame )
	args = Transcluder.parseArgs( frame )

	-- Make sure the requested page exists
	local page = getArg( 1 )
	if not page then return getError( 'no-page' ) end
	local title = mw.title.new(page)
	if not title then return getError( 'no-page' ) end
	if title.isRedirect then title = title.redirectTarget end
	if not title.exists then return getError( 'page-not-found', page ) end
	page = title.prefixedText

	-- Set variables from the template parameters
	local section = getArg( 2, mw.ustring.match( getArg( 1 ), '[^#]+#(.+)' ) )
	local hat = yesno( getArg( 'hat', true ) )
	local edit = yesno( getArg( 'edit', true ) )
	local this = getArg( 'this' )
	local only = getArg( 'only' )
	local files = getArg( 'files', getArg( 'file', ( only == 'file' and 1 ) ) )
	local lists = getArg( 'lists', getArg( 'list', ( only == 'list' and 1 ) ) )
	local tables = getArg( 'tables', getArg( 'table', ( only == 'table' and 1 ) ) )
	local templates = getArg( 'templates', getArg( 'template', ( only == 'template' and 1 ) ) )
	local paragraphs = getArg( 'paragraphs', getArg( 'paragraph', ( only == 'paragraph' and 1 ) ) )
	local references = getArg( 'references' )
	local subsections = not yesno( getArg( 'subsections' ) )
	local noBold = not yesno( getArg( 'bold' ) )
	local freefiles = yesno( getArg( 'freefiles' ) )
	local inline = yesno( getArg( 'inline' ) )
	local quote = yesno( getArg( 'quote' ) )
	local more = yesno( getArg( 'more' ) )
	local class = getArg( 'class' )

	-- Build the hatnote
	if hat and not inline then
		if this then
			hat = this
		elseif quote then
			hat = getMessage( 'this' )
		elseif only then
			hat = getMessage( only )
		else
			hat = getMessage( 'section' )
		end
		hat = hat .. ' ' .. getMessage( 'excerpt' ) .. ' '
		if section and not fragment then
			hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. page
				.. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links
		else
			hat = hat .. '[[:' .. page .. '|' .. page .. ']].'
		end
		if edit then
			hat = hat .. '<span class="mw-editsection-like plainlinks"><span class="mw-editsection-bracket">[</span>['
			hat = hat .. title:fullUrl( 'action=edit' ) .. ' ' .. mw.message.new( 'editsection' ):plain()
			hat = hat .. ']<span class="mw-editsection-bracket">]</span></span>'
		end
		if config.hat then
			hat = config.hat .. hat .. '}}'
			hat = frame:preprocess( hat )
		else
			hat = mw.html.create( 'div' ):addClass( 'dablink excerpt-hat' ):wikitext( hat )
		end
	else
		hat = nil
	end

	-- Build the "Read more" link
	if more and not inline then
		more = "'''[[" .. page .. '#' .. ( section or '' ) .. "|" .. getMessage( 'more' ) .. "]]'''"
		more = mw.html.create( 'div' ):addClass( 'noprint excerpt-more' ):wikitext( more )
	else
		more = nil
	end

	-- Build the options for Module:Transcluder out of the template parameters and the desired defaults
	local options = {
		files = files,
		lists = lists,
		tables = tables,
		paragraphs = paragraphs,
		sections = subsections,
		categories = 0,
		references = references,
		only = only and mw.text.trim( only, 's' ) .. 's',
		noBold = noBold,
		noSelfLinks = true,
		noNonFreeFiles = freefiles,
		noBehaviorSwitches = true,
		fixReferences = true,
		linkBold = true,
	}

	-- Get the excerpt itself
	local title = page .. '#' .. ( section or '' )
	local ok, excerpt = pcall( Transcluder.get, title, options )
	if not ok then return getError( excerpt ) end
	if mw.text.trim( excerpt ) == '' and not only then
		if section then return getError( 'section-empty', section ) else return getError( 'lead-empty' ) end
	end

	-- If no file was found, try to get one from the infobox
	local fileNamespaces = Transcluder.getNamespaces( 'File' )
	if ( ( only == 'file' or only == 'files' ) or ( not only and ( files ~= '0' or not files ) ) ) and -- caller asked for files
		not Transcluder.matchAny( excerpt, '%[%[', fileNamespaces, ':' ) and -- and there are no files in Transcluder's output
		config.captions -- and we have the config option required to try finding files in templates
	then
		-- We cannot distinguish the infobox from the other templates so we search them all
		local infobox = Transcluder.getTemplates( excerpt );
		infobox = table.concat( infobox )
		local parameters = Transcluder.getParameters( infobox )
		local file, captions, caption
		for _, pair in pairs( config.captions ) do
			file = pair[1]
			file = parameters[file]
			if file and Transcluder.matchAny( file, '^.*%.', { '[Jj][Pp][Ee]?[Gg]', '[Pp][Nn][Gg]', '[Gg][Ii][Ff]', '[Ss][Vv][Gg]' }, '.*' ) then
				file = mw.ustring.match( file, '%[?%[?.-:([^{|]+)%]?%]?' ) or file -- [[File:Example.jpg{{!}}upright=1.5]] to Example.jpg
				captions = pair[2]
				for _, p in pairs( captions ) do
					if parameters[ p ] then caption = parameters[ p ] break end
				end
				excerpt = '[[File:' .. file .. '|thumb|' .. ( caption or '' ) .. ']]' .. excerpt
				if ( freefiles ) then
					excerpt = Transcluder.removeNonFreeFiles( excerpt )
				end
				break
			end
		end
	end

	-- Unlike other elements, templates are filtered here
	-- because we had to search the infoboxes for files
	local trash
	if only and ( only == 'template' or only == 'templates' ) then
		trash, excerpt = Transcluder.getTemplates( excerpt, templates );
	else -- Remove blacklisted templates
		local blacklist = config.blacklist and table.concat( config.blacklist, ',' ) or ''
		if templates then
			if string.sub( templates, 1, 1 ) == '-' then --Unwanted templates. Append to blacklist
				blacklist = templates .. ',' .. blacklist
			else --Wanted templates. Replaces blacklist and acts as whitelist
				blacklist = templates
			end
		else
			blacklist = '-' .. blacklist
		end
		trash, excerpt = Transcluder.getTemplates( excerpt, blacklist );
	end

	-- Remove extra line breaks but leave one before and after so the parser interprets lists, tables, etc. correctly
	excerpt = mw.text.trim( excerpt )
	excerpt = string.gsub( excerpt, '\n\n\n+', '\n\n' )
	excerpt = '\n' .. excerpt .. '\n'

	-- Remove nested categories
	excerpt = frame:preprocess( excerpt )
	local categories, excerpt = Transcluder.getCategories( excerpt, options.categories )

	-- Add tracking categories
	if config.categories then
		local contentCategory = config.categories.content
		if contentCategory and mw.title.getCurrentTitle().isContentPage then
			excerpt = excerpt .. '[[Category:' .. contentCategory .. ']]'
		end
		local namespaceCategory = config.categories[ mw.title.getCurrentTitle().namespace ]
		if namespaceCategory then
			excerpt = excerpt .. '[[Category:' .. namespaceCategory .. ']]'
		end
	end

	-- Load the styles
	local styles
	if config.styles then
		styles = frame:extensionTag( 'templatestyles', '', { src = config.styles } )
	end

	-- Combine and return the elements
	local tag1 = 'div'
	local tag2 = 'div'
	if quote then
		tag2 = 'blockquote'
	end
	excerpt = mw.html.create( tag1 ):addClass( 'excerpt' ):wikitext( excerpt )
	if inline then
		excerpt = excerpt:css( 'display', 'inline' )
	end
	local block = mw.html.create( tag2 ):addClass( 'excerpt-block' ):addClass( class )
	if inline then
		block = block:css( 'display', 'inline' )
	end
	return block:node( styles ):node( hat ):node( excerpt ):node( more )
end

-- Entry points for backwards compatibility
function p.lead( frame ) return p.main( frame ) end
function p.excerpt( frame ) return p.main( frame ) end

return p