-- INTERACTIONS.LUA (CLI) -- User interaction functions for Modpol -- Called by modpol.lua modpol.interactions = {} -- DASHBOARDS -- ========== -- 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.interactions.dashboard(user) -- adds user to root org if not already in it if not modpol.instance:has_member(user) then modpol.instance:add_member(user) end local all_users = modpol.instance:list_members() print('\n-=< MODPOL DASHBOARD >=-') print('All orgs: (user orgs indicated by *)') for id, org in ipairs(modpol.orgs.array) do if type(org) == "table" then local indicator = "" if org:has_member(user) then indicator = "*" end print('['..id..'] '..indicator..org.name) end end -- pending list local user_pending = {} local user_pending_count = 0 for k,v in ipairs(modpol.orgs.array) do if v.pending and v.pending[user] then if modpol.util.num_pairs(v.pending[user]) ~= 0 then table.insert(user_pending, v.name) user_pending_count = user_pending_count + 1 end end end print('Pending actions in: '..table.concat(user_pending,', ')) print('All users: ' .. table.concat(all_users, ', ')) print() print("Commands: (O)rg, (U)ser, (R)eset, Enter to close") local sel = io.read() if sel == "O" or sel == "o" then print('Access which org id?') sel = io.read() print() if modpol.orgs.array[tonumber(sel)] then local sel_org = modpol.orgs.array[tonumber(sel)].name modpol.interactions.org_dashboard(user, sel_org) else print("Org id not found") modpol.interactions.dashboard(user) end elseif sel == "U" or sel == "u" then print("Access which user?") sel = io.read() print() if modpol.instance:has_member(sel) then modpol.interactions.user_dashboard( user, sel, function() modpol.interactions.dashboard(user) end ) elseif sel == "" then return else print("User name not found") modpol.interactions.dashboard(user) end elseif sel == "R" or sel == "r" then modpol.instance.members = {} modpol.orgs.reset() print("Orgs and users reset") modpol.interactions.dashboard(user) else print("Invalid input, try again") modpol.interactions.dashboard(user) end end -- Function: modpol.interactions.org_dashboard -- Params: user (string), org_string (string or id) -- Output: Displays a menu of org-specific commands to the user function modpol.interactions.org_dashboard(user, org_string) local org = modpol.orgs.get_org(org_string) if not org then return nil end -- identify parent local parent = "" if org.id == 1 then parent = "none" else parent = modpol.orgs.get_org(org.parent).name end -- identify children 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 -- prepare modules menu local modules = {} if org.modules then for k,v in pairs(org.modules) do if not v.hide then -- hide utility modules local module_entry = v.slug table.insert(modules, module_entry) end end end table.sort(modules) -- list pending local process_msg = #org.processes .. " total processes" if org.pending[user] then process_msg = process_msg .. " (" .. modpol.util.num_pairs(org.pending[user]) .. " pending)" else process_msg = process_msg .. " (0 pending)" end -- set up output print('\n-=< ORG DASHBOARD >=-') print("Org: " .. org.name) print("Parent: " .. parent) print("Members: " .. table.concat(org.members, ", ")) print("Child orgs: " .. table.concat(children, ", ")) print("Modules: " .. table.concat(modules, ", ")) print("Pending: " .. process_msg) print() print("Commands: (M)odules, (P)ending, (B)ack") local sel = io.read() print() if sel == 'm' or sel == 'M' then print("Type module name: ") local module_sel = io.read() print() local module_result = false for k,v in ipairs(modules) do if v == module_sel then module_result = true end end if module_result then org:call_module(module_sel, user) else print("Error: Module not found.") end elseif sel == 'p' or sel == 'P' then local processes = {} print("All processes: (* indicates pending)") for i,v in ipairs(org.processes) do local active = '' if org.pending[user] then if org.pending[user][v.id] then active = '*' end end print("["..v.id.."] "..v.slug..active) end print() print("Interact with which one (use [id] number)?") local to_interact = io.read() local process = org.processes[tonumber(to_interact)] if not process then return end if org:has_pending_actions(user) then if org.pending[user][process.id] then org:interact(process.id, user) end end elseif sel == 'b' or sel == 'B' then modpol.interactions.dashboard(user) else print("Command not found") modpol.interactions.org_dashboard(user, org.name) end end --- Function: modpol.interactions.user_dashboard -- Displays a dashboard about a particular user -- @param viewer Name of user viewing the dashboard (string) -- @param user Name of user being viewed (string) -- @param completion Optional function to call on Done button function modpol.interactions.user_dashboard(viewer, user, completion) local user_orgs = {} local user_modules = {} print("\n-=< USER DASHBOARD: "..user.." >=-") print("User's orgs:") for id, org in ipairs(modpol.orgs.array) do if type(org) == "table" then if org:has_member(user) then print(org.name) end end end print() print("Commands: (M)essage user, Enter when done") local sel = io.read() if sel == "M" or sel == "m" then modpol.interactions.message_user( viewer, user) completion() else completion() end end -- buttons: message, done -- INTERACTION PRIMITIVES -- ====================== -- Function: modpol.interactions.message -- Produces a brief message to a user -- input: user (string), message (string) -- output: prints message to CLI function modpol.interactions.message(user, message) print(user .. ": " .. message) end --- Function: modpol.interactions.message_user -- Gets and sends a message from one user to another -- @param sender Name of user sending (string) -- @param recipient Name of user receiving (string) function modpol.interactions.message_user(sender, recipient) print("Enter your message for "..recipient..":") local sel = io.read() modpol.interactions.message( recipient, sel.." [from "..sender.."]") end --- Function: modpol.interactions.display -- Displays complex data to a user -- @param user Name of target user (string) -- @param message Content of message (string or table of strings) -- @param done Optional function for what happens when user is done function modpol.interactions.display(user, message, completion) local output = "" if type(message) == table then output = table.concat(message,"\n") elseif type(message) == string then output = message elseif type(message) == number then output = message else return nil, "Error: message not typed for display" end print(message) print("\nEnter to continue") io.read() if completion then completion() else modpol.intereactions.dashboard(user) end end -- Function: modpol.interactions.text_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) 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), 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" if answer == "y" then modpol.interactions.message(user, "Response recorded") func("Yes") elseif answer == "n" then modpol.interactions.message(user, "Response recorded") func("No") else modpol.interactions.message(user, "Error: invalid response") end end -- COMPLEX INTERACTIONS -- ==================== -- Function: modpol.interactions.message_org -- input: initiator (string), org (number or string), message (string) -- output: broadcasts message to all org members function modpol.interactions.message_org(initiator, org, message) local this_org = modpol.orgs.get_org(org) local users = this_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_id (number) -- output: gets question from initiator, asks all org members, broadcasts answers -- TESTING --testing command function modpol.msg(text) modpol.interactions.message("TEST MSG: ",text) end