Module:RelatedNews

From TwogPedia
Revision as of 21:27, 7 May 2025 by Admin (talk | contribs)

Documentation for this module may be created at Module:RelatedNews/doc

local getArgs = require('Module:Arguments').getArgs
local cargo = mw.ext.cargo
local p = {}

local function sanitize(input)
	if not input then return '' end
	return mw.ustring.gsub(input, '"', '""')
end

local function splitCsv(str)
	if not str then return {} end
	local parts = {}
	for word in mw.text.gsplit(str, ',') do
		table.insert(parts, mw.text.trim(word))
	end
	return parts
end

local function getContentSnippet(content)
	local words = mw.text.split(content or '', ' ')
	return table.concat(words, ' ', 1, math.min(20, #words)) .. (#words > 20 and '...' or '')
end

local function formatDate(dateStr)
	local y, m, d = dateStr:match("(%d+)%-(%d+)%-(%d+)")
	local ok, time = pcall(os.time, {year = y, month = m, day = d})
	return ok and os.date("%b %d, %Y", time) or 'Unknown'
end

local function getRelatedNewsBy(tagsOrCats, field, exclude, limit)
	local terms = splitCsv(tagsOrCats)
	if #terms == 0 then return {} end

	local where = {}
	if exclude then
		for _, name in ipairs(exclude) do
			table.insert(where, '_pageName != "' .. sanitize(name) .. '"')
		end
	end

	local orClause = {}
	for _, val in ipairs(terms) do
		table.insert(orClause, field .. ' HOLDS "' .. sanitize(val) .. '"')
	end

	table.insert(where, '(' .. table.concat(orClause, ' OR ') .. ')')

	local args = {
		where = table.concat(where, ' AND '),
		orderBy = 'date DESC',
		limit = limit or 5
	}

	return cargo.query('News', '_pageName, date, tags, image, category, content', args)
end

local function renderArticle(result)
	local link = result._pageName
	local image = result.image or 'News placeholder.png'
	local category = mw.text.split(result.category or 'Uncategorized', ',')[1]

	local categoryClassMap = {
		['News'] = 'news-category-news',
		['Transfer Market'] = 'news-category-transfer',
		['Drama'] = 'news-category-drama',
		['Business'] = 'news-category-business',
		['Sponsorships'] = 'news-category-sponsorships'
	}
	local categoryClass = categoryClassMap[category] or 'default-category-class'

	local wrapper = mw.html.create('div'):addClass('related__article')
	wrapper:tag('div'):addClass('news-image-container')
		:wikitext('[[File:' .. image .. '|link=' .. link .. '|800px|class=news-image|alt=News Image]]')

	wrapper:tag('div'):addClass('news-title-twogpedia'):wikitext(link)
	wrapper:tag('p'):addClass('news-content'):wikitext(getContentSnippet(result.content))

	local meta = mw.html.create('div'):addClass('news-meta-container')
	meta:tag('p'):addClass('news-meta news-category-meta ' .. categoryClass):wikitext(category)
	meta:tag('p'):addClass('news-meta news-date'):wikitext(formatDate(result.date))
	wrapper:node(meta)

	return wrapper
end

function p.main(frame)
	local args = getArgs(frame)
	local currentPage = mw.title.getCurrentTitle().text

	local tagNews = getRelatedNewsBy(args.tags, 'tags', {currentPage}, 5)
	local newsItemContainer = mw.html.create('div'):addClass('related__news')

	for _, result in ipairs(tagNews) do
		newsItemContainer:node(renderArticle(result))
	end

	if #tagNews < 5 then
		local excludeList = {}
		for _, r in ipairs(tagNews) do table.insert(excludeList, r._pageName) end
		table.insert(excludeList, currentPage)

		local catNews = getRelatedNewsBy(args.categories, 'category', excludeList, 5 - #tagNews)
		for _, result in ipairs(catNews) do
			newsItemContainer:node(renderArticle(result))
		end
	end

	local container = mw.html.create('div'):addClass('related__news__container')
	local titleRow = mw.html.create('div')
		:addClass('related__news__title-container')
		:attr('style', 'display: flex; align-items: center;')

	titleRow:tag('h3'):addClass('news-icon')
		:wikitext('[[File:News-icon.png|30px|alt=Related News Icon]] Related news')
	titleRow:tag('div'):addClass('related__news__view-all'):attr('id', 'view-all-button'):wikitext('View All')

	container:node(titleRow):node(newsItemContainer)
	return tostring(container)
end

return p