Jump to content

Module:Validate gadgets

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by SD0001 (talk | contribs) at 20:20, 2 January 2024 (tidy). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local messageBox = require('Module:Message box')

local p = {}

local function arr_contains(array, val)
    for _, value in ipairs(array) do
        if value == val then
            return true
        end
    end
    return false
end

-- Valid options for things that aren't exposed to lua 
-- (unlike namespaces that can be accessed from mw.site.namespaces)
local VALID_SKINS = {'vector', 'vector-2022', 'minerva', 'timeless', 'monobook', 'modern', 'cologneblue'}
local VALID_CONTENT_MODELS = {'wikitext', 'javascript', 'css', 'json', 'MassMessage', 'Scribunto', 'sanitized-css'}
local VALID_RIGHTS = {'minoredit', 'viewmywatchlist', 'rollback'} -- not exhaustive
local VALID_ACTIONS = {'view', 'edit', 'history', 'info'} -- not exhaustive

p.validate = function (frame)
	local text = mw.title.new('MediaWiki:Gadgets-definition'):getContent()
	local lines = mw.text.split(text, '\n', false)
	local msg = '<b>Issues in gadget definitions:</b>\n'
	for _, line in ipairs(lines) do
		if line:sub(1, 1) == '*' then
			local name, warnings = p.parse_line(line)
			for _, warning in ipairs(warnings) do
				msg = msg.. '*'..name..': '..warning .. '\n'
			end
		end
	end
	return messageBox.main('ombox', {
		text = msg,
		type = 'delete'
	})
end

p.parse_line = function(def) 
	local pattern = "^%*%s*(%S+)%s*(%b[])%s*(.-)$"
	local name, opts, fileList = string.match(def, pattern)
	
	if name and opts then
	    -- Extracting the options without square brackets and trimming spaces
	    opts = opts:sub(2, -2):gsub("%s+", "")
	end
	
	local warnings = {}
	
	if string.len(name) > 255 then
		table.insert(warnings, 'Gadget name exceeds 255 bytes')
	end
	
	-- Process options string into a Lua table
    local options = {}
    for pair in opts:gmatch("%s*([^|]+)%s*|?") do
	    local key, value = pair:match("%s*([^=]+)%s*=%s*([^=|]+)%s*")
	    if key and value then
	        options[key:match("%s*(.-)%s*$")] = value:match("^%s*(.-)%s*$")
	    else
	        key = pair:match("%s*(.-)%s*$")
	        options[key] = true
	    end
	end


	-- Process file list into an array
	local files = {}
    for file in fileList:gmatch("[^|]+") do
        files[#files + 1] = file:gsub("%s+", "") -- Remove spaces
    end

    if options.ResourceLoader == nil then
    	table.insert(warnings, 'ResourceLoader option must be set')
    end
    if options.type ~= nil and options.type ~= 'general' and options.type ~= 'styles' then
    	table.insert(warnings, 'Allowed values for type are: general, styles')
    end
    if options.targets ~= nil then
    	for _, target in ipairs(mw.text.split(options.targets, ',', false)) do
    		if target ~= 'desktop' and target ~= 'mobile' then
    			table.insert(warnings, 'Target '..target..' is invalid. Allowed values: desktop, mobile')
    		end
    	end
    end
    if options.namespaces ~= nil then
    	for _, id in ipairs(mw.text.split(options.namespaces, ',', false)) do
    		id = tonumber(id)
    		if mw.site.namespaces[id] == nil then
    			table.insert(warnings, 'Namespace '..id..' is invalid')
    		end
    	end
    end
    if options.actions ~= nil then
    	for _, action in ipairs(mw.text.split(options.actions, ',', false)) do
    		if not arr_contains(VALID_ACTIONS, action) then
    			table.insert(warnings, 'Action '..action..' is unrecognised')
    		end
    	end
    end
    if options.contentModels ~= nil then
    	for _, model in ipairs(mw.text.split(options.contentModels, ',', false)) do
    		if not arr_contains(VALID_CONTENT_MODELS, model) then
    			table.insert(warnings, 'Content model '..model..' is unrecognised')
    		end
    	end
    end
    if options.skins ~= nil then
    	for _, skin in ipairs(mw.text.split(options.skins, ',', false)) do
    		if not arr_contains(VALID_SKINS, skin) then
    			table.insert(warnings, 'Skin '..skin..' is not available')
    		end
    	end
    end
    if options.rights ~= nil then
    	for _, right in ipairs(mw.text.split(options.rights, ',', false)) do
    		if not arr_contains(VALID_RIGHTS, right) then
    			table.insert(warnings, 'User right '..right..' does not exist')
    		end
    	end
    end
    
    for _, page in ipairs(files) do
    	page = 'MediaWiki:Gadget-' .. page
    	local title = mw.title.new(page)
    	if title == nil or not title.exists then
    		table.insert(warnings, 'Page '..page..' does not exist')
    	end
    	local ext = title.text:match("%.([^%.]+)$")
    	if ext == 'js' and title.contentModel ~= 'javascript' then
    		table.insert(warnings, 'Page '..page..' is not of JavaScript content model')
    	end
    	if ext == 'css' and title.contentModel ~= 'css' then
    		table.insert(warnings, 'Page '..page..' is not of CSS content model')
    	end
    	if ext == 'json' and title.contentModel ~= 'json' then
    		table.insert(warnings, 'Page '..page..' is not of JSON content model')
    	end
    end

	return name, warnings
end

return p