モジュール:CS1 identifiers
表示
| このLuaモジュールは260,000以上のページで使われています。 余計な混乱やサーバーへの負荷を避けるために、どんな変更でも最初はモジュールのサンドボックス・サブページ、テストケース・サブページで試すべきです。また、変更前に、この項目のノートで議論するようにお願いします。 |
| Luaモジュールを使用しています: |
| このモジュールはテンプレートスタイルを使用しています: |
このモジュールは以下のテンプレートを実装しています。
require ('strict');
local get_args = require ('Module:Arguments').getArgs;
local identifiers = require ('Module:Citation/CS1/Identifiers');
local utilities = require ('Module:Citation/CS1/Utilities');
local in_array = utilities.in_array; -- import functions from Module:Citation/CS1/Utilities
local is_set = utilities.is_set;
local make_sep_list = utilities.make_sep_list;
local set_message = utilities.set_message;
local substitute = utilities.substitute;
local cfg = mw.loadData ('Module:CS1_identifiers/Configuration');
utilities.set_selected_modules (cfg); -- so that functions in Utilities can see the selected cfg tables
identifiers.set_selected_modules (cfg, utilities); -- so that functions in Identifiers can see the selected cfg tables and selected Utilities module
local Frame; -- local copy of <frame> from main(); nil else
local this_page = mw.title.getCurrentTitle(); -- used to limit categorization to certain namepsaces
local no_cat; -- used to limit categorization to certain namespaces
if cfg.uncategorized_namespaces[this_page.namespace] then -- is this page's namespace id one of the uncategorized namespace ids?
no_cat = true; -- set no_cat; this page will not be categorized
end
for _, v in ipairs (cfg.uncategorized_subpages) do -- cycle through page name patterns
if this_page.text:match (v) then -- test page name against each pattern
no_cat = true; -- set no_cat; this page will not be categorized
break; -- bail out if one is found
end
end
--[[--------------------------< E R R _ M E S S A G E _ C O N V E R T >----------------------------------------
converts cs1|2 error message to a message suitable for this module.
converted error messages do not name a parameter as is done in cs1|2. The help link links to the template page
not to a help-namespace page. The prefix is rewritten to name the offending template; not a cs1|2 template.
adds template specific category.
done this way because the identifier functions in Module:Citation/CS1/Identifiers create properly formatted
messages with correct html for styling.
]]
local function err_message_convert (message_prefix, messages, categories, _template)
table.sort (messages); -- sort the error messages list; sorting includes wrapping <span> and <code> tags; hidden-error sorts ahead of visible-error
for i in ipairs(messages) do
messages[i] = messages[i]:gsub (cfg.messages['help page link'] .. '#[^%|]+', substitute ('Template:$1', _template)); -- rewrite help text
end
messages[1] = messages[1]:gsub ('(%b<>)', substitute ('$1$2: ', {'%1', message_prefix}), 1); -- %1 is the opening span tag; insert <message_prefix>
for i in ipairs(categories) do
categories[i] = substitute (cfg.messages['cat wikilink'], categories[i]);
end
local category = no_cat and '' or table.concat (categories); -- limited to certain namespaces
return substitute ('$1$2', {table.concat (messages, '; '), category}); -- make a big string and done
end
--[[--------------------------< M A I N T _ M E S S A G E _ C O N V E R T >------------------------------------
converts cs1|2 maintenance message to a message suitable for this module.
converted maintenance messages have a prefix suitable for the rendered template. The 'link' text links to the
an appropriate maintenance category
]]
local function maint_message_convert (message_prefix, messages, _template)
--message_raw = message_raw:gsub ('CS1 maint: ', ''); -- strip cs1-specific prefix from cat name
local messages2 = {};
for i, message_raw in ipairs(messages) do -- カテゴリ名が'no_cat'の場合は除外する
if message_raw ~= 'no_cat' then table.insert(messages2, message_raw) end
end
if #messages2 == 0 then return '' end -- 'no_cat'しかなければ、何もしない
table.sort (messages2); -- sort the maintenance messages list
messages = {};
for i, message_raw in ipairs(messages2) do
table.insert(messages, substitute ('$1$2 ($3)', {
message_raw,
no_cat and '' or substitute (cfg.messages['cat wikilink'], message_raw),-- the category link; limited to certain namespaces
substitute (cfg.messages[':cat wikilink'], message_raw)} -- links to the maint cat, just as cs1|2 links to its maint cats
));
end
messages[1] = substitute ('$1: $2', {message_prefix, messages[1]}); -- add the template prefix
return substitute (cfg.presentation['hidden-maint'], table.concat (messages, ' ')); -- the maint message text
end
--[[--------------------------< P A R A M S _ G E T >----------------------------------------------------------
extract enumerated parameters from <args_t> where the enumerator is <i>. enumerator is always the last character
of the parameter name (doi-broken-date1 not doi1-broken-date)
special case the enumerator is 1: prefer non-enumerated parameters
returns a table of same-enumerator parameters (without enumerator)
note: <i> is a number
]]
local function params_get (args_t, i)
--mw.logObject (args_t, 'args_t')
local params_t = {}; -- selected parameters go in this table
if 1 == i then -- special case when enumerator (<i>) is 1
for k, v in pairs (args_t) do -- for each parameter
if 'number' == type (k) then -- if this is a positional parameter
if 1 == k then -- and its the first positional parameter
params_t[1] = v; -- save it
end
else -- here for named parameters
local enum = k:match ('%d+$'); -- extract the enumerator from the parameter's name; nil else
if (not enum) or (1 == tonumber (enum)) then -- when not enumerated or when enumerator is 1
k = k:gsub ('%d+$', ''); -- remove the enumerator from parameter name
params_t[k] = v; -- and save this parameter
end
end
end
else
for k, v in pairs (args_t) do -- for each parameter
if 'number' == type (k) then -- if this is a positional parameter
if i == k then -- and is the desired positional parameter
params_t[1] = v; -- save it (as index number 1; not as index <i>)
end
else -- here for named parameters
local enum = k:match ('%d+$'); -- extract the parameter's enumerator; nil else
if enum and (i == tonumber (enum)) then -- when enumerated and the enumerator is same as requested
k = k:gsub ('%d+$', ''); -- remove the enumerator from parameter name
params_t[k] = v; -- and save this parameter
end
end
end
end
--mw.logObject (params_t, 'params_t')
return params_t; -- and done
end
--[[--------------------------< R E N D E R _ F I N A L >------------------------------------------------------
this function applies cs1|2 template style sheet to a rendered identifier or error message
]]
local function render_final (output)
if not Frame then -- not set when this module called from another module
Frame = mw.getCurrentFrame(); -- get the calling module's frame so that we can call extensionTag()
end
return substitute ('$1$2', {
Frame:extensionTag ('templatestyles', '', {src='Module:Citation/CS1/styles.css'}), -- apply templatestyles
output -- to the rendered identifier or error message
});
end
--[[--------------------------< _ M A I N >--------------------------------------------------------------------
entry point when called from another module; example:
local rendered_identifier = require ('Module:CS1 identifiers')._main ({'10.4231/sommat', _template = 'doi', ['doi-access'] = 'free', ['doi-broken-date'] = 'June 2025'});
supported identifier templates are:
{{arxiv}} {{asin}} {{bibcode}} {{biorxiv}} {{citeseerx}} {{doi}}
{{hdl}} {{isbn}} {{ismn}} {{issn}} {{jfm}} {{jstor}}
{{medrxiv}} {{mr}} {{oclc}} {{ol}} {{osti}} {{pmc}}
{{pmid}} {{sbn}} {{ssrn}} {{s2cid}} {{zbl}}
<args_t> is a table of all parameters needed to properly render the identifier
]]
local function _main (args_t)
local ID_list_t = {}; -- sequence table of rendered identifiers
local ID_list_coins_t = {}; -- table of identifiers and their values from args; key is same as cfg.id_handlers's key; COinS not supported in this module
local template_name = args_t._template;
local id_name = args_t._id or template_name
local message_prefix;
if not template_name then
error ('|_template= にテンプレート名を入力してください'); -- a message for template writers; not seen by users
end
if not cfg.id_handlers[id_name:upper()] then
error ('|_id= に有効な識別子名を入力してください');
end
id_name = id_name:lower(); -- force lower case
message_prefix = substitute ('<code class="cs1-code">{{[[Template:$1|$1]]}}</code>', template_name);
local yesno = function () return nil end
if args_t.plainlink or args_t.link or args_t['no-tracking'] or args_t['template-doc-demo'] then
yesno = require('Module:Yesno'); -- plainlinkとlinkの読み取りに従来通りModule:Yesnoを使うが、必要なときのみ読み込む
for k, v in pairs {plainlink = args_t.plainlink, link = args_t.link} do -- yesnoで認識しない場合
if v and type(yesno(v)) ~= type(true) then
set_message ('err_invalid_param_val', {k, v});
end
end
if yesno(args_t['no-tracking']) or yesno(args_t['template-doc-demo']) then no_cat = true end -- 追跡カテゴリの抑制
args_t['no-tracking'] = nil;
args_t['template-doc-demo'] = nil;
end
local rendered_id; -- a single rendered id worked on here
local rendered_ids_t = {}; -- individual rendered ids go here
local i = 1; -- initialize the indexer for the repeat loop
repeat -- loop until no
local params_t = params_get (args_t, i); -- get the parameters associated with enumerator <i>
params_t[id_name] = params_t[1] or params_t['id']; -- assign value from {{{1}}} or |id= to |<identifier>=
params_t._template = nil; -- unset as no longer needed
params_t._id = nil;
params_t.plainlink = nil;
params_t.link = nil;
params_t.leadout = nil;
if not params_t[id_name] then -- in case params_t[1] and params_t.id are nil
params_t[id_name] = ''; -- set <id> to empty string
return render_final ( -- make an error message and done
substitute ('<span class="cs1-visible-error citation-comment">$1: 識別子の値を入力してください ($2)</span>$3', {
message_prefix,
substitute ('[[Template:$1|' .. cfg.messages['help page label'] .. ']]', template_name),
no_cat and '' or '[[Category:モジュール:CS1 identifiersのエラー]]'
})
);
end
do -- 重複引数
local used_params_t = {};
if args_t[i] then table.insert (used_params_t, utilities.wrap_style ('parameter', tostring(i))) end
if args_t['id' .. i] then table.insert (used_params_t, utilities.wrap_style ('parameter', 'id' .. tostring(i))) end
if i == 1 and args_t['id'] then table.insert (used_params_t, utilities.wrap_style ('parameter', 'id')) end
if #used_params_t > 1 then
set_message ('err_redundant_parameters', {make_sep_list (#used_params_t, used_params_t)});
end
end
params_t[1] = nil; -- unset these as no longer needed
params_t.id = nil;
for k, v in pairs (params_t) do -- 不明な引数
if not in_array (k, {'doi-broken-date', 'pmc-embargo-date', 'class', 'asin-tld', id_name, cfg.id_handlers[id_name:upper()].custom_access,}) then-- idに対応しない引数を指定した場合のエラーはID_supportにまかせる
set_message ('err_parameter_ignored', {k});
params_t[k] = nil;
end
end
local DoiBroken = params_t['doi-broken-date']; -- {{doi}} only
local Embargo = params_t['pmc-embargo-date']; -- {{pmc}} only
local Class = params_t['class']; -- {{arxiv}} only
local AsinTLD = params_t['asin-tld']; -- {{asin}} only
local error_list = {}; -- 日付の変換: /Date_validationは必要なときのみ読み込む
if id_name == 'doi' and is_set(DoiBroken) then
local validation = require ('Module:Citation/CS1/Date_validation');
validation.set_selected_modules (cfg, utilities);
local date_parameters_list = {['doi-broken-date'] = {val = DoiBroken, name = 'doi-broken-date'}};
validation.dates(date_parameters_list, {}, error_list);
if 0 == #error_list then
validation.reformat_dates (date_parameters_list, 'ja');
DoiBroken = date_parameters_list['doi-broken-date'].val;
end
elseif id_name == 'pmc' and is_set(Embargo) then
local validation = require ('Module:Citation/CS1/Date_validation');
local _;
validation.set_selected_modules (cfg, utilities);
_, Embargo = validation.dates({['pmc-embargo-date'] = {val = Embargo, name = 'pmc-embargo-date'}}, {}, error_list);
end
if 0 ~= #error_list then
set_message ('err_bad_date', {make_sep_list (#error_list, error_list)});
end
local ID_support = { -- エラーカテゴリは共通のものを使う
{AsinTLD, 'ASIN', 'err_parameter_ignored', 'asin-tld'},
{DoiBroken, 'DOI', 'err_parameter_ignored', 'doi-broken-date'},
{Embargo, 'PMC', 'err_parameter_ignored', 'pmc-embargo-date'},
{Class, 'ARXIV', 'err_parameter_ignored', 'class'},
}
ID_list_t, ID_list_coins_t = identifiers.identifier_lists_get (params_t, {DoiBroken = DoiBroken, Embargo = Embargo, Class = Class, ASINTLD = AsinTLD}, ID_support);
rendered_id = ID_list_t[1];
if utilities.z.error_msgs_t[1] then
rendered_id = substitute ('$1 $2', {
rendered_id,
err_message_convert (message_prefix, utilities.z.error_msgs_t, utilities.z.error_cats_t, template_name, no_cat),
});
end
if utilities.z.maint_cats_t[1] then
rendered_id = substitute ('$1 $2', {
rendered_id,
maint_message_convert (message_prefix, utilities.z.maint_cats_t, template_name, no_cat),
});
end
if in_array (id_name, {'oclc', 'osti', 'pmc', 'pmid', 'rfc', 'ssrn', 's2cid'}) then -- 識別子の上限を読み込めなかった場合
if cfg.id_limits_data_load_fail and not no_cat then -- boolean true when load failed
rendered_id = rendered_id .. substitute (cfg.messages['cat wikilink'], cfg.error_conditions.maint_id_limit_load_fail.category); -- done here because this maint cat emits no message
end
end
utilities.z.error_msgs_t = {}; -- reset these
utilities.z.error_cats_t = {};
utilities.z.maint_cats_t = {};
if (1 ~= i) or ((1 == i) and yesno(args_t.plainlink) ) then -- no label for 2nd... identifiers; when |plainlink=yes then no label for first identifier;
local separator = cfg.id_handlers[id_name:upper()].separator; -- get the identifier label separator
rendered_id = rendered_id:gsub ('^%[%[.-|.-%]%]', ''); -- strip cs1-supplied label
rendered_id = rendered_id:gsub ('^' .. separator, ''); -- and strip the label separator
elseif 1 == i then -- here for first identifier; |plainlink= not set
if false == yesno(args_t.link) then -- |link=no then do not link identifier label
rendered_id = rendered_id:gsub ('^%[%[.-|(.-)%]%]', '%1'); -- strip wikilink markup from cs1-supplied label
end
end
table.insert (rendered_ids_t, rendered_id); -- save the rendered identifier
i = i + 1; -- bump the indexer
until not (args_t[i] or args_t['id' .. i]); -- end of repeat loop
local list_separator = ', '; -- identifier separator for a list of identifiers
local leadout = args_t.leadout and mw.ustring.gsub (args_t.leadout, '^(%a.*)', ' %1 ') or nil; -- insert leading space if first character is a letter; add trailing space
if leadout then -- extra text goes between last two identifiers in the list
return render_final (mw.text.listToText (rendered_ids_t, list_separator, leadout)); -- make a list, add templatestyles, and done
end
return render_final (table.concat (rendered_ids_t, list_separator)); -- make a list, add templatestyles, and done
end
--[[--------------------------< M A I N >----------------------------------------------------------------------
entry point from an invoke
{{#invoke:CS1 identifiers|main|_template=<identifier name>}}
]]
local function main (frame)
Frame = frame; -- make a copy for rendering final output
local args_t = get_args (frame); -- extract the arguments
return _main (args_t); -- render the identifier and done
end
--[[--------------------------< F R A M E O N L Y >----------------------------------------------------------------------
entry point from an invoke
{{#invoke:CS1 identifiers|main|_template=<identifier name>|<identifier>}}
{{Citation/identifier}}で使用
]]
local function frameOnly (frame)
Frame = frame; -- make a copy for rendering final output
local args_t = get_args (frame, {frameOnly = true}); -- extract the arguments
return _main (args_t); -- render the identifier and done
end
--[[--------------------------< E X P O R T S >----------------------------------------------------------------
]]
return {
main = main, -- entry point from an invoke (in a template usually)
_main = _main, -- entry point when called from another module
frameOnly = frameOnly
}