Module:Editnotice load

--- This module is an advanced editnotice loader.
--
--  @module             Editnotice_load
--  @alias              p
--  @require            Module:Arguments
--  @require            Module:Yesno
--  @require            Module:Effective_protection_level
--  @author             [[User:Awesome Aasim]]
--  @release            alpha


local p = {}
local getArgs = require('Module:Arguments').getArgs
local cfg = require("Module:Editnotice_load/config_loader")
local pseudoNs = mw.title.new(cfg.pseudo_ns_name)
local enTypes = cfg.editnotice_types
local enNames = cfg.editnotice_names
local protLevel = require("Module:Effective protection level")._main
local yn = require("Module:Yesno")

function fillStringWithArgs(text, valArray)
	if not valArray then
		return text
	end

	local function getVal(match)
		match = tonumber(match)
		return valArray[match] or ''
	end

	return mw.ustring.gsub(text, '$([1-9][0-9]*)', getVal) .. ''
end

local function detectParameters(text)
	return text and mw.ustring.find(text, '$([1-9][0-9]*)') and true or false
end

--- Gets the notice content of any editnotice.
-- @function getNoticeContent
-- @param {table} frame the current frame
-- @param {table} title the title of the page
-- @param {table} args the processing args
-- @param {boolean} hide whether to hide the notice
-- @return {string} the contents of the editnotice
local function getNoticeContent(frame, title, args, hide)
	if hide then return '' end
	local t = mw.title.new(title)
	if t == nil or t.id == 0 or not t.exists then
		return ''
	end
	local result = frame:expandTemplate{ title = title, args = args }
	result = mw.text.trim(result)
	if result ~= '' and result ~= '-' then
		return result
	end
	return ''
end
p.getNoticeContent = getNoticeContent


--- Builds a link to any editnotice
-- @function makeLink
-- @param {table} builder the main editnotice builder
-- @param {table} links the editnotice builder containing the links
-- @param {string} target the target of the editnotice
-- @param {string} text the text to use for displaying the editnotice
-- @param {string} contents the contents of the editnotice
local function makeLink(builder, links, target, text, contents)
	local builderLinksNodeSame = builder == links
	if (contents == '' or contents == nil) and not builderLinksNodeSame then
		builder = links
		builder = builder
			:tag('li')
	else
		builder = builder:tag('span')
	end
	builder = builder
		:addClass('editnotice-link')
		:css('clear', 'both')
		:css('margin', '0px 0.8em')
		:css('padding', 0)
		:css('line-height', '1em')
		:css('font-size', '90%')
	if not ((contents == '' or contents == nil) and not builderLinksNodeSame) then
		builder = builder:css('float', 'right')
	end
	builder:wikitext(string.format('[[%s|%s]] ', target, text))
end
p.makeLink = makeLink


-- Displays any editnotice
-- @function displayEditnotice
-- @param {table} builder the main editnotice builder
-- @param {string} class the classes to add to the editnotice
-- @param {string} content the contents of the editnotice
local function displayEditnotice(builder, class, content)
	if content ~= nil and content ~= '' then
		return builder:tag('div')
			:addClass(class)
			:css('clear', 'both')
			:css('width', '100%')
			:wikitext(content)
	end
end
p.displayEditnotice = displayEditnotice


-- Gets the editnotice type of any editnotice
-- @function getEditnoticeType
-- @param {table} title a title object for the editnotice
-- @return {string} the editnotice type
function getEditnoticeType(title)
	if title.baseText == title.rootText then
		return title.subpageText
	else
		return getEditnoticeType(title.basePageTitle)
	end
end

p.getEditnoticeType = getEditnoticeType


-- Gets the page that the editnotice is stored
-- @function getEditnoticeMainPage
-- @param {table} currPage the page the editnotice is being displayed on
-- @param {table} editnoticeType the editnotice type
-- @return {string} the page which has the editnotice
function getEditnoticeMainPage(currPage, editnoticeType)
	return mw.ustring.sub(currPage.fullText, mw.ustring.len(pseudoNs.fullText .. "/" .. editnoticeType .. "/") + 1) .. ""
