Module:SST
This module is the core router for the Specific-Source Template System (SSTS). It acts as a dynamic wrapper and dispatcher for specific-source templates. It intercepts specific parameters, routes the request to the correct host engine (such as Internet Archive, HathiTrust, or Google Books), and passes the formatted data to standard citation templates like {{cite book}}.
Usage
Creating a new specific-source template requires two steps: creating the front-end template, and adding the book's data to the correct alphabetized "shard."
Step 1: the front-end template
The front-end template (e.g., {{The Ottoman Empire}}) is a lightweight wrapper that routes the user's input to Module:SST.
- Single-book (example template)
<includeonly>{{#invoke:SST|single|id=HISTORY_OF_YOLO_COUNTY}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
- id: The unique base identifier for the book located in the data shard.
If the book has multiple volumes or editions, replace single with set. You can combine it with a {{#switch}} statement and {{Normalize volume and edition}} to route to the correct data block within the book's standalone shard.
- Three editions (example template)
<includeonly>{{#invoke:SST|set
| id = IMBER_THE_OTTOMAN_EMPIRE
| key = {{#switch:{{#invoke:Normalize volume and edition|edition|{{{edition|}}}}}
| 3 | 2019 = 2019
| 2 | 2009 = 2009
| 1 | 2002 | #default = 2002
}}
}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
- 7 volume set (example template)
<includeonly>{{#invoke:SST|set
| id = BYZANTINE_SEALS
| key = {{#switch:{{#invoke:Normalize volume and edition|volume|{{{volume|}}}}}
| 1 | I = 1
| 2 | II = 2
| 3 | III = 3
| 4 | IV = 4
| 5 | V = 5
| 6 | VI = 6
| 7 | VII = 7
| all | ALL | #default = ALL
}}
}}</includeonly><noinclude>
{{Documentation}}
</noinclude>
- id: The base identifier for the entire set (maps to the shard filename).
- key: The specific variant key (e.g., 1, 2, ALL) to append to the base ID.
Architecture & Data Sharding
To avoid loading a single massive data table into memory, configuration data is split into alphabetical subpages (shards).
- Validation: When invoked, the module verifies that a valid host code (e.g.,
IA,Hathi,GBook) and Book Key are provided. - Data Retrieval: It extracts the first letter of the Book Key (e.g., "B" from "BYZANTINE_SEALS_V1") and uses
mw.loadData()to retrieve the corresponding subpage (e.g., Module:SST/shards/B). - Engine Execution: It then loads the Module:SST/hosts engine, passing the shard data and the user's template arguments to build the final URLs.
- Output: The compiled arguments are expanded into the base template defined in the shard (defaulting to
{{cite book}}).
Error Tracking
The module utilizes an error tracking system.
- Fatal Execution Errors: If the module encounters a system failure (e.g., an unsupported host engine) or a native CS1 red error, it outputs a bold red error message in the citation and categorizes the page into Category:SSTS errors.
- Missing Records: If a citation requests a book key, shard, or volume that does not exist in the database, it outputs a visible error and categorizes the page into Category:SSTS missing records.
- Parameter Logic Faults: If an editor uses an invalid combination of parameters that conflicts with the shard's architecture (e.g., adding
|title=or|article=to a{{cite encyclopedia}}), the module will attempt to silently resolve the citation to prevent a red error, but will flag the page in the hidden category Category:SSTS parameter logic faults.
Testcases
Testcases: Template:Catalogue of Byzantine Seals at Dumbarton Oaks and in the Fogg Museum of Art/testcases
require('strict')
local p = {}
--[[--------------------------< H O S T _ M A P >--------------------------------------------]]
local host_map = {
['ia'] = { engine = 'IA', shard_key = 'ia' },
['internetarchive'] = { engine = 'IA', shard_key = 'ia' },
['hathi'] = { engine = 'Hathi', shard_key = 'hathi' },
['hathitrust'] = { engine = 'Hathi', shard_key = 'hathi' },
['guten'] = { engine = 'Gutenberg', shard_key = 'guten' },
['gutenberg'] = { engine = 'Gutenberg', shard_key = 'guten' },
['wikisrc'] = { engine = 'Wikisource', shard_key = 'wikisrc' },
['wikisource'] = { engine = 'Wikisource', shard_key = 'wikisrc' },
['gbook'] = { engine = 'GBook', shard_key = 'gbook' },
['googlebooks'] = { engine = 'GBook', shard_key = 'gbook' },
['web'] = { engine = 'Web', shard_key = 'web' },
['physical'] = { engine = 'Physical', shard_key = 'physical' }
}
--[[--------------------------< P A R A M E T E R M A P >----------------------------------]]
-- SSTS Architecture Note:
-- The parameter_map is designed specifically for sub-divisions of a single work
-- (like individual chapters or dictionary entries).
-- To maintain the Single-Source Template (SSTS) philosophy, core identity
-- parameters like 'title', 'year', or 'isbn' are managed at the variant
-- level (creating a new library entry) rather than through this map.
local parameter_map_guardrail = {
['chapter'] = true,
['entry'] = true,
['article'] = true,
['section'] = true,
['part'] = true
}
local function apply_parameter_map(user_args, book_data, library)
-- Follow the pointer if an alias exists
local map_source = book_data
if book_data.parameter_map_alias and library and library[book_data.parameter_map_alias] then
map_source = library[book_data.parameter_map_alias]
end
if not map_source.parameter_map then return end
for param, map_table in pairs(map_source.parameter_map) do
if parameter_map_guardrail[param] then
local user_input = user_args[param]
if user_input then
-- Trim whitespace so "|chapter= 8 " matches key ['8']
local clean_input = mw.text.trim(tostring(user_input))
if map_table[clean_input] then
user_args[param] = map_table[clean_input]
end
end
end
end
end
--[[--------------------------< H E L P E R S >----------------------------------------------]]
local function error_msg(msg, tracking_category)
local err_text = string.format('<strong class="error">SSTS Error: %s</strong>', msg)
local title = mw.title.getCurrentTitle()
if title and (title.namespace == 0 or title.namespace == 118) then
if tracking_category then
return err_text .. '[[Category:' .. tracking_category .. ']]'
else
return err_text .. '[[Category:SSTS errors]]'
end
end
return err_text
end
--[[--------------------------< C O R E _ E X E C U T I O N >--------------------------------]]
-- Shared function to build the citation regardless of how it was routed
local function build_citation(frame, book_data, target_host, library)
if not target_host then return error_msg("No host defined for this record.") end
-- 1. Route to the correct Host/Engine
local host_info = host_map[string.lower(mw.text.trim(target_host))]
if not host_info then return error_msg("Unsupported host '" .. target_host .. "'") end
-- 2. Validate Host Data within the record
local host_data = book_data.hosts and book_data.hosts[host_info.shard_key]
if not host_data then
return error_msg("Host '" .. host_info.shard_key .. "' is not defined for this book.")
end
-- 3. Load the Hosts module
local success_engine, hosts_module = pcall(require, 'Module:SST/hosts')
if not success_engine then
return error_msg("Could not load Module:SST/hosts.")
end
local engine = hosts_module[host_info.engine]
if not engine then
return error_msg("Engine '" .. host_info.engine .. "' not found in hosts module.")
end
-- 4. Process Arguments & Apply ignore_args Filter
local ignore_list = {
['ignore_args'] = true,
['id'] = true,
['key'] = true,
['default'] = true
}
if frame.args['ignore_args'] then
for key in string.gmatch(frame.args['ignore_args'], '([^,]+)') do
ignore_list[mw.text.trim(key)] = true
end
end
local combined_args = {}
-- Grab parent args (user input), skipping ignored keys
for k, v in pairs(frame:getParent().args) do
if type(k) == 'string' and not ignore_list[k] then
combined_args[k] = v
elseif type(k) == 'number' and not ignore_list[tostring(k)] then
combined_args[k] = v
end
end
-- Grab current frame args (template overrides like our generated 'chapter')
for k, v in pairs(frame.args) do
if type(k) == 'string' and not ignore_list[k] then
combined_args[k] = v
end
end
-- ==========================================================
-- 4.5 APPLY PARAMETER MAP (Translate shorthand into full titles)
-- ==========================================================
apply_parameter_map(combined_args, book_data, library)
local citeArgs = {}
for k, v in pairs(book_data.cite_params or {}) do
citeArgs[k] = v
end
-- ==========================================================
-- 4.7 SILENT LOGIC TRAP (Inspect raw shard data for faults)
-- ==========================================================
local has_logic_fault = false
local t_type = citeArgs['_template'] or 'cite book'
if t_type == 'cite encyclopedia' then
-- Encyclopedia shards must use 'encyclopedia', not 'title' or specific entries.
-- They also cannot contain book or journal container names.
if citeArgs['title'] or citeArgs['title-link'] or citeArgs['article'] or citeArgs['entry'] then
has_logic_fault = true
elseif citeArgs['journal'] or citeArgs['magazine'] or citeArgs['work'] then
has_logic_fault = true
end
elseif t_type == 'cite journal' then
-- Journal shards must have a journal container.
-- They cannot be locked to specific chapters, nor contain book/encyclopedia containers.
if not citeArgs['journal'] and not citeArgs['work'] and not citeArgs['magazine'] then
has_logic_fault = true
end
if citeArgs['chapter'] or citeArgs['encyclopedia'] then
has_logic_fault = true
end
elseif t_type == 'cite book' then
-- Book shards cannot contain encyclopedia or journal container names.
if citeArgs['encyclopedia'] or citeArgs['journal'] or citeArgs['magazine'] or citeArgs['work'] then
has_logic_fault = true
end
end
-- 5. Pass execution to the Hosts module for parsing and link-building
if type(hosts_module.process) == "function" then
citeArgs = hosts_module.process(engine, host_data, citeArgs, combined_args)
else
return error_msg("System Error: hosts_module.process is missing.")
end
-- 6. Render standard Cite Book/Encyclopedia template
local template_name = citeArgs['_template'] or 'cite book'
citeArgs['_template'] = nil
if citeArgs['ref'] and mw.ustring.match(citeArgs['ref'], '^{{') then
local pre_ref = frame:preprocess(citeArgs['ref'])
citeArgs['ref'] = mw.ustring.gsub(pre_ref, " ", "_")
end
local citation = frame:expandTemplate{ title = template_name, args = citeArgs }
-- 7. Catch CS1/CS2 errors and inject silent tracking categories
local current_page = mw.title.getCurrentTitle()
if current_page and (current_page.namespace == 0 or current_page.namespace == 118) then
-- Catch native CS1 red errors
local has_cs1_error = string.find(citation, "cs1%-visible%-error") or
string.find(citation, "cs1%-hidden%-error") or
string.find(citation, "Category:CS1 errors")
if has_cs1_error then
citation = citation .. "[[Category:SSTS errors]]"
end
-- Catch parameter logical faults
if has_logic_fault then
citation = citation .. "[[Category:SSTS parameter logic faults]]"
end
end
return citation
end
--[[--------------------------< T H E B R I D G E >----------------------------------------]]
-- LEGACY ROUTER: Supports existing live templates. DO NOT DELETE until Phase 4.
function p.main(frame)
local raw_host = frame.args[1]
local book_key = frame.args[2]
if not raw_host then return error_msg("No host specified in #invoke.") end
if not book_key then return error_msg("No book key specified in #invoke.") end
local shard_letter = mw.ustring.upper(mw.ustring.sub(mw.text.trim(book_key), 1, 1))
local success_shard, library = pcall(mw.loadData, 'Module:SST/shards/' .. shard_letter)
if not success_shard then return error_msg("Could not load shard module '" .. shard_letter .. "'.") end
local book_data = library[mw.text.trim(book_key)]
if not book_data then return error_msg("Book key '" .. book_key .. "' not found.", "SSTS missing records") end
-- Smart Host Fallback
local final_host = book_data.host
if not final_host then
-- Fallback 1: Try to grab the first available host from the hosts table
if book_data.hosts and type(book_data.hosts) == 'table' then
for k, _ in pairs(book_data.hosts) do
final_host = k
break
end
end
-- Fallback 2: If still nothing, treat it as a physical book
if not final_host then
final_host = 'physical'
end
end
local output = build_citation(frame, book_data, final_host, library)
-- Only categorize in Main (0) and Template (10) namespaces to ignore sandboxes
local ns = mw.title.getCurrentTitle().namespace
if ns == 0 or ns == 10 then
output = output .. "[[Category:SSTS errors|*LEGACY]]"
end
return output
end
--[[--------------------------< A R C H I T E C T U R E >----------------------------]]
-- ROUTER: Single A-Z Books
function p.single(frame)
local base_id = frame.args.id
if not base_id then return error_msg("No id specified in #invoke.") end
local shard_letter = mw.ustring.upper(mw.ustring.sub(mw.text.trim(base_id), 1, 1))
local success_shard, library = pcall(mw.loadData, 'Module:SST/shards/' .. shard_letter)
if not success_shard then return error_msg("Could not load shard module '" .. shard_letter .. "'.") end
local book_data = library[mw.text.trim(base_id)]
if not book_data then return error_msg("Record '" .. base_id .. "' not found.", "SSTS missing records") end
-- Smart Host Fallback
local final_host = book_data.host
if not final_host then
if book_data.hosts and type(book_data.hosts) == 'table' then
for k, _ in pairs(book_data.hosts) do
final_host = k
break
end
end
if not final_host then final_host = 'physical' end
end
return build_citation(frame, book_data, final_host, library)
end
-- ROUTER: Multi-Volume Sets
function p.set(frame)
local base_id = frame.args.id
if not base_id then return error_msg("No id specified for the set in #invoke.") end
-- Determine target key (e.g. "1", "2", "ALL")
local raw_key = frame.args.key
local target_key
if not raw_key or mw.text.trim(raw_key) == "" then
target_key = frame.args.default or "ALL"
else
target_key = mw.text.trim(raw_key)
end
local prefix = base_id .. "_"
local lookup_id
-- Smart detection: Check if the editor accidentally passed the full book key instead of the short variant
if mw.ustring.sub(target_key, 1, mw.ustring.len(prefix)) == prefix then
lookup_id = target_key
-- Strip the prefix from target_key so error messages still look clean
target_key = mw.ustring.sub(target_key, mw.ustring.len(prefix) + 1)
else
lookup_id = prefix .. target_key
end
-- Route to dedicated /sets/ module
local success_shard, library = pcall(mw.loadData, 'Module:SST/shards/sets/' .. base_id)
if not success_shard then return error_msg("Could not load set module for '" .. base_id .. "'.") end
local book_data = library[lookup_id]
if not book_data then return error_msg("Part '" .. target_key .. "' not found in set '" .. base_id .. "'.", "SSTS missing records") end
-- Smart Host Fallback
local final_host = book_data.host
if not final_host then
if book_data.hosts and type(book_data.hosts) == 'table' then
for k, _ in pairs(book_data.hosts) do
final_host = k
break
end
end
if not final_host then final_host = 'physical' end
end
return build_citation(frame, book_data, final_host, library)
end
return p
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.