From 8c55d2406a89bd873f6c20b329dddb5c1033b7cc Mon Sep 17 00:00:00 2001 From: gnosygnu Date: Mon, 19 Aug 2019 22:21:22 -0400 Subject: [PATCH] Wikibase: Fix 'Module:Infobox/Cycliste:218 attempt to call nil' on many fr.w pages [#551] --- .../libs/Scrib_lib_wikibase_entity.java | 93 ++++++++-- .../scribunto/libs/wikibases/Entity__tst.java | 10 +- .../LuaCommon/lualib/mw.wikibase.entity.lua | 173 +++++++++++++++--- 3 files changed, 239 insertions(+), 37 deletions(-) diff --git a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase_entity.java b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase_entity.java index c511df1ae..d211fea99 100644 --- a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase_entity.java +++ b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase_entity.java @@ -18,10 +18,10 @@ import gplx.xowa.xtns.wbases.*; import gplx.langs.jsons.*; import gplx.xowa.xtns.wbases.core.*; import gplx.xowa.xtns.wbases.claims.*; import gplx.xowa.xtns.wbases.stores.*; import gplx.xowa.xtns.scribunto.procs.*; -public class Scrib_lib_wikibase_entity implements Scrib_lib { +public class Scrib_lib_wikibase_entity implements Scrib_lib { // REF.MW:https://github.com/wikimedia/mediawiki-extensions-Wikibase/blob/master/client/includes/DataAccess/Scribunto/Scribunto_LuaWikibaseEntityLibrary.php public Scrib_lib_wikibase_entity(Scrib_core core) {this.core = core;} private Scrib_core core; public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod; - public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;} + public Scrib_lib Init() {procs.Init_by_lib(this, Proc__names); return this;} public Scrib_lib Clone_lib(Scrib_core core) {return new Scrib_lib_wikibase_entity(core);} public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) { Init(); @@ -31,25 +31,30 @@ public class Scrib_lib_wikibase_entity implements Scrib_lib { public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr(); public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) { switch (key) { - case Proc_getGlobalSiteId: return GetGlobalSiteId(args, rslt); - case Proc_getLanguageCode: return GetLanguageCode(args, rslt); -// case Proc_formatStatements: return FormatStatements(args, rslt); - case Proc_formatPropertyValues: return FormatPropertyValues(args, rslt); + case Proc__getGlobalSiteId: return GetGlobalSiteId(args, rslt); + case Proc__getLanguageCode: return GetLanguageCode(args, rslt); + case Proc__formatStatements: return FormatStatements(args, rslt); + case Proc__formatPropertyValues: return FormatPropertyValues(args, rslt); + case Proc__addStatementUsage: return AddStatementUsage(args, rslt); + case Proc__addLabelUsage: return AddLabelUsage(args, rslt); + case Proc__addDescriptionUsage: return AddDescriptionUsage(args, rslt); + case Proc__addSiteLinksUsage: return AddSiteLinksUsage(args, rslt); + case Proc__addOtherUsage: return AddOtherUsage(args, rslt); + case Proc__getSetting: return GetSetting(args, rslt); + case Proc__incrementStatsKey: return IncrementStatsKey(args, rslt); default: throw Err_.new_unhandled(key); } } - private static final int Proc_getGlobalSiteId = 0, Proc_getLanguageCode = 1, Proc_formatPropertyValues = 2; - public static final String Invk_getGlobalSiteId = "getGlobalSiteId", Invk_getLanguageCode = "getLanguageCode", Invk_formatPropertyValues = "formatPropertyValues"; - private static final String[] Proc_names = String_.Ary(Invk_getGlobalSiteId, Invk_getLanguageCode, Invk_formatPropertyValues); public boolean GetGlobalSiteId(Scrib_proc_args args, Scrib_proc_rslt rslt) { return rslt.Init_obj(core.Wiki().Domain_abrv()); // ;siteGlobalID: This site's global ID (e.g. 'itwiki'), as used in the sites table. Default: $wgDBname.; REF:/xtns/Wikibase/docs/options.wiki } public boolean GetLanguageCode(Scrib_proc_args args, Scrib_proc_rslt rslt) { return rslt.Init_obj(core.Wiki().Lang().Key_bry()); } -// public boolean FormatStatements(Scrib_proc_args args, Scrib_proc_rslt rslt) { + public boolean FormatStatements(Scrib_proc_args args, Scrib_proc_rslt rslt) { + throw Err_.new_unimplemented(); // return FormatPropertyValues(args, rslt); // NOTE: implementation should be like Visit_entity but return [[A]] instead of -// } + } public boolean FormatPropertyValues(Scrib_proc_args args, Scrib_proc_rslt rslt) { // get qid / pid byte[] qid = args.Pull_bry(0); @@ -82,4 +87,70 @@ public class Scrib_lib_wikibase_entity implements Scrib_lib { wdata_mgr.Resolve_to_bfr(bfr, prop_grp, lang, Bool_.N); return rslt.Init_obj(bfr.To_bry_and_rls()); } + public boolean AddStatementUsage(Scrib_proc_args args, Scrib_proc_rslt rslt) { + return rslt.Init_null(); + } + public boolean AddLabelUsage(Scrib_proc_args args, Scrib_proc_rslt rslt) { + return rslt.Init_null(); + } + public boolean AddDescriptionUsage(Scrib_proc_args args, Scrib_proc_rslt rslt) { + return rslt.Init_null(); + } + public boolean AddSiteLinksUsage(Scrib_proc_args args, Scrib_proc_rslt rslt) { + return rslt.Init_null(); + } + public boolean AddOtherUsage(Scrib_proc_args args, Scrib_proc_rslt rslt) { + return rslt.Init_null(); + } + public boolean GetSetting(Scrib_proc_args args, Scrib_proc_rslt rslt) { + String key = args.Cast_str_or(0, ""); + if (String_.Eq(key, "fineGrainedLuaTracking")) {// REF.MW: https://gerrit.wikimedia.org/r/#/c/operations/mediawiki-config/+/412664/3/wmf-config/InitialiseSettings.php + return rslt.Init_obj(false); + } + else { + throw Err_.new_unimplemented(); + } + } + public boolean IncrementStatsKey(Scrib_proc_args args, Scrib_proc_rslt rslt) { + return rslt.Init_null(); + } + private static final int + Proc__getGlobalSiteId = 0 + , Proc__getLanguageCode = 1 + , Proc__formatStatements = 2 + , Proc__formatPropertyValues = 3 + , Proc__addStatementUsage = 4 + , Proc__addLabelUsage = 5 + , Proc__addDescriptionUsage = 6 + , Proc__addSiteLinksUsage = 7 + , Proc__addOtherUsage = 8 + , Proc__getSetting = 9 + , Proc__incrementStatsKey = 10 + ; + public static final String + Invk__getGlobalSiteId = "getGlobalSiteId" + , Invk__getLanguageCode = "getLanguageCode" + , Invk__formatStatements = "formatStatements" + , Invk__formatPropertyValues = "formatPropertyValues" + , Invk__addStatmentUsage = "addStatementUsage" + , Invk__addLabelUsage = "addLabelUsage" + , Invk__addDescriptionUsage = "addDescriptionUsage" + , Invk__addSiteLinkUsage = "addSiteLinkUsage" + , Invk__addOtherUsage = "addOtherUsage" + , Invk__getSetting = "getSetting" + , Invk__incrementStatsKey = "incrementStatsKey" + ; + private static final String[] Proc__names = String_.Ary + ( Invk__getGlobalSiteId + , Invk__getLanguageCode + , Invk__formatStatements + , Invk__formatPropertyValues + , Invk__addStatmentUsage + , Invk__addLabelUsage + , Invk__addDescriptionUsage + , Invk__addSiteLinkUsage + , Invk__addOtherUsage + , Invk__getSetting + , Invk__incrementStatsKey + ); } diff --git a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/wikibases/Entity__tst.java b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/wikibases/Entity__tst.java index 32271abd4..f1965433c 100644 --- a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/wikibases/Entity__tst.java +++ b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/wikibases/Entity__tst.java @@ -22,23 +22,23 @@ public class Entity__tst { lib = fxt.Core().Lib_wikibase_entity().Init(); } private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib; @Test public void GetGlobalSiteId() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk_getGlobalSiteId, Object_.Ary_empty, "enwiki"); + fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk__getGlobalSiteId, Object_.Ary_empty, "enwiki"); } @Test public void GetLanguageCode() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk_getLanguageCode, Object_.Ary_empty, "en"); + fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk__getLanguageCode, Object_.Ary_empty, "en"); } @Test public void FormatPropertyValues() { Wdata_wiki_mgr_fxt wdata_fxt = new Wdata_wiki_mgr_fxt().Init(fxt.Parser_fxt(), false); // lookup by id wdata_fxt.Init__docs__add(wdata_fxt.Wdoc_bldr("Q2").Add_claims(wdata_fxt.Make_claim_string(3, "P3_val")).Xto_wdoc()); - fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk_formatPropertyValues, Object_.Ary("Q2", "P3") , "P3_val"); + fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk__formatPropertyValues, Object_.Ary("Q2", "P3") , "P3_val"); // lookup by name wdata_fxt.Init_pids_add("en", "P3_val", 3); - fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk_formatPropertyValues, Object_.Ary("Q2", "P3_val") , "P3_val"); + fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk__formatPropertyValues, Object_.Ary("Q2", "P3_val") , "P3_val"); } @Test public void FormatPropertyValues__not_found() { // PURPOSE: should return "" not null; PAGE:fr.s:Auteur:Henri_Bergson; DATE:2014-08-13 - fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk_formatPropertyValues, Object_.Ary("Q2", "P3"), ""); + fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk__formatPropertyValues, Object_.Ary("Q2", "P3"), ""); } } diff --git a/res/bin/any/xowa/xtns/Scribunto/engines/LuaCommon/lualib/mw.wikibase.entity.lua b/res/bin/any/xowa/xtns/Scribunto/engines/LuaCommon/lualib/mw.wikibase.entity.lua index 8adb6678b..07fdb09e3 100644 --- a/res/bin/any/xowa/xtns/Scribunto/engines/LuaCommon/lualib/mw.wikibase.entity.lua +++ b/res/bin/any/xowa/xtns/Scribunto/engines/LuaCommon/lualib/mw.wikibase.entity.lua @@ -1,4 +1,4 @@ ---XOWA:updated 2017-03-16 +--XOWA:updated 2019-08-19 --[[ Registers and defines functions to handle Wikibase Entities through the Scribunto extension. @@ -10,7 +10,8 @@ ]] local php = mw_interface -local entity = {} +mw_interface = nil +local Entity = {} local metatable = {} local methodtable = {} local util = require 'libraryUtil' @@ -20,31 +21,124 @@ local checkTypeMulti = util.checkTypeMulti metatable.__index = methodtable -- Claim ranks (Claim::RANK_* in PHP) -entity.claimRanks = { +Entity.claimRanks = { RANK_TRUTH = 3, RANK_PREFERRED = 2, RANK_NORMAL = 1, RANK_DEPRECATED = 0 } +-- Is this a valid property id (Pnnn)? +-- +-- @param {string} propertyId +local isValidPropertyId = function( propertyId ) + return type( propertyId ) == 'string' and propertyId:match( '^P[1-9]%d*$' ) +end + +-- Log access to claims of entity +-- +-- @param {string} entityId +-- @param {string} propertyId +local addStatementUsage = function( entityId, propertyId ) + if isValidPropertyId( propertyId ) then + -- Only attempt to track the usage if we have a valid property id. + php.addStatementUsage( entityId, propertyId ) + end +end + +-- Function to mask an entity's subtables in order to log access +-- Code for logging based on: http://www.lua.org/pil/13.4.4.html +-- +-- @param {table} entity +-- @param {string} tableName +-- @param {function} usageFunc +local maskEntityTable = function( entity, tableName, usageFunc ) + if entity[tableName] == nil then + return + end + local actualEntityTable = entity[tableName] + entity[tableName] = {} + + local pseudoTableMetatable = {} + pseudoTableMetatable.__index = function( _, key ) + usageFunc( entity.id, key ) + return actualEntityTable[key] + end + + pseudoTableMetatable.__newindex = function( _, _, _ ) + error( 'Entity cannot be modified', 2 ) + end + + local logNext = function( _, key ) + local k, v = next( actualEntityTable, key ) + if k ~= nil then + usageFunc( entity.id, k ) + end + return k, v + end + + pseudoTableMetatable.__pairs = function( _ ) + return logNext, {}, nil + end + + setmetatable( entity[tableName], pseudoTableMetatable ) +end + +local noUsageTracking = function() +end + +-- Function to mask an entity's subtables in order to log access and prevent modifications +-- +-- @param {table} entity +-- @param {bool} fineGrainedTracking +local maskEntityTables = function ( entity, fineGrainedTracking ) + if fineGrainedTracking then + maskEntityTable( entity, 'claims', addStatementUsage ) + maskEntityTable( entity, 'labels', php.addLabelUsage ) + maskEntityTable( entity, 'sitelinks', php.addSiteLinksUsage ) + maskEntityTable( entity, 'descriptions', php.addDescriptionUsage ) + maskEntityTable( entity, 'aliases', php.addOtherUsage ) + else + maskEntityTable( entity, 'claims', noUsageTracking ) + maskEntityTable( entity, 'labels', noUsageTracking ) + maskEntityTable( entity, 'sitelinks', noUsageTracking ) + maskEntityTable( entity, 'descriptions', noUsageTracking ) + maskEntityTable( entity, 'aliases', noUsageTracking ) + end +end + -- Create new entity object from given data -- -- @param {table} data -entity.create = function( data ) - if type( data ) ~= 'table' or type( data.schemaVersion ) ~= 'number' then - error( 'The entity data must be a table obtained via mw.wikibase.getEntityObject' ) +Entity.create = function( data ) + if type( data ) ~= 'table' then + error( 'Expected a table obtained via mw.wikibase.getEntityObject, got ' .. type( data ) .. ' instead' ) + end + if next( data ) == nil then + error( 'Expected a non-empty table obtained via mw.wikibase.getEntityObject' ) + end + if type( data.schemaVersion ) ~= 'number' then + error( 'data.schemaVersion must be a number, got ' .. type( data.schemaVersion ) .. ' instead' ) end - if data.schemaVersion < 2 then error( 'mw.wikibase.entity must not be constructed using legacy data' ) end + if type( data.id ) ~= 'string' then + error( 'data.id must be a string, got ' .. type( data.id ) .. ' instead' ) + end local entity = data - setmetatable( entity, metatable ) + maskEntityTables( entity, php.getSetting( 'fineGrainedLuaTracking' ) ) + setmetatable( entity, metatable ) return entity end +-- Get the id serialization from this entity. +methodtable.getId = function( entity ) + return entity.id +end + -- Get a term of a given type for a given language code or the content language (on monolingual wikis) -- or the user's language (on multilingual wikis). -- Second return parameter is the language the term is in. @@ -53,6 +147,8 @@ end -- @param {string} termType A valid key in the entity table (either labels, descriptions or aliases) -- @param {string|number} langCode local getTermAndLang = function( entity, termType, langCode ) + php.incrementStatsKey( 'wikibase.client.scribunto.entity.getTermAndLang.call' ) + langCode = langCode or php.getLanguageCode() if langCode == nil then @@ -121,6 +217,8 @@ end -- -- @param {string|number} [globalSiteId] methodtable.getSitelink = function( entity, globalSiteId ) + php.incrementStatsKey( 'wikibase.client.scribunto.entity.getSitelink.call' ) + checkTypeMulti( 'getSitelink', 1, globalSiteId, { 'string', 'number', 'nil' } ) if entity.sitelinks == nil then @@ -142,18 +240,39 @@ methodtable.getSitelink = function( entity, globalSiteId ) return sitelink.title end --- Get the best statements with the given property id --- --- @param {string} propertyId -methodtable.getBestStatements = function( entity, propertyId ) - if entity.claims == nil or not entity.claims[propertyId] then +-- @param {table} entity +-- @param {string} propertyLabelOrId +-- @param {string} funcName for error logging +local getEntityStatements = function( entity, propertyLabelOrId, funcName ) + php.incrementStatsKey( 'wikibase.client.scribunto.entity.getEntityStatements.call' ) + + checkType( funcName, 1, propertyLabelOrId, 'string' ) + + if not entity.claims then return {} end + local propertyId = propertyLabelOrId + if not isValidPropertyId( propertyId ) then + propertyId = mw.wikibase.resolvePropertyId( propertyId ) + end + + if propertyId and entity.claims[propertyId] then + return entity.claims[propertyId] + end + + return {} +end + +-- Get the best statements with the given property id or label +-- +-- @param {string} propertyLabelOrId +methodtable.getBestStatements = function( entity, propertyLabelOrId ) + local entityStatements = getEntityStatements( entity, propertyLabelOrId, 'getBestStatements' ) local statements = {} local bestRank = 'normal' - for k, statement in pairs( entity.claims[propertyId] ) do + for _, statement in pairs( entityStatements ) do if statement.rank == bestRank then statements[#statements + 1] = statement elseif statement.rank == 'preferred' then @@ -165,8 +284,17 @@ methodtable.getBestStatements = function( entity, propertyId ) return statements end +-- Get all statements with the given property id or label +-- +-- @param {string} propertyLabelOrId +methodtable.getAllStatements = function( entity, propertyLabelOrId ) + return getEntityStatements( entity, propertyLabelOrId, 'getAllStatements' ) +end + -- Get a table with all property ids attached to the entity. methodtable.getProperties = function( entity ) + php.incrementStatsKey( 'wikibase.client.scribunto.entity.getProperties.call' ) + if entity.claims == nil then return {} end @@ -175,7 +303,7 @@ methodtable.getProperties = function( entity ) local properties = {} local n = 0 - for k, v in pairs( entity.claims ) do + for k, _ in pairs( entity.claims ) do n = n + 1 properties[n] = k end @@ -199,8 +327,8 @@ local formatValuesByPropertyId = function( entity, phpFormatterFunction, propert ) local label - if propertyLabelOrId:match( '^P%d+$' ) then - label = mw.wikibase.label( propertyLabelOrId ) + if isValidPropertyId( propertyLabelOrId ) then + label = mw.wikibase.getLabel( propertyLabelOrId ) end if label == nil then @@ -220,6 +348,8 @@ end -- @param {string} propertyLabelOrId -- @param {table} [acceptableRanks] methodtable.formatPropertyValues = function( entity, propertyLabelOrId, acceptableRanks ) + php.incrementStatsKey( 'wikibase.client.scribunto.entity.formatPropertyValues.call' ) + checkType( 'formatPropertyValues', 1, propertyLabelOrId, 'string' ) checkTypeMulti( 'formatPropertyValues', 2, acceptableRanks, { 'table', 'nil' } ) @@ -237,6 +367,8 @@ end -- @param {string} propertyLabelOrId -- @param {table} [acceptableRanks] methodtable.formatStatements = function( entity, propertyLabelOrId, acceptableRanks ) + php.incrementStatsKey( 'wikibase.client.scribunto.entity.formatStatements.call' ) + checkType( 'formatStatements', 1, propertyLabelOrId, 'string' ) checkTypeMulti( 'formatStatements', 2, acceptableRanks, { 'table', 'nil' } ) @@ -248,8 +380,7 @@ methodtable.formatStatements = function( entity, propertyLabelOrId, acceptableRa ); end -mw.wikibase.entity = entity -package.loaded['mw.wikibase.entity'] = entity -mw_interface = nil +mw.wikibase.entity = Entity +package.loaded['mw.wikibase.entity'] = Entity -return entity +return Entity