diff --git a/modpol/DOCS/ledgers b/modpol/DOCS/ledgers new file mode 100644 index 0000000..dedf78a --- /dev/null +++ b/modpol/DOCS/ledgers @@ -0,0 +1,50 @@ +Ledger +************************** + +a legder is a table of records that holds the history of an org, and of modpol itself. + +its structure is a table of legder entries. + +Every org has one, starting with the entry recording its creation. + +Ledger Entry +************************** +a legder entry is a table with the following properties: + +timestamp = nil, +entrytype = nil, +action_msg = '', + +the timestamp will hold the output of os.time at the moment the entry was created. + +the entrytype is optional; it can hold an identifier of the kind of entry it is (e.g. 'org_init', 'new_user', etc etc.) + +action_msg is the record output itself. + + + +Entrytypes +************************** + +These are the entrytypes that modpol uses. Each additional module should document whatever entrytypes it adds. + +'org_init' +type used when an org is created + +'org_purge' +type used when all orgs are purged. -- will only be found in the main modpol ledger. + +'org_remove' +type used when removing an individual org + +'add_member' +type used when adding a member to an org + +'remove_member' +type used when removing a member from an org + + + +Old orgs +************************** +when an org is removed, its ledger is stored in modpol.old_legders = {} Note that multiple ledgers may exist here with the same org name. \ No newline at end of file diff --git a/modpol/DOCS/orgs b/modpol/DOCS/orgs new file mode 100644 index 0000000..971deeb --- /dev/null +++ b/modpol/DOCS/orgs @@ -0,0 +1,137 @@ +== Dev note === +*note, see doc ledgers for info about org ledgers. +they are *not* just a message in a table anymore... they have a timestamp and more. +=============== + + +Orgs are stored in modpol.orgs by the key 'org_name' + +Old ledgers of deleted orgs are stored in modpol.old_ledgers, +if preserve records is enabled + +An org is a table with the following properties: + +{ + name = '', -- a unique name... + this is a reference to the key used to store the org table. Changing this does not change the org name. + policies = {}, -- a table to store the org's policies + members = {}, -- a table that contains the member names + or the org, in no particular order (TKTK: maybe change it so that members have a name:properties table?) + ledger = {}, -- a table of ledger entries. See the ledger docs + parent = nil, -- the name of the org that spawned this org. + removing a parent org removes its children + children = {}, -- a table of strings of org names of child orgs + properties = { -- good for modules to store arbitrary info about the org here. + + }, + +} + + +API functions for orgs +====================== + +a local variable setting 'preserve_records' determines whether +to store old ledgers when an org is deleted. It is set to true. +It would be possible to extend this to a settings file later. + +--==oo888888888888888888888888888888oo==-- +Function: modpol.record(org_name, msg, type) +-- 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. +-- the type input is used to be able to search ledgers for specific events + +--==oo888888888888888888888888888888oo==-- + +Function: modpol.add_org(org_name, members, parent, properties) +-- Params: string name, table members, string parent, table properties +-- Parent must be an existing org, defaults to 'instance', properties +-- are arbitrary properties to initialize the org with + +-- 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 + +--==oo888888888888888888888888888888oo==-- + +-- Function: modpol.remove_org(org_name, reason) +-- 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 + +--==oo888888888888888888888888888888oo==-- + +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. + +--==oo888888888888888888888888888888oo==-- + + +Function: modpol.reset_orgs() +-- Params: None +-- Removes all orgs and recreates blank org "instance" with all +-- current users +-- returns confirmation message +-- if preserve_records is enabled, stores all org ledgers +-- in modpol.old_ledgers + +--==oo888888888888888888888888888888oo==-- + +Function: modpol.add_member(org_name, member) +-- Params: org_name (string), member (string) +-- Output: +-- Adds the specified member to the specified org +-- Returns a boolean success indicator and +-- either a confirmation or error message + +--==oo888888888888888888888888888888oo==-- + +Function: modpol.is_member +-- Params: org (string), member (string) +-- Output: boolean, or nil and error message if not applicable. (org nonexistent) + + +--==oo888888888888888888888888888888oo==-- + +Function: modpol.remove_member +-- Params: org (string), member (string) +-- Output: +-- Removes the specified member from the specified org +-- Returns confirmation or error message \ No newline at end of file diff --git a/modpol/orgs/orgs.lua b/modpol/orgs/orgs.lua index 871edc4..95c05bd 100644 --- a/modpol/orgs/orgs.lua +++ b/modpol/orgs/orgs.lua @@ -1,30 +1,94 @@ + -- =================================================================== -- /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 +-- 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) +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, msg) + 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"] = { msg } + modpol.orgs [org_name]["ledger"] = { entry } else modpol.orgs [org_name]["ledger"] = - table.insert (org_ledger, msg) + table.insert (org_ledger, entry) end end @@ -33,39 +97,144 @@ end -- =================================================================== -- Function: modpol.add_org --- Params: string name, table members +-- Params: string name, table members, string parent, table properties + -- Output: -- --- This function creates an "org". It returns either an error message --- starting with "-!-" or a success message. +-- 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 specified table should be an integer-indexed array of member +-- The members table should be an integer-indexed array of member -- names. -modpol.add_org = function (org_name, members) +-- 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 "Error: Org needs a name" + 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 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 - modpol.orgs [org_name] = { members=members } local msg = "New org: " .. org_name .. " (" .. table.concat (members, ", ") .. ")" - modpol.record (org_name, msg) - return msg + 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 @@ -102,9 +271,17 @@ end 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) + modpol.record(nil, message, 'org_purge') modpol.add_org("instance", users) return message end @@ -114,19 +291,25 @@ end -- Params: org (string), member (string) -- Output: -- Adds the specified member to the specified org --- Returns confirmation or error message +-- Returns a boolean success indicator and +-- either a confirmation or error message -modpol.add_member = function(org, member) - if (modpol.orgs[org] == nil) then - return "Error: No such org" + +-- 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,member) then - return "Error: " .. member .. " is already a member of " .. org + 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]["members"], member) - local message = member .. " added to org " .. org - modpol.record(message, org) - return message + 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 @@ -165,22 +348,26 @@ end function modpol.remove_member(org, member) local message = "Error: No such org" if (modpol.orgs[org] == nil) then - return nil, message + 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 - value = nil + + modpol.orgs[org]["members"][value] = nil + + -- TODO: add callbacks here -- message = member .. " removed from org " .. org - return message + 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 nil, message + return false, message end end @@ -189,6 +376,13 @@ end -- rename_org(old_name, new_name) -- Renames an org + + + + + + + -- =================================================================== -- POLICIES -- Each policy is a table with a string for a key. Contents: diff --git a/modpol_minetest/chatcommands/chatcommands.lua b/modpol_minetest/chatcommands/chatcommands.lua index ecfd082..8f82d5e 100644 --- a/modpol_minetest/chatcommands/chatcommands.lua +++ b/modpol_minetest/chatcommands/chatcommands.lua @@ -16,8 +16,8 @@ regchat = minetest.register_chatcommand chat_table = { privs = {} , func = function (user, param) - local result = modpol.add_org (param, { user }) - return true, result + local success, message = modpol.add_org (param, { user }) + return true, message end } regchat ("addorg" , chat_table) @@ -59,7 +59,7 @@ minetest.register_chatcommand( "joinorg", { privs = {}, func = function(user, param) - local result = modpol.add_member(param, user) + local success, result = modpol.add_member(param, user) return true, result end, })