Module:Portal bar: Difference between revisions
tstyles, some style cleaning, some TODOs |
use flexbox, only say "Portal" once |
||
Line 45: | Line 45: | ||
if #portals < 1 then return '' end |
if #portals < 1 then return '' end |
||
local nav = mw.html.create( ' |
local nav = mw.html.create( 'div' ) |
||
:addClass( 'portal-bar noprint metadata' ) |
:addClass( 'portal-bar noprint metadata nomobile' ) |
||
:attr( 'role', 'navigation' ) |
:attr( 'role', 'navigation' ) |
||
:attr( 'aria-label' , 'Portals' ) |
:attr( 'aria-label' , 'Portals' ) |
||
Line 66: | Line 66: | ||
-- TODO: This used to say 'if no portals are specified', but we return early |
-- TODO: This used to say 'if no portals are specified', but we return early |
||
-- above. Someone should check whether this is really 'portals[1]' or #portals < 0. |
-- above. Someone should check whether this is really 'portals[1]' or #portals < 0. |
||
-- I don't believe the block in the conditional is reachable currently |
|||
-- because if it were we would probably get a Lua script error because |
|||
-- warning was previously undefined |
|||
-- If the first portal is not specified, |
-- If the first portal is not specified, |
||
-- display an error and add the page to a tracking category. |
-- display an error and add the page to a tracking category. |
||
Line 131: | Line 127: | ||
end |
end |
||
local header = nav:tag('span') |
|||
header:addClass('portal-bar-header') |
|||
header:wikitext('[[Wikipedia:Contents/Portals|Portal]]') |
|||
if #portals > 1 then |
|||
header:wikitext('s') |
|||
end |
|||
header:wikitext(':') |
|||
local container = nav:tag('div') |
|||
container:addClass('portal-bar-content') |
|||
for _, portal in ipairs( portals ) do |
for _, portal in ipairs( portals ) do |
||
container |
|||
nav |
|||
:tag( ' |
:tag( 'div' ) |
||
:addClass('portal-bar-item') |
|||
:tag( 'span' ) |
:tag( 'span' ) |
||
:addClass('portal-bar-logo') |
|||
:wikitext( string.format( |
:wikitext( string.format( |
||
'[[File:%s| |
'[[File:%s|21x19px|alt=]]', getImageName{ portal } |
||
) ) |
) ) |
||
:done() |
:done() |
||
:tag('span') |
|||
⚫ | |||
' |
:addClass('portal-bar-link') |
||
⚫ | |||
) ) |
|||
end |
end |
||
Revision as of 07:34, 18 December 2021
This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
This Lua module is used on approximately 150,000 pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
This module depends on the following other modules: |
This module uses TemplateStyles: |
This module implements the {{portal bar}} template. It displays a horizontal bar of portals.
See here and here for testcases.
Note: in order to make the test cases work, the Sandbox CSS classes have "-sand" appended to their names. If you wish to update the CSS, copy the contents of each class from Module:Portal bar/sandbox/styles.css to Module:Portal bar/styles.css, but do not alter the class names, nor just copy-paste the entire CSS file. For the current difference in CSS between Sandbox and Main, see here.
Usage
{{#invoke:Portal bar|main|''portal 1''|''portal 2 ''|...|border=''no''}}
- Positional parameters - the names of the portals to be displayed.
border
- if|border=
is equal tono
,n
,false
, or0
, then the portal box will have no border.redlinks
- if|redlinks=
is equal toyes
,y
,true
orinclude
, then the portal box will show redlinked portals
Examples
{{#invoke:portal bar|main|Visual arts|Science|Literature}}
Produces:
Lua error in package.lua at line 80: module 'Module:No globals' not found.
{{#invoke:portal bar|main|Visual arts|Science|Literature|border=no}}
Produces:
Lua error in package.lua at line 80: module 'Module:No globals' not found.
For further examples, see Template:Portal bar/testcases.
Images
This module uses Module:Portal to get portal images. To add, change, or remove images, please see the instructions at Module:Portal#Image.
-- This module implements {{portal bar}}.
require('Module:No globals')
local getImageName = require( 'Module:Portal' ).image
local yesno = require( 'Module:Yesno' )
local p = {}
local function checkPortalExists(portal)
return not (mw.title.makeTitle(100, portal).id == 0)
end
-- Check whether to do tracking in this namespace
-- Returns true unless the page is one of the banned namespaces
local function isTrackedNamespace()
local thisPageNS = mw.title.getCurrentTitle().namespace
return thisPageNS ~= 1 -- Talk
and thisPageNS ~= 2 -- User
and thisPageNS ~= 3 -- User talk
and thisPageNS ~= 5 -- Wikipedia talk
and thisPageNS ~= 7 -- File talk
and thisPageNS ~= 11 -- Template talk
and thisPageNS ~= 15 -- Category talk
and thisPageNS ~= 101 -- Portal talk
and thisPageNS ~= 118 -- Draft
and thisPageNS ~= 119 -- Draft talk
and thisPageNS ~= 829 -- Module talk
end
-- Check whether to do tracking on this pagename
-- Returns false if the page title matches one of the banned strings
-- Otherwise returns true
local function isTrackedPagename()
local thisPageLC = mw.ustring.lower(mw.title.getCurrentTitle().text)
local match = string.match
return match(thisPageLC, "/archive") == nil
and match(thisPageLC, "/doc") == nil
and match(thisPageLC, "/test") == nil
end
-- Builds the portal bar used by {{portal bar}}.
function p._main( portals, args )
-- Don't display a blank navbox if no portals were specified.
if #portals < 1 then return '' end
local nav = mw.html.create( 'div' )
:addClass( 'portal-bar noprint metadata nomobile' )
:attr( 'role', 'navigation' )
:attr( 'aria-label' , 'Portals' )
if yesno( args.border ) == false then
nav
:addClass('portal-bar-unbordered')
else
nav
:addClass( 'portal-bar-bordered' )
end
local trackingEnabled = true
local tracking = args.tracking
if (tracking == 'no' or tracking == 'n' or tracking == 'false') or
not isTrackedNamespace() or not isTrackedPagename() then
trackingEnabled = false
end
-- TODO: This used to say 'if no portals are specified', but we return early
-- above. Someone should check whether this is really 'portals[1]' or #portals < 0.
-- If the first portal is not specified,
-- display an error and add the page to a tracking category.
if not portals[1] then
local nominimum = args.nominimum
local warning = mw.html.create()
if nominimum ~= 'yes' and nominimum ~= 'y' and nominimum ~= 'true' then
warning:tag('strong')
:addClass('error')
:wikitext('No portals specified: please specify at least one portal')
end
if trackingEnabled then
warning:wikitext('[[Category:Portal templates without a parameter]]')
end
return tostring(warning)
end
-- scan for nonexistent portals, if they exist remove them from the portals
-- table. If redlinks=yes, then don't remove
local portallen = #portals
-- traverse the list backwards to ensure that no portals are missed
-- (table.remove also moves down the portals in the list, so that the next
-- portal isn't checked if going fowards. going backwards allows us to
-- circumvent this issue
for i=portallen,1,-1 do
-- the use of pcall here catches any errors that may occour when
-- attempting to locate pages when the page name is invalid. if pcall
-- returns true, then rerun the function to find if the page exists
if not pcall(checkPortalExists, portals[i]) or not checkPortalExists(portals[i]) then
-- Getting here means a redlinked portal has been found
local redlinks = args.redlinks
if redlinks == 'yes' or redlinks == 'y' or redlinks == 'true' or redlinks == 'include' then
-- if redlinks as been set to yes (or similar), add the cleanup
-- category and then break the loop before the portal is removed
-- from the list
if trackingEnabled then
nav:wikitext('[[Category:Portal templates with redlinked portals]]')
end
-- TODO: This looks buggy given the comment above; we will always
-- break if redlinks are yes, whether or not tracking is enabled
break
end
-- remove the portal (this does not happen if redlinks=yes)
table.remove(portals,i)
end
end
-- if the length of the table is different, then rows were removed from the
-- table, so portals were removed. If this is the case add the cleanup category
if portallen ~= #portals then
if #portals == 0 then
if trackingEnabled then
return '[[Category:Portal templates with all redlinked portals]]'
else
return ""
end
end
if trackingEnabled then
nav:wikitext('[[Category:Portal templates with redlinked portals]]')
end
end
local header = nav:tag('span')
header:addClass('portal-bar-header')
header:wikitext('[[Wikipedia:Contents/Portals|Portal]]')
if #portals > 1 then
header:wikitext('s')
end
header:wikitext(':')
local container = nav:tag('div')
container:addClass('portal-bar-content')
for _, portal in ipairs( portals ) do
container
:tag( 'div' )
:addClass('portal-bar-item')
:tag( 'span' )
:addClass('portal-bar-logo')
:wikitext( string.format(
'[[File:%s|21x19px|alt=]]', getImageName{ portal }
) )
:done()
:tag('span')
:addClass('portal-bar-link')
:wikitext( string.format('[[Portal:%s|%s]]', portal, portal))
end
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Portal bar/styles.css' }
} .. tostring( nav )
end
-- Processes external arguments and sends them to the other functions.
function p.main( frame )
-- If called via #invoke, use the args passed into the invoking template, or
-- the args passed to #invoke if any exist. Otherwise assume args are being
-- passed directly in from the debug console or from another Lua module.
local origArgs
if type( frame.getParent ) == 'function' then
origArgs = frame:getParent().args
for k, v in pairs( frame.args ) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Process the args to make an array of portal names that can be used with
-- ipairs. We need to use ipairs because we want to list all the portals in
-- the order they were passed to the template, but we also want to be able
-- to deal with positional arguments passed explicitly, for example
-- {{portal|2=Politics}}. The behaviour of ipairs is undefined if nil values
-- are present, so we need to make sure they are all removed.
local portals, args = {}, {}
for k, v in pairs( origArgs ) do
-- Make sure we have no non-string portal names.
if type( k ) == 'number' and type( v ) == 'string' then
if mw.ustring.find( v, '%S' ) then -- Remove blank values.
table.insert( portals, k )
end
elseif type( k ) ~= 'number' then -- Separate named arguments from portals.
if type( v ) == 'string' then
v = mw.text.trim( v )
end
args[ k ] = v
end
end
table.sort( portals )
for i, v in ipairs( portals ) do
-- Swap keys with values, trimming whitespace.
portals[ i ] = mw.text.trim( origArgs[ v ] )
end
return p._main( portals, args )
end
return p