Diff: Module:Check isxn
Comparing revision #1 (2022-11-03 23:27:58) with revision #2 (2023-02-03 04:17:56).
| Old | New |
|---|---|
--[[ |
--[[ |
This code is derived from the ISXN validation code at Module:Citation/CS1. It allows validating ISBN, |
This code is derived from the ISXN validation code at Module:Citation/CS1. It allows validating ISBN, |
ISMN, and ISSN without invoking a citation template. |
ISMN, and ISSN without invoking a citation template. |
]] |
]] |
local p = {} |
local p = {} |
--[[--------------------------< E R R _ M S G _ S U P L _ T >-------------------------------------------------- |
--[[--------------------------< E R R _ M S G _ S U P L _ T >-------------------------------------------------- |
error message supplements for check_isbn(); adapted from a similarly named table at Module:Citation/CS1/Configuration |
error message supplements for check_isbn(); adapted from a similarly named table at Module:Citation/CS1/Configuration |
]] |
]] |
local err_msg_supl_t = { |
local err_msg_supl_t = { |
['char'] = 'invalid character', |
['char'] = 'invalid character', |
['check'] = 'checksum', |
['check'] = 'checksum', |
['form'] = 'invalid form', |
['form'] = 'invalid form', |
['group'] = 'invalid group id', |
['group'] = 'invalid group id', |
['length'] = 'length', |
['length'] = 'length', |
['prefix'] = 'invalid prefix', |
['prefix'] = 'invalid prefix', |
} |
} |
--[[--------------------------< IS _ V A L I D _ I S X N >----------------------------------------------------- |
--[[--------------------------< IS _ V A L I D _ I S X N >----------------------------------------------------- |
ISBN-10 and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in check_isbn(). |
ISBN-10 and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in check_isbn(). |
If the number is valid the result will be 0. Before calling this function, issbn/issn must be checked for length and stripped of dashes, |
If the number is valid the result will be 0. Before calling this function, issbn/issn must be checked for length and stripped of dashes, |
spaces and other non-isxn characters. |
spaces and other non-isxn characters. |
]] |
]] |
local function is_valid_isxn (isxn_str, len) |
local function is_valid_isxn (isxn_str, len) |
local temp = 0; |
local temp = 0; |
isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58 |
isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58 |
len = len+1; -- adjust to be a loop counter |
len = len+1; -- adjust to be a loop counter |
for i, v in ipairs( isxn_str ) do -- loop through all of the bytes and calculate the checksum |
for i, v in ipairs( isxn_str ) do -- loop through all of the bytes and calculate the checksum |
if v == string.byte( "X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58) |
if v == string.byte( "X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58) |
temp = temp + 10*( len - i ); -- it represents 10 decimal |
temp = temp + 10*( len - i ); -- it represents 10 decimal |
else |
else |
temp = temp + tonumber( string.char(v) )*(len-i); |
temp = temp + tonumber( string.char(v) )*(len-i); |
end |
end |
end |
end |
return temp % 11 == 0; -- returns true if calculation result is zero |
return temp % 11 == 0; -- returns true if calculation result is zero |
end |
end |
--[[--------------------------< IS _ V A L I D _ I S X N _ 1 3 >---------------------------------------------- |
--[[--------------------------< IS _ V A L I D _ I S X N _ 1 3 >---------------------------------------------- |
ISBN-13 and ISMN validator code calculates checksum across all 13 isbn/ismn digits including the check digit. |
ISBN-13 and ISMN validator code calculates checksum across all 13 isbn/ismn digits including the check digit. |
If the number is valid, the result will be 0. Before calling this function, isbn-13/ismn must be checked for length |
If the number is valid, the result will be 0. Before calling this function, isbn-13/ismn must be checked for length |
and stripped of dashes, spaces and other non-isxn-13 characters. |
and stripped of dashes, spaces and other non-isxn-13 characters. |
]] |
]] |
local function is_valid_isxn_13 (isxn_str) |
local function is_valid_isxn_13 (isxn_str) |
local temp=0; |
local temp=0; |
isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39 |
isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39 |
for i, v in ipairs( isxn_str ) do |
for i, v in ipairs( isxn_str ) do |
temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit |
temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit |
end |
end |
return temp % 10 == 0; -- sum modulo 10 is zero when isbn-13/ismn is correct |
return temp % 10 == 0; -- sum modulo 10 is zero when isbn-13/ismn is correct |
end |
end |
--[[--------------------------< C H E C K _ I S B N >------------------------------------------------------------ |
--[[--------------------------< C H E C K _ I S B N >------------------------------------------------------------ |
Determines whether an ISBN string is valid. Implements an ISBN validity check for {{ISBN}}, {{ISBNT}}, {{SBN}}, and |
Determines whether an ISBN string is valid. Implements an ISBN validity check for {{ISBN}}, {{ISBNT}}, {{SBN}}, and |
{{Format ISBN}}. |
{{Format ISBN}}. |
]] |
]] |
local function check_isbn (isbn_str, frame) |
local function check_isbn (isbn_str, frame) |
local function return_result (check, err_type) -- local function to render the various error returns |
local function return_result (check, err_type) -- local function to render the various error returns |
if not check then -- <check> false when there is an error |
if not check then -- <check> false when there is an error |
local template = ((frame.args.template_name and '' ~= frame.args.template_name) and frame.args.template_name) or nil; -- get template name |
local template = ((frame.args.template_name and '' ~= frame.args.template_name) and frame.args.template_name) or nil; -- get template name |
if not template then |
if not template then |
return '<span class="error" style="font-size:100%"> calling template requires template_name parameter</span>'; |
return '<span class="error" style="font-size:100%"> calling template requires template_name parameter</span>'; |
end |
end |
local out_t = {'<span class="error" style="font-size:100%">'}; -- open the error message span |
local out_t = {'<span class="error" style="font-size:100%">'}; -- open the error message span |
table.insert (out_t, ' Parameter error in {{[[Template:'); -- open 'template' markup; open wikilink with Template namespace |
table.insert (out_t, ' Parameter error in {{[[Template:'); -- open 'template' markup; open wikilink with Template namespace |
table.insert (out_t, template); -- template name wikilink |
table.insert (out_t, template); -- template name wikilink |
table.insert (out_t, '|'); -- its pipe |
table.insert (out_t, '|'); -- its pipe |
table.insert (out_t, template); -- wikilink label |
table.insert (out_t, template); -- wikilink label |
table.insert (out_t, ']]}}: '); -- close wikilink; close 'template' markup |
table.insert (out_t, ']]}}: '); -- close wikilink; close 'template' markup |
table.insert (out_t, err_type); -- type of isbn error |
table.insert (out_t, err_type); -- type of isbn error |
table.insert (out_t, '</span>') -- close the error message span |
table.insert (out_t, '</span>') -- close the error message span |
if 0 == mw.title.getCurrentTitle().namespace then -- categorize only when this template is used in mainspace |
if 0 == mw.title.getCurrentTitle().namespace then -- categorize only when this template is used in mainspace |
local category = table.concat ({'[[Category:Pages with ISBN errors]]'}); |
local category = table.concat ({'[[Category:Pages with ISBN errors]]'}); |
table.insert (out_t, category); |
table.insert (out_t, category); |
end |
end |
return table.concat (out_t); -- make a big string and done |
return table.concat (out_t); -- make a big string and done |
end |
end |
return ''; -- no error, return an empty string |
return ''; -- no error, return an empty string |
end |
end |
if nil ~= isbn_str:match ('[^%s-0-9X]') then |
if nil ~= isbn_str:match ('[^%s-0-9X]') then |
return return_result (false, err_msg_supl_t.char); -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X |
return return_result (false, err_msg_supl_t.char); -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X |
end |
end |
local id = isbn_str:gsub ('[%s-]', ''); -- remove hyphens and whitespace |
local id = isbn_str:gsub ('[%s-]', ''); -- remove hyphens and whitespace |
local len = id:len(); |
local len = id:len(); |
if len ~= 10 and len ~= 13 then |
if len ~= 10 and len ~= 13 then |
return return_result (false, err_msg_supl_t.length); -- fail if incorrect length |
return return_result (false, err_msg_supl_t.length); -- fail if incorrect length |
end |
end |
if len == 10 then |
if len == 10 then |
if id:match ('^%d*X?$') == nil then -- fail if isbn_str has 'X' anywhere but last position |
if id:match ('^%d*X?$') == nil then -- fail if isbn_str has 'X' anywhere but last position |
return return_result (false, err_msg_supl_t.form); |
return return_result (false, err_msg_supl_t.form); |
end |
end |
if id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not valid isbn group ids but are used by amazon as numeric identifiers (asin) |
if id:find ('^63[01]') then -- 630xxxxxxx and 631xxxxxxx are (apparently) not valid isbn group ids but are used by amazon as numeric identifiers (asin) |
return return_result (false, err_msg_supl_t.group); -- fail if isbn-10 begins with 630/1 |
return return_result (false, err_msg_supl_t.group); -- fail if isbn-10 begins with 630/1 |
end |
end |
return return_result (is_valid_isxn (id, 10), err_msg_supl_t.check); -- pass if isbn-10 is numerically valid (checksum) |
return return_result (is_valid_isxn (id, 10), err_msg_supl_t.check); -- pass if isbn-10 is numerically valid (checksum) |
else |
else |
if id:match ('^%d+$') == nil then |
if id:match ('^%d+$') == nil then |
return return_result (false, err_msg_supl_t.char); -- fail if ISBN-13 is not all digits |
return return_result (false, err_msg_supl_t.char); -- fail if ISBN-13 is not all digits |
end |
end |
if id:match ('^97[89]%d*$') == nil then |
if id:match ('^97[89]%d*$') == nil then |
return return_result (false, err_msg_supl_t.prefix); -- fail when ISBN-13 does not begin with 978 or 979 |
return return_result (false, err_msg_supl_t.prefix); -- fail when ISBN-13 does not begin with 978 or 979 |
end |
end |
if id:match ('^9790') then |
if id:match ('^9790') then |
return return_result (false, err_msg_supl_t.group); -- group identifier '0' is reserved to ISMN |
return return_result (false, err_msg_supl_t.group); -- group identifier '0' is reserved to ISMN |
end |
end |
return return_result (is_valid_isxn_13 (id), err_msg_supl_t.check); -- pass if isbn-10 is numerically valid (checksum) |
return return_result (is_valid_isxn_13 (id), err_msg_supl_t.check); -- pass if isbn-10 is numerically valid (checksum) |
end |
end |
end |
end |
--[[--------------------------< C H E C K _ I S M N >------------------------------------------------------------ |
--[[--------------------------< C H E C K _ I S M N >------------------------------------------------------------ |
Determines whether an ISMN string is valid. Similar to isbn-13, ismn is 13 digits begining 979-0-... and uses the |
Determines whether an ISMN string is valid. Similar to isbn-13, ismn is 13 digits begining 979-0-... and uses the |
same check digit calculations. See http://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf |
same check digit calculations. See http://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf |
section 2, pages 9–12. |
section 2, pages 9–12. |
]] |
]] |
local function check_ismn (id, error_string) |
local function check_ismn (id, error_string) |
local text; |
local text; |
local valid_ismn = true; |
local valid_ismn = true; |
id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the ismn |
id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the ismn |
if 13 ~= id:len() or id:match( "^9790%d*$" ) == nil then -- ismn must be 13 digits and begin 9790 |
if 13 ~= id:len() or id:match( "^9790%d*$" ) == nil then -- ismn must be 13 digits and begin 9790 |
valid_ismn = false; |
valid_ismn = false; |
else |
else |
valid_ismn=is_valid_isxn_13 (id); -- validate ismn |
valid_ismn=is_valid_isxn_13 (id); -- validate ismn |
end |
end |
return valid_ismn and '' or error_string |
return valid_ismn and '' or error_string |
end |
end |
--[[--------------------------< I S S N >---------------------------------------------------------------------- |
--[[--------------------------< I S S N >---------------------------------------------------------------------- |
Validate and format an issn. This code fixes the case where an editor has included an ISSN in the citation but has separated the two groups of four |
Validate and format an issn. This code fixes the case where an editor has included an ISSN in the citation but has separated the two groups of four |
digits with a space. When that condition occurred, the resulting link looked like this: |
digits with a space. When that condition occurred, the resulting link looked like this: |
|issn=0819 4327 gives: [http://www.worldcat.org/issn/0819 4327 0819 4327] -- can't have spaces in an external link |
|issn=0819 4327 gives: [http://www.worldcat.org/issn/0819 4327 0819 4327] -- can't have spaces in an external link |
This code now prevents that by inserting a hyphen at the issn midpoint. It also validates the issn for length and makes sure that the checkdigit agrees |
This code now prevents that by inserting a hyphen at the issn midpoint. It also validates the issn for length and makes sure that the checkdigit agrees |
with the calculated value. Incorrect length (8 digits), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check issn |
with the calculated value. Incorrect length (8 digits), characters other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check issn |
error message. |
error message. |
]] |
]] |
local function check_issn(id, error_string) |
local function check_issn(id, error_string) |
local issn_copy = id; -- save a copy of unadulterated issn; use this version for display if issn does not validate |
local issn_copy = id; -- save a copy of unadulterated issn; use this version for display if issn does not validate |
local text; |
local text; |
local valid_issn = true; |
local valid_issn = true; |
if not id:match ('^%d%d%d%d%-%d%d%d[%dX]$') then |
if not id:match ('^%d%d%d%d%-%d%d%d[%dX]$') then |
return error_string; |
return error_string; |
end |
end |
id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the issn |
id=id:gsub( "[%s-–]", "" ); -- strip spaces, hyphens, and endashes from the issn |
if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 digits long, containing only 0-9 or X in the last position |
if 8 ~= id:len() or nil == id:match( "^%d*X?$" ) then -- validate the issn: 8 digits long, containing only 0-9 or X in the last position |
valid_issn=false; -- wrong length or improper character |
valid_issn=false; -- wrong length or improper character |
else |
else |
valid_issn=is_valid_isxn(id, 8); -- validate issn |
valid_issn=is_valid_isxn(id, 8); -- validate issn |
end |
end |
return valid_issn and '' or error_string |
return valid_issn and '' or error_string |
end |
end |
------------------------------< E N T R Y P O I N T S >--------------------------------------------------==== |
------------------------------< E N T R Y P O I N T S >--------------------------------------------------==== |
function p.check_isbn(frame) |
function p.check_isbn(frame) |
return check_isbn(frame.args[1] or frame:getParent().args[1], frame) |
return check_isbn(frame.args[1] or frame:getParent().args[1], frame) |
end |
end |
function p.check_ismn(frame) |
function p.check_ismn(frame) |
return check_ismn(frame.args[1] or frame:getParent().args[1], frame.args['error'] or frame:getParent().args['error'] or 'error') |
return check_ismn(frame.args[1] or frame:getParent().args[1], frame.args['error'] or frame:getParent().args['error'] or 'error') |
end |
end |
function p.check_issn(frame) |
function p.check_issn(frame) |
return check_issn(frame.args[1] or frame:getParent().args[1], frame.args['error'] or frame:getParent().args['error'] or 'error') |
return check_issn(frame.args[1] or frame:getParent().args[1], frame.args['error'] or frame:getParent().args['error'] or 'error') |
end |
end |
return p |
return p |