Module:RelatedNews: Difference between revisions

From TwogPedia
No edit summary
Tag: Manual revert
Speed Related news: skip displaytitle.get, 400px thumbs, drop unused tags field
Line 6: Line 6:
local function sanitizeForSQL(input)
local function sanitizeForSQL(input)
     if not input then return '' end
     if not input then return '' end
    -- Remove control characters
     input = mw.ustring.gsub(input, '[%z\1-\31\127]', '')
     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]', '')
     input = mw.ustring.gsub(input, '[\226\129\160\226\128\139]', '')
    -- Correct way: double the quotes (SQL standard)
     input = mw.ustring.gsub(input, '"', '""')
     input = mw.ustring.gsub(input, '"', '""')
     return input
     return input
end
end


function p.main(frame)  
local function displayName(pageName)
    local name = pageName or ''
    name = mw.ustring.gsub(name, '^News/', '')
    name = mw.ustring.gsub(name, '_', ' ')
    return name
end
 
function p.main(frame)
     local args = getArgs(frame)
     local args = getArgs(frame)
   
 
     local relatedTagsNews = getRelatedTagNews(args.tags, mw.title.getCurrentTitle().text)
     local relatedTagsNews = getRelatedTagNews(args.tags, mw.title.getCurrentTitle().text)
   
 
     local newsItemContainer = mw.html.create('div'):addClass('related__news')
     local newsItemContainer = mw.html.create('div'):addClass('related__news')


     local iconHtml = '[[File:News-icon.png|30px|alt=Related News Icon]]'
     local iconHtml = '[[File:News-icon.png|30px|alt=Related News Icon]]'
     local iconElement = mw.html.create('h3'):addClass('news-icon'):wikitext(iconHtml .. '<span style="margin-left: 10px;">Related news</span>')
     local iconElement = mw.html.create('h3'):addClass('news-icon'):wikitext(iconHtml .. '<span style="margin-left: 10px;">Related news</span>')
   
 
     local titleContainer = mw.html.create('div'):addClass('related__news__title-container'):attr('style', 'display: flex; align-items: center;')
     local titleContainer = mw.html.create('div'):addClass('related__news__title-container'):attr('style', 'display: flex; align-items: center;')
     titleContainer:node(iconElement)
     titleContainer:node(iconElement)


    local viewAllElement = mw.html.create('span')
    local viewAllElement = mw.html.create('span')
         :addClass('related__news__view-all')
         :addClass('related__news__view-all')
         :attr('id', 'view-all-button')
         :attr('id', 'view-all-button')
