2023-12-23 17:51:05 +00:00
|
|
|
-- Register proposals_admin privilege
|
|
|
|
minetest.register_privilege("proposals_admin", {
|
|
|
|
description = "Allows the player to administer proposals",
|
|
|
|
give_to_singleplayer = false,
|
|
|
|
give_to_admin = true
|
|
|
|
})
|
|
|
|
|
|
|
|
local proposals = {}
|
|
|
|
|
|
|
|
-- Load and deserialize proposals from mod storage
|
|
|
|
local mod_storage = minetest.get_mod_storage()
|
|
|
|
local stored_proposals = mod_storage:get_string("proposals")
|
|
|
|
if stored_proposals and stored_proposals ~= "" then
|
|
|
|
proposals = minetest.deserialize(stored_proposals) or {}
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Function to save proposals to mod storage
|
|
|
|
local function save_proposals()
|
|
|
|
mod_storage:set_string("proposals", minetest.serialize(proposals))
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Function to check if a player has already voted on a proposal
|
|
|
|
local function has_voted(player_name, proposal)
|
|
|
|
return proposal.votes_cast and proposal.votes_cast[player_name]
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Function to show the main formspec to a player
|
|
|
|
local function show_formspec(player_name)
|
|
|
|
local formspec = "size[10,8]" ..
|
|
|
|
"label[0.5,0.5;Vote on Changes]" ..
|
|
|
|
"textlist[0.5,1;9,4;proposals;"
|
|
|
|
|
2023-12-23 17:56:50 +00:00
|
|
|
if #proposals > 0 then
|
|
|
|
for _, proposal in ipairs(proposals) do
|
|
|
|
formspec = formspec .. minetest.formspec_escape(proposal.title .. " by " .. proposal.author) .. ","
|
|
|
|
end
|
|
|
|
formspec = formspec:sub(1, -2)
|
2023-12-23 17:51:05 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
formspec = formspec .. "]" ..
|
|
|
|
"button[0.5,6;3,1;add_proposal;Add Proposal]" ..
|
|
|
|
"button_exit[6.5,6;3,1;exit;Exit]"
|
|
|
|
|
|
|
|
minetest.show_formspec(player_name, "vote_changes:main", formspec)
|
|
|
|
end
|
|
|
|
|
2023-12-23 17:56:50 +00:00
|
|
|
|
2023-12-23 17:51:05 +00:00
|
|
|
-- Function to show the add proposal formspec
|
|
|
|
local function show_add_proposal_formspec(player_name)
|
|
|
|
local formspec = "size[8,4]" ..
|
|
|
|
"field[0.5,1;7,1;proposal_title;Title;]" ..
|
|
|
|
"textarea[0.5,2;7,2;proposal_description;Description;]" ..
|
|
|
|
"button[3,3.5;2,1;submit_proposal;Submit]"
|
|
|
|
minetest.show_formspec(player_name, "vote_changes:add_proposal", formspec)
|
|
|
|
end
|
|
|
|
|
2023-12-23 19:58:13 +00:00
|
|
|
local function show_add_comment_formspec(player_name, proposal_index)
|
|
|
|
local formspec = "size[8,4]" ..
|
|
|
|
"textarea[0.5,0.5;7.5,2;comment;Leave your comment (max 380 characters);]" ..
|
|
|
|
"button[3,3.5;2,1;submit_comment;Submit]" ..
|
|
|
|
"field[0,0;0,0;proposal_index;;" .. proposal_index .. "]" -- Hidden field to pass proposal index
|
|
|
|
|
|
|
|
minetest.show_formspec(player_name, "vote_changes:add_comment", formspec)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function show_edit_comment_formspec(player_name, proposal_index, commenter)
|
|
|
|
local proposal = proposals[proposal_index]
|
|
|
|
local comment = proposal.comments[commenter] or ""
|
|
|
|
local formspec = "size[8,4]" ..
|
|
|
|
"textarea[0.5,0.5;7.5,2;edit_comment;Edit your comment (max 380 characters);" .. minetest.formspec_escape(comment) .. "]" ..
|
|
|
|
"button[3,3.5;2,1;submit_edit_comment;Submit]" ..
|
|
|
|
"field[0,0;0,0;proposal_index;;" .. proposal_index .. "]" ..
|
|
|
|
"field[0,0;0,0;commenter;;" .. commenter .. "]" -- Hidden fields
|
|
|
|
|
|
|
|
minetest.show_formspec(player_name, "vote_changes:edit_comment", formspec)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-23 21:00:08 +00:00
|
|
|
-- Function to show proposal details and voting options
|
2023-12-23 20:42:45 +00:00
|
|
|
-- Function to show proposal details and voting options
|
2023-12-23 17:51:05 +00:00
|
|
|
local function show_proposal_details(player_name, proposal_index)
|
|
|
|
local proposal = proposals[proposal_index]
|
|
|
|
if not proposal then return end
|
|
|
|
|
2023-12-23 20:01:02 +00:00
|
|
|
proposal.comments = proposal.comments or {}
|
2023-12-23 17:51:05 +00:00
|
|
|
local player_has_privilege = minetest.check_player_privs(player_name, {proposals_admin=true})
|
|
|
|
local is_author = proposal.author == player_name
|
2023-12-23 20:35:25 +00:00
|
|
|
local has_commented = proposal.comments[player_name] ~= nil
|
2023-12-23 17:51:05 +00:00
|
|
|
|
2023-12-23 20:22:22 +00:00
|
|
|
local formspec = "size[12,12]" ..
|
2023-12-23 17:51:05 +00:00
|
|
|
"label[0.5,0.5;" .. minetest.formspec_escape(proposal.title) .. " by " .. proposal.author .. "]" ..
|
2023-12-23 21:00:08 +00:00
|
|
|
"textarea[0.5,1.5;11,2.5;;" .. minetest.formspec_escape(proposal.description) .. ";]" ..
|
|
|
|
"label[0.5,4.25;Votes: Yes(" .. proposal.votes.yes .. ") No(" .. proposal.votes.no .. ") Abstain(" .. proposal.votes.abstain .. ")]" ..
|
|
|
|
"button[0.5,5.25;2,1;vote_yes;Vote Yes]" ..
|
|
|
|
"button[3,5.25;2,1;vote_no;Vote No]" ..
|
|
|
|
"button[5.5,5.25;2,1;vote_abstain;Abstain]" ..
|
|
|
|
"label[0.5,6.5;Comments:]"
|
2023-12-23 20:29:44 +00:00
|
|
|
|
2023-12-23 20:48:03 +00:00
|
|
|
-- Construct comments string with proper escaping and formatting
|
2023-12-23 20:22:22 +00:00
|
|
|
local comments_str = ""
|
2023-12-23 19:58:13 +00:00
|
|
|
for commenter, comment in pairs(proposal.comments) do
|
2023-12-23 21:00:08 +00:00
|
|
|
-- Escape each comment and add it to the string
|
2023-12-23 20:55:15 +00:00
|
|
|
comments_str = comments_str .. minetest.formspec_escape(commenter .. ":") .. "\n" .. minetest.formspec_escape(comment) .. "\n\n"
|
2023-12-23 19:58:13 +00:00
|
|
|
end
|
2023-12-23 21:00:08 +00:00
|
|
|
-- Remove the last newline characters
|
|
|
|
comments_str = comments_str:gsub("\n\n$", "")
|
2023-12-23 19:58:13 +00:00
|
|
|
|
2023-12-23 21:00:08 +00:00
|
|
|
-- Scrollable area for comments
|
|
|
|
formspec = formspec .. "scroll_container[0.5,7;11,3;scrollbar;vertical]" ..
|
|
|
|
"textarea[0.25,0.25;10.5,10;;;" .. comments_str .. "]" ..
|
2023-12-23 20:29:44 +00:00
|
|
|
"scroll_container_end[]"
|
2023-12-23 20:22:22 +00:00
|
|
|
|
2023-12-23 21:00:08 +00:00
|
|
|
-- Edit and Delete Comment buttons only if the player has commented
|
|
|
|
if has_commented then
|
|
|
|
formspec = formspec .. "button[9,10;3,1;edit_comment;Edit Comment]" ..
|
|
|
|
"button[9,11;3,1;delete_comment;Delete Comment]"
|
2023-12-23 17:51:05 +00:00
|
|
|
end
|
|
|
|
|
2023-12-23 21:00:08 +00:00
|
|
|
-- Add Comment button only if the player has not commented and is not the author
|
|
|
|
if not has_commented and not is_author then
|
|
|
|
formspec = formspec .. "button[0.5,10;3,1;add_comment;Add Comment]"
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Edit and Delete Proposal buttons if the player has privileges or is the author
|
2023-12-23 20:42:45 +00:00
|
|
|
if player_has_privilege or is_author then
|
2023-12-23 20:35:25 +00:00
|
|
|
formspec = formspec .. "button[0.5,11;3,1;edit_proposal;Edit Proposal]" ..
|
|
|
|
"button[4,11;3,1;delete_proposal;Delete Proposal]"
|
2023-12-23 19:58:13 +00:00
|
|
|
end
|
|
|
|
|
2023-12-23 20:29:44 +00:00
|
|
|
formspec = formspec .. "button[9,11;3,1;back;Back]"
|
2023-12-23 17:56:50 +00:00
|
|
|
|
2023-12-23 17:51:05 +00:00
|
|
|
minetest.show_formspec(player_name, "vote_changes:proposal_" .. proposal_index, formspec)
|
|
|
|
end
|
|
|
|
|
2023-12-23 20:48:03 +00:00
|
|
|
|
2023-12-23 20:55:15 +00:00
|
|
|
|
2023-12-23 21:00:08 +00:00
|
|
|
|
2023-12-23 19:04:07 +00:00
|
|
|
-- Function to show the edit proposal formspec
|
|
|
|
local function show_edit_proposal_formspec(player_name, proposal_index)
|
|
|
|
local proposal = proposals[proposal_index]
|
|
|
|
if not proposal then return end
|
|
|
|
|
|
|
|
local formspec = "size[8,4]" ..
|
|
|
|
"field[0.5,1;7,1;edit_proposal_title;Title;" .. minetest.formspec_escape(proposal.title) .. "]" ..
|
|
|
|
"textarea[0.5,2;7,2;edit_proposal_description;Description;" .. minetest.formspec_escape(proposal.description) .. "]" ..
|
|
|
|
"button[3,3.5;2,1;submit_edit;Submit]" ..
|
|
|
|
"field[0,0;0,0;proposal_index;;" .. proposal_index .. "]" -- Hidden field to pass proposal index
|
|
|
|
|
|
|
|
minetest.show_formspec(player_name, "vote_changes:edit_proposal", formspec)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-23 17:56:50 +00:00
|
|
|
|
2023-12-23 18:20:22 +00:00
|
|
|
|
2023-12-23 17:51:05 +00:00
|
|
|
-- Register the /vote_changes command
|
|
|
|
minetest.register_chatcommand("proposals", {
|
|
|
|
description = "Open the voting interface",
|
|
|
|
func = function(name)
|
|
|
|
show_formspec(name)
|
|
|
|
return true
|
|
|
|
end,
|
|
|
|
})
|
|
|
|
|
|
|
|
-- Handle formspec submissions
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
|
|
local player_name = player:get_player_name()
|
|
|
|
|
|
|
|
if formname == "vote_changes:main" then
|
|
|
|
if fields.add_proposal then
|
|
|
|
show_add_proposal_formspec(player_name)
|
|
|
|
elseif fields.proposals then
|
|
|
|
local event = minetest.explode_textlist_event(fields.proposals)
|
|
|
|
if event.type == "CHG" then
|
|
|
|
show_proposal_details(player_name, event.index)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif formname:find("vote_changes:proposal_") then
|
|
|
|
local proposal_index = tonumber(formname:match("proposal_(%d+)"))
|
|
|
|
local proposal = proposals[proposal_index]
|
|
|
|
if proposal then
|
|
|
|
local player_has_privilege = minetest.check_player_privs(player_name, {proposals_admin=true})
|
|
|
|
local is_author = proposal.author == player_name
|
2023-12-23 19:04:07 +00:00
|
|
|
local is_author_or_admin = is_author or player_has_privilege
|
|
|
|
|
|
|
|
if fields.edit_proposal and is_author_or_admin then
|
|
|
|
show_edit_proposal_formspec(player_name, proposal_index)
|
|
|
|
elseif fields.back then
|
2023-12-23 17:56:50 +00:00
|
|
|
show_formspec(player_name)
|
2023-12-23 19:58:13 +00:00
|
|
|
elseif fields.delete_proposal and is_author_or_admin then
|
2023-12-23 17:51:05 +00:00
|
|
|
table.remove(proposals, proposal_index)
|
|
|
|
save_proposals()
|
|
|
|
show_formspec(player_name)
|
2023-12-23 18:20:22 +00:00
|
|
|
elseif fields.vote_yes or fields.vote_no or fields.vote_abstain then
|
|
|
|
if proposal.votes_cast[player_name] then
|
|
|
|
proposal.votes[proposal.votes_cast[player_name]] = proposal.votes[proposal.votes_cast[player_name]] - 1
|
|
|
|
end
|
2023-12-23 17:51:05 +00:00
|
|
|
if fields.vote_yes then
|
|
|
|
proposal.votes.yes = proposal.votes.yes + 1
|
2023-12-23 18:20:22 +00:00
|
|
|
proposal.votes_cast[player_name] = 'yes'
|
2023-12-23 17:51:05 +00:00
|
|
|
elseif fields.vote_no then
|
|
|
|
proposal.votes.no = proposal.votes.no + 1
|
2023-12-23 18:20:22 +00:00
|
|
|
proposal.votes_cast[player_name] = 'no'
|
2023-12-23 17:51:05 +00:00
|
|
|
elseif fields.vote_abstain then
|
|
|
|
proposal.votes.abstain = proposal.votes.abstain + 1
|
2023-12-23 18:20:22 +00:00
|
|
|
proposal.votes_cast[player_name] = 'abstain'
|
2023-12-23 17:51:05 +00:00
|
|
|
end
|
|
|
|
save_proposals()
|
2023-12-23 18:20:22 +00:00
|
|
|
show_proposal_details(player_name, proposal_index)
|
2023-12-23 19:58:13 +00:00
|
|
|
elseif fields.add_comment and player_name ~= proposal.author and not proposal.comments[player_name] then
|
|
|
|
show_add_comment_formspec(player_name, proposal_index)
|
|
|
|
else
|
|
|
|
for field_name, _ in pairs(fields) do
|
|
|
|
if field_name:find("edit_comment_") then
|
|
|
|
local commenter = field_name:match("edit_comment_(.+)")
|
|
|
|
if commenter and (commenter == player_name or player_has_privilege) and proposal.comments[commenter] then
|
|
|
|
show_edit_comment_formspec(player_name, proposal_index, commenter)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
elseif field_name:find("delete_comment_") then
|
|
|
|
local commenter = field_name:match("delete_comment_(.+)")
|
|
|
|
if commenter and (commenter == player_name or player_has_privilege) and proposal.comments[commenter] then
|
|
|
|
proposal.comments[commenter] = nil
|
|
|
|
save_proposals()
|
|
|
|
show_proposal_details(player_name, proposal_index)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2023-12-23 17:51:05 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif formname == "vote_changes:add_proposal" and fields.submit_proposal then
|
|
|
|
if fields.proposal_title ~= "" and fields.proposal_description ~= "" then
|
|
|
|
table.insert(proposals, {
|
|
|
|
title = fields.proposal_title,
|
|
|
|
description = fields.proposal_description,
|
|
|
|
author = player_name,
|
|
|
|
votes = { yes = 0, no = 0, abstain = 0 },
|
2023-12-23 19:58:13 +00:00
|
|
|
votes_cast = {},
|
|
|
|
comments = {}
|
2023-12-23 17:51:05 +00:00
|
|
|
})
|
|
|
|
save_proposals()
|
|
|
|
show_formspec(player_name)
|
|
|
|
end
|
2023-12-23 19:04:07 +00:00
|
|
|
elseif formname == "vote_changes:edit_proposal" then
|
|
|
|
local proposal_index = tonumber(fields.proposal_index)
|
|
|
|
local proposal = proposals[proposal_index]
|
|
|
|
if proposal and (proposal.author == player_name or minetest.check_player_privs(player_name, {proposals_admin=true})) then
|
|
|
|
if fields.edit_proposal_title ~= "" and fields.edit_proposal_description ~= "" then
|
|
|
|
proposal.title = fields.edit_proposal_title
|
|
|
|
proposal.description = fields.edit_proposal_description
|
2023-12-23 19:58:13 +00:00
|
|
|
proposal.comments = proposal.comments or {}
|
|
|
|
save_proposals()
|
|
|
|
show_proposal_details(player_name, proposal_index)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif formname == "vote_changes:add_comment" then
|
|
|
|
local proposal_index = tonumber(fields.proposal_index)
|
|
|
|
local proposal = proposals[proposal_index]
|
|
|
|
if proposal and player_name ~= proposal.author and not proposal.comments[player_name] then
|
|
|
|
if fields.comment and fields.comment ~= "" and string.len(fields.comment) <= 380 then
|
|
|
|
proposal.comments[player_name] = fields.comment
|
|
|
|
save_proposals()
|
|
|
|
show_proposal_details(player_name, proposal_index)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif formname == "vote_changes:edit_comment" then
|
|
|
|
local proposal_index = tonumber(fields.proposal_index)
|
|
|
|
local commenter = fields.commenter
|
|
|
|
local proposal = proposals[proposal_index]
|
|
|
|
if proposal and commenter and proposal.comments[commenter] and (commenter == player_name or player_has_privilege) then
|
|
|
|
if fields.edit_comment and fields.edit_comment ~= "" and string.len(fields.edit_comment) <= 380 then
|
|
|
|
proposal.comments[commenter] = fields.edit_comment
|
2023-12-23 19:04:07 +00:00
|
|
|
save_proposals()
|
|
|
|
show_proposal_details(player_name, proposal_index)
|
|
|
|
end
|
|
|
|
end
|
2023-12-23 17:51:05 +00:00
|
|
|
end
|
|
|
|
end)
|
2023-12-23 18:20:22 +00:00
|
|
|
|
2023-12-23 19:04:07 +00:00
|
|
|
|
2023-12-23 19:58:13 +00:00
|
|
|
|
|
|
|
|