Module:NorbyteLink

From bg3.wiki
Jump to navigation Jump to search

Doc page: Module:NorbyteLink/doc

This module handles link generation for searches on Norbyte.

Usage

{{#invoke:NorbyteLink|main}}

The module automatically reads all parameters passed to the parent template {{NorbyteLink}}.

Arguments

The module loops through all arguments passed to the calling template and processes them based on their keys:

  • Named Parameters: Standard named arguments are encoded and formatted as structured key:value filters (e.g., |type=weapon translates to type:weapon in the search URL).
  • Positional Arguments: Unnamed positional arguments are treated as raw search terms and are appended to the URL without a key prefix.

Magic Parameters

Note: To prevent parameter leaking, all arguments starting with an underscore are treated as internal configuration and are never passed to the search URL as key:value pairs.

  • _h: Controls the height (in pixels) of the rendered search icon. Defaults to 14 if not specified.
  • _q: Explicitly forces its value to be passed as a raw search query without a key prefix (identical to a positional argument).
  • _validate: Defaults to true. Controls whether internal spacing issues apply a tracking category (see below).

Validation & Query Flattening

The module automatically sanitizes inputs and appends the tracking category Category:Gamedata Fields for Review in the main article namespace under the following rules:

  • Key Stripping (Query Flattening): If a key contains any spaces or non-whitelist characters, or if a value contains internal spaces, the key is stripped and the value is passed as a raw, keyless query term.
  • Tracking Category:
    • Non-whitelist characters (outside a-zA-Z0-9_-%'()&/.) anywhere in a key or value, or spaces in a key, will always append the tracking category.
    • Internal spaces in a value field will only append the tracking category if _validate=true (or left at its default).

local p = {}
local yesno = require('Module:Yesno')

--  Returns: 0 (neither), 1 (space), 2 (illegal), 3 (both)
local function validate_arg(arg)
    local a = arg:match("%S%s+%S") and 1 or 0
    local b = arg:match("[^a-zA-Z0-9_%s',()&/%.%-]") and 2 or 0
    return a + b
end

local blanks = {none = true, null = true}

local function is_blank(arg)
	if not arg or arg == "" or (blanks)[arg:lower()] then return true end
	return false
end

function p.validate(frame)
    local arg = frame.args[1] and mw.text.trim(frame.args[1]) or nil
    if not arg or arg == '' then
        return 0 
    end
    
    return validate_arg(arg)
end

function p.internal(frame, args)
	local query = {}
    local validate_score = 0

    local search = yesno(args['_search'] or 'true')
    local validate = yesno(args['_validate'] or 'true')
    
    local base_link = search and "https://bg3.norbyte.dev/search?q=" or "https://bg3.norbyte.dev/search?"
    local namespace = mw.title.getCurrentTitle().namespace

    for _k, _v in pairs(args) do
        local k = mw.text.trim(tostring(_k))
        local v = mw.text.trim(tostring(_v))
        
        if (k == '_q' or k == 'name') and is_blank(v) then return "" end
        
        local magic = k:sub(1, 1) == '_'
        if not magic or k == '_q' then 
        	local k_val, v_val = validate_arg(k), validate_arg(v)
            validate_score = math.max(validate_score, k_val, v_val)
            if k_val ~= 0 or v_val % 2 ~= 0 then k = '_q' end
        end
        
        if type(_k) == 'number' or k == '_q' then
        	if search then 
            	table.insert(query, mw.uri.encode(v))
            else
            	table.insert(query, v)
            end
        elseif not magic and v ~= '' then -- likely dead code atp
        	if search then
            	table.insert(query, mw.uri.encode(k) .. ':' .. mw.uri.encode(v))
            else
            	table.insert(query, k .. '=' .. v)
            end
        end
    end
    
    if #query == 0 then return "" end
    
    local icon_html = frame:expandTemplate{
        title = 'icon',
        args = {
            'Search icon.svg',
            h = args._h or '14',
            link = base_link .. table.concat(query, "+")
        }
    }
    
    if namespace == 0 then
        if validate and validate_score > 0 then
            return icon_html .. "[[Category:Gamedata Fields for Review]]"
        end
    end

    return icon_html
end

function p.main(frame)
    local args = frame.args
    if frame == mw.getCurrentFrame() and next(args) == nil then
        args = frame:getParent().args
    end
    return p.internal(frame, args)
end

return p