Module:Artwork/core
From WWII Archives
Documentation for this module may be created at Module:Artwork/core/doc
--[[ __ __ _ _ _ _ _ __ | \/ | ___ __| |_ _| | ___ _ / \ _ __| |___ _____ _ __| | __ / /__ ___ _ __ ___ | |\/| |/ _ \ / _` | | | | |/ _ (_) / _ \ | '__| __\ \ /\ / / _ \| '__| |/ / / / __/ _ \| '__/ _ \ | | | | (_) | (_| | |_| | | __/_ / ___ \| | | |_ \ V V / (_) | | | < / / (_| (_) | | | __/ |_| |_|\___/ \__,_|\__,_|_|\___(_)_/ \_\_| \__| \_/\_/ \___/|_| |_|\_\/_/ \___\___/|_| \___| This submodule is intended for converting inputs into html. Please do not modify this code without applying the changes first at "Module:Artwork/sandbox" and testing at "Template: /testcases". Authors and maintainers: * User:Jarekt - original version ]] require('strict') -- used for debugging purposes as it detects cases of unintended global variables local getLabel = require("Module:Wikidata label")._getLabel -- used for creation of name based on Wikidata local core = require("Module:Core") local labels = require("Module:I18n/artwork") -- internationalization of labels local bit32 = require("bit32") local TagQS = require('Module:TagQS') local City = require("Module:City") -- used to add Wikidata based links to names of places local ISOdate = require('Module:ISOdate') -- used for simple date formating local p = {} -- Lazy loading function: load them only if they are needed local function ObjectLocation_label() return mw.loadData('Module:i18n/coordinates').ObjectLocation end local function Creator(args) return require("Module:Creator")._creator(args) end local function Institution(args) return require("Module:Institution")._institution(args) end -- ================================================== -- === Internal functions =========================== -- ================================================== ------------------------------------------------------------------------------- local function isodate2timestamp(dateStr) -- convert isodate to timestamp used by quick statements local tStamp = nil if string.match(dateStr,"^[0-1]%d%d%d$") then -- if YYYY format tStamp = '+' .. dateStr .. '-00-00T00:00:00Z/9' elseif string.match(dateStr,"^[0-1]%d%d%d%-[0-1]%d$") then -- if YYYY-MM format tStamp = '+' .. dateStr .. '-00T00:00:00Z/10' elseif string.match(dateStr,"^[0-1]%d%d%d%-[0-1]%d%-[0-3]%d$") then -- if YYYY-MM-DD format tStamp = '+' .. dateStr .. 'T00:00:00Z/11' end return tStamp end ------------------------------------------------------------------------------- local function if_else(Boolean, TrueStatement, FalseStatement) if Boolean then return TrueStatement else return FalseStatement end end ------------------------------------------------------------------------------- local function empty2nil(str) if str=='' then return nil else return str end end -- ==================================================================== -- This function is responsible for producing HTML of a single row of the template -- At this stage all the fields are already filed. There is either one or two fields -- INPUTS: -- * param1 and param2 - structures for 2 fields containing fields: -- - tag - I18n tag used for localization of the field name. Usually name of page in MediaWiki -- namespace which was imported from translatewiki.org. -- Alternative is to pass already translated field name. -- - field - field content -- - id - ID tag added to HTML's <td> cell. if IDs of 2 fields are the same than we ignore the second one -- - wrapper - some fields need a <span class=...> wrapper around the field content -- ==================================================================== local function Build_html_row(param, args) local LUT = {artwork=0, photograph=1, book=2} local demo = args.demo and bit32.extract( param.demo or 0, LUT[args.infobox])==1 local field = args[param.field] if field=='' then field=nil; end if not (field or demo) then return nil end if not param.id then -- "other fields" parameter return field end local tag = param.tag or 'bad' if string.sub(tag,1,10) == 'wm-license' then tag = mw.message.new( tag ):inLanguage(args.lang):plain() -- label message in args.lang language elseif string.match(tag, "^[QP]%d+$") then tag = getLabel(tag, args.lang, "-", "ucfirst") elseif labels[tag] then tag = core.langSwitch(labels[tag], args.lang) end local cell1 = string.format('<td id="%s" class="fileinfo-paramfield" lang="%s">%s</td>\n', param.id, args.lang, tag) local cell2 = string.format('<td>\n'.. param.wrapper ..'</td>', field or '') return string.format('<tr>\n%s%s\n</tr>\n\n', cell1, cell2) end -- ==================================================================== -- === This function is just responsible for producing HTML of the === -- === template. At this stage all the fields are already filled === -- ==================================================================== function p.build_html(args) -- get text direction local dir = if_else(mw.language.new( args.lang ):isRTL(),'rtl','ltr') -- original_description row has a different look than other rows if args.original_description and (args.original_description_info or args.biased) then local tag1, tag2 = "", "" if args.original_description_info then tag1 = string.format('<div style="background:#dde; font-size:86%%; direction:%s;">%s</div>', dir, args.original_description_info) end if args.biased then tag2 = core.langSwitch(labels.inaccurate_description, args.lang) tag2 = string.format('<div style="padding:0.5ex; margin:0 0 0.5ex 0; border: 1px solid red;">%s: %s</div>', tag2, args.biased) end args.original_description = tag1 .. tag2 .. args.original_description end -- files with no source will be flagged if (not args.source) and (not args.source_) and (args.strict==true) and (args.namespace==6) then args.nosource = mw.getCurrentFrame():expandTemplate{ title = 'Source missing' } end if args.demo or args.coordinates then labels.ObjectLocation = ObjectLocation_label() end local nCol = 2 if not args.image and args.demo then args.image = args.demo_image end if args.image then nCol = 3 end -- Top line local top, results = {}, {} if args.name then table.insert(top, string.format('<span class="fn" id="artwork"><bdi>%s\n</bdi></span>', args.name ) ) end if args.linkback then -- Wikidata Link table.insert(top, string.format('[[File:Blue pencil.svg|15px|%s|link=%s]]', args.linkback, args.linkback) ) end if args.wikidata then -- Wikidata Link table.insert(top, string.format('[[File:Wikidata-logo.svg|20px|wikidata:%s|link=wikidata:%s]]', args.wikidata, args.wikidata) ) table.insert(top, string.format('[[File:Wikidata-Reasonator_small_logo.svg|5px|reasonator:%s|link=https://reasonator.toolforge.org/test/?q=%s]]', args.wikidata, args.wikidata) ) end if args.wikisource then --Wikisource link table.insert(top, string.format('[[File:Wikisource-logo.svg|15px|%s|link=%s]]', args.wikisource, args.wikisource) ) end if args.wikiquote then --Wikiquote link table.insert(top, string.format('[[File:Wikiquote-logo.svg|15px|%s|link=%s]]', args.wikiquote, args.wikiquote) ) end if #top>0 and args.QS then -- quick_statement link to upload missing info to Wikidata (add if the row is not empty) table.insert(top, string.format('%s', args.QS) ) end if #top>0 then local line = string.format('<th colspan="%i" style="background-color:#ccf; font-weight:bold; border:1px solid #aaa" text-align="left">%s</th>', nCol, table.concat(top, ' ')) table.insert(results, string.format('<tr valign="top">\n%s\n</tr>\n', line)) end -- Permissions tag local tag1 = mw.message.new( "wm-license-information-permission" ):inLanguage(args.lang):plain() local tag2 = mw.message.new( "wm-license-information-permission-reusing-link" ):inLanguage(args.lang):plain() local tag3 = mw.message.new( "wm-license-information-permission-reusing-text" ):inLanguage(args.lang):plain() local permission_tag = string.format("%s<br /><small>([[%s|%s]])</small>", tag1, tag2, tag3) -- define constants for readability -- demo=art+photo+book will show that row in demo mode in {{artwork}, {{Photograph}} and {{Book}} templates local none, art, photo, book = 0, 1, 2, 4 -- add other fields 'author_of_foreword', 'author_of_afterword' local param = { -- field name machine readable tag field name i18n approach show in demo mode? field value wrapper {field='artist' , id='fileinfotpl_aut' , tag='wm-license-artwork-artist', demo=art, wrapper='<div class="fn value">\n%s</div>'}, {field='author' , id='fileinfotpl_aut' , tag='wm-license-information-author', demo= book, wrapper='<div class="fn value">\n%s</div>'}, {field='editor' , id='fileinfotpl_book_editor' , tag='wm-license-book-editor', demo= book, wrapper='<div class="fn value">\n%s</div>'}, {field='translator' , id='fileinfotpl_book_translator' , tag='wm-license-book-translator', demo= book, wrapper='<div class="fn value">\n%s</div>'}, {field='illustrator' , id='fileinfotpl_book_illustrator' , tag='wm-license-book-illustrator', demo= book, wrapper='<div class="fn value">\n%s</div>'}, {field='author_of_foreword' , id='fileinfotpl_aut' , tag='P2679', demo= book, wrapper='<div class="fn value">\n%s</div>'}, {field='author_of_afterword' , id='fileinfotpl_aut' , tag='P2680', demo= book, wrapper='<div class="fn value">\n%s</div>'}, {field='architect' , id='fileinfotpl_aut' , tag='Q42973', demo=none, wrapper='<div class="fn value">\n%s</div>'}, {field='designer' , id='fileinfotpl_aut' , tag='Q5322166', demo=none, wrapper='<div class="fn value">\n%s</div>'}, {field='photographer' , id='fileinfotpl_aut' , tag='Q33231', demo= photo, wrapper='<div class="fn value">\n%s</div>'}, {field='other_fields_1'}, -- title & desctiption block {field='title' , id='fileinfotpl_art_title' , tag='wm-license-artwork-title', demo=art+photo+book, wrapper='<div class="fn">\n%s</div>'}, {field='subtitle' , id='fileinfotpl_book_subtitle' , tag='wm-license-book-subtitle', demo= book, wrapper='%s'}, {field='part_of' , id='fileinfotpl_art_part_of' , tag='P361', demo=art+photo+book, wrapper='%s'}, {field='series_title' , id='fileinfotpl_book_series-title' , tag='wm-license-book-series-title', demo= book, wrapper='%s'}, {field='volume' , id='fileinfotpl_book_volume' , tag='wm-license-book-volume', demo= book, wrapper='%s'}, {field='edition' , id='fileinfotpl_edition' , tag='wm-license-book-edition', demo= book, wrapper='%s'}, {field='publisher' , id='fileinfotpl_book_publisher' , tag='wm-license-book-publisher', demo= book, wrapper='<div class="fn value">\n%s</div>'}, {field='printer' , id='fileinfotpl_book_printer' , tag='wm-license-book-printer', demo= book, wrapper='<div class="fn value">\n%s</div>'}, {field='object_type' , id='fileinfotpl_art_object_type' , tag='object_type', demo=art, wrapper='%s'}, {field='genre' , id='fileinfotpl_art_genre' , tag='Q483394', demo= book, wrapper='%s'}, {field='original_description' , id='fileinfotpl_desc' , tag='original_description', demo= photo, wrapper='<div class="description">\n%s</div>'}, {field='description' , id='fileinfotpl_desc' , tag='wm-license-information-description', demo=art+photo+book, wrapper='<div class="description">\n%s</div>'}, {field='pageoverview' , id='fileinfotpl_book-page-overview' , tag='wm-license-book-page-overview', demo=none, wrapper='%s'}, {field='depicted_people' , id='fileinfotpl_art_depicted_people' , tag='depicted_people', demo=art+photo, wrapper='%s'}, {field='depicted_place' , id='fileinfotpl_art_depicted_place' , tag='depicted_place', demo=art+photo, wrapper='%s'}, {field='depicted_part' , id='fileinfotpl_art_depicted_part' , tag='P5961', demo=art+photo+book, wrapper='%s'}, {field='language' , id='fileinfotpl_book_language' , tag='wm-license-book-language', demo= book, wrapper='%s'}, {field='other_fields_2'}, -- date, object outside description, history, etc. {field='date' , id='fileinfotpl_date' , tag='wm-license-information-date', demo=art+photo, wrapper='%s'}, {field='publication_date' , id='fileinfotpl_publication_date' , tag='P577', demo= book, wrapper='%s'}, {field='medium' , id='fileinfotpl_art_medium' , tag='wm-license-artwork-medium', demo=art+photo, wrapper='%s'}, {field='dimensions' , id='fileinfotpl_art_dimensions' , tag='wm-license-artwork-dimensions', demo=art+photo, wrapper='%s'}, {field='institution' , id='fileinfotpl_art_gallery' , tag='P195', demo=art+photo, wrapper='%s'}, {field='department' , id='fileinfotpl_art_location' , tag='wm-license-artwork-current-location', demo=art+photo , wrapper='<div class="locality">\n%s</div>'}, {field='id' , id='fileinfotpl_art_id' , tag='wm-license-artwork-id', demo=art+photo, wrapper='<div class="identifier">\n%s</div>'}, {field='coordinates' , id='fileinfo-paramfield' , tag='ObjectLocation', demo=art+photo, wrapper='%s'}, {field='place_of_publication' , id='fileinfotpl_book_place-of-publication' , tag='wm-license-book-place-of-publication', demo= book, wrapper='%s'}, {field='place_of_creation' , id='fileinfotpl_art_creation_place' , tag='place_of_creation', demo=art, wrapper='%s'}, {field='place_of_discovery' , id='fileinfotpl_art_discovery_place' , tag='place_of_discovery', demo=art, wrapper='%s'}, {field='object_history' , id='fileinfotpl_art_object_history' , tag='wm-license-artwork-object-history', demo=art, wrapper='%s'}, {field='exhibition_history' , id='fileinfotpl_art_exhibition_history' , tag='exhibition_history', demo=art+photo, wrapper='%s'}, {field='credit_line' , id='fileinfotpl_art_credit_line' , tag='wm-license-artwork-credit-line', demo=art, wrapper='%s'}, {field='inscriptions' , id='fileinfotpl_art_inscriptions' , tag='wm-license-artwork-inscriptions', demo=art, wrapper='%s'}, {field='notes' , id='fileinfotpl_art_notes' , tag='wm-license-artwork-notes', demo=art+photo, wrapper='%s'}, {field='other_fields_3'}, -- references, and sources {field='references' , id='fileinfotpl_art_references' , tag='wm-license-artwork-references', demo=art+photo+book, wrapper='%s'}, {field='authority' , id='fileinfotpl_art_authority' , tag='Q36524', demo=none, wrapper='%s'}, {field='source' , id='fileinfotpl_src' , tag='wm-license-artwork-source', demo=art, wrapper='%s'}, -- source/photographer {field='source_' , id='fileinfotpl_src' , tag='wm-license-information-source', demo= photo+book, wrapper='%s'}, -- source {field='nosource' , id='fileinfotpl_nosrc' , tag='wm-license-information-source', demo=none, wrapper='%s'}, {field='permission' , id='fileinfotpl_perm' , tag=permission_tag, demo=art+photo+book, wrapper='%s'}, {field='other_versions' , id='fileinfotpl_ver' , tag='wm-license-information-other-versions', demo=art+photo+book, wrapper='%s'}, {field='other_fields'}, {field='camera_coord'}, } for i=1,#param do table.insert(results, Build_html_row(param[i], args)) end -- add material on the right: image, wikisource icon, etc. if args.image then if args.image_page and args.image then -- page parameter for DjVu and PDF files args.image = string.format('%s|page=%i', args.image, args.image_page) end if args.infobox=='book' then -- page parameter for DjVu and PDF files tag1 = mw.message.new( 'wm-license-book-start-this-book' ):inLanguage(args.lang):plain() tag2 = string.format('|thumb|[[:File:%s|%s]]', args.image, tag1) else tag2 = '' end local field = string.format('[[File:%s|250x250px|alt=image of artwork listed in title parameter on this page|class=photo%s]]', args.image, args.name or '', tag2) local nRow = #results -- number of rows below local line = string.format('<td rowspan="%i" style="width:200px; text-align: right;" id="fileinfotpl_creator_image"><div class="wpImageAnnotatorControl wpImageAnnotatorOff">%s</div></td></tr>\n\n', nRow, field) results[2] = mw.ustring.gsub(results[2], "</tr>%s*$", line); -- attach image section to the right side of the table, by attaching to row #2 end local templatestyles = mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Module:Information/styles.css' } } -- add table and outer layers local style = string.format('class="fileinfotpl-type-artwork vevent mw-content-%s" dir="%s"', dir, dir) results = string.format('<table %s>\n%s\n</table>\n', style, table.concat(results)) -- combine "results", an array of strings into a single string results = string.format('<div class="hproduct commons-file-information-table">\n%s\n%s\n</div>', templatestyles, results) return results end -- =========================================================================== -- === Read input "frame", normalize input parameters (lower case, etc.) === -- === and resolve potential aliases === -- === INPUTS: === -- === * frame - contains input parameters passed from the template === -- === OUTPUTS: === -- === * args - cleaned up inputs === -- =========================================================================== function p.read_input_parameters(frame) -- switch to lowercase parameters to make them case independent local args = core.getArgs(frame) -- resolve aliases args.permission = args.permission or args.license args.medium = args.medium or args.technique args.date = args.date or args.year args.department = args.department or args.location args.id = args.accession_number or args.id args.object_type = args.object_type or args.type args.dimensions = args.dimensions or args.size args.object_history = args.object_history or args.history args.coordinates = args.coordinates or args.object_location args.institution = args.institution or args.gallery or args.museum args.place_of_creation = args.place_of_creation or args.place_of_origin or args.country args.original_description = args.original_description or args.original_caption -- remove unneeded parameters args.technique, args.year, args.size, args.gallery = nil, nil, nil, nil args.location, args.type, args.museum, args.accession_number = nil, nil, nil, nil args.place_of_origin, args.country, args.history, args.license = nil, nil, nil, nil args.object_location, args.original_caption = nil, nil -- ensure the right format args.wikidata_cat = core.yesno(args.wikidata_cat, true) args.strict = core.yesno(args.strict, true) args.noimage = core.yesno(args.noimage, false) args.no_qs = core.yesno(args.no_qs, false) args.no_sdc = core.yesno(args.no_sdc, false) args.image_page = tonumber(args.image_page) if args.language and #args.language==2 then args.language = frame:callParserFunction( "#language", { args.language, args.lang } ) -- get language of the written work end return args end -- =========================================================================== function p.verify_input_parameters(args) local cats = '' -- categories -- add [[Category:Creator templates with unknown parameter]] category, if some parameter not on the following list is used local fields = { 'title', 'object_type', 'description', 'date', 'medium', 'permission', 'artist', 'author', 'architect', 'designer', 'illustrator', 'publisher', 'editor', 'translator', 'printer', 'photographer', 'dimensions', 'institution', 'department', 'references', 'object_history', 'genre', 'exhibition_history', 'credit_line', 'other_versions', 'source', 'strict', 'inscriptions', 'notes', 'linkback', 'camera_coord', 'other_fields', 'other_fields_1', 'other_fields_2', 'other_fields_3', 'demo', 'id', 'wikidata', 'year', 'homecat', 'authority', 'place_of_creation', 'place_of_discovery', 'source_', 'wikidata_cat', 'namespace', 'lang', 'image', 'noimage', 'depicted_people', 'depicted_place', 'original_description_info', 'original_description', 'biased', 'photo_date', 'infobox', 'place_of_publication', 'publication_date', 'language', 'subtitle', 'series_title', 'volume', 'edition', 'edition_of', 'pageoverview', 'wikisource', 'wikiquote', 'demo_image', 'image_page', 'depicted_part', 'mimeType', 'num_pages', 'author_of_foreword', 'author_of_afterword', 'infobox', 'no_qs', 'no_sdc', 'part_of' } local set = {} for _, field in ipairs(fields) do set[field] = true end for field, _ in pairs( args ) do if not set[field] then local LUT = {artwork='Artwork', photograph='Photograph', book='Book'} local infobox = LUT[args.infobox] cats = cats .. '[[Category:Pages using ' .. infobox .. ' template with incorrect parameter]]' cats = cats .. string.format('\n;<span style="color:red">Error in [[Template:%s|{{%s}} template]]: unknown parameter "%s".</span>', infobox, infobox, field) end end return cats end -- =========================================================================== function p.clean_input_parameters(args) local lang = args.lang -- user's language -- === Step 1: clean up of template arguments "args" local page = mw.title.getCurrentTitle() args.namespace = page.namespace -- get page namespace args.url = page:canonicalUrl() args.pagename = page.text if args.namespace==6 then -- file namespace args.mimeType = page.file.mimeType args.num_pages = 1 if page.file.pages then args.num_pages = #page.file.pages -- in case of DjVu or PDF files count pages end end if args.date then args.year = empty2nil(ISOdate._ISOyear(args.date)) -- get year end -- for places run them through {{City}} template local fields = { 'depicted_people', 'depicted_place', 'place_of_discovery', 'part_of' } for _, field in ipairs( fields ) do if args[field] and not string.find(args[field], ' ') then args[field] = City._city(args[field], lang) -- single word depicted_people will get a link end end -- for dates run them through {{ISOdate}} template and add invisible QS tag if possible local fields = { 'date', 'publication_date'} for _, field in ipairs( fields ) do if args[field] then local val = isodate2timestamp(args[field]) -- if date is in YYYY, YYYY-MM or YYYY-MM-DD formats than it will be saved args[field] = ISOdate._ISOdate(args[field], lang) -- apply ISODate to function to date string to convert date in ISO format to translated date string if val then -- if date is in ISO format than add an invisible tag which will be used to potentially add this date to QS used to move it to Wikidata args[field] = args[field] .. TagQS.createTag('date', nil, val) end end end -- collapse local {{Creator}} and {{Institution}} templates and extract item ID from them local fields = {author='creator', artist='creator', photographer='creator', architect='creator', printer='creator', designer='creator', editor='creator', translator='creator', illustrator='creator', institution='institution'} for field, keyword in pairs( fields ) do if args[field] then if string.match(args[field], "^Q%d+$") and keyword=='creator' then -- this is wikidata item args[field..'_id'] = args[field] if keyword=='creator' then args[field] = Creator({wikidata=args[field], lang=lang, collapse=1})-- create creator based on item id elseif keyword=='institution' then args[field] = Institution({wikidata=args[field], lang=lang, collapse=1})-- create institution based on item id end else -- collapse local {{Creator}} and {{Institution}} templates args[field] = mw.ustring.gsub (args[field], 'table class="toccolours collapsible%s*"', 'table class="toccolours collapsible collapsed"') -- extract item ID: retrieve the tag and grab the second component local v = mw.text.split( TagQS.readTag(args[field], keyword) or '', '|', true ) if v and #v>=2 then args[field..'_id'] = v[2] end end end end -- in case of invisible QS tags add correct property based on which field and infobox it come from local repList = { {'author', 'book', 'creator', 'P170', 'P50' }, {'artist', 'artwork', 'creator', 'P170', 'P170' }, {'illustrator', 'book', 'creator', 'P170', 'P110'}, {'editor', 'book', 'creator', 'P170', 'P98' }, {'translator', 'book', 'creator', 'P170', 'P655'}, {'printer', 'book', 'creator', 'P170', 'P872'}, {'publication_date', 'book', 'date', nil, 'P577'}, {'date', 'photograph', 'date', nil, 'P571'}, {'date', 'artwork', 'date', nil, 'P571'}} for _, repItem in ipairs( repList ) do local field, infobox, tag, oldP, newP = unpack(repItem) if args[field] and args.infobox==infobox then args[field] = TagQS.changeProperty(args[field], tag, oldP, newP) args[field] = TagQS.changeField(args[field], tag, field) end end if args.source and mw.ustring.find( args.source, 'www%.wga%.hu' ) then -- code to help copy links to www.wga.hu to Wikidata args.reference_wga = string.gsub(args.source, 'http://www%.wga%.hu', 'https://www.wga.hu') end return args end -- =========================================================================== function p.test(frame) local args = p.read_input_parameters(frame) args.infobox = 'artwork' local cats0 = p.verify_input_parameters(args) args = p.clean_input_parameters(args) return p.build_html(args) end return p