Module:RelatedNews: Difference between revisions

From TwogPedia
No edit summary
No edit summary
Tag: Reverted
Line 1: Line 1:
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs
local cargo = mw.ext.cargo
local cargo = mw.ext.cargo
local p = {}
local p = {}


local function sanitizeForSQL(input)
local function sanitize(input)
    if not input then return '' end
if not input then return '' end
    -- Remove control characters
return mw.ustring.gsub(input, '"', '""')
    input = mw.ustring.gsub(input, '[%z\1-\31\127]', '')
    -- Remove U+2060 (word joiner) and U+200B (zero-width space)
    input = mw.ustring.gsub(input, '[\226\129\160\226\128\139]', '')
    -- Correct way: double the quotes (SQL standard)
    input = mw.ustring.gsub(input, '"', '""')
    return input
end
end


function p.main(frame)  
local function splitCsv(str)
    local args = getArgs(frame)
if not str then return {} end
   
local parts = {}
    local relatedTagsNews = getRelatedTagNews(args.tags, mw.title.getCurrentTitle().text)
for word in mw.text.gsplit(str, ',') do
   
table.insert(parts, mw.text.trim(word))
    local newsItemContainer = mw.html.create('div'):addClass('related__news')
end
return parts
end


    local iconHtml = '[[File:News-icon.png|30px|alt=Related News Icon]]'
local function getContentSnippet(content)
    local iconElement = mw.html.create('h3'):addClass('news-icon'):wikitext(iconHtml .. ' Related news')
local words = mw.text.split(content or '', ' ')
   
