Merge branch 'interaction-nested-functions' into 'master'
Moved code over to nested function approach, working See merge request medlabboulder/modpol!27
This commit is contained in:
commit
fa4283dce6
@ -9,22 +9,46 @@ modpol.interactions = {}
|
||||
-- DASHBOARDS
|
||||
-- ==========
|
||||
|
||||
-- Function: modpol.dashboard(user)
|
||||
function modpol.interactions.login()
|
||||
print("Log in as which user?")
|
||||
local username = io.read()
|
||||
|
||||
while true do
|
||||
local org = modpol.interactions.dashboard(username)
|
||||
if org then
|
||||
modpol.interactions.org_dashboard(username, org)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Function: modpol.interactions.dashboard(user)
|
||||
-- Params: user (string)
|
||||
-- Q: Should this return a menu of commands relevant to the specific user?
|
||||
-- Output: Displays a menu of commands to the user
|
||||
-- TKTK currently just prints all of modpol---needs major improvement
|
||||
function modpol.dashboard(user)
|
||||
local output = ""
|
||||
-- Org status
|
||||
output = output .. "Orgs:\n" ..
|
||||
table.concat(modpol.orgs.list_all(),"\n")
|
||||
-- Command list (should be redone to be org-specific)
|
||||
output = output .. "\nCommand list:\n"
|
||||
for key,value in pairs(modpol) do
|
||||
output = output .. "modpol." .. key .. " "
|
||||
function modpol.interactions.dashboard(user)
|
||||
print()
|
||||
local org_list = modpol.orgs.list_all()
|
||||
|
||||
print('Select an org')
|
||||
for i, org_name in ipairs(org_list) do
|
||||
local org = modpol.orgs.get_org(org_name)
|
||||
local indicator = ""
|
||||
if org:has_pending_actions(user) then
|
||||
indicator = "*"
|
||||
end
|
||||
print('['..i..']'..' '..org_name..indicator)
|
||||
end
|
||||
print(output)
|
||||
|
||||
local sel = io.read()
|
||||
local sel_org = org_list[tonumber(sel)]
|
||||
|
||||
if not sel_org then return end
|
||||
|
||||
modpol.interactions.org_dashboard(user, sel_org)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@ -32,40 +56,58 @@ end
|
||||
-- Params: user (string), org_name (string)
|
||||
-- Output: Displays a menu of org-specific commands to the user
|
||||
function modpol.interactions.org_dashboard(user, org_name)
|
||||
print()
|
||||
local org = modpol.orgs.get_org(org_name)
|
||||
if not org then return nil end
|
||||
local is_member = org:has_member(user)
|
||||
local membership_toggle = function()
|
||||
local toggle_code = ""
|
||||
if is_member then
|
||||
toggle_code = "[Leave]"
|
||||
else
|
||||
toggle_code = "[Join]"
|
||||
end
|
||||
return toggle_code
|
||||
end
|
||||
|
||||
|
||||
local children = {}
|
||||
for k,v in ipairs(org.children) do
|
||||
local this_child = modpol.orgs.get_org(v)
|
||||
table.insert(children, this_child.name)
|
||||
end
|
||||
|
||||
local processes = {}
|
||||
for k,v in ipairs(org.processes) do
|
||||
print(k, v)
|
||||
local this_request = org.requests[v.request_id]
|
||||
if type(this_request) == "table" then
|
||||
local active = ''
|
||||
if org.pending[user] then
|
||||
if org.pending[user][v.id] then
|
||||
active = '*'
|
||||
end
|
||||
end
|
||||
local req_str = "[" .. v.id .. "] " ..
|
||||
active .. this_request.type
|
||||
if this_request.params[1] then
|
||||
req_str = req_str ": " ..
|
||||
table.concat(this_request.params, ", ")
|
||||
end
|
||||
local req_str = v.id .. " (" .. this_request.type .. " -> " .. table.concat(this_request.params, ", ") .. ")" .. active
|
||||
table.insert(processes, req_str)
|
||||
end
|
||||
end
|
||||
-- set up output
|
||||
local dashboard_table = {
|
||||
"Org: " .. org_name,
|
||||
membership_toggle(),
|
||||
"Members: " .. table.concat(org.members, ", "),
|
||||
"Children: " .. table.concat(children, ", "),
|
||||
"Policies: " .. table.concat(org.policies, ", "),
|
||||
"Processes: " .. table.concat(org.processes, ", "),
|
||||
"[Add child]",
|
||||
"[Remove org]",
|
||||
"[Dashboard: modpol.dashboard()]"
|
||||
}
|
||||
-- present to player
|
||||
print(table.concat(dashboard_table, "\n"))
|
||||
print("Org: " .. org_name)
|
||||
print("Members: " .. table.concat(org.members, ", "))
|
||||
print("Children: " .. table.concat(children, ", "))
|
||||
print("Processes: " .. table.concat(processes, ", "))
|
||||
|
||||
print('Interact with which process?')
|
||||
local sel = io.read()
|
||||
local process = org.processes[tonumber(sel)]
|
||||
if not process then return end
|
||||
process:interact(user)
|
||||
end
|
||||
|
||||
-- ===================================================================
|
||||
-- Function: modpol.interactions.policy_dashboard
|
||||
-- input: user (string), org_id (int), policy (string)
|
||||
-- if policy is nil, enables creating a new policy
|
||||
-- output: opens a dashboard for viewing/editing policy details
|
||||
-- TODO
|
||||
|
||||
|
||||
-- Function: modpol.interactions.message
|
||||
-- input: user (string), message (string)
|
||||
-- output: prints message to CLI
|
||||
@ -73,31 +115,86 @@ function modpol.interactions.message(user, message)
|
||||
print(user .. ": " .. message)
|
||||
end
|
||||
|
||||
-- ===================================================================
|
||||
-- Function: modpol.interactions.text_query
|
||||
-- input: Query (string)
|
||||
-- output: User response (string)
|
||||
function modpol.interactions.text_query(query)
|
||||
-- TODO
|
||||
-- input: User (string), Query (string), func (function)
|
||||
-- func input: user input (string)
|
||||
-- output: Applies "func" to user input
|
||||
function modpol.interactions.text_query(user, query, func)
|
||||
print(user .. ": " .. query)
|
||||
answer = io.read()
|
||||
func(answer)
|
||||
end
|
||||
|
||||
-- Function: dropdown_query
|
||||
-- input: user (string), label (string), options (table of strings), func(choice) (function)
|
||||
-- func input: choice (string)
|
||||
-- output: calls func on choice
|
||||
function modpol.interactions.dropdown_query(user, label, options, func)
|
||||
-- set up options
|
||||
local options_display = ""
|
||||
local options_number = 0
|
||||
for k,v in ipairs(options) do
|
||||
options_display = options_display .. k .. ". " ..
|
||||
options[k] .. "\n"
|
||||
options_number = options_number + 1
|
||||
end
|
||||
options_display = options_display .. "Select number:"
|
||||
if options_number == 0 then
|
||||
print("Error: No options given for dropdown")
|
||||
return nil
|
||||
end
|
||||
-- begin displaying
|
||||
print(user .. ": " .. label)
|
||||
print(options_display)
|
||||
-- read input and produce output
|
||||
local answer
|
||||
answer = io.read()
|
||||
answer = tonumber(answer)
|
||||
if answer then
|
||||
if answer >= 1 and answer <= options_number then
|
||||
print("Selection: " .. options[answer])
|
||||
func(options[answer])
|
||||
else
|
||||
print("Error: Not in dropdown range")
|
||||
return nil
|
||||
end
|
||||
else
|
||||
print("Error: Must be a number")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- ===================================================================
|
||||
-- Function: modpol.binary_poll_user(user, question)
|
||||
-- Params: user (string), question (string)
|
||||
-- Output:
|
||||
-- presents a yes/no/abstain poll to a user, returns answer
|
||||
function modpol.interactions.binary_poll_user(user, question)
|
||||
-- Params: user (string), question (string), func (function)
|
||||
-- func input: user input (string: y/n)
|
||||
-- Output: Applies "func" to user input
|
||||
function modpol.interactions.binary_poll_user(user, question, func)
|
||||
local query = "Poll for " .. user .. " (y/n): ".. question
|
||||
local answer
|
||||
repeat
|
||||
print(query)
|
||||
answer = io.read()
|
||||
until answer == "y" or answer == "n" or answer == "a"
|
||||
until answer == "y" or answer == "n"
|
||||
if answer == "y" then
|
||||
return "Yes"
|
||||
modpol.interactions.message(user, "Response recorded")
|
||||
func("yes")
|
||||
elseif answer == "n" then
|
||||
return "No"
|
||||
modpol.interactions.message(user, "Response recorded")
|
||||
func("no")
|
||||
else
|
||||
return "Abstain"
|
||||
modpol.interactions.message(user, "Error: invalid response")
|
||||
end
|
||||
end
|
||||
|
||||
-- COMPLEX INTERACTIONS
|
||||
-- ====================
|
||||
|
||||
-- Function: modpol.interactions.message_org
|
||||
-- input: initiator (string), org_id (number), message (string)
|
||||
-- output: broadcasts message to all org members
|
||||
|
||||
-- Function: modpol.interactions.binary_poll_org
|
||||
-- input: initator (user string), org_id (number)
|
||||
-- output: gets question from initiator, asks all org members, broadcasts answers
|
||||
|
||||
|
||||
|
@ -38,6 +38,21 @@ function modpol.modules.consent:new_process(id, request_id, org_id)
|
||||
return process
|
||||
end
|
||||
|
||||
-- ============================
|
||||
-- interact function for the consent module
|
||||
-- input: user (string)
|
||||
function modpol.modules.consent:interact(user)
|
||||
-- TODO this needs more context on the vote at hand
|
||||
modpol.interactions.binary_poll_user(
|
||||
user, "Do you consent?",
|
||||
function(vote)
|
||||
if vote == 'yes' then
|
||||
self:approve(user, true)
|
||||
elseif vote == 'no' then
|
||||
self:approve(user, false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- =========================================
|
||||
-- function to delete a process, called when process finishes
|
||||
@ -90,4 +105,4 @@ function modpol.modules.consent:update_status()
|
||||
else
|
||||
modpol.ocutil.log('Waiting for more votes...')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -11,7 +11,12 @@ function temp_org()
|
||||
return {
|
||||
id = nil,
|
||||
name = nil,
|
||||
policies = {},
|
||||
policies = {
|
||||
add_org={process_type='consent', must_be_member=false},
|
||||
delete={process_type='consent', must_be_member=false},
|
||||
add_member={process_type='consent', must_be_member=false},
|
||||
remove_member={process_type='consent', must_be_member=false}
|
||||
},
|
||||
processes = {},
|
||||
requests = {},
|
||||
pending = {},
|
||||
@ -96,15 +101,15 @@ function modpol.orgs.init_instance()
|
||||
|
||||
local instance = temp_org()
|
||||
instance.id = 1
|
||||
instance.name = "instance"
|
||||
instance.name = "root"
|
||||
|
||||
setmetatable(instance, modpol.orgs)
|
||||
|
||||
-- adding instance to org list
|
||||
modpol.orgs.array[1] = instance
|
||||
|
||||
modpol.ocutil.log('Initialized the instance org')
|
||||
modpol.orgs:record('Initialized the instance org', 'create_instance')
|
||||
modpol.ocutil.log('Initialized the instance root org')
|
||||
modpol.orgs:record('Initialized the instance root org', 'create_instance')
|
||||
|
||||
return instance
|
||||
end
|
||||
@ -148,6 +153,7 @@ end
|
||||
|
||||
-- ==================================================
|
||||
-- adds a new sub org to the org it is called on
|
||||
-- input: name (string), user (string)
|
||||
-- ex: instance:add_org('town hall')
|
||||
function modpol.orgs:add_org(name, user)
|
||||
if self.id == nil then
|
||||
@ -171,6 +177,7 @@ function modpol.orgs:add_org(name, user)
|
||||
child_org.id = modpol.orgs.count
|
||||
child_org.name = name
|
||||
child_org.parent = self.id
|
||||
child_org.processes = self.processes
|
||||
|
||||
setmetatable(child_org, modpol.orgs)
|
||||
|
||||
@ -275,18 +282,14 @@ function modpol.orgs:has_member(user)
|
||||
end
|
||||
|
||||
-- ==================================
|
||||
-- returns a list of users in an org
|
||||
function modpol.orgs:list_member()
|
||||
local str
|
||||
for k, v in ipairs(self.members) do
|
||||
-- checking to see if member name is valid
|
||||
if str and str ~= '' then
|
||||
str = str .. '\n' .. v
|
||||
else
|
||||
str = v
|
||||
end
|
||||
end
|
||||
return str
|
||||
-- Function: modpol.orgs:list_members()
|
||||
-- output: a table of the names (string) of members
|
||||
function modpol.orgs:list_members()
|
||||
local members = {}
|
||||
for k, v in ipairs(self.members) do
|
||||
table.insert(members, v)
|
||||
end
|
||||
return members
|
||||
end
|
||||
|
||||
-- ==============================
|
||||
@ -311,6 +314,7 @@ function modpol.orgs:set_policy(policy_type, process_type, must_be_member)
|
||||
}
|
||||
self.policies[policy_type] = new_policy
|
||||
modpol.ocutil.log('Added policy for ' .. policy_type .. ' in ' .. self.name)
|
||||
self:record('Added policy for ' .. policy_type, 'set_policy')
|
||||
end
|
||||
|
||||
|
||||
|
@ -13,10 +13,11 @@ function modpol.orgs:create_process(process_type, request_id)
|
||||
return
|
||||
end
|
||||
|
||||
local empty_index = nil
|
||||
-- linear search for empty process slots (lazy deletion)
|
||||
for k, v in ipairs(self.processes) do
|
||||
if v == 'deleted' then
|
||||
local empty_index = k
|
||||
empty_index = k
|
||||
break
|
||||
end
|
||||
end
|
||||
@ -35,6 +36,9 @@ function modpol.orgs:create_process(process_type, request_id)
|
||||
|
||||
self.processes[index] = new_process
|
||||
|
||||
modpol.ocutil.log('Created process #' .. index .. ' - ' .. process_type .. ' for ' .. self.name)
|
||||
self:record('Created process #' .. index .. ' - ' .. process_type, 'add_process')
|
||||
|
||||
return index
|
||||
end
|
||||
|
||||
@ -46,7 +50,8 @@ function modpol.orgs:add_pending_action(process_id, user)
|
||||
-- flagging actual action
|
||||
self.pending[user][process_id] = true
|
||||
|
||||
modpol.ocutil.log("Added pending action to " .. user .. " in process #" .. process_id)
|
||||
modpol.ocutil.log("Added pending action to " .. user .. " in process #" .. process_id .. ' from ' .. self.name)
|
||||
self:record('Added pending action to ' .. user .. ' in process #' .. process_id, "add_pending_action")
|
||||
end
|
||||
|
||||
-- ========================
|
||||
@ -55,7 +60,8 @@ function modpol.orgs:remove_pending_action(process_id, user)
|
||||
|
||||
if self.pending[user] then
|
||||
self.pending[user][process_id] = nil
|
||||
modpol.ocutil.log("Removed pending action from " .. user .. " in process #" .. process_id)
|
||||
modpol.ocutil.log("Removed pending action from " .. user .. " in process #" .. process_id .. ' from ' .. self.name)
|
||||
self:record('Removed pending action from ' .. user .. ' in process #' .. process_id, "del_pending_action")
|
||||
else
|
||||
modpol.ocutil.log("Could not remove pending action from " .. user .. " in process #" .. process_id)
|
||||
end
|
||||
@ -68,6 +74,23 @@ function modpol.orgs:wipe_pending_actions(process_id)
|
||||
self.pending[user][process_id] = nil
|
||||
end
|
||||
modpol.ocutil.log("Removed all pending actions for process #" .. process_id)
|
||||
self:record('Removed all pending actions for process #' .. process_id, "del_pending_action")
|
||||
end
|
||||
|
||||
-- ======================
|
||||
-- returns a boolean to indicate whether a user has any active pending actions
|
||||
function modpol.orgs:has_pending_actions(user)
|
||||
-- next() will return the next pair in a table
|
||||
-- if next() returns nil, the table is empty
|
||||
if not self.pending[user] then
|
||||
return false
|
||||
else
|
||||
if not next(self.pending[user]) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ===========================
|
||||
@ -130,6 +153,9 @@ function modpol.orgs:resolve_request(request_id, approve)
|
||||
end
|
||||
|
||||
self.requests[request_id] = "deleted"
|
||||
|
||||
modpol.ocutil.log("Resolved request #" .. request_id .. ' in ' .. self.name)
|
||||
self:record("Resolved request #" .. request_id, "resolve_request")
|
||||
end
|
||||
|
||||
-- ================================
|
||||
@ -189,10 +215,11 @@ function modpol.orgs:make_request(request)
|
||||
return false
|
||||
end
|
||||
|
||||
local empty_index = nil
|
||||
-- linear search for empty process slots (lazy deletion)
|
||||
for k, v in ipairs(self.requests) do
|
||||
if v == 'deleted' then
|
||||
local empty_index = k
|
||||
empty_index = k
|
||||
break
|
||||
end
|
||||
end
|
||||
@ -206,7 +233,9 @@ function modpol.orgs:make_request(request)
|
||||
table.insert(self.requests, request)
|
||||
request_id = #self.requests
|
||||
end
|
||||
modpol.ocutil.log("Request made by " .. request.user .. " to " .. request.type)
|
||||
|
||||
modpol.ocutil.log("Request made by " .. request.user .. " to " .. request.type .. " in " .. self.name)
|
||||
self:record("Request made by " .. request.user .. " to " .. request.type, "make_request")
|
||||
|
||||
-- launching process tied to this request
|
||||
local process_id = self:create_process(requested_policy.process_type, request_id)
|
||||
|
@ -25,18 +25,17 @@ dofile (modpol.topdir .. "/util/serpent/serpent.lua")
|
||||
-- ===================================================================
|
||||
-- This function stores "ledger" data to disk.
|
||||
|
||||
local store_ledger = function()
|
||||
local ok = modpol.ocutil.file_write (modpol.file_ledger,
|
||||
modpol.serpent.dump (modpol.ledger))
|
||||
local store_ledger = function(verbose)
|
||||
local ok = modpol.ocutil.file_write (modpol.file_ledger, modpol.serpent.dump (modpol.ledger))
|
||||
|
||||
if ok ~= true then
|
||||
if ok ~= true then
|
||||
modpol.ocutil.fatal_error ("store_data: ledger")
|
||||
end
|
||||
|
||||
local nn = modpol.ocutil.table_length (modpol.ledger)
|
||||
local nn = modpol.ocutil.table_length (modpol.ledger)
|
||||
local str = "entries"
|
||||
if nn == 1 then str = "entry" end
|
||||
modpol.ocutil.log (nn .. " global ledger entries stored to disk")
|
||||
if verbose then modpol.ocutil.log (nn .. " global ledger entries stored to disk") end
|
||||
end
|
||||
|
||||
-- ===================================================================
|
||||
@ -52,35 +51,16 @@ local store_orgs = function()
|
||||
local nn = modpol.ocutil.table_length (modpol.orgs.array)
|
||||
local str = "entries"
|
||||
if nn == 1 then str = "entry" end
|
||||
modpol.ocutil.log (nn .. " orgs stored to disk")
|
||||
if verbose then modpol.ocutil.log (nn .. " orgs stored to disk") end
|
||||
end
|
||||
|
||||
-- ===================================================================
|
||||
-- This function stores "old_ledgers" data to disk.
|
||||
|
||||
local store_old_ledgers = function()
|
||||
|
||||
local ok = modpol.ocutil.file_write (modpol.file_old_ledgers,
|
||||
modpol.serpent.dump (modpol.old_ledgers))
|
||||
|
||||
|
||||
if ok ~= true then
|
||||
modpol.ocutil.fatal_error ("store_data: orgs")
|
||||
end
|
||||
|
||||
local nn = modpol.ocutil.table_length (modpol.old_ledgers)
|
||||
local str = "entries"
|
||||
if nn == 1 then str = "entry" end
|
||||
modpol.ocutil.log (nn .. " orgs stored to disk")
|
||||
end
|
||||
|
||||
-- ===================================================================
|
||||
-- This function stores data to disk.
|
||||
|
||||
modpol.store_data = function()
|
||||
store_ledger()
|
||||
store_orgs()
|
||||
-- store_old_ledgers()
|
||||
modpol.store_data = function(verbose)
|
||||
store_ledger(verbose)
|
||||
store_orgs(verbose)
|
||||
end
|
||||
|
||||
-- ===================================================================
|
||||
|
31
modpol/tests/nested_functions.lua
Normal file
31
modpol/tests/nested_functions.lua
Normal file
@ -0,0 +1,31 @@
|
||||
dofile("../modpol.lua")
|
||||
|
||||
print('\nRemoving existing orgs')
|
||||
modpol.orgs.reset()
|
||||
|
||||
print('\nCreating an org called "test_org"')
|
||||
test_org = modpol.instance:add_org('test_org', 'luke')
|
||||
|
||||
print('\nAdding user "nathan" to test_org')
|
||||
test_org:add_member('nathan')
|
||||
|
||||
print("\nOkay, let's start with requests. Setting up an org...")
|
||||
modpol.instance:add_org('test_org', 'luke')
|
||||
test_org:add_member('nathan')
|
||||
|
||||
print('\nMaking consent the add_member policy')
|
||||
test_org:set_policy("add_member", "consent", false);
|
||||
|
||||
print('\nMaking a request to add Josh')
|
||||
add_josh = {
|
||||
user = "josh",
|
||||
type = "add_member",
|
||||
params = {"josh"}
|
||||
}
|
||||
request_id = test_org:make_request(add_josh)
|
||||
|
||||
|
||||
print('\nHave the two members vote on it')
|
||||
modpol.interactions.org_dashboard("nathan","test_org")
|
||||
modpol.interactions.org_dashboard("luke","test_org")
|
||||
|
@ -28,7 +28,7 @@ end
|
||||
modpol.instance:set_policy("add_org", "consent", false);
|
||||
|
||||
new_request = {
|
||||
user = "lukvmil",
|
||||
user = "luke",
|
||||
type = "add_org",
|
||||
params = {"new_org"}
|
||||
}
|
||||
@ -39,6 +39,13 @@ modpol.instance:add_member('josh')
|
||||
modpol.instance:add_member('nathan')
|
||||
|
||||
request_id = modpol.instance:make_request(new_request)
|
||||
modpol.instance:make_request({
|
||||
user="luke",
|
||||
type="add_org",
|
||||
params={"second_org"}
|
||||
})
|
||||
|
||||
modpol.interactions.login()
|
||||
|
||||
for id, process in ipairs(modpol.instance.processes) do
|
||||
-- process:approve('luke', true)
|
||||
|
@ -3,7 +3,7 @@
|
||||
-- CONTEXTUAL STUFF
|
||||
-- ================
|
||||
|
||||
-- First, set up contexts to enable passing across formspecs
|
||||
-- _contexts to enable passing across formspecs
|
||||
-- https://rubenwardy.com/minetest_modding_book/en/players/formspecs.html#contexts
|
||||
|
||||
local _contexts = {}
|
||||
@ -16,10 +16,6 @@ minetest.register_on_leaveplayer(function(player)
|
||||
_contexts[player:get_player_name()] = nil
|
||||
end)
|
||||
|
||||
-- table of formspec field responses
|
||||
local formspec_fields = {}
|
||||
|
||||
|
||||
-- UTILITIES
|
||||
-- =========
|
||||
|
||||
@ -38,7 +34,6 @@ local function formspec_list(array)
|
||||
return table.concat(escaped,",")
|
||||
end
|
||||
|
||||
|
||||
-- DASHBOARDS
|
||||
-- ==========
|
||||
|
||||
@ -57,7 +52,7 @@ function modpol.interactions.dashboard(user)
|
||||
local formspec = {
|
||||
"formspec_version[4]",
|
||||
"size[10,8]",
|
||||
"label[0.5,0.5;M O D U L A R P O L I T I C S]",
|
||||
"label[0.5,0.5;MODPOL DASHBOARD]",
|
||||
"label[0.5,2;All orgs:]",
|
||||
"dropdown[2,1.5;5,0.8;all_orgs;"..formspec_list(all_orgs)..";;]",
|
||||
"label[0.5,3;Your orgs:]",
|
||||
@ -67,6 +62,7 @@ function modpol.interactions.dashboard(user)
|
||||
"button[0.5,7;1,0.8;test_poll;Test poll]",
|
||||
"button[2,7;1,0.8;add_org;Add org]",
|
||||
"button[3.5,7;1.5,0.8;remove_org;Remove org]",
|
||||
"button[5.5,7;1.5,0.8;reset_orgs;Reset orgs]",
|
||||
"button_exit[8.5,7;1,0.8;close;Close]",
|
||||
}
|
||||
local formspec_string = table.concat(formspec, "")
|
||||
@ -78,14 +74,29 @@ minetest.register_on_player_receive_fields(function (player, formname, fields)
|
||||
if formname == "modpol:dashboard" then
|
||||
local pname = player:get_player_name()
|
||||
if nil then
|
||||
-- buttons first
|
||||
-- buttons first
|
||||
elseif fields.test_poll then
|
||||
modpol.interactions.binary_poll_org(pname, 1, "Poll question (yes/no):")
|
||||
-- FOR TESTING PURPOSES ONLY
|
||||
modpol.interactions.text_query(
|
||||
pname,"Poll question:",
|
||||
function(input)
|
||||
modpol.interactions.binary_poll_user(
|
||||
pname, input,
|
||||
function(vote)
|
||||
modpol.interactions.message(
|
||||
pname, pname .. " voted " .. vote)
|
||||
end)
|
||||
end)
|
||||
elseif fields.add_org then
|
||||
modpol.interactions.add_org(pname, 1)
|
||||
elseif fields.remove_org then
|
||||
modpol.interactions.remove_org(pname)
|
||||
-- dropdowns need to go last
|
||||
elseif fields.reset_orgs then
|
||||
modpol.orgs.reset()
|
||||
modpol.instance:add_member(pname)
|
||||
modpol.interactions.dashboard(pname)
|
||||
|
||||
-- Put all dropdowns at the end
|
||||
elseif fields.close then
|
||||
minetest.close_formspec(pname, formname)
|
||||
elseif fields.all_orgs or fields.user_orgs then
|
||||
@ -117,11 +128,43 @@ function modpol.interactions.org_dashboard(user, org_name)
|
||||
end
|
||||
return toggle_code
|
||||
end
|
||||
local children = {}
|
||||
-- identify parent
|
||||
local parent = modpol.orgs.get_org(org.parent)
|
||||
if parent then parent = parent.name
|
||||
else parent = "none" end
|
||||
-- prepare children menu
|
||||
local children = {"View..."}
|
||||
for k,v in ipairs(org.children) do
|
||||
local this_child = modpol.orgs.get_org(v)
|
||||
table.insert(children, this_child.name)
|
||||
end
|
||||
-- prepare policies menu
|
||||
local policies = {"View..."}
|
||||
for k,v in pairs(org.policies) do
|
||||
table.insert(policies, k .. ": " ..
|
||||
org.policies[k].process_type)
|
||||
end
|
||||
table.insert(policies, "Add policy")
|
||||
-- prepare processes menu
|
||||
local processes = {"View..."}
|
||||
for k,v in ipairs(org.processes) do
|
||||
local this_request = org.requests[v.request_id]
|
||||
if type(this_request) == "table" then
|
||||
local active = ''
|
||||
if org.pending[user] then
|
||||
if org.pending[user][v.id] then
|
||||
active = '*'
|
||||
end
|
||||
end
|
||||
local req_str = "[" .. v.id .. "] " ..
|
||||
active .. this_request.type
|
||||
if this_request.params[1] then
|
||||
req_str = req_str .. ": " ..
|
||||
table.concat(this_request.params, ", ")
|
||||
end
|
||||
table.insert(processes, req_str)
|
||||
end
|
||||
end
|
||||
-- set player context
|
||||
local user_context = {}
|
||||
user_context["current_org"] = org_name
|
||||
@ -132,16 +175,16 @@ function modpol.interactions.org_dashboard(user, org_name)
|
||||
"size[10,8]",
|
||||
"label[0.5,0.5;Org: "..
|
||||
minetest.formspec_escape(org_name).."]",
|
||||
"label[0.5,1;Parent: TODO]",
|
||||
"label[0.5,1;Parent: "..parent.."]",
|
||||
"button[8.5,0.5;1,0.8;"..membership_toggle(),
|
||||
"label[0.5,2;Members:]",
|
||||
"dropdown[2,1.5;5,0.8;user_orgs;"..formspec_list(org.members)..";;]",
|
||||
"label[0.5,3;Children:]",
|
||||
"dropdown[2,2.5;5,0.8;children;"..formspec_list(children)..";;]",
|
||||
"label[0.5,4;Policies:]",
|
||||
"dropdown[2,3.5;5,0.8;policies;"..formspec_list(org.policies)..";;]",
|
||||
"dropdown[2,3.5;5,0.8;policies;"..formspec_list(policies)..";;]",
|
||||
"label[0.5,5;Processes:]",
|
||||
"dropdown[2,4.5;5,0.8;processes;"..formspec_list(org.processes)..";;]",
|
||||
"dropdown[2,4.5;5,0.8;processes;"..formspec_list(processes)..";;]",
|
||||
"button[0.5,7;1,0.8;test_poll;Test poll]",
|
||||
"button[2,7;1,0.8;add_child;Add child]",
|
||||
"button[3.5,7;1.5,0.8;remove_org;Remove org]",
|
||||
@ -159,29 +202,89 @@ minetest.register_on_player_receive_fields(function (player, formname, fields)
|
||||
if nil then
|
||||
elseif fields.join then
|
||||
local new_request = {
|
||||
user = player,
|
||||
user = pname,
|
||||
type = "add_member",
|
||||
params = {player}
|
||||
params = {pname}
|
||||
}
|
||||
org:make_request(new_request)
|
||||
--org:add_member(pname)
|
||||
modpol.interactions.org_dashboard(pname,org.name)
|
||||
elseif fields.leave then
|
||||
org:remove_member(pname)
|
||||
modpol.interactions.dashboard(pname)
|
||||
elseif fields.test_poll then
|
||||
modpol.interactions.binary_poll_org(pname, _contexts.pname.current_org.id, "Poll question (yes/no):")
|
||||
modpol.interactions.binary_poll_org(
|
||||
pname, org.id,
|
||||
function(input)
|
||||
modpol.interactions.message_org(
|
||||
pname,
|
||||
org.id,
|
||||
pname .. " responded " .. input)
|
||||
end)
|
||||
elseif fields.add_child then
|
||||
modpol.interactions.add_org(pname, org.id)
|
||||
modpol.interactions.text_query(
|
||||
pname, "Child org name:",
|
||||
function(input)
|
||||
local new_request = {
|
||||
user = pname,
|
||||
type = "add_org",
|
||||
params = {input,pname}
|
||||
}
|
||||
org:make_request(new_request)
|
||||
modpol.interactions.message(pname,"requested")
|
||||
modpol.interactions.org_dashboard(
|
||||
pname,org.name)
|
||||
end)
|
||||
elseif fields.remove_org then
|
||||
modpol.interactions.remove_org(pname)
|
||||
local new_request = {
|
||||
user = pname,
|
||||
type = "delete",
|
||||
params = {}
|
||||
}
|
||||
org:make_request(new_request)
|
||||
modpol.interactions.org_dashboard(pname,org.name)
|
||||
elseif fields.back then
|
||||
modpol.interactions.dashboard(pname)
|
||||
|
||||
-- Put all dropdowns at the end
|
||||
elseif fields.policies
|
||||
and fields.policies ~= "View..." then
|
||||
local policy
|
||||
if fields.policies == "Add policy" then
|
||||
policy = nil
|
||||
elseif fields.policies == "View..." then
|
||||
return
|
||||
else
|
||||
policy = string.match(fields.policies,"(%w+)%:")
|
||||
end
|
||||
modpol.interactions.policy_dashboard(
|
||||
pname, org.id, policy)
|
||||
elseif fields.processes
|
||||
and fields.processes ~= "View..." then
|
||||
local sel = string.match(fields.processes,"%[(%d)%]")
|
||||
local process = org.processes[tonumber(sel)]
|
||||
if process then
|
||||
process:interact(pname)
|
||||
end
|
||||
elseif fields.children
|
||||
and fields.children ~= "View..." then
|
||||
local org_name = fields.children
|
||||
modpol.interactions.org_dashboard(pname, org_name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
-- Function: modpol.interactions.policy_dashboard
|
||||
-- input: user (string), org_id (int), policy (string)
|
||||
-- output: opens a dashboard for viewing/editing policy details
|
||||
-- TODO
|
||||
function modpol.interactions.policy_dashboard(
|
||||
user, org_id, policy)
|
||||
modpol.interactions.message(
|
||||
user,
|
||||
"Not yet implemented: " .. policy)
|
||||
end
|
||||
|
||||
|
||||
-- BASIC INTERACTION FUNCTIONS
|
||||
-- ===========================
|
||||
@ -189,16 +292,16 @@ end)
|
||||
-- Function: modpol.interactions.message
|
||||
-- input: message (string)
|
||||
-- output
|
||||
modpol.interactions.message = function(user, message)
|
||||
function modpol.interactions.message(user, message)
|
||||
minetest.chat_send_player(user, message)
|
||||
end
|
||||
|
||||
-- Function: modpol.interactions.text_query
|
||||
-- Overrides function at modpol/interactions.lua
|
||||
-- input: Query (string), User (string)
|
||||
-- output: User response (string)
|
||||
-- TODO Need to switch "user" to index not name
|
||||
function modpol.interactions.text_query(user, query)
|
||||
-- input: user (string), query (string), func (function)
|
||||
-- func input: user input (string)
|
||||
-- output: Applies "func" to user input
|
||||
function modpol.interactions.text_query(user, query, func)
|
||||
-- set up formspec
|
||||
local formspec = {
|
||||
"formspec_version[4]",
|
||||
@ -210,40 +313,63 @@ function modpol.interactions.text_query(user, query)
|
||||
local formspec_string = table.concat(formspec, "")
|
||||
-- present to players
|
||||
minetest.show_formspec(user, "modpol:text_query", formspec_string)
|
||||
-- put func in _contexts
|
||||
if _contexts[user] == nil then _contexts[user] = {} end
|
||||
_contexts[user]["text_query_func"] = func
|
||||
end
|
||||
-- receive fields
|
||||
minetest.register_on_player_receive_fields(function (player, formname, fields)
|
||||
if formname == "modpol:text_query" then
|
||||
local pname = player:get_player_name()
|
||||
if _contexts[pname] then
|
||||
_contexts[pname](fields.input)
|
||||
local input = fields.input
|
||||
if not input then
|
||||
-- no input, do nothing
|
||||
else
|
||||
local func = _contexts[pname]["text_query_func"]
|
||||
if func then
|
||||
func(input)
|
||||
else
|
||||
modpol.interactions.message(pname, "text_query: " .. input)
|
||||
end
|
||||
end
|
||||
minetest.close_formspec(pname, formname)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
-- Function: dropdown_query
|
||||
-- input: user (string), label (string), options (table of strings)
|
||||
function modpol.interactions.dropdown_query(user, label, options)
|
||||
-- input: user (string), label (string), options (table of strings), func (function)
|
||||
-- func input: choice (string)
|
||||
-- output: calls func on user choice
|
||||
function modpol.interactions.dropdown_query(user, label, options, func)
|
||||
-- set up formspec
|
||||
local formspec = {
|
||||
"formspec_version[4]",
|
||||
"size[10,4]",
|
||||
"label[0.5,1;"..minetest.formspec_escape(label).."]",
|
||||
"dropdown[0.5,1.25;9,0.8;input;"..formspec_list(options)..";;]",
|
||||
"button[0.5,2.5;1,0.8;yes;OK]",
|
||||
"button[0.5,2.5;1,0.8;cancel;Cancel]",
|
||||
}
|
||||
local formspec_string = table.concat(formspec, "")
|
||||
-- present to players
|
||||
minetest.show_formspec(user, "modpol:dropdown_query", formspec_string)
|
||||
-- put func in _contexts
|
||||
if _contexts[user] == nil then _contexts[user] = {} end
|
||||
_contexts[user]["dropdown_query_func"] = func
|
||||
end
|
||||
-- receive fields
|
||||
minetest.register_on_player_receive_fields(function (player, formname, fields)
|
||||
if formname == "modpol:dropdown_query" then
|
||||
local pname = player:get_player_name()
|
||||
if _contexts[pname] then
|
||||
_contexts[pname](fields.input)
|
||||
local pname = player:get_player_name()
|
||||
if fields.cancel ~= "cancel" then
|
||||
local choice = fields.input
|
||||
local func = _contexts[pname]["dropdown_query_func"]
|
||||
if not choice then
|
||||
-- no choice, do nothing
|
||||
elseif func then
|
||||
func(choice)
|
||||
else
|
||||
modpol.interactions.message(pname, "dropdown_query: " .. choice)
|
||||
end
|
||||
end
|
||||
minetest.close_formspec(pname, formname)
|
||||
end
|
||||
@ -253,27 +379,28 @@ end)
|
||||
-- SECONDARY INTERACTIONS
|
||||
-- ======================
|
||||
|
||||
-- Function: modpol.binary_poll_user(user, question)
|
||||
-- Function: modpol.binary_poll_user(user, question, function)
|
||||
-- Overrides function at modpol/interactions.lua
|
||||
-- presents a yes/no poll to a user, returns answer
|
||||
--
|
||||
function modpol.interactions.binary_poll_user(user, question)
|
||||
-- Params: user (string), question (string), func (function)
|
||||
-- func input: user input (string: y/n)
|
||||
-- Output: Applies "func" to user input
|
||||
function modpol.interactions.binary_poll_user(user, question, func)
|
||||
-- set up formspec
|
||||
local text = "Poll: " .. question
|
||||
local formspec = {
|
||||
"formspec_version[4]",
|
||||
"size[5,3]",
|
||||
"label[0.375,0.5;", minetest.formspec_escape(text), "]",
|
||||
"label[0.375,0.5;",minetest.formspec_escape(question), "]",
|
||||
"button[1,1.5;1,0.8;yes;Yes]",
|
||||
"button[2,1.5;1,0.8;no;No]",
|
||||
--TKTK can we enable text wrapping?
|
||||
--TKTK we could use scroll boxes to contain the text
|
||||
}
|
||||
local formspec_string = table.concat(formspec, "")
|
||||
if _contexts[user] == nil then _contexts[user] = {} end
|
||||
_contexts[user]["binary_poll_func"] = func
|
||||
-- present to player
|
||||
minetest.show_formspec(user, "modpol:binary_poll_user", formspec_string)
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function (player, formname, fields)
|
||||
local pname = player:get_player_name()
|
||||
-- modpol:binary_poll
|
||||
@ -281,79 +408,80 @@ minetest.register_on_player_receive_fields(function (player, formname, fields)
|
||||
local vote = nil
|
||||
if fields.yes then vote = fields.yes
|
||||
elseif fields.no then vote = fields.no
|
||||
elseif fields.abstain then vote = fields.abstain
|
||||
end
|
||||
if vote then
|
||||
modpol.interactions.message(pname, "Vote recorded")
|
||||
minetest.chat_send_all(pname .. " voted " .. vote)
|
||||
--TODO : we should send the message to all in that org, not to all players
|
||||
modpol.interactions.message(pname, "Responded " .. vote)
|
||||
local func = _contexts[pname]["binary_poll_func"]
|
||||
if func then func(vote) end
|
||||
end
|
||||
minetest.close_formspec(pname, formname)
|
||||
return vote
|
||||
end
|
||||
end)
|
||||
|
||||
-- COMPLEX INTERACTIONS
|
||||
-- ====================
|
||||
|
||||
-- Function: modpol.interactions.message_org
|
||||
-- input: initiator (string), org_id (number), message (string)
|
||||
-- output: broadcasts message to all org members
|
||||
function modpol.interactions.message_org(initiator, org_id, message)
|
||||
local org = modpol.orgs.get_org(org_id)
|
||||
local users = org:list_members()
|
||||
for k,v in ipairs(users) do
|
||||
modpol.interactions.message(v, message)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Function: modpol.interactions.binary_poll_org
|
||||
-- input: initator (user string), org (number)
|
||||
-- output: interaction begins
|
||||
function modpol.interactions.binary_poll_org(initiator, org)
|
||||
-- start formspec
|
||||
modpol.interactions.text_query(initiator, "Poll question (yes/no):")
|
||||
-- set user's context to followup function
|
||||
_contexts[initiator] =
|
||||
-- input: initator (user string), org_id (number)
|
||||
-- output: gets question from initiator, asks all org members, broadcasts answers
|
||||
-- TODO for testing. This should be implemented as a request.
|
||||
function modpol.interactions.binary_poll_org(initiator, org_id, func)
|
||||
local org = modpol.orgs.get_org(org_id)
|
||||
local users = org:list_members()
|
||||
modpol.interactions.text_query(
|
||||
initiator, "Yes/no poll question:",
|
||||
function(input)
|
||||
local users = modpol.list_users()
|
||||
for k,v in ipairs(users) do
|
||||
modpol.interactions.binary_poll_user(v, input)
|
||||
modpol.interactions.binary_poll_user(v, input, func)
|
||||
end
|
||||
_contexts[initiator] = nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Function: modpol.interactions.add_org
|
||||
-- input: initator (user string), base_org_id (ID)
|
||||
-- output: interaction begins
|
||||
function modpol.interactions.add_org(initiator, base_org_id)
|
||||
-- start formspec
|
||||
modpol.interactions.text_query(initiator, "Org name:")
|
||||
-- set user's context to followup function
|
||||
_contexts[initiator] = function(input)
|
||||
if input then
|
||||
local base_org = modpol.orgs.get_org(base_org_id)
|
||||
local result = base_org:add_org(input, initiator)
|
||||
if result then
|
||||
local message = input .. " created"
|
||||
modpol.interactions.message(initiator, message)
|
||||
end
|
||||
end
|
||||
_contexts[initiator] = nil
|
||||
modpol.interactions.dashboard(initiator)
|
||||
end
|
||||
-- GODMODE
|
||||
function modpol.interactions.add_org(user, base_org_id)
|
||||
modpol.interactions.text_query(
|
||||
user,"Org name:",
|
||||
function(input)
|
||||
local base_org = modpol.orgs.get_org(1)
|
||||
local result = base_org:add_org(input, user)
|
||||
local message = input .. " created"
|
||||
modpol.interactions.message(user, message)
|
||||
modpol.interactions.dashboard(user)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Function: modpol.interactions.remove_org
|
||||
-- input: initator (user string)
|
||||
-- output: interaction begins
|
||||
function modpol.interactions.remove_org(initiator)
|
||||
-- GODMODE
|
||||
function modpol.interactions.remove_org(user)
|
||||
-- start formspec
|
||||
local orgs_list = modpol.orgs.list_all()
|
||||
local label = "Choose an org to remove:"
|
||||
modpol.interactions.dropdown_query(initiator, label, orgs_list)
|
||||
-- set user's context to followup function
|
||||
_contexts[initiator] = function(input)
|
||||
if input then
|
||||
local target_org = modpol.orgs.get_org(input)
|
||||
local result = target_org:delete()
|
||||
if result then
|
||||
modpol.interactions.dropdown_query(
|
||||
user, label, orgs_list,
|
||||
function(input)
|
||||
if input then
|
||||
local target_org = modpol.orgs.get_org(input)
|
||||
local result = target_org:delete()
|
||||
local message = input .. " deleted"
|
||||
modpol.interactions.message(initiator, message)
|
||||
modpol.interactions.message(user, message)
|
||||
end
|
||||
end
|
||||
_contexts[initiator] = nil
|
||||
modpol.interactions.dashboard(initiator)
|
||||
end
|
||||
modpol.interactions.dashboard(user)
|
||||
end)
|
||||
end
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user