Module:Excerpt
Revision as of 16:17, 10 July 2023 by en>Sophivorus (Update to latest: refine error handling for invalid titles, make some methods local, add Aidan9382 as author, remove old reference to fragment param)
Documentation for this module may be created at Module:Excerpt/doc
-- Module:Excerpt implements the Excerpt template -- Documentation and master version: https://en.wikipedia.org/wiki/Module:Excerpt -- Authors: User:Sophivorus, User:Certes, User:Aidan9382 & others -- License: CC-BY-SA-3.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 local 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 local 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 local 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 -- Main entry point for templates function p.main( frame ) args = Transcluder.parseArgs( frame ) -- Make sure the requested page exists local page = getArg( 1 ) if not page or page == '{{{1}}}' then return getError( 'no-page' ) end local title = mw.title.new(page) if not title then return getError( 'invalid-title', 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 noLinks = not yesno( getArg( 'links', true ) ) local noBold = not yesno( getArg( 'bold' ) ) local onlyFreeFiles = yesno( getArg( 'onlyfreefiles', true ) ) local briefDates = yesno( getArg( 'briefdates', false ) ) local inline = yesno( getArg( 'inline' ) ) local quote = yesno( getArg( 'quote' ) ) local more = yesno( getArg( 'more' ) ) local class = getArg( 'class' ) local displaytitle = getArg( 'displaytitle' ) or page -- 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 then hat = hat .. '[[:' .. page .. '#' .. mw.uri.anchorEncode( section ) .. '|' .. displaytitle .. ' § ' .. mw.ustring.gsub( section, '%[%[([^]|]+)|?[^]]*%]%]', '%1' ) .. ']].' -- remove nested links else hat = hat .. '[[:' .. page .. '|' .. displaytitle .. ']].' 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', noLinks = noLinks, noBold = noBold, noSelfLinks = true, noNonFreeFiles = onlyFreeFiles, 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 -- Fix birth and death dates, but only in the first paragraph if briefDates then local startpos = 1 -- skip initial templates local s local e = 0 repeat startpos = e + 1 s, e = mw.ustring.find( excerpt, "%s*%b{}%s*", startpos ) until not s or s > startpos s, e = mw.ustring.find( excerpt, "%b()", startpos ) -- get (...), which may be (year–year) if s and s < startpos + 100 then -- look only near the start local year1, conjunction, year2 = mw.ustring.match( mw.ustring.sub( excerpt, s, e ), '(%d%d%d+)(.-)(%d%d%d+)' ) if year1 and year2 and (mw.ustring.match( conjunction, '[%-–—]' ) or mw.ustring.match( conjunction, '{{%s*[sS]nd%s*}}' )) then local y1 = tonumber(year1) local y2 = tonumber(year2) if y2 > y1 and y2 < y1 + 125 and y1 <= tonumber( os.date( "%Y" )) then excerpt = mw.ustring.sub( excerpt, 1, s ) .. year1 .. "–" .. year2 .. mw.ustring.sub( excerpt, e ) end end 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 ( onlyFreeFiles ) 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 if inline then return mw.text.trim( excerpt ) end local tag = 'div' if quote then tag = 'blockquote' end excerpt = mw.html.create( 'div' ):addClass( 'excerpt' ):wikitext( excerpt ) local block = mw.html.create( tag ):addClass( 'excerpt-block' ):addClass( class ) 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