Модуль:External links: различия между версиями
Перейти к навигации
Перейти к поиску
м (1 версия импортирована: Импорт из Википедии) |
w>Putnik (более контрастные иконки в тёмной теме) |
||
Строка 1: | Строка 1: | ||
+ | local data = require( 'Module:External links/data' ) | ||
-- Localizable part | -- Localizable part | ||
− | -- Please | + | -- Please note that labels for websites and catalogs are taken from Wikidata (i.e. Wikidata label) |
− | + | -- Feel free to correct labels and categories here | |
− | local | + | local categoryTemplateEmpty = 'Википедия:Шаблон «Внешние ссылки» пуст' |
− | + | local categoryCustomColor = 'Википедия:Шаблон «Внешние ссылки» с нестандартным цветом' | |
− | local | ||
local templateLink = 'Внешние ссылки' | local templateLink = 'Внешние ссылки' | ||
+ | local templateTitle = 'Ссылки на внешние ресурсы' | ||
+ | local wikidataLink = 'Перейти к элементу Викиданных' | ||
+ | local refLabels = { | ||
+ | Q1406 = 'Windows', | ||
+ | Q10677 = 'PS1', | ||
+ | Q10680 = 'PS2', | ||
+ | Q10683 = 'PS3', | ||
+ | Q16338 = 'ПК', | ||
+ | Q135321 = 'FDS', | ||
+ | Q170323 = 'DS', | ||
+ | Q170325 = 'PSP', | ||
+ | Q172742 = 'NES', | ||
+ | Q182172 = 'GameCube', | ||
+ | Q183259 = 'SNES', | ||
+ | Q184839 = 'N64', | ||
+ | Q188642 = 'GBA', | ||
+ | Q188808 = 'PS Vita', | ||
+ | Q203597 = '3DS', | ||
+ | Q5014725 = 'PS4', | ||
+ | Q19610114 = 'Switch', | ||
+ | Q63184502 = 'PS5', | ||
+ | } | ||
− | + | -- The language codes that should always be displayed even if they have normal rank and a claim with another language and preferred rank exists | |
− | + | local preferredLanguage = 'Q7737' -- russian | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | -- The language codes that should be | ||
− | local preferredLanguage = 'Q7737' | ||
− | local templateColorName = 'цвет' | + | local templateColorName = 'цвет' |
-- Some projects have "named" colors, defined by templates | -- Some projects have "named" colors, defined by templates | ||
− | function colorByTitle( frame, colorTitle ) | + | local function colorByTitle( frame, colorTitle ) |
− | local templateName = 'Цвет/' .. colorTitle | + | local templateName = 'Цвет/' .. colorTitle |
− | local templateTitle = mw.title.makeTitle( 'Template', templateName ) | + | local templateTitle = mw.title.makeTitle( 'Template', templateName ) |
− | if | + | if not templateTitle or not templateTitle.exists then |
− | return false | + | return false |
end | end | ||
− | return frame:expandTemplate{ title = templateName } | + | return frame:expandTemplate{ title = templateName } |
end | end | ||
− | + | -- Non-localizable part (not need to localize ) | |
− | -- | + | local moduleNavbox = require( 'Module:Navbox' ) |
− | + | local moduleNavbar = require( 'Module:Navbar' )._ruwikiNavbar -- can be changed to a regular one | |
− | + | local moduleLanguages -- accessed if necessary | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | local propertyQualifiers = { | ||
+ | P553 = { | ||
+ | P554 = 'url', | ||
+ | }, | ||
+ | P1343 = { | ||
+ | P805 = 'iw', | ||
+ | P248 = 'iw', -- deprecated, fallback for P805 | ||
+ | P953 = 'url', | ||
+ | P854 = 'url', -- deprecated, fallback for P953 | ||
+ | }, | ||
} | } | ||
− | + | local p = {} | |
− | -- | + | -- Helper functions |
− | + | local function replace( str, pattern, repl ) | |
+ | pattern = mw.ustring.gsub( pattern, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1" ) -- escape pattern | ||
+ | repl = mw.ustring.gsub( repl, "[%%]", "%%%%" ) -- escape replacement | ||
+ | repl = mw.ustring.gsub( repl, " ", "%%%%20" ) -- escape replacement | ||
+ | return mw.ustring.gsub( str, pattern, repl ) | ||
+ | end | ||
− | |||
− | local | + | -- Render functions |
+ | local function renderList( elements ) | ||
+ | if #elements == 0 then | ||
+ | return '' | ||
+ | end | ||
− | + | return '*' .. table.concat( elements , '\n*' ) .. '\n' | |
− | |||
end | end | ||
− | function | + | local function renderLabel( params ) |
− | + | if type( params ) == 'string' then | |
− | + | return params | |
− | + | end | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | local qid = params[ 1 ] | ||
+ | local default = params[ 2 ] | ||
− | + | if #params >= 3 then | |
− | + | local label = params[ 3 ] | |
− | + | local link = mw.wikibase.sitelink( qid ) | |
+ | if link then | ||
+ | return '[[' .. link .. '|' .. label .. ']]' | ||
+ | end | ||
+ | local title = mw.wikibase.label( qid ) or default | ||
+ | if title ~= label then | ||
+ | return '<span title="' .. title .. '" style="border-bottom: 1px dotted; cursor: help;">' .. label .. '</span>' | ||
+ | end | ||
+ | end | ||
+ | |||
+ | return mw.wikibase.label( qid ) or default | ||
end | end | ||
− | function | + | local function renderLink( resourceData, label, formatter, idAsLabel ) |
− | + | if resourceData.itemId == nil then | |
− | + | return '<span class="error">' .. label .. ': Не удаётся определить элемент</span>[[Категория:Статьи с ошибкой в шаблоне Внешние ссылки]]' | |
− | + | end | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | local link | |
− | if | + | if not formatter then |
− | + | link = resourceData.itemId | |
+ | idAsLabel = false | ||
+ | elseif type( formatter ) == 'string' then | ||
+ | link = replace( formatter, '$1', resourceData.itemId ) | ||
+ | elseif type ( formatter ) == 'function' then | ||
+ | link = formatter( resourceData.itemId, resourceData.qualifiers ) | ||
end | end | ||
− | if | + | |
− | return ' | + | -- "Label: ID" without link |
+ | if not link or link == '' then | ||
+ | return renderLabel( label ) .. ': ' .. resourceData.itemId | ||
end | end | ||
− | if | + | |
− | + | local resourceLabel = resourceData.itemId | |
+ | if not idAsLabel then | ||
+ | resourceLabel = renderLabel( label ) | ||
end | end | ||
− | + | ||
− | return ' | + | local linkFirstChar = mw.ustring.sub( link, 1, 1 ) |
+ | if linkFirstChar == ':' then | ||
+ | return '[[' .. link .. '|' .. resourceLabel .. ']]' | ||
end | end | ||
− | return | + | return '[' .. link .. ' ' .. resourceLabel .. ']' |
end | end | ||
− | function | + | local function renderLangRef( languages ) |
− | + | local result = '' | |
− | + | if languages and #languages > 0 then | |
+ | if moduleLanguages ~= false then -- not false, but maybe nil | ||
+ | if mw.title.makeTitle( 'Module', 'Languages' ).exists | ||
+ | and mw.title.makeTitle( 'Module', 'Languages/data' ).exists | ||
+ | and mw.title.makeTitle( 'Module', 'Wikidata/Language-codes' ).exists then | ||
+ | moduleLanguages = require( 'Module:Languages' ) | ||
+ | else | ||
+ | moduleLanguages = false | ||
+ | end | ||
+ | end | ||
+ | |||
+ | if moduleLanguages then | ||
+ | for langIndex, language in pairs( languages ) do | ||
+ | result = result .. ' ' .. moduleLanguages.getWikidataRefHtml( language ) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | |||
+ | return result | ||
end | end | ||
− | function | + | local function renderRefs( refs ) |
− | + | local result = '' | |
− | + | if refs and #refs > 0 then | |
− | + | for _, ref in ipairs( refs ) do | |
− | + | result = result .. ' <span class="ref-info">(' .. ref .. ')</span>' | |
− | end | + | end |
− | + | end | |
− | return | + | |
+ | return result | ||
end | end | ||
− | function | + | local function renderLinkWithRef( resourceData, label, formatter, idAsLabel ) |
− | + | local link = renderLink( resourceData, label, formatter, idAsLabel ) | |
− | + | if link ~= '' then | |
+ | link = link .. renderLangRef( resourceData.languages ) | ||
+ | link = link .. renderRefs( resourceData.refs ) | ||
+ | end | ||
+ | return link | ||
end | end | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | -- Data fetching functions | |
− | function | + | local function getValueFromSnak( snak ) |
− | + | if not snak.datavalue then | |
− | + | return | |
− | + | end | |
− | + | if snak.datavalue.type == 'wikibase-entityid' then | |
− | + | return snak.datavalue.value.id | |
− | + | end | |
− | + | if snak.datavalue.type == 'monolingualtext' then | |
− | + | return snak.datavalue.value.text | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | return | ||
end | end | ||
− | return | + | return snak.datavalue.value |
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | function | + | local function getQualifierSingleValue( statement, qualifierName ) |
− | + | if not statement or not statement.qualifiers or not statement.qualifiers[ qualifierName ] then | |
− | + | return nil | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | return | ||
end | end | ||
− | + | for qualifierIndex, qualifier in pairs( statement.qualifiers[ qualifierName ] ) do | |
− | + | if qualifier.datavalue and qualifier.datavalue.type and qualifier.datavalue.value then | |
− | + | return getValueFromSnak( qualifier ) | |
− | |||
− | |||
− | |||
− | |||
− | return | ||
end | end | ||
− | |||
− | |||
end | end | ||
− | return | + | return nil |
end | end | ||
− | local | + | local function getQualifierValues( statement ) |
− | + | if not statement or not statement.qualifiers then | |
− | + | return {} | |
− | + | end | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | local | + | local result = {} |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | } | ||
− | + | for qualifierName, qualifiers in pairs( statement.qualifiers ) do | |
− | + | for _, qualifier in pairs( qualifiers ) do | |
− | + | if qualifier.datavalue and qualifier.datavalue.type and qualifier.datavalue.value then | |
− | + | if not result[ qualifierName ] then | |
− | + | result[ qualifierName ] = {} | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | for | ||
− | if | ||
− | |||
− | |||
− | |||
− | if | ||
− | |||
end | end | ||
− | + | table.insert( result[ qualifierName ], getValueFromSnak( qualifier ) ) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
+ | end | ||
− | + | return result | |
− | return | ||
end | end | ||
− | function | + | local function contains( tableStructure, value ) |
− | + | if not tableStructure or not value then | |
− | if | + | return true |
− | + | end | |
− | + | for index, line in pairs( tableStructure ) do | |
− | + | if line == value then | |
− | + | return true | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
− | return | + | return false |
end | end | ||
− | function | + | local function filterByRank( resourceDatas ) |
+ | -- itemId, languages. rank = rank | ||
− | local | + | local hasPreffered = false |
− | + | for index, resourceData in pairs( resourceDatas ) do | |
− | + | if resourceData.rank == 'preferred' then | |
− | + | hasPreffered = true | |
− | + | end | |
− | |||
− | |||
end | end | ||
− | + | if not hasPreffered then | |
− | + | return resourceDatas | |
− | |||
− | |||
− | |||
− | |||
− | if | ||
− | return | ||
end | end | ||
− | + | local result = {} | |
− | + | for index, resourceData in pairs( resourceDatas ) do | |
− | + | if resourceData.rank == 'preferred' or contains( resourceData.languages, preferredLanguage ) then | |
− | + | table.insert( result, resourceData ) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
− | + | return result | |
− | + | end | |
− | + | local function getLinkData( statement, qualifier, project ) | |
− | + | local rank = statement.rank or 'normal' | |
− | + | if rank == 'deprecated' or not statement.mainsnak.datavalue then | |
− | + | return nil | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | local itemId | |
− | + | if qualifier then | |
+ | itemId = getQualifierSingleValue( statement, qualifier ) | ||
+ | else | ||
+ | itemId = statement.mainsnak.datavalue.value | ||
end | end | ||
− | + | if itemId and project then | |
+ | itemId = mw.wikibase.getSitelink( itemId, project ) | ||
+ | end | ||
− | + | if not itemId then | |
− | + | return nil | |
− | + | end | |
− | + | local qualifiers = getQualifierValues( statement ) | |
− | + | local languages = qualifiers[ 'P407' ] | |
− | + | if not languages then | |
− | + | languages = {} | |
− | + | end | |
− | + | local refs = {} | |
− | local | + | if qualifiers[ 'P400' ] then |
− | + | for _, refQid in ipairs( qualifiers[ 'P400' ] ) do | |
− | + | local refLabel = refLabels | |
− | + | if refLabels[ refQid ] then | |
− | + | refLabel = refLabels[ refQid ] | |
− | + | else | |
+ | refLabel = mw.wikibase.label( refQid ) or refQid | ||
end | end | ||
− | + | if refLabel ~= refQid then | |
− | + | if #qualifiers[ 'P400' ] > 3 then | |
− | + | refLabel = refLabel .. ' + ' .. ( #qualifiers[ 'P400' ] - 1 ) .. ' платформ' | |
− | + | table.insert( refs, refLabel ) | |
− | if | + | break |
− | - | ||
− | |||
− | |||
− | |||
end | end | ||
− | + | table.insert( refs, refLabel ) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
− | + | return { | |
− | + | itemId = itemId, | |
− | + | qualifiers = qualifiers, | |
− | + | languages = languages, | |
− | + | refs = refs, | |
+ | rank = rank, | ||
+ | } | ||
end | end | ||
− | function | + | local function collectLinks( configuration, entityId, separateLabel ) |
− | --Create rows | + | -- Create rows |
local elements = {} | local elements = {} | ||
+ | local data = {} | ||
− | + | for _, params in pairs( configuration ) do | |
− | + | local resourceId = params | |
− | + | local propertyId = resourceId[ 2 ] | |
− | + | local qid | |
− | + | if string.match( propertyId, '^P%d+:Q%d+$' ) then | |
− | + | local parts = mw.text.split( propertyId, ':', true ) | |
− | + | propertyId = parts[ 1 ] | |
− | + | qid = parts[ 2 ] | |
− | + | end | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | local claims = mw.wikibase.getBestStatements( entityId, propertyId ) or {} | |
− | + | for _, statement in pairs( claims ) do | |
− | + | local linkData | |
− | + | if not qid then | |
− | + | linkData = getLinkData( statement ) | |
− | + | elseif getValueFromSnak( statement.mainsnak ) == qid then | |
− | + | for qualifierId, qualifierType in pairs( propertyQualifiers[ propertyId ] ) do | |
+ | local project = nil | ||
+ | if qualifierType == 'iw' then | ||
+ | project = params.project | ||
end | end | ||
− | + | linkData = getLinkData( statement, qualifierId, project ) | |
− | + | if linkData then | |
− | + | break | |
− | |||
− | |||
− | |||
− | |||
− | if | ||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
end | end | ||
− | |||
− | |||
− | + | if linkData then | |
− | + | if not data[ resourceId ] then | |
− | + | data[ resourceId ] = {} | |
− | if | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
+ | table.insert( data[ resourceId ], linkData ) | ||
end | end | ||
end | end | ||
end | end | ||
− | + | for resourceId, resourceDatas in pairs( data ) do | |
− | for | + | data[ resourceId ] = filterByRank( resourceDatas ) |
− | + | end | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | for _, params in pairs( configuration ) do | |
− | + | local resourceId = params | |
− | + | local resourceDatas = data[ resourceId ] | |
− | + | if resourceDatas ~= nil then | |
− | + | local links = {} | |
+ | for _, resourceData in pairs( resourceDatas ) do | ||
+ | if separateLabel then | ||
+ | -- Label: ID1, ID2 | ||
+ | table.insert( links, renderLinkWithRef( resourceData, '', params[ 3 ], true ) ) | ||
+ | else | ||
+ | -- Label · Label | ||
+ | local label = params[ 1 ] | ||
+ | table.insert( elements, renderLinkWithRef( resourceData, label, params[ 3 ] ) ) | ||
end | end | ||
+ | end | ||
+ | if separateLabel and #links then | ||
+ | local result = renderLabel( params[ 1 ] ) .. ': ' .. table.concat( links, ', ' ) | ||
+ | table.insert( elements, result ) | ||
end | end | ||
end | end | ||
Строка 1087: | Строка 394: | ||
end | end | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
function p.render( frame ) | function p.render( frame ) | ||
− | local colorArg = '' | + | local colorArg = '' |
− | local | + | local byTemplate |
− | if | + | local entityId = nil |
+ | if frame ~= nil then | ||
local parentArgs = frame:getParent().args | local parentArgs = frame:getParent().args | ||
− | colorArg = parentArgs[templateColorName] or parentArgs['color'] or parentArgs[1] or '' | + | colorArg = parentArgs[ templateColorName ] or parentArgs[ 'color' ] or parentArgs[ 1 ] or '' |
− | if parentArgs['d'] and parentArgs['d'] ~= '' then | + | if parentArgs[ 'from' ] and parentArgs[ 'from' ] ~= '' then |
− | + | entityId = string.upper( parentArgs[ 'from' ] ) | |
+ | elseif parentArgs[ 'd' ] and parentArgs[ 'd' ] ~= '' then | ||
+ | entityId = string.upper( parentArgs[ 'd' ] ) | ||
+ | else | ||
+ | entityId = mw.wikibase.getEntityIdForCurrentPage() | ||
end | end | ||
− | if | + | if colorArg ~= '' then |
− | local firstChar = mw.ustring.sub( colorArg, 1, 1 ) | + | local firstChar = mw.ustring.sub( colorArg, 1, 1 ) |
− | if | + | if firstChar ~= '#' then |
− | + | byTemplate = colorByTitle( frame, colorArg ) | |
− | if | + | if byTemplate then |
− | colorArg = byTemplate | + | colorArg = byTemplate |
end | end | ||
end | end | ||
end | end | ||
+ | end | ||
+ | |||
+ | if not entityId then | ||
+ | return '' | ||
end | end | ||
local navboxData = { | local navboxData = { | ||
name = 'External links', | name = 'External links', | ||
− | + | title = templateTitle, | |
+ | titlestyle = 'display:none', | ||
+ | state = 'plain', | ||
bodyclass = 'hlist', | bodyclass = 'hlist', | ||
− | } | + | bodystyle = 'padding-top:1px', |
+ | } | ||
if colorArg and colorArg ~= '' then | if colorArg and colorArg ~= '' then | ||
− | navboxData.groupstyle = 'background: ' .. colorArg .. ';' | + | navboxData.groupstyle = 'background: ' .. colorArg .. ';' |
end | end | ||
− | local rowIndex = 1 | + | local rowIndex = 1 |
− | local | + | for _, groupData in pairs( data ) do |
− | + | local isAuthorityControl = groupData.isAuthorityControl | |
− | + | local groupLabel = groupData.label | |
− | + | local groupList = groupData.list | |
− | + | local groupElements = collectLinks( groupList, entityId, isAuthorityControl ) | |
− | + | if #groupElements > 0 then | |
+ | navboxData[ 'group' .. rowIndex ] = groupLabel | ||
+ | navboxData[ 'list' .. rowIndex ] = renderList( groupElements ) | ||
− | + | if isAuthorityControl then | |
− | + | if #groupElements > 5 then | |
− | + | navboxData[ 'group' .. rowIndex ] = nil | |
− | + | local templateStyles = frame:extensionTag{ | |
− | + | name = 'templatestyles', | |
− | + | args = { src = 'Шаблон:Навигационная таблица/styles.css' } | |
+ | } | ||
+ | local collapsibleNavbox = moduleNavbox._navbox( { | ||
+ | title = groupLabel, | ||
+ | list1 = navboxData['list' .. rowIndex], | ||
+ | border = 'subgroup', | ||
+ | navbar = 'plain', | ||
+ | state = 'collapsed', | ||
+ | titleclass = 'ts-navbox-plaintitle', | ||
+ | bodyclass = 'authoritycontrol', | ||
+ | titlestyle = navboxData.groupstyle, | ||
+ | bodystyle = 'text-align: left;', | ||
+ | } ) | ||
+ | navboxData[ 'list' .. rowIndex ] = templateStyles .. collapsibleNavbox | ||
+ | end | ||
+ | end | ||
− | + | rowIndex = rowIndex + 1 | |
− | + | end | |
− | |||
− | |||
− | |||
end | end | ||
− | + | if rowIndex == 1 then | |
− | + | if mw.title.getCurrentTitle().namespace == 0 then | |
− | + | return '[[Category:' .. categoryTemplateEmpty .. ']]' | |
− | + | end | |
− | + | return '' | |
end | end | ||
− | local | + | local buttons = moduleNavbar({ templatelink }) .. |
− | if | + | ' [[File:OOjs UI icon edit-ltr-progressive.svg|14px|' .. wikidataLink .. '|link=d:' |
− | navboxData[' | + | .. (entityId or mw.wikibase.getEntityIdForCurrentPage()) .. '#identifiers]]' |
− | + | if navboxData[ 'group1' ] then | |
− | + | navboxData[ 'group1' ] = '<div style="padding: 0 35px 0 0; width: 100%;">' .. | |
− | + | '<div class="skin-invert-image" style="float: left;">' .. buttons .. '</div> ' .. | |
+ | navboxData[ 'group1' ] .. '</div>' | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
else | else | ||
− | navboxData['group1'] = '<div style="padding: | + | navboxData[ 'group1' ] = '<div style="padding: 0; width: 100%;">' .. buttons .. '</div>' |
− | |||
− | |||
end | end | ||
− | local | + | local out = moduleNavbox._navbox( navboxData ) |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | if colorArg and colorArg ~= '' and not byTemplate then | |
− | + | out = out .. '[[Category:' .. categoryCustomColor .. ']]' | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | ||
− | return | + | return out |
end | end | ||
return p | return p |
Версия от 22:51, 8 июля 2024
Для документации этого модуля может быть создана страница Модуль:External links/doc
local data = require( 'Module:External links/data' )
-- Localizable part
-- Please note that labels for websites and catalogs are taken from Wikidata (i.e. Wikidata label)
-- Feel free to correct labels and categories here
local categoryTemplateEmpty = 'Википедия:Шаблон «Внешние ссылки» пуст'
local categoryCustomColor = 'Википедия:Шаблон «Внешние ссылки» с нестандартным цветом'
local templateLink = 'Внешние ссылки'
local templateTitle = 'Ссылки на внешние ресурсы'
local wikidataLink = 'Перейти к элементу Викиданных'
local refLabels = {
Q1406 = 'Windows',
Q10677 = 'PS1',
Q10680 = 'PS2',
Q10683 = 'PS3',
Q16338 = 'ПК',
Q135321 = 'FDS',
Q170323 = 'DS',
Q170325 = 'PSP',
Q172742 = 'NES',
Q182172 = 'GameCube',
Q183259 = 'SNES',
Q184839 = 'N64',
Q188642 = 'GBA',
Q188808 = 'PS Vita',
Q203597 = '3DS',
Q5014725 = 'PS4',
Q19610114 = 'Switch',
Q63184502 = 'PS5',
}
-- The language codes that should always be displayed even if they have normal rank and a claim with another language and preferred rank exists
local preferredLanguage = 'Q7737' -- russian
local templateColorName = 'цвет'
-- Some projects have "named" colors, defined by templates
local function colorByTitle( frame, colorTitle )
local templateName = 'Цвет/' .. colorTitle
local templateTitle = mw.title.makeTitle( 'Template', templateName )
if not templateTitle or not templateTitle.exists then
return false
end
return frame:expandTemplate{ title = templateName }
end
-- Non-localizable part (not need to localize )
local moduleNavbox = require( 'Module:Navbox' )
local moduleNavbar = require( 'Module:Navbar' )._ruwikiNavbar -- can be changed to a regular one
local moduleLanguages -- accessed if necessary
local propertyQualifiers = {
P553 = {
P554 = 'url',
},
P1343 = {
P805 = 'iw',
P248 = 'iw', -- deprecated, fallback for P805
P953 = 'url',
P854 = 'url', -- deprecated, fallback for P953
},
}
local p = {}
-- Helper functions
local function replace( str, pattern, repl )
pattern = mw.ustring.gsub( pattern, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1" ) -- escape pattern
repl = mw.ustring.gsub( repl, "[%%]", "%%%%" ) -- escape replacement
repl = mw.ustring.gsub( repl, " ", "%%%%20" ) -- escape replacement
return mw.ustring.gsub( str, pattern, repl )
end
-- Render functions
local function renderList( elements )
if #elements == 0 then
return ''
end
return '*' .. table.concat( elements , '\n*' ) .. '\n'
end
local function renderLabel( params )
if type( params ) == 'string' then
return params
end
local qid = params[ 1 ]
local default = params[ 2 ]
if #params >= 3 then
local label = params[ 3 ]
local link = mw.wikibase.sitelink( qid )
if link then
return '[[' .. link .. '|' .. label .. ']]'
end
local title = mw.wikibase.label( qid ) or default
if title ~= label then
return '<span title="' .. title .. '" style="border-bottom: 1px dotted; cursor: help;">' .. label .. '</span>'
end
end
return mw.wikibase.label( qid ) or default
end
local function renderLink( resourceData, label, formatter, idAsLabel )
if resourceData.itemId == nil then
return '<span class="error">' .. label .. ': Не удаётся определить элемент</span>[[Категория:Статьи с ошибкой в шаблоне Внешние ссылки]]'
end
local link
if not formatter then
link = resourceData.itemId
idAsLabel = false
elseif type( formatter ) == 'string' then
link = replace( formatter, '$1', resourceData.itemId )
elseif type ( formatter ) == 'function' then
link = formatter( resourceData.itemId, resourceData.qualifiers )
end
-- "Label: ID" without link
if not link or link == '' then
return renderLabel( label ) .. ': ' .. resourceData.itemId
end
local resourceLabel = resourceData.itemId
if not idAsLabel then
resourceLabel = renderLabel( label )
end
local linkFirstChar = mw.ustring.sub( link, 1, 1 )
if linkFirstChar == ':' then
return '[[' .. link .. '|' .. resourceLabel .. ']]'
end
return '[' .. link .. ' ' .. resourceLabel .. ']'
end
local function renderLangRef( languages )
local result = ''
if languages and #languages > 0 then
if moduleLanguages ~= false then -- not false, but maybe nil
if mw.title.makeTitle( 'Module', 'Languages' ).exists
and mw.title.makeTitle( 'Module', 'Languages/data' ).exists
and mw.title.makeTitle( 'Module', 'Wikidata/Language-codes' ).exists then
moduleLanguages = require( 'Module:Languages' )
else
moduleLanguages = false
end
end
if moduleLanguages then
for langIndex, language in pairs( languages ) do
result = result .. ' ' .. moduleLanguages.getWikidataRefHtml( language )
end
end
end
return result
end
local function renderRefs( refs )
local result = ''
if refs and #refs > 0 then
for _, ref in ipairs( refs ) do
result = result .. ' <span class="ref-info">(' .. ref .. ')</span>'
end
end
return result
end
local function renderLinkWithRef( resourceData, label, formatter, idAsLabel )
local link = renderLink( resourceData, label, formatter, idAsLabel )
if link ~= '' then
link = link .. renderLangRef( resourceData.languages )
link = link .. renderRefs( resourceData.refs )
end
return link
end
-- Data fetching functions
local function getValueFromSnak( snak )
if not snak.datavalue then
return
end
if snak.datavalue.type == 'wikibase-entityid' then
return snak.datavalue.value.id
end
if snak.datavalue.type == 'monolingualtext' then
return snak.datavalue.value.text
end
return snak.datavalue.value
end
local function getQualifierSingleValue( statement, qualifierName )
if not statement or not statement.qualifiers or not statement.qualifiers[ qualifierName ] then
return nil
end
for qualifierIndex, qualifier in pairs( statement.qualifiers[ qualifierName ] ) do
if qualifier.datavalue and qualifier.datavalue.type and qualifier.datavalue.value then
return getValueFromSnak( qualifier )
end
end
return nil
end
local function getQualifierValues( statement )
if not statement or not statement.qualifiers then
return {}
end
local result = {}
for qualifierName, qualifiers in pairs( statement.qualifiers ) do
for _, qualifier in pairs( qualifiers ) do
if qualifier.datavalue and qualifier.datavalue.type and qualifier.datavalue.value then
if not result[ qualifierName ] then
result[ qualifierName ] = {}
end
table.insert( result[ qualifierName ], getValueFromSnak( qualifier ) )
end
end
end
return result
end
local function contains( tableStructure, value )
if not tableStructure or not value then
return true
end
for index, line in pairs( tableStructure ) do
if line == value then
return true
end
end
return false
end
local function filterByRank( resourceDatas )
-- itemId, languages. rank = rank
local hasPreffered = false
for index, resourceData in pairs( resourceDatas ) do
if resourceData.rank == 'preferred' then
hasPreffered = true
end
end
if not hasPreffered then
return resourceDatas
end
local result = {}
for index, resourceData in pairs( resourceDatas ) do
if resourceData.rank == 'preferred' or contains( resourceData.languages, preferredLanguage ) then
table.insert( result, resourceData )
end
end
return result
end
local function getLinkData( statement, qualifier, project )
local rank = statement.rank or 'normal'
if rank == 'deprecated' or not statement.mainsnak.datavalue then
return nil
end
local itemId
if qualifier then
itemId = getQualifierSingleValue( statement, qualifier )
else
itemId = statement.mainsnak.datavalue.value
end
if itemId and project then
itemId = mw.wikibase.getSitelink( itemId, project )
end
if not itemId then
return nil
end
local qualifiers = getQualifierValues( statement )
local languages = qualifiers[ 'P407' ]
if not languages then
languages = {}
end
local refs = {}
if qualifiers[ 'P400' ] then
for _, refQid in ipairs( qualifiers[ 'P400' ] ) do
local refLabel = refLabels
if refLabels[ refQid ] then
refLabel = refLabels[ refQid ]
else
refLabel = mw.wikibase.label( refQid ) or refQid
end
if refLabel ~= refQid then
if #qualifiers[ 'P400' ] > 3 then
refLabel = refLabel .. ' + ' .. ( #qualifiers[ 'P400' ] - 1 ) .. ' платформ'
table.insert( refs, refLabel )
break
end
table.insert( refs, refLabel )
end
end
end
return {
itemId = itemId,
qualifiers = qualifiers,
languages = languages,
refs = refs,
rank = rank,
}
end
local function collectLinks( configuration, entityId, separateLabel )
-- Create rows
local elements = {}
local data = {}
for _, params in pairs( configuration ) do
local resourceId = params
local propertyId = resourceId[ 2 ]
local qid
if string.match( propertyId, '^P%d+:Q%d+$' ) then
local parts = mw.text.split( propertyId, ':', true )
propertyId = parts[ 1 ]
qid = parts[ 2 ]
end
local claims = mw.wikibase.getBestStatements( entityId, propertyId ) or {}
for _, statement in pairs( claims ) do
local linkData
if not qid then
linkData = getLinkData( statement )
elseif getValueFromSnak( statement.mainsnak ) == qid then
for qualifierId, qualifierType in pairs( propertyQualifiers[ propertyId ] ) do
local project = nil
if qualifierType == 'iw' then
project = params.project
end
linkData = getLinkData( statement, qualifierId, project )
if linkData then
break
end
end
end
if linkData then
if not data[ resourceId ] then
data[ resourceId ] = {}
end
table.insert( data[ resourceId ], linkData )
end
end
end
for resourceId, resourceDatas in pairs( data ) do
data[ resourceId ] = filterByRank( resourceDatas )
end
for _, params in pairs( configuration ) do
local resourceId = params
local resourceDatas = data[ resourceId ]
if resourceDatas ~= nil then
local links = {}
for _, resourceData in pairs( resourceDatas ) do
if separateLabel then
-- Label: ID1, ID2
table.insert( links, renderLinkWithRef( resourceData, '', params[ 3 ], true ) )
else
-- Label · Label
local label = params[ 1 ]
table.insert( elements, renderLinkWithRef( resourceData, label, params[ 3 ] ) )
end
end
if separateLabel and #links then
local result = renderLabel( params[ 1 ] ) .. ': ' .. table.concat( links, ', ' )
table.insert( elements, result )
end
end
end
return elements
end
function p.render( frame )
local colorArg = ''
local byTemplate
local entityId = nil
if frame ~= nil then
local parentArgs = frame:getParent().args
colorArg = parentArgs[ templateColorName ] or parentArgs[ 'color' ] or parentArgs[ 1 ] or ''
if parentArgs[ 'from' ] and parentArgs[ 'from' ] ~= '' then
entityId = string.upper( parentArgs[ 'from' ] )
elseif parentArgs[ 'd' ] and parentArgs[ 'd' ] ~= '' then
entityId = string.upper( parentArgs[ 'd' ] )
else
entityId = mw.wikibase.getEntityIdForCurrentPage()
end
if colorArg ~= '' then
local firstChar = mw.ustring.sub( colorArg, 1, 1 )
if firstChar ~= '#' then
byTemplate = colorByTitle( frame, colorArg )
if byTemplate then
colorArg = byTemplate
end
end
end
end
if not entityId then
return ''
end
local navboxData = {
name = 'External links',
title = templateTitle,
titlestyle = 'display:none',
state = 'plain',
bodyclass = 'hlist',
bodystyle = 'padding-top:1px',
}
if colorArg and colorArg ~= '' then
navboxData.groupstyle = 'background: ' .. colorArg .. ';'
end
local rowIndex = 1
for _, groupData in pairs( data ) do
local isAuthorityControl = groupData.isAuthorityControl
local groupLabel = groupData.label
local groupList = groupData.list
local groupElements = collectLinks( groupList, entityId, isAuthorityControl )
if #groupElements > 0 then
navboxData[ 'group' .. rowIndex ] = groupLabel
navboxData[ 'list' .. rowIndex ] = renderList( groupElements )
if isAuthorityControl then
if #groupElements > 5 then
navboxData[ 'group' .. rowIndex ] = nil
local templateStyles = frame:extensionTag{
name = 'templatestyles',
args = { src = 'Шаблон:Навигационная таблица/styles.css' }
}
local collapsibleNavbox = moduleNavbox._navbox( {
title = groupLabel,
list1 = navboxData['list' .. rowIndex],
border = 'subgroup',
navbar = 'plain',
state = 'collapsed',
titleclass = 'ts-navbox-plaintitle',
bodyclass = 'authoritycontrol',
titlestyle = navboxData.groupstyle,
bodystyle = 'text-align: left;',
} )
navboxData[ 'list' .. rowIndex ] = templateStyles .. collapsibleNavbox
end
end
rowIndex = rowIndex + 1
end
end
if rowIndex == 1 then
if mw.title.getCurrentTitle().namespace == 0 then
return '[[Category:' .. categoryTemplateEmpty .. ']]'
end
return ''
end
local buttons = moduleNavbar({ templatelink }) ..
' [[File:OOjs UI icon edit-ltr-progressive.svg|14px|' .. wikidataLink .. '|link=d:'
.. (entityId or mw.wikibase.getEntityIdForCurrentPage()) .. '#identifiers]]'
if navboxData[ 'group1' ] then
navboxData[ 'group1' ] = '<div style="padding: 0 35px 0 0; width: 100%;">' ..
'<div class="skin-invert-image" style="float: left;">' .. buttons .. '</div> ' ..
navboxData[ 'group1' ] .. '</div>'
else
navboxData[ 'group1' ] = '<div style="padding: 0; width: 100%;">' .. buttons .. '</div>'
end
local out = moduleNavbox._navbox( navboxData )
if colorArg and colorArg ~= '' and not byTemplate then
out = out .. '[[Category:' .. categoryCustomColor .. ']]'
end
return out
end
return p