(Cargo port) |
(Page names and item names can contain double quotes, so we must escape them with backslashes) |
||
(22 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
⚫ | |||
− | -- Item link module |
||
-- |
-- |
||
+ | -- Module:Item link |
||
⚫ | |||
-- |
-- |
||
+ | -- This module implements Template:Item link. |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
+ | -- tests this amounted to only a ~10% difference in page load times at best. It |
||
⚫ | |||
+ | -- page load times. |
||
⚫ | |||
+ | local getArgs = require('Module:Arguments').getArgs |
||
⚫ | |||
− | -- Imports |
||
⚫ | |||
local m_util = require('Module:Util') |
local m_util = require('Module:Util') |
||
− | local |
+ | local m_cargo = require('Module:Cargo') |
+ | -- The cfg table contains all localisable strings and configuration, to make it |
||
− | -- ---------------------------------------------------------------------------- |
||
+ | -- easier to port this module to another wiki. |
||
− | -- Strings |
||
+ | local cfg = mw.loadData('Module:Item link/config') |
||
− | -- ---------------------------------------------------------------------------- |
||
+ | |||
− | -- This section contains strings used by this module. |
||
⚫ | |||
− | -- Add new strings here instead of in-code directly, this will help other |
||
− | -- people to correct spelling mistakes easier and help with translation to |
||
− | -- other PoE wikis. |
||
⚫ | |||
− | -- TODO: Maybe move this out to a separate sub-page module |
||
⚫ | |||
− | categories = { |
||
− | -- maintenance cats |
||
− | broken_item_links = '[[Category:Pages with broken item links]]', |
||
⚫ | |||
− | errors = { |
||
− | invalid_args = 'Item link: page, item_name or item_name_exact must be specified', |
||
− | no_results = 'Item link: No results found for search parameter "%s".', |
||
− | too_many_results = 'Item link: Too many results for search parameter "%s". Consider using page parameter instead.', |
||
− | alt_art_undefined = 'Item link: Image parameter was specified, but there is no alternate art defined on page "%s"', |
||
− | alt_art_invalid_index = 'Item Link: Alternate art with index/name "%s" not found on page "%s"', |
||
⚫ | |||
− | } |
||
-- ---------------------------------------------------------------------------- |
-- ---------------------------------------------------------------------------- |
||
+ | -- Exported functions |
||
− | -- Constants & Data |
||
-- ---------------------------------------------------------------------------- |
-- ---------------------------------------------------------------------------- |
||
− | local |
+ | local p = {} |
− | c.image_size = 39 |
||
− | c.image_size_full = c.image_size * 2 |
||
− | c.parameters = { |
||
⚫ | |||
− | inventory_icon = 'items.inventory_icon', |
||
− | html = 'items.html', |
||
− | width = 'items.size_x', |
||
− | height = 'items.size_y', |
||
− | } |
||
− | |||
− | c.selectors = {'page', 'item_name', 'item_name_exact'} |
||
− | |||
− | -- ---------------------------------------------------------------------------- |
||
− | -- Invokable code |
||
− | -- ---------------------------------------------------------------------------- |
||
-- |
-- |
||
− | -- Template:Item link |
+ | -- Template:Item link |
-- |
-- |
||
⚫ | |||
− | |||
function p.item_link (frame) |
function p.item_link (frame) |
||
− | -- |
+ | --[[ |
+ | Creates a link to the item and displays the item info box on hover |
||
− | -- Args/Frame |
||
− | + | on the link. |
|
+ | Examples |
||
+ | -------- |
||
+ | = p.item_link{'Multistrike'} |
||
+ | = p.item_link{'Multistrike Support'} |
||
⚫ | |||
⚫ | |||
⚫ | |||
+ | -- Get arguments: |
||
local tpl_args = getArgs(frame, { |
local tpl_args = getArgs(frame, { |
||
parentFirst = true, |
parentFirst = true, |
||
Line 72: | Line 50: | ||
frame = m_util.misc.get_frame(frame) |
frame = m_util.misc.get_frame(frame) |
||
− | -- Backwards compability |
||
tpl_args.item_name = tpl_args.item_name or tpl_args[1] |
tpl_args.item_name = tpl_args.item_name or tpl_args[1] |
||
⚫ | |||
⚫ | |||
⚫ | |||
tpl_args.name = tpl_args.name or tpl_args[2] |
tpl_args.name = tpl_args.name or tpl_args[2] |
||
− | if m_util.table.has_all_value(tpl_args, |
+ | if m_util.table.has_all_value(tpl_args, cfg.selectors) and tpl_args.skip_query == nil then |
error(i18n.errors.invalid_args) |
error(i18n.errors.invalid_args) |
||
end |
end |
||
Line 88: | Line 62: | ||
local result |
local result |
||
− | if m_util.table.has_one_value(tpl_args, |
+ | if m_util.table.has_one_value(tpl_args, cfg.selectors, nil) and tpl_args.skip_query == nil then |
⚫ | |||
local query = { |
local query = { |
||
groupBy='items._pageID', |
groupBy='items._pageID', |
||
} |
} |
||
− | + | if tpl_args.metadata_id then |
|
− | + | query.where = string.format( |
|
+ | 'items.metadata_id="%s"', |
||
⚫ | |||
− | + | tpl_args.metadata_id |
|
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
+ | 'items._pageName="%s"', |
||
⚫ | |||
⚫ | |||
⚫ | |||
+ | query.where = string.format( |
||
+ | 'items.name = "%s" AND items._pageNamespace = %i', |
||
⚫ | |||
+ | cfg.primary_namespace |
||
⚫ | |||
else |
else |
||
+ | -- explicitly join a copy of the list child's table to the parent |
||
⚫ | |||
+ | -- instead of using HOLDS; we have to add one table & a join condition here |
||
⚫ | |||
+ | tables[#tables+1] = 'items__name_list' |
||
⚫ | |||
− | + | query.where = string.format( |
|
+ | 'items__name_list._value="%s" AND items._pageNamespace = %i', |
||
⚫ | |||
− | + | m_cargo.addslashes(tpl_args.item_name), |
|
− | + | cfg.primary_namespace |
|
⚫ | |||
− | query[#query] = query[#query] .. ' [[Concept:Skill gems]]' |
||
+ | query.join = 'items._ID = items__name_list._rowID' |
||
⚫ | |||
end |
end |
||
⚫ | |||
if tpl_args.link_type == 'skill' then |
if tpl_args.link_type == 'skill' then |
||
− | query.where = string.format( |
+ | query.where = string.format( |
+ | '%s AND (items.class = "Active Skill Gems" OR items.class = "Support Skill Gems")', |
||
+ | query.where |
||
+ | ) |
||
end |
end |
||
− | result = |
+ | result = m_cargo.query( |
− | + | tables, |
|
⚫ | |||
⚫ | |||
{ |
{ |
||
'items._pageName', |
'items._pageName', |
||
Line 124: | Line 112: | ||
'items.size_x', |
'items.size_x', |
||
'items.size_y', |
'items.size_y', |
||
+ | 'items.drop_enabled', |
||
⚫ | |||
}, |
}, |
||
query |
query |
||
Line 129: | Line 119: | ||
local err |
local err |
||
⚫ | |||
if #result == 0 then |
if #result == 0 then |
||
− | err = m_util.misc.raise_error_or_return{ |
+ | err = m_util.misc.raise_error_or_return{ |
− | + | raise_required=true, |
|
− | tpl_args |
+ | args=tpl_args, |
− | + | msg=string.format( |
|
+ | i18n.errors.no_results, |
||
+ | tpl_args.page or tpl_args.item_name or tpl_args.item_name_exact |
||
+ | ) |
||
+ | } |
||
elseif #result > 1 then |
elseif #result > 1 then |
||
+ | -- If only one of the results is drop enabled then use that one: |
||
⚫ | |||
− | + | local n = 0 |
|
+ | for i,v in ipairs(result) do |
||
⚫ | |||
+ | if m_util.cast.boolean(v['items.drop_enabled']) then |
||
⚫ | |||
+ | j = i |
||
+ | n = n+1 |
||
+ | end |
||
+ | end |
||
+ | |||
+ | if n ~= 1 then |
||
⚫ | |||
+ | raise_required=true, |
||
+ | args=tpl_args, |
||
+ | msg=string.format( |
||
+ | i18n.errors.too_many_results, |
||
+ | tpl_args.page or tpl_args.item_name or tpl_args.item_name_exact |
||
+ | ) |
||
+ | } |
||
+ | end |
||
end |
end |
||
if err ~= nil then |
if err ~= nil then |
||
− | return err .. i18n.categories.broken_item_links |
+ | return err .. m_util.misc.add_category({i18n.categories.broken_item_links}) |
end |
end |
||
− | result = result[ |
+ | result = result[j] |
else |
else |
||
− | result = { |
+ | result = { |
+ | ['items._pageName'] = tpl_args.page or tpl_args.name |
||
+ | } |
||
end |
end |
||
− | for k, prop in pairs( |
+ | for k, prop in pairs(cfg.parameters) do |
if tpl_args[k] ~= nil then |
if tpl_args[k] ~= nil then |
||
result[prop] = tpl_args[k] |
result[prop] = tpl_args[k] |
||
Line 157: | Line 169: | ||
if tpl_args.image ~= nil then |
if tpl_args.image ~= nil then |
||
− | if result['items.alternate_art_inventory_icons'] == |
+ | if result['items.alternate_art_inventory_icons'] == nil then |
− | return m_util.misc.raise_error_or_return{ |
+ | return m_util.misc.raise_error_or_return{ |
− | + | raise_required=true, |
|
− | + | args=tpl_args, |
|
− | + | msg=string.format( |
|
+ | i18n.errors.alt_art_undefined, |
||
+ | result['items._pageName'] |
||
+ | ) .. m_util.misc.add_category({i18n.categories.broken_item_links}) |
||
+ | } |
||
end |
end |
||
− | result['items.alternate_art_inventory_icons'] = m_util.string.split( |
+ | result['items.alternate_art_inventory_icons'] = m_util.string.split( |
+ | result['items.alternate_art_inventory_icons'], |
||
+ | ',%s*' |
||
+ | ) |
||
local index = tonumber(tpl_args.image) |
local index = tonumber(tpl_args.image) |
||
Line 184: | Line 203: | ||
if img == nil then |
if img == nil then |
||
− | return m_util.misc.raise_error_or_return{ |
+ | return m_util.misc.raise_error_or_return{ |
− | + | raise_required=true, |
|
− | tpl_args |
+ | args=tpl_args, |
− | + | msg=string.format( |
|
+ | i18n.errors.alt_art_invalid_index, |
||
+ | tpl_args.image, result['items._pageName'] |
||
+ | ) .. m_util.misc.add_category({i18n.categories.broken_item_links}) |
||
+ | } |
||
end |
end |
||
− | elseif result['items.inventory_icon'] ~= |
+ | elseif result['items.inventory_icon'] ~= nil then |
img = result['items.inventory_icon'] |
img = result['items.inventory_icon'] |
||
end |
end |
||
+ | -- |
||
-- output |
-- output |
||
+ | -- |
||
+ | |||
+ | -- Maps have their main page on the item name now, link there instead. |
||
+ | -- Hopefully there are no maps with identical names besides the series. |
||
+ | local linked_page |
||
+ | if result['items.class_id'] == 'Map' and tpl_args.page == nil then |
||
⚫ | |||
+ | else |
||
+ | linked_page = tpl_args.link or result['items._pageName'] |
||
+ | end |
||
local container = mw.html.create('span') |
local container = mw.html.create('span') |
||
container:addClass('c-item-hoverbox') |
container:addClass('c-item-hoverbox') |
||
+ | |||
⚫ | |||
+ | container:addClass('c-item-hoverbox--large') |
||
+ | end |
||
local activator = mw.html.create('span') |
local activator = mw.html.create('span') |
||
Line 204: | Line 242: | ||
activator:wikitext(string.format('[[%s|16x16px|link=|alt=]]', img)) |
activator:wikitext(string.format('[[%s|16x16px|link=|alt=]]', img)) |
||
end |
end |
||
− | + | ||
− | + | if #result['items.name'] > 0 then |
|
+ | activator:wikitext(string.format( |
||
+ | '[[%s|%s]]', |
||
+ | linked_page, |
||
+ | result['items.name'] or result['items._pageName'] |
||
+ | ) |
||
+ | ) |
||
+ | end |
||
local display = mw.html.create('span') |
local display = mw.html.create('span') |
||
display:attr('class', 'c-item-hoverbox__display') |
display:attr('class', 'c-item-hoverbox__display') |
||
− | if result['items.html'] ~= |
+ | if result['items.html'] ~= nil then |
display:wikitext(result['items.html']) |
display:wikitext(result['items.html']) |
||
Line 222: | Line 267: | ||
local height = tonumber(result['items.size_y']) or tonumber(tpl_args.height) |
local height = tonumber(result['items.size_y']) or tonumber(tpl_args.height) |
||
if width and height then |
if width and height then |
||
− | img = string.format( |
+ | img = string.format( |
+ | '[[%s|%sx%spx|link=%s|alt=]]', |
||
+ | img, |
||
+ | width*cfg.image_size, |
||
+ | height*cfg.image_size, |
||
+ | linked_page |
||
+ | ) |
||
elseif width then |
elseif width then |
||
− | img = string.format( |
+ | img = string.format( |
+ | '[[%s|%spx|link=%s|alt=]]', |
||
+ | img, |
||
+ | width*cfg.image_size, |
||
+ | linked_page |
||
+ | ) |
||
elseif height then |
elseif height then |
||
− | img = string.format( |
+ | img = string.format( |
+ | '[[%s|x%spx|link=%s|alt=]]', |
||
+ | img, |
||
+ | height*cfg.image_size, |
||
+ | linked_page |
||
+ | ) |
||
else |
else |
||
− | img = string.format( |
+ | img = string.format( |
+ | '[[%s|link=%s|alt=]]', |
||
+ | img, |
||
+ | linked_page |
||
+ | ) |
||
end |
end |
||
activator:wikitext(img) |
activator:wikitext(img) |
Latest revision as of 22:44, 8 July 2021
This Lua module is used on a very large number of pages. To avoid large-scale disruption and unnecessary server load, any changes to this module should first be tested in its /sandbox or /testcases subpages. The tested changes can then be added to this page in one single edit. Please consider discussing any changes on the talk page before implementing them. |
This module depends on the following other modules: |
The item module provides functionality for linking to items.
Overview
This module is responsible for {{Item link}}
(({{Il}}
) which create links to items exposed via cargo through Module:Item2.
Item templates
Module:Item2
All templates defined in Module:Item2:
Module:Item table
All templates defined in Module:Item table:
- {{Item table}}
- {{Query base items}}
- {{Query unique items}}
- {{Item unique versions}}
- {{Area item drops}}
- {{Item table/skill gems}}
- {{Map item drops}}
- {{Prophecy description}}
- {{Simple item list}}
Module:Item link
All templates defined in Module:Item link:
- {{Item link}}
- {{Il}}
- {{Item icon link}}
Module:Item acquisition
- {{Item acquisition}}
The above documentation is transcluded from Module:Item link/doc.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
-------------------------------------------------------------------------------
--
-- Module:Item link
--
-- This module implements Template:Item link.
--
-- This is separate from the main item module for small speed ups. Those speed
-- ups are only sigificant if the module is called a lot of times (100+), in
-- tests this amounted to only a ~10% difference in page load times at best. It
-- should be noted those tests are difficult because of the large variance in
-- page load times.
-------------------------------------------------------------------------------
local getArgs = require('Module:Arguments').getArgs
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = mw.loadData('Module:Item link/config')
local i18n = cfg.i18n
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local p = {}
--
-- Template:Item link
--
function p.item_link (frame)
--[[
Creates a link to the item and displays the item info box on hover
on the link.
Examples
--------
= p.item_link{'Multistrike'}
= p.item_link{'Multistrike Support'}
]]
-- Get arguments:
local tpl_args = getArgs(frame, {
parentFirst = true,
removeBlanks = false,
})
frame = m_util.misc.get_frame(frame)
tpl_args.item_name = tpl_args.item_name or tpl_args[1]
tpl_args.name = tpl_args.name or tpl_args[2]
if m_util.table.has_all_value(tpl_args, cfg.selectors) and tpl_args.skip_query == nil then
error(i18n.errors.invalid_args)
end
tpl_args.large = m_util.cast.boolean(tpl_args.large)
local img
local result
if m_util.table.has_one_value(tpl_args, cfg.selectors, nil) and tpl_args.skip_query == nil then
local tables = {'items'}
local query = {
groupBy='items._pageID',
}
if tpl_args.metadata_id then
query.where = string.format(
'items.metadata_id="%s"',
tpl_args.metadata_id
)
elseif tpl_args.page then
-- TODO returns the result even if the + format is specified.
query.where = string.format(
'items._pageName="%s"',
tpl_args.page
)
elseif tpl_args.item_name_exact then
query.where = string.format(
'items.name = "%s" AND items._pageNamespace = %i',
m_cargo.addslashes(tpl_args.item_name_exact),
cfg.primary_namespace
)
else
-- explicitly join a copy of the list child's table to the parent
-- instead of using HOLDS; we have to add one table & a join condition here
tables[#tables+1] = 'items__name_list'
query.where = string.format(
'items__name_list._value="%s" AND items._pageNamespace = %i',
m_cargo.addslashes(tpl_args.item_name),
cfg.primary_namespace
)
query.join = 'items._ID = items__name_list._rowID'
end
if tpl_args.link_type == 'skill' then
query.where = string.format(
'%s AND (items.class = "Active Skill Gems" OR items.class = "Support Skill Gems")',
query.where
)
end
result = m_cargo.query(
tables,
{
'items._pageName',
'items.name',
'items.inventory_icon',
'items.html',
'items.alternate_art_inventory_icons',
'items.size_x',
'items.size_y',
'items.drop_enabled',
'items.class_id',
},
query
)
local err
local j = 1
if #result == 0 then
err = m_util.misc.raise_error_or_return{
raise_required=true,
args=tpl_args,
msg=string.format(
i18n.errors.no_results,
tpl_args.page or tpl_args.item_name or tpl_args.item_name_exact
)
}
elseif #result > 1 then
-- If only one of the results is drop enabled then use that one:
local n = 0
for i,v in ipairs(result) do
if m_util.cast.boolean(v['items.drop_enabled']) then
j = i
n = n+1
end
end
if n ~= 1 then
err = m_util.misc.raise_error_or_return{
raise_required=true,
args=tpl_args,
msg=string.format(
i18n.errors.too_many_results,
tpl_args.page or tpl_args.item_name or tpl_args.item_name_exact
)
}
end
end
if err ~= nil then
return err .. m_util.misc.add_category({i18n.categories.broken_item_links})
end
result = result[j]
else
result = {
['items._pageName'] = tpl_args.page or tpl_args.name
}
end
for k, prop in pairs(cfg.parameters) do
if tpl_args[k] ~= nil then
result[prop] = tpl_args[k]
end
end
if tpl_args.image ~= nil then
if result['items.alternate_art_inventory_icons'] == nil then
return m_util.misc.raise_error_or_return{
raise_required=true,
args=tpl_args,
msg=string.format(
i18n.errors.alt_art_undefined,
result['items._pageName']
) .. m_util.misc.add_category({i18n.categories.broken_item_links})
}
end
result['items.alternate_art_inventory_icons'] = m_util.string.split(
result['items.alternate_art_inventory_icons'],
',%s*'
)
local index = tonumber(tpl_args.image)
if index ~= nil then
img = result['items.alternate_art_inventory_icons'][index]
else
-- offset 1 is needed
local suffix = string.len(' inventory icon.png') + 1
-- add an extra offset by 1 to account for the space
local prefix = string.len(string.sub(result['items.inventory_icon'], 1, -suffix)) + 2
for _, filename in ipairs(result['items.alternate_art_inventory_icons']) do
if string.sub(filename, prefix, -suffix) == tpl_args.image then
img = filename
break
end
end
end
if img == nil then
return m_util.misc.raise_error_or_return{
raise_required=true,
args=tpl_args,
msg=string.format(
i18n.errors.alt_art_invalid_index,
tpl_args.image, result['items._pageName']
) .. m_util.misc.add_category({i18n.categories.broken_item_links})
}
end
elseif result['items.inventory_icon'] ~= nil then
img = result['items.inventory_icon']
end
--
-- output
--
-- Maps have their main page on the item name now, link there instead.
-- Hopefully there are no maps with identical names besides the series.
local linked_page
if result['items.class_id'] == 'Map' and tpl_args.page == nil then
linked_page = tpl_args.link or tpl_args.item_name
else
linked_page = tpl_args.link or result['items._pageName']
end
local container = mw.html.create('span')
container:addClass('c-item-hoverbox')
if tpl_args.large then
container:addClass('c-item-hoverbox--large')
end
local activator = mw.html.create('span')
activator:addClass('c-item-hoverbox__activator')
if img and not tpl_args.large then
activator:wikitext(string.format('[[%s|16x16px|link=|alt=]]', img))
end
if #result['items.name'] > 0 then
activator:wikitext(string.format(
'[[%s|%s]]',
linked_page,
result['items.name'] or result['items._pageName']
)
)
end
local display = mw.html.create('span')
display:attr('class', 'c-item-hoverbox__display')
if result['items.html'] ~= nil then
display:wikitext(result['items.html'])
if img then
display:wikitext(string.format('[[%s|link=|alt=]]', img))
end
end
if img and tpl_args.large then
local width = tonumber(result['items.size_x']) or tonumber(tpl_args.width)
local height = tonumber(result['items.size_y']) or tonumber(tpl_args.height)
if width and height then
img = string.format(
'[[%s|%sx%spx|link=%s|alt=]]',
img,
width*cfg.image_size,
height*cfg.image_size,
linked_page
)
elseif width then
img = string.format(
'[[%s|%spx|link=%s|alt=]]',
img,
width*cfg.image_size,
linked_page
)
elseif height then
img = string.format(
'[[%s|x%spx|link=%s|alt=]]',
img,
height*cfg.image_size,
linked_page
)
else
img = string.format(
'[[%s|link=%s|alt=]]',
img,
linked_page
)
end
activator:wikitext(img)
end
container
:node(activator)
:node(display)
:done()
return tostring(container)
end
return p