Модуль:Источники по теме

Материал из Викитеки — свободной библиотеки
Перейти к навигации Перейти к поиску
Документация Документация

Подгружает ссылки на энциклопедии и братские википроекты из Викиданных и параметров шаблона-шапки и интерпретирует их, показывая в шапках статей.

Используется в Модуль:Отексте, Шаблон:Отексте, Модуль:Обавторе, Шаблон:Обавторе.

Использует:

Подмодули:

Функции

  • p.get_data(frame) — основная
local RU = 'ru'
local WIKISOURCE = 'wikisource'
local RUWIKISOURCE = 'ruwikisource'

-- Приоритет выбора ссылок на интервики. Расставлены примерно по величине размера проекта и числу админов, с приоритетом для европ., и en-de/pl-uk языков. '*' — любая другая другая интервика
local preferredLanguages = { RU, 'en', 'de', 'pl', 'uk', 'fr', 'it', 'es', 'cs', 'pt', 'da', 'sv', 'fi', 'ja', 'zh', 'he', 'ar', '*' }

local p = {}
local util = require("Module:Util")
local wikidata = require("Модуль:Источники по теме/wikidata")
local projects_cfg = util.get_json('MediaWiki:Wikiprojects settings.json')
local encyclopediasData = util.get_json("MediaWiki:Encyclopedias settings.json") -- настройки заголовков энциклопедий и их id в ВД
local encyclopedias_sites_cfg = util.get_json("MediaWiki:Encyclopedias sites.json") -- настройки энциклопедий на внеших сайтах

local page = require("Модуль:Данные страницы").page
function page:add_project(project, manual, wd)
	local d = p.set_priority_links(manual, wd)
	table.insert(self.projects, { project = project, title = d.title, link = d.link, lang = d.lang, manual_link = manual.link, manual_lang = manual.lang, wd_link = wd.link, wd_lang = wd.lang })
end
function page:add_enc(s) table.insert(self.enc_wikilinks, s) end
page.enc_links_raw = {}


-- Приоритет ссылок на википроекты: ручных ссылок или из ВД
function p.set_priority_links(manual, wd)
	local data = {}
	if manual.link and wd.link then
		-- RU ссылки приоритетней из ВД
		-- Ручные обычно устарели на 5-10 лет, часто ведут на ошибочные, удалённые статьи или переделанные в неоднозначности
		-- Ссылка на интервики из ВД только если нет ручной ссылки. Поскольку ручная ссылка обычно точнее общей, указанной в preferredLanguages
		if wd.lang == RU then
			data = wd

		else
			for _, preflang in pairs(preferredLanguages) do
				if preflang ~= RU and preflang ~= '*' then
					if preflang == wd.link then
						data = wd
						break
					end
				end
			end
		end
		if data.link == nil then data = manual end
	elseif manual.link then
		data = manual
	elseif wd.link then
		data = wd
	end
	return data
end


-- сортировка интервик в формате [проект.язык.страница]. Без monolanguage-проектов.
local function reshape_interwiki_by_projects(wikidataInterwiki)
	local interwiki_by_projects = {}
	for langprojectcode, title in pairs(wikidataInterwiki) do
		if title and langprojectcode ~= 'commonswiki' and langprojectcode ~= 'specieswiki' then
			for _, project in pairs(projects_cfg) do
				if not project.monolanguage then
					local project_code = project.project
					local lang = string.match(langprojectcode, '^(.*)' .. project_code .. '$')
					if is(lang) then
						if not interwiki_by_projects[project_code] then interwiki_by_projects[project_code] = {} end
						interwiki_by_projects[project_code][lang] = title
						break
					end
				end
			end
		end
	end
	return interwiki_by_projects
end