end

p.getEditnoticeMainPage = getEditnoticeMainPage


-- Gets the page the editnotice belongs to
-- @function p.page
-- @param {table} the calling frame
-- @return {string} the page by which the editnotice belongs
function p.page(frame)
	local args = getArgs(frame)
	local currPage = args['title'] and mw.title.new(args['title']) or mw.title.getCurrentTitle()
	local editnoticeType = p.editnotice(frame)
	local titleName = mw.ustring.sub(currPage.fullText, mw.ustring.len(pseudoNs.fullText .. "/" .. enTypes[editnoticeType] .. "/") + 1) .. ""
	if editnoticeType == "page" or editnoticeType == "protection_id" then
		local page = mw.title.new(tonumber(titleName))
		return page and page.fullText or ""
	else
		return enTypes[editnoticeType] ~= nil and titleName .. "" or ''
	end
end

function key(title, var, name)
	return title ~= nil and title ~= 0 and (var[name] or name) or nil
end


-- Gets any editnotice and its associated data
-- @function p.getEditnotice
-- @param {table} frame the processing frame
-- @param {table} noticeArgs the arguments to pass to the editnotice
-- @param {string} title the string for the page the editnotice belongs to
-- @param {string} type the editnotice type
-- @return {table} a table with the editnotice name and contents
function p.getEditnotice(frame, noticeArgs, title, type, hide)
	--- return a table of values with the editnotice
	local noticeName = pseudoNs.prefixedText .. "/" .. type .. "/" ..  title
	local noticeContent = getNoticeContent(frame, noticeName, noticeArgs, hide)
	return {
		["title"] = noticeName,
		["content"] = noticeContent
	}
end


-- Shows any editnotice
-- @function p.showEditnotice
-- @param {table} builder the builder for the editnotice
-- @param {table} links a container for all the links
-- @param {table} editnoticeData a table with editnotice data from p.getEditnotice
-- @param {string} editnoticeName the name of the editnotice
-- @param {string} editnoticeClass the class to add to the editnotice
function p.showEditnotice(builder, links, editnoticeData, editnoticeName, editnoticeClass)
	makeLink(builder, links, editnoticeData.title, editnoticeName, editnoticeData.content)
	displayEditnotice(builder, editnoticeClass, editnoticeData.content)
end


-- Generates only the contents for protection editnotices
-- @function p.protectionEditnotice
-- @param {table} frame the preprocessing frame
-- @return {string} builder the editnotice contents
function p.protectionEditnotice(frame)
	local args = getArgs(frame)
	local noticeAction = args['notice action']
	local noticeArgs = {['notice action'] = noticeAction}
	local currentTitle = args['title'] and mw.title.new(args['title']) or mw.title.getCurrentTitle()
	local builder = mw.html.create('div')
		:attr('id', 'editnotice-area')
		:addClass('editnotice-area mw-parser-output')
		:css('clear', 'both')
		:css('width', '100%')
	
	local protectionTitleNoticeData = p.getEditnotice(frame, noticeArgs, currentTitle.prefixedText, enTypes["protection"])
	if protectionTitleNoticeData.content ~= "" then
		p.showEditnotice(builder, builder, protectionTitleNoticeData, enNames["protection"], 'editnotice-protection-title')
	end
	
	local protectionNoticeData = p.getEditnotice(frame, noticeArgs, currentTitle.id .. '', enTypes["protection_id"])
	if protectionNoticeData.content ~= "" then
		p.showEditnotice(builder, builder, protectionNoticeData, enNames["protection_id"], 'editnotice-protection')
	end
	
	builder:tag('div')
		:css('clear', 'both')
	if protectionNoticeData.content == "" and protectionTitleNoticeData.content == "" then
		return ""
	end
	return builder
