Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

Jump to content

Module:Sandbox/Aidan9382/DiscussionOverview

From Wikipedia, the free encyclopedia

--[=[
A module designed to provide an overall summary and some statistics on a discussion board
Inspired by, and partially borrowed from, [[Module:Sandbox/Smalljim/DiscussionIndexTest]]
--]=]
local Redirect = require("Module:Redirect")
local p = {}

table.find = function(t,o) --Used to luau, so assumed this existed. Heres a quick version
	for a,b in next,t do
		if b == o then
			return true
		end
	end
	return false
end

local lang = mw.getContentLanguage()
local function getTime(timestamp)
	return tonumber(lang:formatDate("U",timestamp))
end

local function concatUsers(users)
	local final = ""
	for _,user in next,users do
		if string.match(user,"^%d+%.%d+%.%d+%.%d+$") or string.match(user,"^%x+:[%x:]+$") or string.match(user, "^~%d%d%d%d%-%d%d%d%d%d%-%d%d%d$") then --Lazy but mostly working basic IPv6 regex
			final = final .. "[[Special:Contributions/"..user.."|"..user.."]], "
		else
			final = final .. "[[:User:"..user.."|"..user.."]], "
		end
	end
	return string.sub(final,1,-3)
end

--Specialised version of Sandbox/Smalljim/ParsePageTest.formatDateDiff that just does hours (better for sorting cause im not learning wikitables)
local function formatDateDiff( date_diff )
    return tonumber( math.floor(date_diff/(6*60))/10 ) .. ' hours';
end

