--- Basic function for orgs -- @module modpol.orgs.base modpol.orgs = modpol.orgs or { count = 1, array = {} } -- sets modpol.orgs as its own fallback modpol.orgs.__index = modpol.orgs function temp_org() return { id = nil, name = nil, policies = {}, processes = {}, pending = {}, members = {}, parent = nil, children = {}, } end --- Return org when given its id or name -- @function modpol.orgs.get_org -- @param arg string for name of org or id of org -- @return org specified by id or name function modpol.orgs.get_org(arg) if type(arg) == 'string' then for id, org in ipairs(modpol.orgs.array) do if org.name == arg then return org end end elseif type(arg) == 'number' then return modpol.orgs.array[arg] end return nil end --- Return a table list of all org names -- @function modpol.orgs.list_all -- @return a table list of all org names function modpol.orgs.list_all() local org_table for k, v in ipairs(modpol.orgs.array) do if type(v) == 'table' then if org_table then table.insert(org_table, v.name) else org_table = {v.name} end end end return org_table end --- Return the orgs of a user -- @function modpol.orgs.user_orgs -- @param user string of user name -- @return table of strings of org names function modpol.orgs.user_orgs(user) local all_orgs = modpol.orgs.list_all() local user_orgs = {} for i,v in ipairs(all_orgs) do local this_table = modpol.orgs.get_org(v) if this_table:has_member(user) then table.insert(user_orgs,v) end end return user_orgs end --- Deletes all orgs except for the -- @function modpol.orgs.reset function modpol.orgs.reset() local instance_members = modpol.util.copy_table(modpol.instance.members) for id, org in ipairs(modpol.orgs.array) do if id > 1 then modpol.orgs.array[id] = "removed" end end modpol.orgs.array[1] = nil modpol.instance = modpol.orgs.init_instance() modpol.instance.members = instance_members modpol.ocutil.log('All orgs reset') modpol.orgs:record('Resetting all orgs', 'org_reset') end --- Initializes the instance (root org) -- can only be run once, as only one instance can exist -- @function modpol.orgs.init_instance function modpol.orgs.init_instance() local error_msg if modpol.orgs.array[1] then modpol.ocutil.log('Error in orgs.init_instance -> instance has already been initialized') return false end local instance = temp_org() instance.id = 1 instance.name = "Root" for i,v in pairs(modpol.modules) do instance.policies[i] = {} end setmetatable(instance, modpol.orgs) -- adding instance to org list modpol.orgs.array[1] = instance modpol.ocutil.log('Initialized the instance root org') modpol.orgs:record('Initialized the instance root org', 'create_instance') return instance end -- FUNCTIONS BEYOND HERE OPERATE ON ORG OBJECTS --- Records a log message to the modpol ledger -- @function modpol.orgs:record 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 modpol.ocutil.log('Error in ' .. self.name .. ':record -> 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 modpol.ocutil.log('Error in ' .. self.name .. ':record -> entry_type must be a non empty string') modpol.ocutil.log(msg, entry_type) 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 -- @param name (string) name of new org -- @param user (string) -- @return child org created function modpol.orgs:add_org(name, user) if self.id == nil then modpol.ocutil.log('Error in ' .. self.name .. ':add_org -> add_org can only be called by another org') return false end if modpol.ocutil.str_empty(name) then modpol.ocutil.log('Error in ' .. self.name .. ':add_org -> org name is required') return false end if modpol.orgs.get_org(name) then modpol.ocutil.log('Error in ' .. self.name .. ':add_org -> org name is already being used') return false end -- creating the child sub org modpol.orgs.count = modpol.orgs.count + 1 local child_org = temp_org() child_org.id = modpol.orgs.count child_org.name = name child_org.parent = self.id child_org.processes = {} child_org.policies = modpol.util.copy_table(self.policies) 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 -- adding creator of org as the first member child_org:add_member(user) self:record('created sub org ' .. name, 'add_org') modpol.ocutil.log('Created ' .. name .. ' (suborg of ' .. self.name .. ')') 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 function modpol.orgs:delete() if self.id == 1 then modpol.ocutil.log('Error in ' .. self.name .. ':delete -> cannot delete instance') return false end if #self.children > 0 then for i, child_id in pairs(self.children) do local child = modpol.orgs.get_org(child_id) modpol.ocutil.log("Deleting child org...") child:delete() end end modpol.orgs.array[self.id] = 'removed' modpol.orgs.count = modpol.orgs.count - 1 modpol.ocutil.log('Deleted org ' .. self.name .. ': ' .. self.id) self:record('Deleted ' .. self.name .. ' and all child orgs', 'del_org') end --- Internal function to get the index of a member name -- @function modpol.orgs:get_member_index -- @param member -- @return index of given member 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 -- @param user function modpol.orgs:add_member(user) for id, name in ipairs(self.members) do if user == name then modpol.ocutil.log('Error in ' .. self.name .. ':add_member -> user already in org') return false end end -- trys to fill in empty spots first local 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 modpol.ocutil.log('Added member ' .. user .. ' to ' .. self.name) self:record('Added member ' .. user, 'add_member') end --- Removes a user from an org -- @function modpol.orgs:remove_member -- @param user 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 local user_index = self:get_member_index(user) if user_index then self.members[user_index] = '' else modpol.ocutil.log('Error in ' .. self.name .. ':remove_member -> user not in org') end modpol.ocutil.log('Removed member ' .. user .. ' from ' .. self.name) self:record('Removed member ' .. user, 'del_member') end --- Boolean check whether user is an org -- @function modpol.orgs:has_member -- @param user -- @return true if user is in org, false if not function modpol.orgs:has_member(user) local user_index = self:get_member_index(user) if user_index then return true else return false end end --- -- @function modpol.orgs:list_members() -- @return 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 --- Because member list uses lazy deletion, using #org.members will not show an accurate number -- @function modpol.orgs:get_member_count -- @return numbers of members function modpol.orgs:get_member_count() local count = 0 for k, v in ipairs(self.members) do -- the empty string represents a deleted member in the members list if v ~= '' then count = count + 1 end end return count end