Merge branch 'renaming' into 'master'

Renaming

See merge request medlabboulder/modpol!16
This commit is contained in:
Nathan Schneider 2021-04-15 04:35:38 +00:00
commit defa4fac50
6 changed files with 232 additions and 772 deletions

View File

@ -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")

View File

@ -77,9 +77,10 @@ dofile (topdir .. "/api.lua")
-- Final checks
-- create instance if not present
-- if not modpol.get_org_id_by_name('instance') then
-- modpol.add_org("instance", modpol.list_users())
-- end
if not modpol.orgs.get_org('instance') then
modpol.orgs.init_instance()
modpol.instance = modpol.orgs.get_org('instance')
end
modpol.ocutil.log ("modpol loaded")

View File

@ -1,225 +0,0 @@
modpol.orgs =
{
count = 1,
list = {}
}
-- sets modpol.orgs as it's own fallback
modpol.orgs.__index = modpol.orgs
-- ==================================================
-- returns org when given its id
function modpol.orgs.get_org(id)
return modpol.orgs.list[id]
end
-- ===============================================
-- returns a string list of all orgs
function modpol.orgs.list_orgs()
local str
for k, v in ipairs(modpol.orgs.list) 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 k, v in ipairs(modpol.orgs.list) do
if k > 1 then
modpol.orgs.list[k] = 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.list[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.list[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.list[child_org.id] = child_org
return child_org
end
-- ========================================
-- recursively deletes an org and its suborgs
-- leaves entry in modpol.orgs.list 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.list[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

View File

@ -1,550 +1,234 @@
-- ===================================================================
-- /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(arg)
if type(arg) == 'string' then
for id, org in ipairs(modpol.orgs.array) do
if org.name == arg then
return org
end
end
-- Build output string
outbuf = outbuf .. org_name .. " " .. memcat .. "\n"
elseif type(arg) == 'number' then
return modpol.orgs.array[arg]
end
return outbuf
return nil
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

View File

@ -34,7 +34,7 @@ regchat(
"reset", {
privs = {},
func = function(user)
modpol.reset_orgs();
modpol.orgs.reset();
return true, "Reset orgs"
end,
})
@ -51,7 +51,7 @@ regchat(
"addorg", {
privs = {} ,
func = function (user, param)
local success, message = modpol.add_org (param, { user })
local success, message = modpol.instance:add_org (param)
return true, message
end
})
@ -67,7 +67,7 @@ regchat(
"listorg", {
privs = {} ,
func = function (user, param)
return true, "Orgs:\n" .. modpol.list_orgs()
return true, "Orgs:\n" .. modpol.orgs.list_all()
end
})
@ -88,8 +88,8 @@ regchat(
"joinorg", {
privs = {},
func = function(user, param)
local org_id = modpol.get_org_id_by_name(param)
local success, result = modpol.add_member(org_id, user)
local org = modpol.orgs.get_org(param)
local success, result = org:add_member(user)
return true, result
end,
})

View File

@ -2,5 +2,5 @@
minetest.register_on_joinplayer(function(player)
local p_name = player:get_player_name()
modpol.add_member(1, p_name)
modpol.instance:add_member(p_name)
end)