--Specialised version of Transcluder.getSections, using a similar design
local function getSectionData(text)
	local sections = {}
	text = "\n"..text.."\n== "
	while true do
		local section,content = string.match(text,"\n==%s*([^=]-)%s*==\n(.-)\n==[^=]")
		if not section then
			break
		end
		text = string.sub(text,string.find(text,content,1,true)+#content,-1)
		sections[#sections+1] = {name=section,content=content}
	end
	return sections
end

--This is a bloody mess of a mix of ideas, but it mostly works, so good enough
local function getUserMentions(text)
	--Returns a list of users, and if they were considered a "participant" or someone who was just mentioned
	local mentions = {}
	text = mw.text.decode(text,true)
	--Timestamp is %d%d:%d%d, %d%d? %w+ %d%d%d%d %(UTC%)) but we allow some (minor) leniancy for those who just slightly edit their dates (why?) so that it still picks up
	local timestampRegex = "((%d%d:%d%d, %d%d? %w+,? %d%d%d%d) %(UTC%))"
	local userRegex = "(%[%[:?[Uu]ser:([^#|%]]+))"
	local userTalkRegex = "(%[%[:?[Uu]ser[ _][Tt]alk:([^#|%]]+))"
	local userContribRegex = "(%[%[:?[Ss]pecial:[Cc]ontributions/([^#|%]]+))"
	for line in string.gmatch(text,"[^\n]+") do
		--Split by line and check all content on said line. This assumes all signatures never use newlines, which they should not be doing anyways.
		--Bar of entry for being labelled a "participant" is a valid timestamp along with their user/usertalk/contribs
		--Users can be noted as being both a participant and a mention during the data, so be smart in using this data
		local usersOnThisLine = {}
		for _,reg in next,{userRegex,userTalkRegex,userContribRegex} do
			local index = 1
			while true do
				local targetText = string.sub(line,index,-1)
				local wholeText,identifier = string.match(targetText,reg)
				if not wholeText then
					break
				end
				identifier = string.gsub(identifier,"_"," ")
				if not string.find(identifier,"/") then --Avoid subpage nonsense
					usersOnThisLine[string.find(targetText,reg)] = identifier
				end
				index = index + string.find(targetText,reg) + #wholeText
			end
		end
		--Start associating timestamps with users
		local index = 1
		local pindex = {} --Lazy coding
		local participants = {}
		while true do
			local targetText = string.sub(line,index,-1)
			local wholeText,identifier = string.match(targetText,timestampRegex)
			if not wholeText then
				break
			end
			--Backtrack through the text for a mention
			local timestampLocation = string.find(targetText,identifier)
			local user,where
			for i = timestampLocation,1,-1 do
				user,where = usersOnThisLine[i],i
				if user then
					break
				end
			end
			if user then
				participants[#participants+1] = {user=user,when=identifier,participated=true}
				pindex[user] = true
			else --else: be confused as hell
				mw.log("Timestamp had no associated users on its line (TS "..wholeText..")")
			end
			index = index + timestampLocation + #wholeText
		end
		local pings = {}
		for _,user in next,usersOnThisLine do
			if not pings[user] and not pindex[user] then --If they participated on a line, just ignore all pings
				pings[user] = true
			end
		end
		--Integrate the new data
		for user,_ in next,pings do
			mentions[#mentions+1] = {user=user,participated=false}
		end
		for _,userData in next,participants do
			mentions[#mentions+1] = userData
		end
	end
	return mentions
end

function p.main(frame)
	local page = frame.args[1] or frame.args.page
	assert(type(page)=="string","Invalid or no page provided")
	
	local _, text = xpcall(function()
		return mw.title.new(Redirect.getTarget(page) or page):getContent()
	end, function(err)
		error(debug.traceback())
	end)
	
	local sections = getSectionData(text)
	local tableContent = '{| class="wikitable sortable"\n! style="text-align:center" colspan=6 | Conversation summary for [[' .. page .. ']]' ..
		'<span style="position:absolute;right:2em;font-size:90%">[[Module:Sandbox/Aidan9382/DiscussionOverview|v]]</span>' ..
		'\n|-\n! Section !! Initiator !! Last Comment !! Size !! Participants !! Mentions'
	for _,section in next,sections do
		local sanitisedName = string.gsub(string.gsub(mw.uri.anchorEncode(frame:preprocess(section.name)),"%[%[:?[^|]-|([^%]]-)]]","%1"),"%[%[:?([^%]]-)]]","%1")
		local displayName = string.gsub(string.gsub(frame:preprocess(section.name),"%[%[:?[^|]-|([^%]]-)]]","%1"),"%[%[:?([^%]]-)]]","%1")
		local wikilinkAnchor = "[[:"..page.."#"..sanitisedName.."|"..displayName.."]]"
		local membersInText = getUserMentions(section.content)
		local uniqueParticipants = {}
		for _,userData in next,membersInText do
			if userData.participated and not table.find(uniqueParticipants,userData.user) then
				uniqueParticipants[#uniqueParticipants+1] = userData.user
			end
		end
		local mentionedUsers = {}
		for _,userData in next,membersInText do
			if not userData.participated and not table.find(uniqueParticipants,userData.user) and not table.find(mentionedUsers,userData.user) then
				mentionedUsers[#mentionedUsers+1] = userData.user
			end
		end
		local now = getTime()
		local orderedComments = {}
		for _,userData in next,membersInText do
			if userData.participated then
				local when = getTime(userData.when)
				if now > when then --Ensure comment is from the past
					if #orderedComments == 0 then
						orderedComments[#orderedComments+1] = {user=userData.user,when=when}
					else
						for i = 1,#orderedComments do
							local comment = orderedComments[i]
							if when < comment.when then
								for i2 = #orderedComments+1,i,-1 do
									orderedComments[i2] = orderedComments[i2-1]
								end
								orderedComments[i] = {user=userData.user,when=when}
								break
							end
							if i == #orderedComments then
								orderedComments[#orderedComments+1] = {user=userData.user,when=when} --Reached the end, latest comment
								break
							end
						end
					end
				end
			end
		end
		
		local firstComment,lastComment
		if #orderedComments == 0 then
			firstComment = "N/A"
			lastComment = "N/A"
		elseif #orderedComments == 1 then
			firstComment = formatDateDiff(now-orderedComments[1].when) .. " ago" .. "<br>" .. concatUsers({orderedComments[1].user})
			lastComment = "N/A"
		else
			firstComment = formatDateDiff(now-orderedComments[1].when) .. " ago" .. "<br>" .. concatUsers({orderedComments[1].user})
			lastComment = formatDateDiff(now-orderedComments[#orderedComments].when) .. " ago" .. "<br>" .. concatUsers({orderedComments[#orderedComments].user})
		end
		local participants = #uniqueParticipants .. ": " .. concatUsers(uniqueParticipants)
		local mentions = #mentionedUsers .. ": " .. concatUsers(mentionedUsers)
		local sectionContent = "\n|-\n| "..wikilinkAnchor.." || "..firstComment.." || "..lastComment.." || "..#section.content.." || "..participants.." || "..mentions
		tableContent = tableContent .. sectionContent
	end
	return tableContent .. "\n|}"
end

function p.maindev(frame)
	local content = p.main(frame)
	return content .. "\n\n" .. frame:extensionTag("syntaxhighlight",content,{lang="html5"})
end

p.getSectionData = getSectionData
p.getUserMentions = getUserMentions

return p