orgs.lua 13 KB

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