Module:workgroup ping

This module page is in beta stage.
Its interface has been stabilised, but the module page may still contain errors. Do not deploy widely until the module page has been tested.

This module implements {{wgping}}, which enables one to quickly and conveniently notify users interested in a particular topic of a discussion. See documentation for {{wgping}} for details.

For workgroup membership data, see Module:workgroup ping/data.


local export = {}
local m_data = mw.loadData('Module:workgroup ping/data')

function export.ping(frame)
	local parent_frame = frame:getParent()
	local args = parent_frame.args
	local users, errors = {}, {}
	local had_ind = false
	
	local function gather_users(myself)
		local had_user = { }
		if myself and not args.fix then
			had_user[myself] = true
		end

		local empty = true		
		for _, wgid in ipairs(args) do
			local _, user = wgid:match("^User:(.*)")
			if user then
				table.insert(users, user)
				had_user[user] = true
				had_ind = true
			else
				local data = m_data[wgid]
				if data then
					if data[1] then
						for i, user in ipairs(data) do
							if not had_user[user] then
								table.insert(users, user)
							end
						end
					else
						table.insert(errors, ('%s=empty'):format(wgid))
					end
				else
					table.insert(errors, ('%s=404'):format(wgid))
				end
			end
			empty = false
		end
		
		if empty then
			table.insert(errors, "nogroups")
		end
	end
	
	local title = mw.title.getCurrentTitle()
	if mw.isSubsting() then
		local output = { '{{wgping' }
		
		local my_username = frame:callParserFunction("REVISIONUSER", title.fullText)
		gather_users(my_username)
		
		for _, wgid in ipairs(args) do
			table.insert(output, '|' .. wgid)
		end

		for i, user in ipairs(users) do
			table.insert(output, ('|u%u=%s'):format(i, user))
		end
		
		-- [[gerrit:159800]]
		if #users > 50 then
			table.insert(errors, "limit")
		end

		for i, err in ipairs(errors) do
			table.insert(output, ('|e%u=%s'):format(i, err))
		end
		
		table.insert(output, '}}')
		return table.concat(output)
	else
		if parent_frame:getTitle() == title.fullText then
			return '<small>(Notifying [[User:Example|Example]]): </small>'	
		end

		local output = { '<small>(Notifying ' }
		local i

		-- the |u#= and |e#= parameters are for internal use only.
		i = 1
		while args['e' .. i] do
			table.insert(errors, args['e' .. i])
			i = i + 1	
		end

		i = 1
		while args['u' .. i] do
			table.insert(users, args['u' .. i])
			i = i + 1	
		end

		local is_preview = frame:preprocess("{{REVISIONID}}") == ""

		if (#users + #errors) == 0 then
			if is_preview then
				local unfmted = {}
				for _, item in ipairs(args) do
					table.insert(unfmted, '|' .. item)
					-- XXX: more detailed?
				end

				return '<strong class="error">Error: <code>{{wgping}}</code> must be [[mw:Manual:Substitution|substituted]]! ' ..
					'Use <code>{{subst:wgping' .. table.concat(unfmted) .. '}}</code> instead</strong>'
			else
				table.insert(output, "[[Category:unsubstituted wgping]]")
				table.insert(errors, "subst")
				gather_users()
			end
		end

		for i, user in ipairs(users) do
			table.insert(output, ('%s[[User:%s|%s]]'):format(
				(i == 1) and "" or ", ",
				user, user
			))
		end

		local function explain_error(e)
			if e == "subst" then
				return "template was not substituted; please substitute passing |fix=1"
			elseif e == "nogroups" then
				return "no workgroups specified"
			else
				local g, e = e:match("(.-)=(.*)")
				if e == "404" then
					return "group '" .. g .. "' does not exist"
				elseif e == "empty" then
					return "group '" .. g .. "' is empty"
				elseif e == "limit" then
					return "more than 50 users to notify: notification will not work"
				end
			end
			return "unknown error '" .. e .. "'"
		end
	
		if #errors > 0 then
			if is_preview then
				table.insert(output, '; <strong class="error">errors: ')
				for i, e in ipairs(errors) do
					table.insert(output, ((i == 1) and "" or ", ") .. explain_error(e))
				end
				table.insert(output, '</strong>')
			else
				table.insert(output, '; <span style="border-bottom: 1px dotted red; color: red; font-weight: bold;" title="')
				for i, e in ipairs(errors) do
					table.insert(output, ((i == 1) and "" or ", ") .. explain_error(e))
				end
				table.insert(output, '">errors</span>')
			end
		end

		table.insert(output, '): </small>')
		return table.concat(output)
	end
end

function export.show_list(frame)
	local categories_list = {}
	local categories_map = {}
	local wg_codes, wg_aliases = {}, {}
	
	for key, data in pairs(m_data) do
		if type(data) == 'string' then
			if not wg_aliases[data] then
				wg_aliases[data] = {}	
			end
			table.insert(wg_aliases[data], ("<code>%s</code>"):format(key))
		else
			local categ = data.category or ""
			if not categories_map[categ] then
				local newcat = { desc = categ, groups = {} }
				table.insert(categories_list, newcat)
				categories_map[categ] = newcat
			end
			categ = categories_map[categ]
			wg_codes[data] = key
			
			table.insert(categ.groups, data)
		end
	end

	table.sort(categories_list, function (apple, orange)
		return apple.desc < orange.desc
	end)
	
	local output = {
		'{| class="wikitable" style="width: 90%;" \n|-\n! style="width: 8em;" | Shortcut(s) \n! Group name \n! Members'
	}
	for _, categ in pairs(categories_list) do
		table.sort(categ.groups, function (apple, orange)
			return apple.desc < orange.desc
		end)

		if categ.desc ~= "" then
			table.insert(output, '|-\n! colspan="3" style="font-size: smaller; text-align: left;" | ' .. categ.desc)
		end
		for _, wgdata in pairs(categ.groups) do
			local members = {}
			local had_users, had_tfs = {}, {}
			
			if type(wgdata) ~= 'table' then
				return	
			end
			
			for _, user in ipairs(wgdata) do
				if not had_users[user] then
					table.insert(members, ("[[User:%s|%s]]"):format(user, user))
					had_users[user] = true
				end
			end

			local wg_code = ("<code>%s</code>"):format(wg_codes[wgdata])
			if wg_aliases[wg_codes[wgdata]] then
				wg_code = ('%s<br/><small>(%s)</small>'):format(wg_code, table.concat(wg_aliases[wg_codes[wgdata]], ", "))
			end
			table.insert(output, ('|- id="%s" style="vertical-align: top;" \n|%s\n|%s\n|%s <small>(%u %s)</small>'):format(
				wg_codes[wgdata], wg_code, wgdata.desc, table.concat(members, ", "), #members, ((#members == 1) and 'user' or 'users')
			))
		end
	end
	table.insert(output, "|}")
	return table.concat(output, "\n")
end

return export