end


--- Gets the editnotice type
-- @function p.editnotice
-- @param {table} frame the preprocessing frame
-- @return {string} type the string corresponding to the type
function p.editnotice(frame)
	local args = getArgs(frame)
	local currPage = args['title'] and mw.title.new(args['title']) or mw.title.getCurrentTitle()
	if currPage.rootText == pseudoNs.text and currPage.nsText == pseudoNs.nsText then
		local editNoticeType = getEditnoticeType(currPage)
		local title = mw.title.new(getEditnoticeMainPage(currPage, editNoticeType))
		if editNoticeType == enTypes["protection"] then
			return key(title, args, "protection") or args["#default"] or ''
		elseif editNoticeType == enTypes["protection_id"] then
			title = mw.title.new(tonumber(getEditnoticeMainPage(currPage, editNoticeType)) or 0)
			return key(title, args, "protection_id") or args["#default"] or ''
		elseif editNoticeType == enTypes["title"] then
			return key(title, args, "title") or args["#default"] or ''
		elseif editNoticeType == enTypes["page"] then
			title = mw.title.new(tonumber(getEditnoticeMainPage(currPage, editNoticeType)) or 0)
			return key(title, args, "page") or args["#default"] or ''
		elseif editNoticeType == enTypes["group"] then
			return key(title, args, "group") or args["#default"] or ''
		elseif editNoticeType == enTypes["category"] then
			return key(title, args, "category") or args["#default"] or ''
		elseif editNoticeType == enTypes["namespace"] then
			return key(title, args, "namespace") or args["#default"] or ''
		else
			return args["#default"] or ''
		end
	end
end


