The wiki is currently a work in progress. If you'd like to help out, please check the Community Portal and our getting started guide. Also, check out our sister project on poewiki.net.

Module:Item link: Difference between revisions

From Path of Exile 2 Wiki
Jump to navigation Jump to search
(Better handling for maps, which nearly always return multiple results when queried by name. Now uses the one from the most recent map series, regardless of whether it's drop enabled. This prevents item links from breaking when maps are removed from the Atlas.)
No edit summary
 
(15 intermediate revisions by the same user not shown)
Line 4: Line 4:
--  
--  
-- This module implements Template: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
require('Module:No globals')
local m_util = require('Module:Util')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
local m_item_util = require('Module:Item util')


-- Should we use the sandbox version of our submodules?
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Item link')
local use_sandbox = m_util.misc.maybe_sandbox('Item link')
local m_item_util = use_sandbox and require('Module:Item util/sandbox') or require('Module:Item util')


-- The cfg table contains all localisable strings and configuration, to make it
-- The cfg table contains all localisable strings and configuration, to make it
Line 27: Line 21:


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- Exported functions
-- Main functions
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------


local p = {}
local function _main(args)
 
--
-- Template:Item link
--
function p.item_link (frame)
     --[[
     --[[
     Creates a link to the item and displays the item info box on hover  
     Creates a link to the item and displays the item info box on hover  
Line 44: Line 33:
     = p.item_link{'Multistrike'}
     = p.item_link{'Multistrike'}
     = p.item_link{'Multistrike Support'}
     = p.item_link{'Multistrike Support'}
    --]]
      
      
     ]]
     args.item_name = args.item_name or args[1]
   
     args.name = args.name or args[2]
    -- Get arguments:
     args.large = m_util.cast.boolean(args.large)
    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 img
     local result
     local result
      
     local linked_page
     if m_util.table.has_one_value(tpl_args, cfg.selectors, nil) and tpl_args.skip_query == nil then
     if m_util.cast.boolean(args.skip_query) then
         local tables = {'items', 'maps', 'map_series'}
         result = {
        local fields = {
            _pageName = args.page or args.name
            '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',
         }
         }
         local query = {
        linked_page = args.link or result._pageName
            join = 'items._pageID = maps._pageID, maps.series = map_series.name',
    else
             groupBy = 'items._pageID',
         local qargs = {}
             orderBy = 'map_series.ordinal DESC, items.drop_enabled DESC',
        qargs.tables = {'main_pages'}
        qargs.join = 'items._pageName = main_pages.data_page'
        qargs.fields = {
            'items.name=name',
             'items.inventory_icon=inventory_icon',
            'items.html=html',
             'items.alternate_art_inventory_icons=alternate_art_inventory_icons',
            'items.size_x=size_x',
            'items.size_y=size_y',
            'main_pages._pageName=main_page',
         }
         }
         local search_param
         result = m_item_util.query_item(args, qargs)
        if tpl_args.metadata_id then
         if result.error then
            query.where = string.format(
             if not m_util.cast.boolean(args.nocat) then
                'items.metadata_id="%s"',
                 return result.error:get_html() .. m_util.misc.add_category({i18n.categories.broken_item_links})
                tpl_args.metadata_id
            )
            search_param = 'metadata_id'
        elseif tpl_args.page then
            -- Join with _pageData in order to check for page redirect
            tables[#tables+1] = '_pageData'
            fields[#fields+1] = '_pageData._pageNameOrRedirect'
            query.where = string.format(
                '_pageData._pageName="%s"',
                tpl_args.page
            )
            query.join = query.join .. ', items._pageName = _pageData._pageNameOrRedirect'
            search_param = 'page'
        elseif tpl_args.item_name_exact then
            query.where = string.format(
                'items.name = "%s" AND items._pageNamespace IN (%s)',
                m_cargo.addslashes(tpl_args.item_name_exact),
                m_item_util.get_item_namespaces{format = 'list'}
            )
            search_param = 'item_name_exact'
        else
            -- Explicitly join name_list child table instead of using HOLDS
            tables[#tables+1] = 'items__name_list'
            query.where = string.format(
                'items__name_list._value="%s" AND items._pageNamespace IN (%s)',
                m_cargo.addslashes(tpl_args.item_name),
                m_item_util.get_item_namespaces{format = 'list'}
            )
            query.join = query.join .. ', items._ID = items__name_list._rowID'
            search_param = 'item_name'
        end
       
        result = m_cargo.query(
            tables,
            fields,
            query
        )
       
        local err
         if #result == 0 then
            -- No results found
            err = m_util.misc.raise_error_or_return{
                raise_required=true,
                args=tpl_args,
                msg=string.format(
                    i18n.errors.no_results,
                    search_param,
                    tpl_args[search_param]
                )
            }
        elseif #result > 1 then
             -- More than one result found
            --
            -- If results are all maps, use the one from the most recent map
            -- series, regardless of whether it's drop enabled. Otherwise,
            -- if only one of the results is drop enabled then use that one.
            local map_count = 0
            local drop_enabled_count = 0
            for i, v in ipairs(result) do
                if v['items.class_id'] == 'Map' then
                    map_count = map_count + 1
                end
                if m_util.cast.boolean(v['items.drop_enabled']) then
                    drop_enabled_count = drop_enabled_count + 1
                 end
            end
            if (map_count == 0 or map_count ~= #result) and drop_enabled_count ~= 1 then
                err = m_util.misc.raise_error_or_return{
                    raise_required=true,
                    args=tpl_args,
                    msg=string.format(
                        i18n.errors.too_many_results,
                        search_param,
                        tpl_args[search_param]
                    )
                }
             end
             end
            return result.error:get_html()
         end
         end
          
 
         if err ~= nil then
         -- If specifying an item by name, link to the main page; otherwise, link to where the item data lives.
            return err .. m_util.misc.add_category({i18n.categories.broken_item_links})
         if result.main_page and m_util.table.has_any_key(args, {'item_name', 'item_name_exact'}) then
            linked_page = result.main_page
        else
            linked_page = result._pageName
         end
         end
       
        result = result[1] -- Use the first result
    else
        result = {
            ['items._pageName'] = tpl_args.page or tpl_args.name
        }
     end
     end
      
      
    -- Overrides from template parameters
     for k, prop in pairs(cfg.parameters) do
     for k, prop in pairs(cfg.parameters) do
         if tpl_args[k] ~= nil then
         if args[k] ~= nil then
             result[prop] = tpl_args[k]
             result[prop] = args[k]
         end
         end
     end
     end
      
      
     if tpl_args.image ~= nil then
     if args.image ~= nil then
         if result['items.alternate_art_inventory_icons'] == nil then
         if result.alternate_art_inventory_icons == nil then
             return m_util.misc.raise_error_or_return{
             local err = m_util.Error{
                 raise_required=true,
                 message = string.format(
                args=tpl_args,
                msg=string.format(
                     i18n.errors.alt_art_undefined,
                     i18n.errors.alt_art_undefined,
                     result['items._pageName']
                     result._pageName
                 ) .. m_util.misc.add_category({i18n.categories.broken_item_links})
                ),
             }
                code = 'alt_art_undefined',
                 issue = args.issue_all_errors or false,
                category = i18n.categories.broken_item_links,
            }:throw()
             return err:get_html() .. err:get_category()
         end
         end
          
          
         result['items.alternate_art_inventory_icons'] = m_util.string.split(
         result.alternate_art_inventory_icons = m_util.string.split(
             result['items.alternate_art_inventory_icons'],  
             result.alternate_art_inventory_icons,  
             ',%s*'
             ',%s*'
         )
         )
          
          
         local index = tonumber(tpl_args.image)
         local index = tonumber(args.image)
         if index ~= nil then
         if index ~= nil then
             img = result['items.alternate_art_inventory_icons'][index]
             img = result.alternate_art_inventory_icons[index]
         else
         else
             -- offset 1 is needed
             -- offset 1 is needed
             local suffix = string.len(' inventory icon.png') + 1  
             local suffix = string.len(' inventory icon.png') + 1  
             -- add an extra offset by 1 to account for the space  
             -- add an extra offset by 1 to account for the space  
             local prefix = string.len(string.sub(result['items.inventory_icon'], 1, -suffix)) + 2
             local prefix = string.len(string.sub(result.inventory_icon, 1, -suffix)) + 2
              
              
             for _, filename in ipairs(result['items.alternate_art_inventory_icons']) do
             for _, filename in ipairs(result.alternate_art_inventory_icons) do
                 if string.sub(filename, prefix, -suffix) == tpl_args.image then
                 if string.sub(filename, prefix, -suffix) == args.image then
                     img = filename
                     img = filename
                     break
                     break
Line 219: Line 120:
          
          
         if img == nil then
         if img == nil then
             return m_util.misc.raise_error_or_return{
             local err = m_util.Error{
                 raise_required=true,
                 message = string.format(
                args=tpl_args,
                msg=string.format(
                     i18n.errors.alt_art_invalid_index,
                     i18n.errors.alt_art_invalid_index,
                     tpl_args.image, result['items._pageName']
                     args.image,
                 ) .. m_util.misc.add_category({i18n.categories.broken_item_links})
                    result._pageName
             }
                ),
                code = 'alt_art_invalid_index',
                 issue = args.issue_all_errors or false,
                category = i18n.categories.broken_item_links,
            }:throw()
             return err:get_html() .. err:get_category()
         end
         end
     elseif result['items.inventory_icon'] ~= nil then
     elseif result.inventory_icon ~= nil then
         img = result['items.inventory_icon']
         img = result.inventory_icon
     end
     end
      
      
Line 235: Line 139:
     -- 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
        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')
     local container = mw.html.create('span')
     container:addClass('c-item-hoverbox')
     container:addClass('hoverbox c-item-hoverbox')


     if tpl_args.large then
     if args.large then
         container:addClass('c-item-hoverbox--large')
         container:addClass('c-item-hoverbox--large')
     end
     end
      
      
     local activator = mw.html.create('span')
     local activator = mw.html.create('span')
     activator:addClass('c-item-hoverbox__activator')
     activator:addClass('hoverbox__activator c-item-hoverbox__activator')


     if img and not tpl_args.large then
     if img and not args.large then
         activator:wikitext(string.format('[[%s|16x16px|link=|alt=]]', img))
         activator:wikitext(
            string.format(
                '[[%s|%sx%spx|link=|alt=]]',
                img,
                cfg.icon_size_inline,
                cfg.icon_size_inline
            )
        )
     end
     end
      
      
     if #result['items.name'] > 0 then
     if #result.name > 0 then
         activator:wikitext(string.format(
         activator:wikitext(
            '[[%s|%s]]',  
            string.format(
            linked_page,  
                '[[%s|%s]]',  
            result['items.name'] or result['items._pageName']
                linked_page,  
                result.name or result._pageName
            )
        )
    end
 
    local width = tonumber(result.size_x) or tonumber(args.width) or 1
    local height = tonumber(result.size_y) or tonumber(args.height) or 1
    if img and args.large then
        activator:wikitext(
            string.format(
                '[[%s|%sx%spx|link=%s|alt=]]',
                img,
                width * cfg.icon_size_large,
                height * cfg.icon_size_large,
                linked_page
             )
             )
         )
         )
Line 269: Line 186:
      
      
     local display = mw.html.create('span')
     local display = mw.html.create('span')
     display:attr('class', 'c-item-hoverbox__display')
     display:addClass('hoverbox__display c-item-hoverbox__display')
 
     if result.html ~= nil then
     if result['items.html'] ~= nil then
         display:wikitext(result.html)
         display:wikitext(result['items.html'])
           
         if img then
         if img then
             display:wikitext(string.format('[[%s|link=|alt=]]', img))
             display:wikitext(
        end
                string.format(
    end
                    '[[%s|%sx%spx|link=|alt=|class=item-icon]]',
 
                    img,
    if img and tpl_args.large then
                    width * cfg.icon_size_full,
        local width = tonumber(result['items.size_x']) or tonumber(tpl_args.width)
                    height * cfg.icon_size_full
        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
         end
        activator:wikitext(img)
     end
     end


Line 317: Line 204:
         :node(activator)
         :node(activator)
         :node(display)
         :node(display)
        :done()
       
     return tostring(container)
     return tostring(container)
end
end
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local p = {}
--
-- Template:Item link
--
p.main = m_util.misc.invoker_factory(_main, {
    parentFirst = true,
    removeBlanks = false,
})
p.item_link = p.main


return p
return p

Latest revision as of 15:32, 15 April 2025

Module documentation[view] [edit] [history] [purge]


Lua logo

This module depends on the following other modules:

This module implements {{item link}} and facilitates the creation of item links.

-------------------------------------------------------------------------------
-- 
--                             Module:Item link
-- 
-- This module implements Template:Item link.
-------------------------------------------------------------------------------

require('Module:No globals')
local m_util = require('Module:Util')

-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Item link')

local m_item_util = use_sandbox and require('Module:Item util/sandbox') or require('Module:Item util')

-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Item link/config/sandbox') or mw.loadData('Module:Item link/config')

local i18n = cfg.i18n

-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------

local function _main(args)
    --[[
    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'}
    --]]
    
    args.item_name = args.item_name or args[1]
    args.name = args.name or args[2]
    args.large = m_util.cast.boolean(args.large)
    
    local img
    local result
    local linked_page
    if m_util.cast.boolean(args.skip_query) then
        result = {
            _pageName = args.page or args.name
        }
        linked_page = args.link or result._pageName
    else
        local qargs = {}
        qargs.tables = {'main_pages'}
        qargs.join = 'items._pageName = main_pages.data_page'
        qargs.fields = {
            'items.name=name',
            'items.inventory_icon=inventory_icon',
            'items.html=html',
            'items.alternate_art_inventory_icons=alternate_art_inventory_icons',
            'items.size_x=size_x',
            'items.size_y=size_y',
            'main_pages._pageName=main_page',
        }
        result = m_item_util.query_item(args, qargs)
        if result.error then
            if not m_util.cast.boolean(args.nocat) then
                return result.error:get_html() .. m_util.misc.add_category({i18n.categories.broken_item_links})
            end
            return result.error:get_html()
        end

        -- If specifying an item by name, link to the main page; otherwise, link to where the item data lives.
        if result.main_page and m_util.table.has_any_key(args, {'item_name', 'item_name_exact'}) then
            linked_page = result.main_page
        else
            linked_page = result._pageName
        end
    end
    
    -- Overrides from template parameters
    for k, prop in pairs(cfg.parameters) do
        if args[k] ~= nil then
            result[prop] = args[k]
        end
    end
    
    if args.image ~= nil then
        if result.alternate_art_inventory_icons == nil then
            local err = m_util.Error{
                message = string.format(
                    i18n.errors.alt_art_undefined,
                    result._pageName
                ),
                code = 'alt_art_undefined',
                issue = args.issue_all_errors or false,
                category = i18n.categories.broken_item_links,
            }:throw()
            return err:get_html() .. err:get_category()
        end
        
        result.alternate_art_inventory_icons = m_util.string.split(
            result.alternate_art_inventory_icons, 
            ',%s*'
        )
        
        local index = tonumber(args.image)
        if index ~= nil then
            img = result.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.inventory_icon, 1, -suffix)) + 2
            
            for _, filename in ipairs(result.alternate_art_inventory_icons) do
                if string.sub(filename, prefix, -suffix) == args.image then
                    img = filename
                    break
                end
            end
        end
        
        if img == nil then
            local err = m_util.Error{
                message = string.format(
                    i18n.errors.alt_art_invalid_index,
                    args.image,
                    result._pageName
                ),
                code = 'alt_art_invalid_index',
                issue = args.issue_all_errors or false,
                category = i18n.categories.broken_item_links,
            }:throw()
            return err:get_html() .. err:get_category()
        end
    elseif result.inventory_icon ~= nil then
        img = result.inventory_icon
    end
    
    --
    -- output
    --
    
    local container = mw.html.create('span')
    container:addClass('hoverbox c-item-hoverbox')

    if args.large then
        container:addClass('c-item-hoverbox--large')
    end
    
    local activator = mw.html.create('span')
    activator:addClass('hoverbox__activator c-item-hoverbox__activator')

    if img and not args.large then
        activator:wikitext(
            string.format(
                '[[%s|%sx%spx|link=|alt=]]',
                img,
                cfg.icon_size_inline,
                cfg.icon_size_inline
            )
        )
    end
    
    if #result.name > 0 then
        activator:wikitext(
            string.format(
                '[[%s|%s]]', 
                linked_page, 
                result.name or result._pageName
            )
        )
    end

    local width = tonumber(result.size_x) or tonumber(args.width) or 1
    local height = tonumber(result.size_y) or tonumber(args.height) or 1
    if img and args.large then
        activator:wikitext(
            string.format(
                '[[%s|%sx%spx|link=%s|alt=]]',
                img,
                width * cfg.icon_size_large,
                height * cfg.icon_size_large,
                linked_page
            )
        )
    end
    
    local display = mw.html.create('span')
    display:addClass('hoverbox__display c-item-hoverbox__display')
    if result.html ~= nil then
        display:wikitext(result.html)
        if img then
            display:wikitext(
                string.format(
                    '[[%s|%sx%spx|link=|alt=|class=item-icon]]',
                    img,
                    width * cfg.icon_size_full,
                    height * cfg.icon_size_full
                )
            )
        end
    end

    container
        :node(activator)
        :node(display)
    return tostring(container)
end

-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------

local p = {}

-- 
-- Template:Item link
-- 
p.main = m_util.misc.invoker_factory(_main, {
    parentFirst = true,
    removeBlanks = false,
})

p.item_link = p.main

return p