-- заполняет список `page.projects`
local function make_list_projects_links()
	local args = page.args
	local sitelinks = page.sitelinks
	local sitelinks_by_projects = reshape_interwiki_by_projects(sitelinks)

	for _, project in pairs(projects_cfg) do
		local projectCode = project.project -- like 'wikisource'
		local prj_code = project.code
		local prj_sitelinks = sitelinks_by_projects[projectCode]

		local manual = { lang = nil, link = nil, title = nil } -- ссылки из аргумента шаблона
		local wd = { lang = nil, link = nil, title = nil } -- ссылка из Викиданных

		-- ручная ссылка в параметре шаблона
		local value = args[project.arg] -- значение параметра
		if is(value) then
			local lang, title = string.match(value, '^:?([a-z]+):(.+)') -- ссылка формата ':код_языка:Название' в параметрах
			if not title then
				lang, title = RU, value
			end
			manual.lang, manual.title, manual.link = lang, title, util.make_interlink(prj_code, lang, title)
		end

		-- поиск ссылки в Викиданных
		-- моноязычные проекты (Викисклад, Викивиды, Викиданные)
		if project.monolanguage then
			local title = sitelinks[projectCode]
			if projectCode == 'commons' and not title then
				title = sitelinks['commonswiki']
			end
			if title then
				wd.title, wd.link = title, util.make_interlink(prj_code, nil, title)
			end

		-- мультиязычные проекты
		elseif prj_sitelinks then
			local foundProjectLink = false

			-- перебор предпочтительных языков preferredLanguages
			for _, preflang in pairs(preferredLanguages) do

				if preflang == '*' then
					-- языки под '*' в preferredLanguages
					for lang, title in pairs(prj_sitelinks) do
						if lang then --  and projectCode ~= WIKISOURCE -- ссылки на малоразвитые интерВикитеки не нужны
							wd.lang, wd.title, wd.link = lang, title, util.make_interlink(prj_code, lang, title)
							foundProjectLink = true
							break
						end
					end

				else
					-- языки не под '*' в preferredLanguages
					local lang = preflang
					local title = prj_sitelinks[lang]
					if title then
						wd.lang, wd.title, wd.link = lang, title, util.make_interlink(prj_code, lang, title)
						foundProjectLink = true
					end
				end

				if foundProjectLink then break end
			end

			if manual.link and manual.title then
				manual.title = mw.ustring.gsub(manual.title, '_', ' ')
			end
		end

		if manual.link or wd.link then
			page:add_project(project, manual, wd)
			
		elseif projectCode == 'wiki' then
			-- если нет ссылок на Википедию, то ссылка на Википедию по параметру ПОИСК
			local searchString = args['ПОИСК'] or page.frame1.args.search
			if searchString and #searchString > 0 then
				table.insert(page.projects, { project = project, link = ':w:Special:Search/' .. searchString, lang = RU })
			end
		end
		
	end
	-- mw.logObject(page, "page")
	return page
end


local function getPageTitleFromArgument(enc, title, isPRS)
	if enc.project ~= RUWIKISOURCE then
		return ':' .. enc.projectCode .. enc.prefix .. title .. enc.suffix
	end
	local linkVT = nil
	local linkDO = nil
	if enc.id == "Q1970746" then -- workaround для ручного параметра ТСД=
		linkVT = "ТСД/" .. title
		linkDO = linkVT .. "/ДО"
	else
		linkVT = string.gsub(enc.titleVT, "$1", title)
		if enc.titleDO then
			linkDO = string.gsub(enc.titleDO, "$1", title)
		end
	end
	if isPRS then
		if mw.title.new(linkDO, 0).exists then
			return linkDO
		elseif mw.title.new(linkVT, 0).exists then
			return linkVT
		else
			return linkDO
		end
	else
		if mw.title.new(linkVT, 0).exists then
			return linkVT
		elseif mw.title.new(linkDO, 0).exists then
			return linkDO
		else
			return linkVT
		end
	end
end

