orgs.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. -- ===================================================================
  2. -- /orgs.lua
  3. -- Org-related functions for Modular Politics
  4. -- Called by modpol/modpol/api.lua
  5. -- ===================================================================
  6. -- preserve records -- if true, will store org ledgers when they are closed, for later reference.
  7. local preserve_records = true
  8. -- policies assigned to any org can go here
  9. modpol.default_org_policies = modpol.default_org_policies or {}
  10. -- initiate the table of orgs, if it doesnt already exist from storage!
  11. modpol.orgs = modpol.orgs or {}
  12. if preserve_records then
  13. modpol.old_ledgers = modpol.old_ledgers or {} -- a backup of all legders of all closed orgs.
  14. end
  15. -- this counter will increment every time a new org is created, and the current value will be used as the org id
  16. -- 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.
  17. -- this ensures a unique id for each org.
  18. modpol.id_counter = 1
  19. if #modpol.orgs > 0 then
  20. for id,org in pairs(modpol.orgs) do
  21. if id > modpol.id_counter then
  22. modpol.id_counter = id
  23. end
  24. end
  25. for id,ledg in pairs(modpol.old_ledgers) do
  26. if id > modpol.id_counter then
  27. modpol.id_counter = id
  28. end
  29. end
  30. modpol.id_counter = modpol.id_counter +1
  31. end
  32. --define the template of a ledger entry
  33. modpol.ledger_entry_temp = {
  34. timestamp = '',
  35. entrytype = nil,
  36. action_msg = '',
  37. org_name = nil,
  38. org_id = nil,
  39. }
  40. -- define the basic template of an org.
  41. modpol.org_temp = {
  42. id = nil,
  43. name = nil,
  44. policies = {},
  45. members = {},
  46. ledger = {},
  47. parent = nil,
  48. children = {},
  49. properties = {},
  50. old_names = {},
  51. }
  52. -- ===================================================================
  53. -- Function: modpol.create_ledger_entry
  54. -- Params: strings: action_msg, type, number: org_id
  55. -- Outputs:
  56. -- returns a ledger entry table, with a timestamp
  57. -- This function accepts a message and an optional org_id and type specification
  58. -- and returns a valid ledger entry table
  59. modpol.create_ledger_entry = function(action_msg, org_id, entry_type)
  60. local entry = modpol.ledger_entry_temp
  61. if action_msg and type(action_msg) == 'string' then
  62. entry.action_msg = action_msg
  63. end
  64. if org_id and type(org_id) == 'number' and modpol.orgs [org_id] then
  65. entry.org_id = org_id
  66. if modpol.orgs[org_id].name then
  67. entry.org_name = modpol.orgs[org_id].name
  68. end
  69. end
  70. if entry_type and type(entry_type) == 'string' then
  71. entry.entrytype = entry_type
  72. end
  73. entry.timestamp = os.time()
  74. return entry
  75. end
  76. -- ===================================================================
  77. -- Function: modpol.record
  78. -- Params: strings msg, type, number org_id
  79. -- Outputs:
  80. -- "msg" specifies an event and/or status message.
  81. -- "org" specifies an "org" id.
  82. -- "type" -- optional type of legder entry. Could be e.g. 'election_result', 'add_member', etc
  83. -- This function adds the message to a global ledger and, if "org"
  84. -- specifies a valid "org", to an "org"-specific ledger. Both the mem-
  85. -- ory-resident and on-disk copies of the data structures used are up-
  86. -- dated.
  87. modpol.record = function (org_id, msg, type)
  88. local entry = modpol.create_ledger_entry(msg, org_id, type)
  89. -- Record in global ledger
  90. table.insert (modpol.ledger, entry)
  91. -- Record in "org"-specific ledger
  92. if modpol.orgs [org_id] ~= nil then
  93. local org_ledger = modpol.orgs [org_id]["ledger"]
  94. if org_ledger == nil then
  95. modpol.orgs [org_id]["ledger"] = { entry }
  96. else
  97. modpol.orgs [org_id]["ledger"] = table.insert (org_ledger, entry)
  98. end
  99. end
  100. modpol.store_data() -- Copy data to disk
  101. end
  102. -- ===================================================================
  103. -- Function: modpol.get_org_id_by_name(org_name)
  104. -- Params: org_name
  105. -- Output: an org id or nil
  106. --
  107. modpol.get_org_id_by_name = function(org_name)
  108. local id = nil
  109. for org_id,org in pairs(modpol.orgs) do
  110. if org.name == org_name then
  111. id = org_id
  112. end
  113. end
  114. return id
  115. end
  116. -- ===================================================================
  117. -- Function: modpol.add_org
  118. -- Params: string name, table members, string parent, table properties
  119. -- Output:
  120. --
  121. -- This function creates an "org". It returns a boolean success indicator and
  122. -- either an error message starting with "Error:" or a success message.
  123. --
  124. -- The string parameter specifies the "org" name.
  125. --
  126. -- The members table should be an integer-indexed array of member
  127. -- names.
  128. -- parent should be nil or a valid existing org name. If nil, defaults to
  129. -- 'instance'
  130. -- properties defaults to an empty table. Use it to initialize the org
  131. -- with arbitrary properties
  132. modpol.add_org = function(org_name, members, parent_id, properties)
  133. local str
  134. if modpol.ocutil.str_empty (org_name) then
  135. return false,"Error: Org needs a name"
  136. end
  137. if parent_id and not modpol.orgs[parent_id] then
  138. return false,"Error: Parent_id must be a valid existing org id"
  139. end
  140. if properties and not type(properties) == 'table' then
  141. return false,"Error: Properties must be a table"
  142. end
  143. modpol.ocutil.log ("Attempting to add new org " .. org_name)
  144. if modpol.get_org_id_by_name(org_name) ~= nil then
  145. str = "Error: Org " .. org_name .. " already exists"
  146. modpol.ocutil.log (str)
  147. return false,str
  148. end
  149. local org_id = modpol.id_counter
  150. modpol.id_counter = modpol.id_counter + 1
  151. -- the instance will have a parent of nil if it is the instance.
  152. -- otherwise, it will have a parent of instance's id if not otherwise defined
  153. local parent_org_id = nil
  154. if parent_id and not(modpol.get_org_id_by_name('instance') == org_id) then
  155. parent_org_id = parent_id
  156. end
  157. if not parent_id and not(modpol.get_org_id_by_name('instance') == org_id) then
  158. parent_org_id = modpol.get_org_id_by_name('instance')
  159. end
  160. local props = properties or {}
  161. -- actually add the org
  162. local org = modpol.org_temp
  163. local default_policies = modpol.default_org_policies or {}
  164. org.id = org_id
  165. 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.
  166. org.members = members
  167. org.parent = parent_org_id
  168. org.properties = props
  169. org.policies = default_policies
  170. modpol.orgs [org_id] = org
  171. --record the child org in the parent's children table
  172. if parent_org_id then
  173. if modpol.orgs[parent_org_id].children then
  174. table.insert(modpol.orgs[parent_org_id].children, org_id)
  175. else
  176. modpol.orgs[parent_org_id].children = {org_id}
  177. end
  178. end
  179. local msg = "New org: " .. org_name .. " ["..org_id.."], parent = ".. dump(parent_org_id)..", members = "..
  180. " (" .. table.concat (members, ", ") .. ")"
  181. modpol.record (org_id, msg, 'org_init')
  182. return true, msg
  183. end
  184. -- ===================================================================
  185. -- Function: modpol.remove_org
  186. -- Params: number id, opt string reason
  187. -- Output:
  188. --
  189. -- This function removes an "org". It returns a boolean
  190. -- success indicator and either an error message
  191. -- starting with "Error:" or a success message. It also recursively
  192. -- removes all child orgs, with 'remove parent org' as reason. If
  193. -- preserve_records is enabled, it logs the removal in the org ledger,
  194. -- and stores the ledger in modpol.old_ledgers
  195. -- The number parameter specifies the "org" id.
  196. --
  197. -- The reason is an optional string to additionally include in the
  198. -- log as reason for removal
  199. modpol.remove_org = function (org_id, reason)
  200. local str
  201. if not reason then reason = '' end
  202. if not type(org_id) == 'number' or not modpol.orgs[org_id] then
  203. str = "Error: Specify an existing org id to remove"
  204. modpol.ocutil.log (str)
  205. return false,str
  206. end
  207. local org_name = modpol.orgs[org_id]
  208. if org_name == 'instance' then
  209. return false,"Error: You cannot remove instance"
  210. end
  211. --log msg
  212. local msg = 'Removing org '..org_name ..' ['..org_id..'], reason: '.. reason
  213. modpol.ocutil.log (msg)
  214. -- actually remove the org
  215. -- TODO: if callbacks are implemented, do so here --
  216. if preserve_records then
  217. modpol.old_ledgers[org_id] = modpol.orgs[org_id].ledger
  218. end
  219. -- also remove all children
  220. if modpol.orgs[org_id].children and #modpol.orgs[org_id].children > 0 then
  221. for _,child_name in pairs(modpol.orgs[org_id].children) do
  222. --this is recursion. If it gets to be a problem, we will need a separate function
  223. modpol.remove_org(child_name, 'remove parent org')
  224. end
  225. end
  226. --also remove references to this org in parent orgs
  227. if modpol.orgs[org_id].parent then
  228. local parent_id = modpol.orgs[org_id].parent
  229. modpol.orgs[parent_id].children[org_id] = nil
  230. end
  231. modpol.orgs[org_id] = nil
  232. modpol.record (org_id, msg, 'org_remove')
  233. return true,msg
  234. end
  235. -- ===================================================================
  236. -- Function: modpol.list_orgs
  237. -- Params: None
  238. -- Output:
  239. -- This function returns a text-format list of "orgs". The list shows
  240. -- one "org" per line in the following format:
  241. -- org_name (member, member, ...)
  242. -- If there are no "orgs", the output is an empty string.
  243. modpol.list_orgs = function()
  244. local outbuf = ""
  245. local str
  246. for org_id, org_data in pairs (modpol.orgs) do
  247. -- Process next "org"
  248. -- Build string version of member list
  249. local memcat = org_data ["members"]
  250. local org_name = org_data ["name"].."["..org_id.."]"
  251. if modpol.ocutil.str_empty (memcat) then
  252. memcat = "(empty)"
  253. else
  254. memcat = "(" .. table.concat (memcat, ", ") .. ")"
  255. end
  256. -- Build output string
  257. outbuf = outbuf .. org_name .. " " .. memcat .. "\n"
  258. end
  259. return outbuf
  260. end
  261. -- ===================================================================
  262. -- Function: modpol.reset_orgs
  263. -- Params: None
  264. -- Removes all orgs and recreates blank org "instance" with all
  265. -- current users
  266. -- returns confirmation message
  267. modpol.reset_orgs = function()
  268. local users = modpol.list_users()
  269. --store the ledgers in modpol.old_ledgers, and note that each org was closed in each record.
  270. if preserve_records then
  271. for id, org in pairs(modpol.orgs) do
  272. local old_ledger = org.ledger
  273. table.insert(old_ledger,modpol.create_ledger_entry('Removing org '.. id, id, 'org_purge'))
  274. modpol.old_ledgers[id] = old_ledger
  275. end
  276. end
  277. modpol.orgs = {}
  278. local message = "Orgs purged"
  279. modpol.record(nil, message, 'org_purge')
  280. modpol.add_org("instance", users)
  281. return message
  282. end
  283. -- ===================================================================
  284. -- Function: modpol.add_member
  285. -- Params: org_id (number), member (string)
  286. -- Output:
  287. -- Adds the specified member to the specified org
  288. -- Returns a boolean success indicator and
  289. -- either a confirmation or error message
  290. -- TODO, check that member is a user, if the org_name is not instance
  291. modpol.add_member = function(org_id, member)
  292. if (modpol.orgs[org_id] == nil) then
  293. return false,"Error: No such org"
  294. elseif type(member) ~= 'string' then
  295. return false,"Error: member is wrong type"
  296. else
  297. local org_name = modpol.orgs [org_id].name
  298. if modpol.is_member(org_id,member) then
  299. return false,"Error: " .. member .. " is already a member of " .. org_name
  300. else
  301. table.insert(modpol.orgs[org_id]["members"], member)
  302. local message = member .. " added to org " .. org_name .. " ["..org_id.."]"
  303. modpol.record(org_id, message, 'add_member')
  304. return true,message
  305. end
  306. end
  307. end
  308. -- ===================================================================
  309. -- Function: modpol.is_member
  310. -- Params: org (number), member (string)
  311. -- Output: Boolean depending on membership or nil/error message
  312. modpol.is_member = function(org, member)
  313. if (modpol.orgs[org] == nil) then
  314. return nil, "Error: No such org"
  315. else
  316. local member_table = modpol.orgs[org]["members"]
  317. if member_table then
  318. for index, value in pairs(member_table) do
  319. if value == member then
  320. -- match found
  321. return true
  322. end
  323. end
  324. -- no match found
  325. return false
  326. end
  327. return false
  328. end
  329. end
  330. -- ===================================================================
  331. -- Function: modpol.remove_member
  332. -- Params: org (number), member (string)
  333. -- Output:
  334. -- Removes the specified member from the specified org
  335. -- Returns confirmation or error message
  336. function modpol.remove_member(org, member)
  337. local message = "Error: No such org"
  338. if (modpol.orgs[org] == nil) then
  339. return false, message
  340. else
  341. local member_table = modpol.orgs[org]["members"]
  342. if member_table then
  343. for index, value in pairs(member_table) do
  344. if value == member then
  345. -- match found, set to nil
  346. modpol.orgs[org]["members"][value] = nil
  347. -- TODO: add callbacks here, for such as revoking privs, etc --
  348. message = member .. " removed from org " .. org
  349. modpol.record(org, message, 'remove_member')
  350. return true, message
  351. end
  352. end
  353. end
  354. -- no match found (or org has no members)
  355. message = member .. " not found in org " .. org
  356. return false, message
  357. end
  358. end
  359. -- ===================================================================
  360. -- TKTK
  361. -- rename_org(old_name, new_name)
  362. -- Renames an org
  363. -- ===================================================================
  364. -- POLICIES
  365. -- Each policy is a table with a string for a key. Contents:
  366. -- KEY - name of the policy (string)
  367. -- [member orgs]: external orgs allowed to call policy
  368. -- [function]: the function that initiates the policy
  369. -- ===================================================================
  370. -- ===================================================================
  371. -- modpol.add_policy
  372. -- Adds a policy to an org's [org].policies table
  373. -- Params: org_name (string),
  374. -- policy_name (string),
  375. -- policy_table (table)
  376. -- Output: true if successful, nil if error
  377. -- TKTK NEEDS TO BE REWRITTEN
  378. modpol.add_policy = function(org_name, policy_name, policy_table)
  379. -- first, basic checks
  380. local message = "Error: No such org"
  381. if (modpol.orgs[org_name] == nil) then
  382. return nil, message
  383. elseif (modpol[target_function] == nil) then
  384. message = "Error: No such target function"
  385. return nil, message
  386. elseif (modpol[policy_function]) then
  387. message = "Error: No such policy function"
  388. return nil, message
  389. else
  390. -- okay, proceed
  391. modpol.orgs[org_name].policies[target_function] = policy_function
  392. message = "In org " .. org_name .. ", policy for " .. target_function
  393. .. " set to " .. policy_function
  394. record(org_name, message)
  395. return true, message
  396. end
  397. end
  398. -- ===================================================================
  399. -- End of file.