--- The main entry point for the editnotice loader
-- @function p.main
-- @param {table} frame the preprocessing frame
-- @return {string} output the output of the module
function p.main(frame)
	local args = getArgs(frame)
	local noticeAction = args['notice action']
	local noticeArgs = {['notice action'] = noticeAction}
	local noText = yn(args['notext']) or false
	local currentTitle = args['title'] and mw.title.new(args['title']) or mw.title.getCurrentTitle()
	
	local builder = mw.html.create('div')
		:attr('id', 'editnotice-area')
		:addClass('editnotice-area mw-parser-output')
		:css('clear', 'both')
		:css('width', '100%')
	local hide = noText
	if noText then
		local editNoticePage = mw.title.new("MediaWiki:Editnotice-" .. tostring(currentTitle.namespace))
		if not editNoticePage.exists then
			builder
				:tag("span")
				:addClass("sysop-show")
				:tag("strong")
				:addClass("error")
				:wikitext(frame:preprocess(fillStringWithArgs(cfg.noEditnoticePage, {'[[' .. editNoticePage.fullText .. ']]', '<code><nowiki>{{</nowiki>#invoke:[[Module:Editnotice load|editnotice load]]|main<nowiki>}}</nowiki></code>'})))
			hide = false
		end
	end
	
	local links = builder:tag("table")
		:addClass('wikitable editnotice-links mw-collapsible mw-collapsed')
		:addClass(cfg.editnotice_classes)
		:css("width", "100%")
		:tag("tr")
		:tag("th")
		:wikitext(cfg.links):done():done()
		:tag("td")
		:tag("ul")
		:attr("id", "editnotice-links")
		:css("display", "block")
		:addClass('hlist')

	local editnotices = ''

	if noticeAction ~= 'view' and noticeAction ~= 'protect' then
		local namespace = currentTitle.nsText
		if namespace == '' then namespace = (cfg.mainspace or mw.site.namespaces[0].displayName) end
		local nsNoticeData = p.getEditnotice(frame, noticeArgs, namespace, enTypes["namespace"], hide)
		p.showEditnotice(builder, links, nsNoticeData, enNames["namespace"], 'editnotice-namespace')
		editnotices = editnotices .. nsNoticeData.content
	end
	if protLevel("edit", currentTitle.fullText) or 
		protLevel("move", currentTitle.fullText) or 
		protLevel("create", currentTitle.fullText) or 
		protLevel("upload", currentTitle.fullText) then
		local protectionTitleNoticeData = p.getEditnotice(frame, noticeArgs, currentTitle.prefixedText, enTypes["protection"], hide)
		if noticeAction ~= "protect" then protectionTitleNoticeData.content = "" end
		p.showEditnotice(builder, links, protectionTitleNoticeData, enNames["protection"], 'editnotice-protection-title')
		editnotices = editnotices .. protectionTitleNoticeData.content
		
		if currentTitle.id ~= 0 then
			local protectionNoticeData = p.getEditnotice(frame, noticeArgs, currentTitle.id .. '', enTypes["protection_id"], hide)
			if noticeAction ~= "protect" then protectionNoticeData.content = "" end
			p.showEditnotice(builder, links, protectionNoticeData, enNames["protection_id"], 'editnotice-protection')
			editnotices = editnotices .. protectionNoticeData.content
		end
	end

	if mw.site.namespaces[currentTitle.namespace].hasSubpages then
		local splitTitle = mw.text.split(currentTitle.prefixedText, "/")
		local groupTitle = ''
		for k,v in ipairs(splitTitle) do
			groupTitle = groupTitle .. v
			local groupNoticeData = p.getEditnotice(frame, noticeArgs, groupTitle, enTypes["group"], hide)
			p.showEditnotice(builder, links, groupNoticeData, enNames["group"] .. " (" .. groupTitle .. ")", "editnotice-group")
			editnotices = editnotices .. groupNoticeData.content
			groupTitle = groupTitle .. '/'
		end
	end
	
	if cfg.user_editnotice ~= nil and ((currentTitle:hasSubjectNamespace(2) or currentTitle:hasSubjectNamespace(3)) and not currentTitle.isSubpage) then
		--- display user page notice
		local userPageNoticeName = currentTitle.prefixedText .. '/' .. cfg.user_editnotice
		local userPageNoticeContent = getNoticeContent(frame, userPageNoticeName, noticeArgs, hide)
		makeLink(builder, builder, userPageNoticeName, enNames["user"], userPageNoticeContent)
		displayEditnotice(builder, 'usernotice-page', userPageNoticeContent)
		editnotices = editnotices .. userPageNoticeContent
	end
	
	local titleNoticeData = p.getEditnotice(frame, noticeArgs, currentTitle.prefixedText, enTypes["title"], hide)
	p.showEditnotice(builder, links, titleNoticeData, enNames["title"], "editnotice-title")
	editnotices = editnotices .. titleNoticeData.content
	
	if currentTitle.id ~= 0 then
		local pageNoticeData = p.getEditnotice(frame, noticeArgs, currentTitle.prefixedText, enTypes["page"], hide)
		p.showEditnotice(builder, links, pageNoticeData, enNames["page"], "editnotice-page")
		editnotices = editnotices .. pageNoticeData.content
	end
	
	local categories = currentTitle.categories or {}
	mw.logObject(categories)
	for k,v in ipairs(categories) do
		local categoryNoticeData = p.getEditnotice(frame, noticeArgs, v, enTypes["category"], hide)
		p.showEditnotice(builder, links, categoryNoticeData, enNames["category"] .. " (" .. v .. ")", "editnotice-page")
		editnotices = editnotices .. categoryNoticeData.content
	end
	if editnotices == '' and not noText then return '' end

	builder:tag('div')
		:css('clear', 'both')
	local success, templateStyles = pcall(function(frame)
		return frame:callParserFunction('#tag:templatestyles', {'', src = cfg.templateStyles})
	end, frame) 
	if not success then return builder end
	return tostring( builder ) .. templateStyles
end

p.cfg = cfg
return p