-- заполняет список `page.enc_wikilinks` викиссылками на энциклопедии
local function make_list_encyclopedias_links(frame)
	local args = page.args
	local enc_links_raw = page.enc_links_raw

	for _, enc in pairs(encyclopediasData) do
		local dictArgName = enc.argument
		local id = enc.id
		local is_title_matches_enc_link = (enc.project == RUWIKISOURCE and (wikidata.is_match_pagenames(page.title, enc.titleVT) or (enc.titleDO and wikidata.is_match_pagenames(page.title, enc.titleDO))))
		if not is_title_matches_enc_link then -- исключение страниц энциклопедий идентичных ссылке. TODO: Возможно это дублирует схожий метод из подмодуля ../wikidata : sitlinks[entity.id] = nil  -- исключить id базовой энциклопедии
			local manual_links, wd_links = {}, {}

			-- ссылки из Викиданных
			local ids = page.encyclopedias_ids[dictArgName]
			if ids then
				for _, eid in pairs(ids) do
					local link = wikidata.getLink(enc, eid, page.isPRS)
					if link then
						table.insert(wd_links, link)
					end
				end
			end

			-- ссылки из параметров шаблона
			local value = args[dictArgName]
			if is(value) then
				for elem in mw.text.gsplit(value, '%s*~%s*') do -- несколько значений в одном параметре
					local link = getPageTitleFromArgument(enc, elem, page.isPRS)
					table.insert(manual_links, link)
				end
			end

			-- добавление ссылок
			if #manual_links > 0 or #wd_links > 0 then
				-- сырые ссылки, разделённые по источникам, используются ниже и для категоризации
				enc_links_raw[dictArgName] = { manual_links = manual_links, wd_links = wd_links, enc = enc }

				-- викификация и фильтрация ссылок

				-- любые из шаблона, могут включать ссылки с якорями #
				for _, mlink in pairs(manual_links) do
					page:add_enc(util.make_wikilink(mlink, enc.title))
				end

				-- из Викиданных, исключая дубли шаблонных ссылок и содержащие #
				for _, wlink in pairs(wd_links) do
					local wlink_eq_mlink = false
					for _, mlink in pairs(manual_links) do
						if mlink == wlink or mw.ustring.match(mlink, '^(.+)#') then
							wlink_eq_mlink = true
							break
						end
					end
					if not wlink_eq_mlink then
						page:add_enc(util.make_wikilink(wlink, enc.title))
					end
				end
			end
		end
	end

	-- внешние сайты
	for enc_name, site in pairs(page.external_sites) do
		page:add_enc(site.wikilink)
	end

	-- mw.logObject(page, "page")
	return page
end


function p.get_data(frame)
	page:init(frame)

	page = wikidata.get_links(page)
	-- mw.logObject(page, "page")

	make_list_projects_links()
	p.render_projects_links()

	make_list_encyclopedias_links()
	if page.is_author_page then
		-- рендеринг тега для отображения размещён в [[Модуль:Обавторе]]
	else
		p.renderEncyclopedias_aboutText()
	end

	page = require("Модуль:Источники по теме/Категоризация").get_categories(page)
	
	-- mw.logObject(page, "page")
	return page
end


-- оформление строки для {{отексте}} и Модуль:Отексте
function p.render_projects_links()
	local sources = page.projects
	local result = ''
	if #sources > 0 then
		local links = {}
		for _, v in pairs(sources) do
			local project, link, lang, title = v.project, v.link, v.lang, v.title
			local is_recursive_link = page.title == title and lang == RU and project.project == WIKISOURCE
			if link and not is_recursive_link then
				local prj_title = ''; if page.isPRS then prj_title = project.titlePRS else prj_title = project.title end
				local lang_label = ''; if lang and lang ~= RU then lang_label = "&thinsp;<sup><small>(" .. lang .. ")</small></sup>" end
				table.insert(links, '<li><span class="about-extlink">' .. "[[Файл:" .. project.logo .. "|13px|link=]]&nbsp;[[" .. link .. "|" .. prj_title .. "]]" .. lang_label .. '</span></li>')
			end
		end
		if #links > 0 then
			result = '<li id="menu-wiki">' .. "'''Википроекты'''<ul>" .. table.concat(links, '\n') .. '</ul></li>'
		end
	end
	
	-- mw.logObject(result, "result")
	page.projects_links_rendered = result
	return result
end

-- оформление ссылок для шапки {{отексте}}, вызывается из Модуль:Отексте
function p.renderEncyclopedias_aboutText()
	local result = ''
	local wikilinks = page.enc_wikilinks
	
	if #wikilinks > 0 then
		local _links = {}; for _, v in pairs(wikilinks) do table.insert(_links, "<li>" .. v .. "</li>") end
		local links = table.concat(_links, '\n')
		local desc; if page.isPRS then desc = 'Энциклопедіи' else desc = 'Энциклопедии' end
		result = '<li id="menu-dicts">' .. "'''" .. desc .. "'''<ul>" .. links .. '</ul></li>'
	end
	page.enc_links_rendered = result
	return result
end


-- проверка переменной, возврат её или nil если пустая
function is(var) if (var == '' or var == nil) then return nil else return var end end

return p