return table.concat(words, ' ', 1, math.min(20, #words)) .. (#words > 20 and '...' or '')
    local titleContainer = mw.html.create('div'):addClass('related__news__title-container'):attr('style', 'display: flex; align-items: center;')
end
    titleContainer:node(iconElement)


    local viewAllElement = mw.html.create('div'):addClass('related__news__view-all'):attr('id', 'view-all-button'):wikitext('View All')
local function formatDate(dateStr)
    titleContainer:node(viewAllElement)
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 container = mw.html.create('div'):addClass('related__news__container'):node(titleContainer):node(newsItemContainer)
local function getRelatedNewsBy(tagsOrCats, field, exclude, limit)
local terms = splitCsv(tagsOrCats)
if #terms == 0 then return {} end


    if #relatedTagsNews > 0 then
local where = {}
        for i = 1, #relatedTagsNews do
if exclude then
            addArticleToContainer(relatedTagsNews[i], newsItemContainer)
for _, name in ipairs(exclude) do
        end
table.insert(where, '_pageName != "' .. sanitize(name) .. '"')
    end
end
   
end
    if #relatedTagsNews < 5 then
        local relatedCatNews = getRelatedCatNews(args.categories, mw.title.getCurrentTitle().text, relatedTagsNews, 5 - #relatedTagsNews)
        for i = 1, #relatedCatNews do
            addArticleToContainer(relatedCatNews[i], newsItemContainer)
        end
    end


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


function addArticleToContainer(result, container)
table.insert(where, '(' .. table.concat(orClause, ' OR ') .. ')')
    local image = result.image or 'News placeholder.png'
    local link = result._pageName
    local category = result.category and mw.text.split(result.category, ',')[1] or 'Uncategorized'


    local categoryClassMap = {
local args = {
        ['News'] = 'news-category-news',
where = table.concat(where, ' AND '),
        ['Transfer Market'] = 'news-category-transfer',
orderBy = 'date DESC',
        ['Drama'] = 'news-category-drama',
limit = limit or 5
        ['Business'] = 'news-category-business',
}
        ['Sponsorships'] = 'news-category-sponsorships'
    }


    local categoryClass = categoryClassMap[category] or 'default-category-class'
return cargo.query('News', '_pageName, date, tags, image, category, content', args)
   
end
    local date = result.date and formatDate(result.date) or 'Unknown date'
   
    local title = mw.html.create('div'):addClass('news-title-twogpedia'):wikitext(mw.ext.displaytitle.get(result._pageName))


    local metaContainer = mw.html.create('div'):addClass('news-meta-container')
local function renderArticle(result)
    local categoryElement = mw.html.create('p'):addClass('news-meta news-category-meta ' .. categoryClass):wikitext(category)
local link = result._pageName
    local dateElement = mw.html.create('p'):addClass('news-meta news-date'):wikitext(date)
local image = result.image or 'News placeholder.png'
   
local category = mw.text.split(result.category or 'Uncategorized', ',')[1]
    metaContainer:node(categoryElement):node(dateElement)


    local contentSnippet = getContentSnippet(result.content)
local categoryClassMap = {
    local contentElement = mw.html.create('p'):addClass('news-content'):wikitext(contentSnippet)
['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 imageHtml = '[[File:' .. image .. '|link=' .. link .. '|800px|class=news-image|alt=News Image]]'
local wrapper = mw.html.create('div'):addClass('related__article')
   
wrapper:tag('div'):addClass('news-image-container')
    local imageContainer = mw.html.create('div'):addClass('news-image-container'):wikitext(imageHtml)
:wikitext('[[File:' .. image .. '|link=' .. link .. '|800px|class=news-image|alt=News Image]]')
   
    local newsItem = mw.html.create('div')
        :addClass('related__article')
        :node(imageContainer)
        :node(title)
        :node(contentElement)
        :node(metaContainer)


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


function getContentSnippet(content)
local meta = mw.html.create('div'):addClass('news-meta-container')
    local words = mw.text.split(content, ' ')
meta:tag('p'):addClass('news-meta news-category-meta ' .. categoryClass):wikitext(category)
    local snippet = table.concat(words, ' ', 1, math.min(20, #words))
meta:tag('p'):addClass('news-meta news-date'):wikitext(formatDate(result.date))
    return snippet .. ( #words > 20 and '...' or '' )  
wrapper:node(meta)
end


function formatDate(dateStr)
return wrapper
    local year, month, day = dateStr:match("(%d+)%-(%d+)%-(%d+)")
    local time = os.time({year = year, month = month, day = day})
    return os.date("%b %d, %Y", time)
end
end


function getRelatedTagNews(tags, pageName)
function p.main(frame)
    if not tags then return {} end
local args = getArgs(frame)
   
local currentPage = mw.title.getCurrentTitle().text
    pageName = sanitizeForSQL(pageName)
   
    local whereStr = '_pageName != "' .. pageName .. '" AND ('
    local tagsSplit = mw.text.split(tags, ',')
    for i = 1, #tagsSplit do
        if i > 1 then whereStr = whereStr .. ' OR ' end
        whereStr = whereStr .. 'tags HOLDS "' .. sanitizeForSQL(mw.text.trim(tagsSplit[i])) .. '"'
    end
    whereStr = whereStr .. ')'


    local tables = 'News'
local tagNews = getRelatedNewsBy(args.tags, 'tags', {currentPage}, 5)
    local fields = '_pageName, date, tags, image, category, content'
local newsItemContainer = mw.html.create('div'):addClass('related__news')
    local cargoArgs = {
        where = whereStr,
        orderBy = 'date DESC',
        limit = 5
    }
    local results = cargo.query(tables, fields, cargoArgs)
    return results
end


function getRelatedCatNews(categories, pageName, tagNews, missingAmount)
for _, result in ipairs(tagNews) do
    if not categories then return {} end
newsItemContainer:node(renderArticle(result))
end


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


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


    if #tagNews > 0 then
local container = mw.html.create('div'):addClass('related__news__container')
        for i = 1, #tagNews do
local titleRow = mw.html.create('div')
            whereStr = whereStr .. '_pageName != "' .. sanitizeForSQL(tagNews[i]._pageName) .. '" AND '
:addClass('related__news__title-container')
        end
:attr('style', 'display: flex; align-items: center;')
    end


    whereStr = whereStr .. '_pageName != "' .. pageName .. '" AND ('  
titleRow:tag('h3'):addClass('news-icon')
    local catSplit = mw.text.split(categories, ',')
:wikitext('[[File:News-icon.png|30px|alt=Related News Icon]] Related news')
    for i = 1, #catSplit do
titleRow:tag('div'):addClass('related__news__view-all'):attr('id', 'view-all-button'):wikitext('View All')
        if i > 1 then whereStr = whereStr .. ' OR ' end
        whereStr = whereStr .. 'category HOLDS "' .. sanitizeForSQL(mw.text.trim(catSplit[i])) .. '"'
    end
    whereStr = whereStr .. ')'


    local tables = 'News'
container:node(titleRow):node(newsItemContainer)
    local fields = '_pageName, date, tags, image, category, content'
return tostring(container)
    local cargoArgs = {
        where = whereStr,
        orderBy = 'date DESC',
        limit = missingAmount
    }
    local results = cargo.query(tables, fields, cargoArgs)
    return results
end
end


return p
return p

Revision as of 21:27, 7 May 2025

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