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

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

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

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

Использует:

Подмодули:

Функции

  • 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, index)
	local d = p.set_priority_links(manual, wd)
	if not self.projects[index] then self.projects[index] = { label = self.sitelinks[index].label, id = self.sitelinks[index].id, links = {} } end
	table.insert(self.projects[index].links, { 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
	for index, section in ipairs(page.sitelinks) do
		local sitelinks = section.data
		--mw.logObject(sitelinks,"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
				if project.monolanguage then
					manual.lang, manual.title, manual.link = nil, title, util.make_interlink(prj_code, nil, title)
				else
					manual.lang, manual.title, manual.link = lang, title, util.make_interlink(prj_code, lang, title)
				end
			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, index)
				
			elseif projectCode == 'wiki' then
				-- если нет ссылок на Википедию, то ссылка на Википедию по параметру ПОИСК
				local searchString = args['ПОИСК'] or page.frame1.args.search
				-- mw.logObject(page.projects, "page.projects")
				-- mw.logObject(index, "index")
				if searchString and #searchString > 0 then
					manual.link = ':w:Special:Search/' .. searchString
					manual.lang = RU
					page:add_project(project, manual, wd, index)
				end
			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.projects, "page.projects")
	return page
end


-- оформление меню со ссылками, как html в виде строки, для {{отексте}} и Модуль:Отексте
function p.render_projects_links()
	local sources = page.projects
	-- mw.logObject(page.projects, "page.projects")
	local menu = ''
	if #sources > 0 then
		for i, w in ipairs(sources) do
			local links = {}
			local prefix = '<li class="submenu-label">' .. w.label .. '<ul>'
			local suffix = '</ul></li>'
			if #sources == 1 then prefix, suffix = '', '' end
			for _, v in pairs(w.links) 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 b = ""; if project.project == "wikidata" then b = "'''" end
					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 id="menu-wiki-'..project.project..'"><span class="about-extlink">' .. b .. "[[Файл:" .. project.logo .. "|13px|link=]]&nbsp;[[" .. link .. "|" .. prj_title .. "]]" .. b .. lang_label .. '</span></li>')
				end
			end
			if #links > 0 then
				menu = menu .. prefix .. table.concat(links, '\n') .. suffix
			end
		end
	end
	
	-- mw.logObject(menu, "menu")
	if is(menu) then menu = '<li id="menu-wiki">' .. "'''Википроекты'''<ul>" .. menu .. '</ul></li>' end
	page.projects_links_rendered = menu
	return menu
end

-- оформление меню со ссылками, как html в виде строки, для шапки {{отексте}}, вызывается из Модуль:Отексте
function p.renderEncyclopedias_aboutText()
	local menu = ''
	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 hr = "<hr style='width:100%%'>"
		local pre = "<div class=flexsep style='display:flex;justify-content:center;align-items:center;user-select:none'>" .. hr .. "<span style='font-size:smaller;padding:0 5px'>"
		local post = "</span>" .. hr .. "</div>"
		local otherlang, external = pre .. "на других языках" .. post, pre .. "внешние ссылки" .. post
		links = links:gsub( "(<li>%[%[:)", "<li class=lisep>" .. otherlang .. "</li>\n%1", 1 )
		links = links:gsub( "(<li>%[http)", "<li class=lisep>" .. external .. "</li>\n%1", 1 )
		local desc; if page.isPRS then desc = 'Энциклопедіи' else desc = 'Энциклопедии' end
		menu = '<li id="menu-dicts">' .. "'''" .. desc .. "'''<ul>" .. links .. '</ul></li>'
	end
	page.enc_links_rendered = menu
	-- mw.logObject(menu,"menu")
	return menu
end


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

return p