diff --git a/modpol/api.lua b/modpol/api.lua index b5092de..d2be724 100644 --- a/modpol/api.lua +++ b/modpol/api.lua @@ -6,7 +6,7 @@ local localdir = modpol.topdir dofile (localdir .. "/users/users.lua") --orgs -dofile (localdir .. "/orgs/new_orgs.lua") +dofile (localdir .. "/orgs/orgs.lua") --interactions dofile (localdir .. "/interactions/interactions.lua") diff --git a/modpol/orgs/new_orgs.lua b/modpol/orgs/new_orgs.lua deleted file mode 100644 index bc78cd1..0000000 --- a/modpol/orgs/new_orgs.lua +++ /dev/null @@ -1,233 +0,0 @@ -modpol.orgs = -{ - count = 1, - array = {} -} - --- sets modpol.orgs as it's own fallback -modpol.orgs.__index = modpol.orgs - --- ================================================== --- returns org when given its id or name -function modpol.orgs.get_org(id) - if type(id) == 'string' then - for id, org in ipairs(modpol.orgs.array) do - if org.name == id then - return org - end - end - elseif type(id) == 'number' then - return modpol.orgs.array[id] - end -end - --- =============================================== --- returns a string list of all orgs -function modpol.orgs.list_all() - local str - for k, v in ipairs(modpol.orgs.array) do - if type(v) == 'table' then - if str then - str = str .. '\n' .. v.name - else - str = v.name - end - end - end - return str -end - --- =========================================== --- deletes all orgs except for the instance -function modpol.orgs.reset() - for id, org in ipairs(modpol.orgs.array) do - if id > 1 then - modpol.orgs.array[id] = nil - end - end -end - --- =================================================== --- initializes the instance (root org) --- can only be run once, as only one instance can exist -function modpol.orgs.init_instance() - local error_msg - if modpol.orgs.array[1] then - error_msg = 'Error: instance has already been initialized' - modpol.ocutil.log(error_msg) - return false, error_msg - end - - local instance = { - id = 1, - name = "instance", - policies = {}, - members = {}, - ledger = {}, - parent = nil, - children = {}, - properties = {}, - old_names = {} - } - setmetatable(instance, modpol.orgs) - - -- adding instance to org list - modpol.orgs.array[1] = instance - return instance -end - - --- FUNCTIONS BEYOND HERE OPERATE ON ORG OBJECTS - --- ======================================================= --- records a log message to the modpol ledger -function modpol.orgs:record(msg, entry_type) - local entry = { - timestamp = '', - entry_type = nil, - action_msg = '', - org_name = '', - org_id = nil, - } - - if type(msg) == 'string' and not(modpol.ocutil.str_empty(msg)) then - entry.action_msg = msg - else - print('Error: msg must be a non empty string') - return false - end - - if type(entry_type) == 'string' and not(modpol.ocutil.str_empty(entry_type)) then - entry.entry_type = entry_type - else - print('Error: entry_type must be a non empty string') - return false - end - - entry.timestamp = os.time() - entry.org_id = self.id - entry.org_name = self.name - - table.insert(modpol.ledger, entry) - modpol.store_data() -end - --- ================================================== --- adds a new sub org to the org it is called on --- ex: instance:add_org('town hall') -function modpol.orgs:add_org(name) - if self.id == nil then - error_msg = 'Error: add_org can only be called by another org' - modpol.ocutil.log(error_msg) - return false, error_msg - end - - if modpol.ocutil.str_empty(name) then - error_msg = 'Error: org name is required' - modpol.ocutil.log(error_msg) - return false, error_msg - end - - -- creating the child sub org - modpol.orgs.count = modpol.orgs.count + 1 - local child_org = { - id = modpol.orgs.count, - name = name, - policies = {}, - members = {}, - parent = self.id, - children = {}, - } - setmetatable(child_org, modpol.orgs) - - -- adding child id to list of children - table.insert(self.children, child_org.id) - - -- adding child to org list - modpol.orgs.array[child_org.id] = child_org - - - return child_org -end - --- ======================================== --- recursively deletes an org and its suborgs --- leaves entry in modpol.orgs.array as a string "removed" --- note: "reason" param was removed, can be added back -function modpol.orgs:delete() - if self.id == 1 then - return false, 'Error: cannot delete instance' - end - - if #self.children > 0 then - for i, child_id in pairs(self.children) do - local child = modpol.orgs.get_org(child_id) - print(child_id, child) - child:delete() - end - end - - modpol.orgs.array[self.id] = 'removed' - print('Removed ' .. self.name .. ': ' .. self.id) - -end - - --- =========================================== --- internal function to get the index of a member name -function modpol.orgs:get_member_index(member) - for k, v in ipairs(self.members) do - if v == member then - return k - end - end -end - --- =========================================== --- adds a user to an org -function modpol.orgs:add_member(user) - -- trys to fill in empty spots first - empty_index = self:get_member_index('') - if empty_index then - self.members[empty_index] = user - else - -- adds to end if no empty spots - table.insert(self.members, user) - end -end - --- ======================================= --- removes a user from an org -function modpol.orgs:remove_member(user) - -- sets the array index to an empty string so that consecutive list is preserved - -- empty spots will get filled in by new members - user_index = self:get_member_index(user) - if user_index then - self.members[user_index] = '' - end -end - --- =========================================== --- boolean check whether user is an org -function modpol.orgs:has_member(user) - user_index = self:get_member_index(user) - if user_index then - return true - else - return false - end -end - --- ================================== --- returns a list of users in an org -function modpol.orgs:list_member() - local str - for k, v in ipairs(self.members) do - if str then - str = str .. '\n' .. v - else - str = v - end - end - return str -end diff --git a/modpol/orgs/orgs.lua b/modpol/orgs/orgs.lua index 1d9efd8..bc78cd1 100644 --- a/modpol/orgs/orgs.lua +++ b/modpol/orgs/orgs.lua @@ -1,550 +1,233 @@ - --- =================================================================== --- /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 - --- policies to be assigned to every org can go here -modpol.default_org_policies = modpol.default_org_policies or {} - - - --- initiate the table of orgs, if it doesnt already exist from storage! -modpol.orgs = modpol.orgs or {} - - - -if preserve_records then - modpol.old_ledgers = modpol.old_ledgers or {} -- a backup of all legders of all closed orgs. -end - - --- this counter will increment every time a new org is created, and the current value will be used as the org id --- we set it to 1 for init, and then run through all existing orgs and take only the greatest... which we then add 1 to. --- this ensures a unique id for each org. -modpol.id_counter = 1 -if #modpol.orgs > 0 then - for id,org in pairs(modpol.orgs) do - if id > modpol.id_counter then - modpol.id_counter = id - end - - end - for id,ledg in pairs(modpol.old_ledgers) do - if id > modpol.id_counter then - modpol.id_counter = id - end - end - modpol.id_counter = modpol.id_counter +1 -end - - - - ---define the template of a ledger entry -modpol.ledger_entry_temp = function() - - return { - timestamp = '', - entrytype = nil, - action_msg = '', - org_name = nil, - org_id = nil, - } - -end - - --- define the basic template of an org. - - -modpol.org_temp = function() - - return { - id = nil, - name = nil, - policies = {}, - members = {}, - ledger = {}, - parent = nil, - children = {}, - properties = {}, - old_names = {}, - - } - -end - --- =================================================================== --- Function: modpol.create_ledger_entry --- Params: strings: action_msg, type, number: org_id --- Outputs: --- returns a ledger entry table, with a timestamp --- This function accepts a message and an optional org_id and type specification --- and returns a valid ledger entry table - -modpol.create_ledger_entry = function(action_msg, org_id, 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_id and type(org_id) == 'number' and modpol.orgs [org_id] then - entry.org_id = org_id - if modpol.orgs[org_id].name then - entry.org_name = modpol.orgs[org_id].name - end - 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, type, number org_id --- Outputs: --- "msg" specifies an event and/or status message. --- "org" specifies an "org" id. --- "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_id, msg, type) - - local entry = modpol.create_ledger_entry(msg, org_id, type) - -- Record in global ledger - table.insert (modpol.ledger, entry) - -- Record in "org"-specific ledger - - - if modpol.orgs [org_id] ~= nil then - - local org_ledger = modpol.orgs [org_id]["ledger"] - - if org_ledger == nil then - - modpol.orgs [org_id]["ledger"] = { entry } - print("Warning: Org ".. org_id .. " did not have a ledger. This is an error.") - - else - - table.insert (org_ledger, entry) - modpol.orgs [org_id]["ledger"] = org_ledger - - end - end - - modpol.store_data() -- Copy data to disk -end - --- =================================================================== --- Function: modpol.get_org_id_by_name(org_name) --- Params: org_name - --- Output: an org id or nil --- -modpol.get_org_id_by_name = function(org_name) - local id = nil - for org_id,org in pairs(modpol.orgs) do - if org.name == org_name then - id = org_id - end - end - return id -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_id, properties) - - local str - - if modpol.ocutil.str_empty (org_name) then - return false,"Error: Org needs a name" - end - - if parent_id and not modpol.orgs[parent_id] then - return false,"Error: Parent_id must be a valid existing org id" - end - - if properties and not type(properties) == 'table' then - return false,"Error: Properties must be a table" - end - - - modpol.ocutil.log ("Attempting to add new org " .. org_name) - - if modpol.get_org_id_by_name(org_name) ~= nil then - str = "Error: Org " .. org_name .. " already exists" - modpol.ocutil.log (str) - return false,str - end - - -- assign a new id to the new org - local org_id = modpol.id_counter - modpol.id_counter = modpol.id_counter + 1 - - - -- the instance will have a parent of nil if it is the instance. - -- otherwise, it will have a parent of instance's id if not otherwise defined - - local parent_org_id = nil - - local instance_id = modpol.get_org_id_by_name('instance') - - if parent_id and instance_id and not(instance_id == org_id) then - parent_org_id = parent_id - end - - if not parent_id and instance_id and not ( instance_id == org_id ) then - parent_org_id = modpol.get_org_id_by_name ( 'instance' ) - end - - - - - -- actually add the org - local org = modpol.org_temp() - local default_policies = modpol.default_org_policies or {} - org.id = org_id - 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_id - org.properties = properties or org.properties - org.policies = default_policies - org.children = {} - - modpol.orgs [org_id] = org - - --record the child org in the parent's children table - if parent_org_id then - - if modpol.orgs[parent_org_id].children then - table.insert(modpol.orgs[parent_org_id].children, org_id) - else - modpol.orgs[parent_org_id].children = {org_id} - end - end - - local parent_id_str = "none" - if parent_org_id then - parent_id_str = tostring(parent_org_id) - end - - local msg = "New org: " .. org_name .. " ["..org_id.."], parent = ".. parent_id_str ..", members = ".. - " (" .. table.concat (members, ", ") .. ")" - - modpol.record (org_id, msg, 'org_init') - return true, msg -end - - - - --- =================================================================== --- Function: modpol.remove_org --- Params: number id, 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_ledgers - - --- The number parameter specifies the "org" id. --- --- The reason is an optional string to additionally include in the --- log as reason for removal - -modpol.remove_org = function (org_id, reason) - local str - if not reason then reason = '' end - if not type(org_id) == 'number' or not modpol.orgs[org_id] then - str = "Error: Specify an existing org id to remove" - modpol.ocutil.log (str) - return false,str - end - local org_name = modpol.orgs[org_id] - if org_name == 'instance' then - return false,"Error: You cannot remove instance" - end - - --log msg - local msg = 'Removing org '..org_name ..' ['..org_id..'], reason: '.. reason - - modpol.ocutil.log (msg) - - - -- actually remove the org - - -- TODO: if callbacks are implemented, do so here -- - - if preserve_records then - modpol.old_ledgers[org_id] = modpol.orgs[org_id].ledger - end - - -- also remove all children - if modpol.orgs[org_id].children and #modpol.orgs[org_id].children > 0 then - for _,child_name in pairs(modpol.orgs[org_id].children) do - --this is recursion. If it gets to be a problem, we will need a separate function - modpol.remove_org(child_name, 'remove parent org') - end - end - - --also remove references to this org in parent orgs - if modpol.orgs[org_id].parent then - local parent_id = modpol.orgs[org_id].parent - modpol.orgs[parent_id].children[org_id] = nil - end - - modpol.orgs[org_id] = nil - - modpol.record (org_id, msg, 'org_remove') - - - 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_id, org_data in pairs (modpol.orgs) do - -- Process next "org" - -- Build string version of member list - local memcat = org_data ["members"] - local org_name = org_data ["name"].."["..org_id.."]" - if modpol.ocutil.str_empty (memcat) then - memcat = "(empty)" - else - memcat = "(" .. table.concat (memcat, ", ") .. ")" +modpol.orgs = +{ + count = 1, + array = {} +} + +-- sets modpol.orgs as it's own fallback +modpol.orgs.__index = modpol.orgs + +-- ================================================== +-- returns org when given its id or name +function modpol.orgs.get_org(id) + if type(id) == 'string' then + for id, org in ipairs(modpol.orgs.array) do + if org.name == id then + return org + end end - -- Build output string - outbuf = outbuf .. org_name .. " " .. memcat .. "\n" + elseif type(id) == 'number' then + return modpol.orgs.array[id] 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 id, org in pairs(modpol.orgs) do - local old_ledger = org.ledger - local entry = modpol.create_ledger_entry('Removing org '.. id, id, 'org_purge') - - if old_ledger == nil then - old_ledger = { entry } - else - table.insert(old_ledger, entry) - end - modpol.old_ledgers[id] = old_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_id (number), 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_id, member) - - if (modpol.orgs[org_id] == nil) then - return false,"Error: No such org" - elseif type(member) ~= 'string' then - return false,"Error: member is wrong type" - else - local org_name = modpol.orgs [org_id].name - if modpol.is_member(org_id,member) then - return false,"Error: " .. member .. " is already a member of " .. org_name - else - table.insert(modpol.orgs[org_id]["members"], member) - local message = member .. " added to org " .. org_name .. " ["..org_id.."]" - modpol.record(org_id, message, 'add_member') - return true,message - end - end -end - --- =================================================================== --- Function: modpol.is_member --- Params: org (number), 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 +-- =============================================== +-- returns a string list of all orgs +function modpol.orgs.list_all() + local str + for k, v in ipairs(modpol.orgs.array) do + if type(v) == 'table' then + if str then + str = str .. '\n' .. v.name + else + str = v.name end - end - -- no match found - return false - end - return false - end + end + end + return str end --- =================================================================== --- Function: modpol.remove_member --- Params: org (number), 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, for such as revoking privs, etc -- - - message = member .. " removed from org " .. org - modpol.record(org, 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 +-- =========================================== +-- deletes all orgs except for the instance +function modpol.orgs.reset() + for id, org in ipairs(modpol.orgs.array) do + if id > 1 then + modpol.orgs.array[id] = nil + end + end end --- =================================================================== --- TKTK --- rename_org(old_name, new_name) --- Renames an org +-- =================================================== +-- initializes the instance (root org) +-- can only be run once, as only one instance can exist +function modpol.orgs.init_instance() + local error_msg + if modpol.orgs.array[1] then + error_msg = 'Error: instance has already been initialized' + modpol.ocutil.log(error_msg) + return false, error_msg + end + local instance = { + id = 1, + name = "instance", + policies = {}, + members = {}, + ledger = {}, + parent = nil, + children = {}, + properties = {}, + old_names = {} + } + setmetatable(instance, modpol.orgs) - - - - - - --- =================================================================== --- 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 + -- adding instance to org list + modpol.orgs.array[1] = instance + return instance end --- =================================================================== --- End of file. + +-- FUNCTIONS BEYOND HERE OPERATE ON ORG OBJECTS + +-- ======================================================= +-- records a log message to the modpol ledger +function modpol.orgs:record(msg, entry_type) + local entry = { + timestamp = '', + entry_type = nil, + action_msg = '', + org_name = '', + org_id = nil, + } + + if type(msg) == 'string' and not(modpol.ocutil.str_empty(msg)) then + entry.action_msg = msg + else + print('Error: msg must be a non empty string') + return false + end + + if type(entry_type) == 'string' and not(modpol.ocutil.str_empty(entry_type)) then + entry.entry_type = entry_type + else + print('Error: entry_type must be a non empty string') + return false + end + + entry.timestamp = os.time() + entry.org_id = self.id + entry.org_name = self.name + + table.insert(modpol.ledger, entry) + modpol.store_data() +end + +-- ================================================== +-- adds a new sub org to the org it is called on +-- ex: instance:add_org('town hall') +function modpol.orgs:add_org(name) + if self.id == nil then + error_msg = 'Error: add_org can only be called by another org' + modpol.ocutil.log(error_msg) + return false, error_msg + end + + if modpol.ocutil.str_empty(name) then + error_msg = 'Error: org name is required' + modpol.ocutil.log(error_msg) + return false, error_msg + end + + -- creating the child sub org + modpol.orgs.count = modpol.orgs.count + 1 + local child_org = { + id = modpol.orgs.count, + name = name, + policies = {}, + members = {}, + parent = self.id, + children = {}, + } + setmetatable(child_org, modpol.orgs) + + -- adding child id to list of children + table.insert(self.children, child_org.id) + + -- adding child to org list + modpol.orgs.array[child_org.id] = child_org + + + return child_org +end + +-- ======================================== +-- recursively deletes an org and its suborgs +-- leaves entry in modpol.orgs.array as a string "removed" +-- note: "reason" param was removed, can be added back +function modpol.orgs:delete() + if self.id == 1 then + return false, 'Error: cannot delete instance' + end + + if #self.children > 0 then + for i, child_id in pairs(self.children) do + local child = modpol.orgs.get_org(child_id) + print(child_id, child) + child:delete() + end + end + + modpol.orgs.array[self.id] = 'removed' + print('Removed ' .. self.name .. ': ' .. self.id) + +end + + +-- =========================================== +-- internal function to get the index of a member name +function modpol.orgs:get_member_index(member) + for k, v in ipairs(self.members) do + if v == member then + return k + end + end +end + +-- =========================================== +-- adds a user to an org +function modpol.orgs:add_member(user) + -- trys to fill in empty spots first + empty_index = self:get_member_index('') + if empty_index then + self.members[empty_index] = user + else + -- adds to end if no empty spots + table.insert(self.members, user) + end +end + +-- ======================================= +-- removes a user from an org +function modpol.orgs:remove_member(user) + -- sets the array index to an empty string so that consecutive list is preserved + -- empty spots will get filled in by new members + user_index = self:get_member_index(user) + if user_index then + self.members[user_index] = '' + end +end + +-- =========================================== +-- boolean check whether user is an org +function modpol.orgs:has_member(user) + user_index = self:get_member_index(user) + if user_index then + return true + else + return false + end +end + +-- ================================== +-- returns a list of users in an org +function modpol.orgs:list_member() + local str + for k, v in ipairs(self.members) do + if str then + str = str .. '\n' .. v + else + str = v + end + end + return str +end