Line 42: Line 46:
         end
         end
     end
     end
   
 
     if #relatedTagsNews < 5 then
     if #relatedTagsNews < 5 then
         local relatedCatNews = getRelatedCatNews(args.categories, mw.title.getCurrentTitle().text, relatedTagsNews, 5 - #relatedTagsNews)
         local relatedCatNews = getRelatedCatNews(args.categories, mw.title.getCurrentTitle().text, relatedTagsNews, 5 - #relatedTagsNews)
Line 67: Line 71:


     local categoryClass = categoryClassMap[category] or 'default-category-class'
     local categoryClass = categoryClassMap[category] or 'default-category-class'
   
 
     local date = result.date and formatDate(result.date) or 'Unknown date'
     local date = result.date and formatDate(result.date) or 'Unknown date'
      
 
  local title = mw.html.create('div')
     local title = mw.html.create('div')
    :addClass('news-title-twogpedia')
        :addClass('news-title-twogpedia')
    :wikitext('[[' .. result._pageName .. '|' .. mw.ext.displaytitle.get(result._pageName) .. ']]')
        :wikitext('[[' .. result._pageName .. '|' .. displayName(result._pageName) .. ']]')


     local metaContainer = mw.html.create('div'):addClass('news-meta-container')
     local metaContainer = mw.html.create('div'):addClass('news-meta-container')
     local categoryElement = mw.html.create('p'):addClass('news-meta news-category-meta ' .. categoryClass):wikitext(category)
     local categoryElement = mw.html.create('p'):addClass('news-meta news-category-meta ' .. categoryClass):wikitext(category)
     local dateElement = mw.html.create('p'):addClass('news-meta news-date'):wikitext(date)
     local dateElement = mw.html.create('p'):addClass('news-meta news-date'):wikitext(date)
   
 
     metaContainer:node(categoryElement):node(dateElement)
     metaContainer:node(categoryElement):node(dateElement)


Line 83: Line 87:
     local contentElement = mw.html.create('p'):addClass('news-content'):wikitext(contentSnippet)
     local contentElement = mw.html.create('p'):addClass('news-content'):wikitext(contentSnippet)


     local imageHtml = '[[File:' .. image .. '|link=' .. link .. '|800px|class=news-image|alt=News Image]]'
    -- 400px matches related-card CSS; 800px forced a heavy thumb transform on parse.
   
     local imageHtml = '[[File:' .. image .. '|link=' .. link .. '|400px|class=news-image|alt=News Image]]'
 
     local imageContainer = mw.html.create('div'):addClass('news-image-container'):wikitext(imageHtml)
     local imageContainer = mw.html.create('div'):addClass('news-image-container'):wikitext(imageHtml)
   
 
     local newsItem = mw.html.create('div')
     local newsItem = mw.html.create('div')
         :addClass('related__article')
         :addClass('related__article')
Line 101: Line 106:
         return ''
         return ''
     end
     end
   
 
     local plain = mw.ustring.gsub(content, '<[^>]+>', '')
     local plain = mw.ustring.gsub(content, '<[^>]+>', '')
   
 
     plain = mw.ustring.gsub(plain, "'''", '')
     plain = mw.ustring.gsub(plain, "'''", '')
     plain = mw.ustring.gsub(plain, "''", '')
     plain = mw.ustring.gsub(plain, "''", '')
     plain = mw.ustring.gsub(plain, '^%s+', '')
     plain = mw.ustring.gsub(plain, '^%s+', '')
     plain = mw.ustring.gsub(plain, '%s+$', '')
     plain = mw.ustring.gsub(plain, '%s+$', '')
   
 
     local words = mw.text.split(plain, '%s+')
     local words = mw.text.split(plain, '%s+')
     local maxWords = 20
     local maxWords = 20
Line 115: Line 120:
         table.insert(snippetWords, words[i])
         table.insert(snippetWords, words[i])
     end
     end
   
 
     local snippet = table.concat(snippetWords, ' ')
     local snippet = table.concat(snippetWords, ' ')
     if #words > maxWords then
     if #words > maxWords then
         snippet = snippet .. '...'
         snippet = snippet .. '...'
     end
     end
   
 
     return snippet
     return snippet
end
end
Line 132: Line 137:
function getRelatedTagNews(tags, pageName)
function getRelatedTagNews(tags, pageName)
     if not tags then return {} end
     if not tags then return {} end
   
 
     pageName = sanitizeForSQL(pageName)
     pageName = sanitizeForSQL(pageName)
   
 
     local whereStr = '_pageName != "' .. pageName .. '" AND ('  
     local whereStr = '_pageName != "' .. pageName .. '" AND ('
     local tagsSplit = mw.text.split(tags, ',')
     local tagsSplit = mw.text.split(tags, ',')
     for i = 1, #tagsSplit do
     for i = 1, #tagsSplit do
Line 144: Line 149:


     local tables = 'News'
     local tables = 'News'
     local fields = '_pageName, date, tags, image, category, content'
     local fields = '_pageName, date, image, category, content'
     local cargoArgs = {
     local cargoArgs = {
         where = whereStr,
         where = whereStr,
Line 167: Line 172:
     end
     end


     whereStr = whereStr .. '_pageName != "' .. pageName .. '" AND ('  
     whereStr = whereStr .. '_pageName != "' .. pageName .. '" AND ('
     local catSplit = mw.text.split(categories, ',')
     local catSplit = mw.text.split(categories, ',')
     for i = 1, #catSplit do
     for i = 1, #catSplit do
Line 176: Line 181:


     local tables = 'News'
     local tables = 'News'
     local fields = '_pageName, date, tags, image, category, content'
     local fields = '_pageName, date, image, category, content'
     local cargoArgs = {
     local cargoArgs = {
         where = whereStr,
         where = whereStr,

Revision as of 05:30, 31 August 2026

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 sanitizeForSQL(input)
    if not input then return '' end
    input = mw.ustring.gsub(input, '[%z\1-\31\127]', '')
    input = mw.ustring.gsub(input, '[\226\129\160\226\128\139]', '')
    input = mw.ustring.gsub(input, '"', '""')
    return input
end

local function displayName(pageName)
    local name = pageName or ''
    name = mw.ustring.gsub(name, '^News/', '')
    name = mw.ustring.gsub(name, '_', ' ')
    return name
end

function p.main(frame)
    local args = getArgs(frame)

    local relatedTagsNews = getRelatedTagNews(args.tags, mw.title.getCurrentTitle().text)

    local newsItemContainer = mw.html.create('div'):addClass('related__news')

    local iconHtml = '[[File:News-icon.png|30px|alt=Related News Icon]]'
    local iconElement = mw.html.create('h3'):addClass('news-icon'):wikitext(iconHtml .. '<span style="margin-left: 10px;">Related news</span>')

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

    local viewAllElement = mw.html.create('span')
        :addClass('related__news__view-all')
        :attr('id', 'view-all-button')
        :wikitext('[[News|View All]]')

    titleContainer:node(viewAllElement)

    local container = mw.html.create('div'):addClass('related__news__container'):node(titleContainer):node(newsItemContainer)

    if #relatedTagsNews > 0 then
        for i = 1, #relatedTagsNews do
            addArticleToContainer(relatedTagsNews[i], newsItemContainer)
        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
end

function addArticleToContainer(result, container)
    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 = {
        ['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 date = result.date and formatDate(result.date) or 'Unknown date'

    local title = mw.html.create('div')
        :addClass('news-title-twogpedia')
        :wikitext('[[' .. result._pageName .. '|' .. displayName(result._pageName) .. ']]')

    local metaContainer = mw.html.create('div'):addClass('news-meta-container')
    local categoryElement = mw.html.create('p'):addClass('news-meta news-category-meta ' .. categoryClass):wikitext(category)
    local dateElement = mw.html.create('p'):addClass('news-meta news-date'):wikitext(date)

    metaContainer:node(categoryElement):node(dateElement)

    local contentSnippet = getContentSnippet(result.content)
    local contentElement = mw.html.create('p'):addClass('news-content'):wikitext(contentSnippet)

    -- 400px matches related-card CSS; 800px forced a heavy thumb transform on parse.
    local imageHtml = '[[File:' .. image .. '|link=' .. link .. '|400px|class=news-image|alt=News Image]]'

    local imageContainer = mw.html.create('div'):addClass('news-image-container'):wikitext(imageHtml)

    local newsItem = mw.html.create('div')
        :addClass('related__article')
        :node(imageContainer)
        :node(title)
        :node(contentElement)
        :node(metaContainer)

    container:node(newsItem)
end

function getContentSnippet(content)
    if not content or content == '' then
        return ''
    end

    local plain = mw.ustring.gsub(content, '<[^>]+>', '')

    plain = mw.ustring.gsub(plain, "'''", '')
    plain = mw.ustring.gsub(plain, "''", '')
    plain = mw.ustring.gsub(plain, '^%s+', '')
    plain = mw.ustring.gsub(plain, '%s+$', '')

    local words = mw.text.split(plain, '%s+')
    local maxWords = 20
    local snippetWords = {}
    for i = 1, math.min(maxWords, #words) do
        table.insert(snippetWords, words[i])
    end

    local snippet = table.concat(snippetWords, ' ')
    if #words > maxWords then
        snippet = snippet .. '...'
    end

    return snippet
end

function formatDate(dateStr)
    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

function getRelatedTagNews(tags, pageName)
    if not tags then return {} end

    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 fields = '_pageName, date, image, category, content'
    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)
    if not categories then return {} end

    pageName = sanitizeForSQL(pageName)

    local whereStr = ''

    if #tagNews > 0 then
        for i = 1, #tagNews do
            whereStr = whereStr .. '_pageName != "' .. sanitizeForSQL(tagNews[i]._pageName) .. '" AND '
        end
    end

    whereStr = whereStr .. '_pageName != "' .. pageName .. '" AND ('
    local catSplit = mw.text.split(categories, ',')
    for i = 1, #catSplit do
        if i > 1 then whereStr = whereStr .. ' OR ' end
        whereStr = whereStr .. 'category HOLDS "' .. sanitizeForSQL(mw.text.trim(catSplit[i])) .. '"'
    end
    whereStr = whereStr .. ')'

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

return p