Module:Item util
Jump to navigation
Jump to search
This is a meta module.
This module is meant to be used only by other modules. It should not be invoked in wikitext.
This meta module provides utility functions for modules that deal with items.
Usage
This module should be loaded with require()
.
The above documentation is transcluded from Module:Item util/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 util
--
-- This meta module contains utility functions for modules that deal with items
-------------------------------------------------------------------------------
local m_util = require('Module:Util')
local m_cargo -- Lazy load require('Module:Cargo')
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('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 util/config/sandbox') or mw.loadData('Module:Item util/config')
local i18n = cfg.i18n
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local m = {}
function m.get_item_namespaces(args)
-- Returns item namespaces from config as a table or as a comma-separated string
args.format = args.format or 'table'
if args.format == 'list' then
return cfg.item_namespaces_list
end
return cfg.item_namespaces
end
function m.query_item(args, qargs)
qargs = qargs or {}
m_cargo = m_cargo or require('Module:Cargo')
if not m_util.table.has_any_key(args, cfg.search_terms) then
error(i18n.errors.missing_search_term)
end
local tables = {'items'}
local fields = {
'items._pageName=_pageName',
'items.drop_enabled=drop_enabled',
'items.class_id=class_id',
}
local query = {
groupBy = 'items._pageID',
orderBy = 'items.drop_enabled DESC',
}
local search_param
local results
if args.metadata_id then
query.where = string.format(
'items.metadata_id = "%s"',
args.metadata_id
)
search_param = 'metadata_id'
elseif args.page then
-- Join with _pageData in order to check for page redirect
tables[#tables+1] = '_pageData'
query.join = 'items._pageName = _pageData._pageNameOrRedirect'
query.where = string.format(
'_pageData._pageName = "%s"',
m_cargo.addslashes(args.page)
)
search_param = 'page'
else
tables[#tables+1] = 'maps'
tables[#tables+1] = 'map_series'
query.join = 'items._pageID = maps._pageID, maps.series = map_series.name'
query.where = string.format(
'items._pageNamespace IN (%s)',
m.get_item_namespaces{format = 'list'}
)
query.orderBy = 'map_series.ordinal DESC, ' .. query.orderBy
if args.item_name_exact then
query.where = query.where .. string.format(
' AND items.name = "%s"',
m_cargo.addslashes(args.item_name_exact)
)
search_param = 'item_name_exact'
else
--[[
Cargo's implementation of HOLDS breaks when there is a properly
escaped quotation mark at the end of a string literal.
Example of a WHERE clause that results in an error:
items.name_list HOLDS "\"O\' Eternal\""
Instead, we avoid using HOLDS by explicitly joining the child table
and then comparing using a native operator.
--]]
tables[#tables+1] = 'items__name_list'
query.join = query.join .. ', items._ID = items__name_list._rowID'
query.where = query.where .. string.format(
' AND items__name_list._value = "%s"',
m_cargo.addslashes(args.item_name)
)
search_param = 'item_name'
end
end
-- Append additional tables and fields
if type(qargs.tables) == 'table' and #qargs.tables > 0 then
tables = m_util.table.merge(tables, qargs.tables)
end
if type(qargs.fields) == 'table' and #qargs.fields > 0 then
fields = m_util.table.merge(fields, qargs.fields)
end
-- Append to join, where and orderBy clauses
if qargs.join then
-- m_util.table.merge rebuilds the table, which removes empty values
-- TODO: Use a better function than m_util.table.merge
query.join = table.concat(m_util.table.merge({query.join, qargs.join}), ', ')
end
if qargs.where then
query.where = table.concat(m_util.table.merge({query.where, qargs.where}), ' AND ')
end
if qargs.orderBy then
query.orderBy = table.concat(m_util.table.merge({qargs.orderBy, query.orderBy}), ', ')
end
results = m_cargo.query(tables, fields, query)
local err
if #results == 0 then
-- No results found
err = m_util.Error{
message = string.format(
i18n.errors.no_results_found,
search_param,
args[search_param]
),
code = 'no_results_found',
issue = args.issue_all_errors or false,
category = i18n.categories.failed_query,
}:throw()
elseif #results > 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(results) do
if v.class_id == 'Map' then
map_count = map_count + 1
end
if m_util.cast.boolean(v.drop_enabled) then
drop_enabled_count = drop_enabled_count + 1
end
end
if (map_count == 0 or map_count ~= #results) and drop_enabled_count ~= 1 then
err = m_util.Error{
message = string.format(
i18n.errors.many_results_found,
search_param,
args[search_param]
),
code = 'many_results_found',
issue = args.issue_all_errors or false,
category = i18n.categories.failed_query,
}:throw()
end
end
if args.debug then
mw.logObject(results)
end
if err then
return {error = err}
end
return results[1] -- orderBy ensures that the first result is the one we want
end
return m