-- =================================================================== -- /orgs.lua -- Org-related functions for Modular Politics -- Called by modpol/modpol/api.lua -- =================================================================== -- preserve records -- if true, will store org ledgers when they are closed, for later reference. local preserve_records = true modpol.default_org_policies = modpol.default_org_policies or {} -- initiate the table of orgs modpol.orgs = {} if preserve_records then modpol.old_ledgers = {} -- a backup of all legders of all closed orgs. end --define the template of a ledger entry modpol.ledger_entry_temp = { timestamp = '', entrytype = nil, action_msg = '', org_name = nil, } -- define the basic template of an org. modpol.org_temp = { name = nil, policies = {}, members = {}, ledger = {}, parent = nil, children = {}, properties = {}, old_names = {}, } -- =================================================================== -- Function: modpol.create_ledger_entry -- Params: strings action_msg, org_name, type -- Outputs: -- returns a ledger entry table, with a timestamp -- This function accepts a message and an optional org_name and type specification -- and returns a valid ledger entry table modpol.create_ledger_entry = function(action_msg, org_name, entry_type) local entry = modpol.ledger_entry_temp if action_msg and type(action_msg) == 'string' then entry.action_msg = action_msg end if org_name and type(org_name) == 'string' and modpol.orgs [org_name] then entry.org_name = org_name end if entry_type and type(entry_type) == 'string' then entry.entrytype = entry_type end entry.timestamp = os.time() return entry end -- =================================================================== -- Function: modpol.record -- Params: strings msg, org, type -- Outputs: -- "msg" specifies an event and/or status message. -- "org" specifies an "org" name. -- "type" -- optional type of legder entry. Could be e.g. 'election_result', 'add_member', etc -- This function adds the message to a global ledger and, if "org" -- specifies a valid "org", to an "org"-specific ledger. Both the mem- -- ory-resident and on-disk copies of the data structures used are up- -- dated. modpol.record = function (org_name, msg, type) local entry = modpol.create_ledger_entry(msg,org_name,type) -- Record in global ledger table.insert (modpol.ledger, entry) -- Record in "org"-specific ledger if modpol.orgs [org_name] ~= nil then local org_ledger = modpol.orgs [org_name]["ledger"] if org_ledger == nil then modpol.orgs [org_name]["ledger"] = { entry } else modpol.orgs [org_name]["ledger"] = table.insert (org_ledger, entry) end end modpol.store_data() -- Copy data to disk end -- =================================================================== -- Function: modpol.add_org -- Params: string name, table members, string parent, table properties -- Output: -- -- This function creates an "org". It returns a boolean success indicator and -- either an error message starting with "Error:" or a success message. -- -- The string parameter specifies the "org" name. -- -- The members table should be an integer-indexed array of member -- names. -- parent should be nil or a valid existing org name. If nil, defaults to -- 'instance' -- properties defaults to an empty table. Use it to initialize the org -- with arbitrary properties modpol.add_org = function(org_name, members, parent, properties) local str if modpol.ocutil.str_empty (org_name) then return false,"Error: Org needs a name" end if parent and not modpol.orgs[parent] then return false,"Error: Parent must be a valid org" end if properties and not type(properties) == 'table' then return false,"Error: Properties must be a table" end modpol.ocutil.log ("Adding org " .. org_name) if modpol.orgs [org_name] ~= nil then str = "Error: Org " .. org_name .. " already exists" modpol.ocutil.log (str) return false,str end local parent_org = parent or 'instance' local props = properties or {} -- actually add the org local org = modpol.org_temp local default_policies = modpol.default_org_policies or {} org.name = org_name -- do not change this without the proper api function. This is only a reference, not the only place this is recorded. org.members = members org.parent = parent_org org.properties = props org.policies = default_policies modpol.orgs [org_name] = org --record the child org in the parent's children table if modpol.orgs[parent_org].children then table.insert(modpol.orgs[parent_org].children, org_name) else modpol.orgs[parent].children = {org_name} end local msg = "New org: " .. org_name .. " (" .. table.concat (members, ", ") .. ")" modpol.record (org_name, msg, 'org_init') return true, msg end -- =================================================================== -- Function: modpol.remove_org -- Params: string name, opt string reason -- Output: -- -- This function removes an "org". It returns a boolean -- success indicator and either an error message -- starting with "Error:" or a success message. It also recursively -- removes all child orgs, with 'remove parent org' as reason. If -- preserve_records is enabled, it logs the removal in the org ledger, -- and stores the ledger in modpol.old_legders -- The string parameter specifies the "org" name. -- -- The reason is an optional string to additionally include in the -- log as reason for removal modpol.remove_org = function (org_name, reason) local str if not reason then reason = '' end if modpol.ocutil.str_empty (org_name) then return false,"Error: Specify an org name to remove" end if org_name == 'instance' then return false,"Error: You cannot remove instance" end modpol.ocutil.log ("Removing org " .. org_name) if modpol.orgs [org_name] == nil then str = "Error: Cannot remove non-existant org " .. org_name modpol.ocutil.log (str) return false,str end --log msg local msg = 'Removing org '..org_name ..', reason: '.. reason -- actually remove the org modpol.record (org_name, msg, 'org_remove') -- TODO: if callbacks are implemented, do so here -- if preserve_records then modpol.old_ledgers = modpol.orgs[org_name].ledger end -- also remove all children if modpol.orgs[org_name].children and #modpol.orgs[org_name].children > 0 then for _,child_name in pairs(modpol.orgs[org_name].children) do modpol.remove_org(child_name, 'remove parent org') --this is recursion. If it gets to be a problem, we will need a separate function end end modpol.orgs[org_name] = nil return true,msg end -- =================================================================== -- Function: modpol.list_orgs -- Params: None -- Output: -- This function returns a text-format list of "orgs". The list shows -- one "org" per line in the following format: -- org_name (member, member, ...) -- If there are no "orgs", the output is an empty string. modpol.list_orgs = function() local outbuf = "" local str for org_name, org_data in pairs (modpol.orgs) do -- Process next "org" -- Build string version of member list local memcat = org_data ["members"] if modpol.ocutil.str_empty (memcat) then memcat = "(empty)" else memcat = "(" .. table.concat (memcat, ", ") .. ")" end -- Build output string outbuf = outbuf .. org_name .. " " .. memcat .. "\n" end return outbuf end -- =================================================================== -- Function: modpol.reset_orgs -- Params: None -- Removes all orgs and recreates blank org "instance" with all -- current users -- returns confirmation message modpol.reset_orgs = function() local users = modpol.list_users() --store the ledgers in modpol.old_ledgers, and note that each org was closed in each record. if preserve_records then for name, org in pairs(modpol.orgs) do local old_ledger = org.ledger table.insert(old_ledger,modpol.create_ledger_entry('Removing org '.. name, name, 'org_purge')) table.insert(modpol.old_ledgers, org.ledger) end end modpol.orgs = {} local message = "Orgs purged" modpol.record(nil, message, 'org_purge') modpol.add_org("instance", users) return message end -- =================================================================== -- Function: modpol.add_member -- Params: org (string), member (string) -- Output: -- Adds the specified member to the specified org -- Returns a boolean success indicator and -- either a confirmation or error message -- TODO, check that member is a user, if the org_name is not instance modpol.add_member = function(org_name, member) if (modpol.orgs[org_name] == nil) then return false,"Error: No such org" elseif type(member) ~= 'string' then return false,"Error: member is wrong type" else if modpol.is_member(org_name,member) then return false,"Error: " .. member .. " is already a member of " .. org_name else table.insert(modpol.orgs[org_name]["members"], member) local message = member .. " added to org " .. org_name modpol.record(org_name, message, 'add_member') return true,message end end end -- =================================================================== -- Function: modpol.is_member -- Params: org (string), member (string) -- Output: Boolean depending on membership or nil/error message modpol.is_member = function(org, member) if (modpol.orgs[org] == nil) then return nil, "Error: No such org" else local member_table = modpol.orgs[org]["members"] if member_table then for index, value in pairs(member_table) do if value == member then -- match found return true end end -- no match found return false end return false end end -- =================================================================== -- Function: modpol.remove_member -- Params: org (string), member (string) -- Output: -- Removes the specified member from the specified org -- Returns confirmation or error message function modpol.remove_member(org, member) local message = "Error: No such org" if (modpol.orgs[org] == nil) then return false, message else local member_table = modpol.orgs[org]["members"] if member_table then for index, value in pairs(member_table) do if value == member then -- match found, set to nil modpol.orgs[org]["members"][value] = nil -- TODO: add callbacks here -- message = member .. " removed from org " .. org modpol.record(org_name, message, 'remove_member') return true, message end end end -- no match found (or org has no members) message = member .. " not found in org " .. org return false, message end end -- =================================================================== -- TKTK -- rename_org(old_name, new_name) -- Renames an org -- =================================================================== -- POLICIES -- Each policy is a table with a string for a key. Contents: -- KEY - name of the policy (string) -- [member orgs]: external orgs allowed to call policy -- [function]: the function that initiates the policy -- =================================================================== -- =================================================================== -- modpol.add_policy -- Adds a policy to an org's [org].policies table -- Params: org_name (string), -- policy_name (string), -- policy_table (table) -- Output: true if successful, nil if error -- TKTK NEEDS TO BE REWRITTEN modpol.add_policy = function(org_name, policy_name, policy_table) -- first, basic checks local message = "Error: No such org" if (modpol.orgs[org_name] == nil) then return nil, message elseif (modpol[target_function] == nil) then message = "Error: No such target function" return nil, message elseif (modpol[policy_function]) then message = "Error: No such policy function" return nil, message else -- okay, proceed modpol.orgs[org_name].policies[target_function] = policy_function message = "In org " .. org_name .. ", policy for " .. target_function .. " set to " .. policy_function record(org_name, message) return true, message end end -- =================================================================== -- End of file.