ldap-rest 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +48 -0
  3. package/bin/cleanup-external-users.mjs +219 -0
  4. package/bin/index.mjs +54 -0
  5. package/bin/sync-james.mjs +190 -0
  6. package/dist/abstract/ldapFlat.js +1 -0
  7. package/dist/abstract/plugin.js +1 -0
  8. package/dist/abstract/twakePlugin.js +1 -0
  9. package/dist/bin/index.js +1 -0
  10. package/dist/config/args.js +1 -0
  11. package/dist/lib/auth/base.js +1 -0
  12. package/dist/lib/authz/base.js +1 -0
  13. package/dist/lib/expressFormatedResponses.js +1 -0
  14. package/dist/lib/ldapActions.js +1 -0
  15. package/dist/lib/parseConfig.js +1 -0
  16. package/dist/lib/utils.js +1 -0
  17. package/dist/logger/winston.js +1 -0
  18. package/dist/plugins/auth/authzLinid1.js +1 -0
  19. package/dist/plugins/auth/authzPerBranch.js +1 -0
  20. package/dist/plugins/auth/crowdsec.js +1 -0
  21. package/dist/plugins/auth/llng.js +1 -0
  22. package/dist/plugins/auth/openidconnect.js +1 -0
  23. package/dist/plugins/auth/rateLimit.js +1 -0
  24. package/dist/plugins/auth/token.js +1 -0
  25. package/dist/plugins/configApi.js +1 -0
  26. package/dist/plugins/demo/helloworld.js +1 -0
  27. package/dist/plugins/ldap/bulkImport.js +1 -0
  28. package/dist/plugins/ldap/departmentSync.js +1 -0
  29. package/dist/plugins/ldap/externalUsersInGroups.js +1 -0
  30. package/dist/plugins/ldap/flatGeneric.js +1 -0
  31. package/dist/plugins/ldap/groups.js +1 -0
  32. package/dist/plugins/ldap/onChange.js +1 -0
  33. package/dist/plugins/ldap/organization.js +1 -0
  34. package/dist/plugins/ldap/trash.js +1 -0
  35. package/dist/plugins/priority.json.js +1 -0
  36. package/dist/plugins/static.js +1 -0
  37. package/dist/plugins/twake/calendarResources.js +1 -0
  38. package/dist/plugins/twake/james.js +1 -0
  39. package/dist/plugins/weblogs.js +1 -0
  40. package/dist/src/abstract/ldapFlat.d.ts +94 -0
  41. package/dist/src/abstract/plugin.d.ts +43 -0
  42. package/dist/src/abstract/twakePlugin.d.ts +90 -0
  43. package/dist/src/bin/index.d.ts +43 -0
  44. package/dist/src/browser/ldap-group-editor/LdapGroupEditor.d.ts +31 -0
  45. package/dist/src/browser/ldap-group-editor/api/GroupApiClient.d.ts +23 -0
  46. package/dist/src/browser/ldap-group-editor/components/CreateGroupModal.d.ts +19 -0
  47. package/dist/src/browser/ldap-group-editor/components/GroupPropertyEditor.d.ts +30 -0
  48. package/dist/src/browser/ldap-group-editor/components/GroupTree.d.ts +17 -0
  49. package/dist/src/browser/ldap-group-editor/components/MoveGroupModal.d.ts +20 -0
  50. package/dist/src/browser/ldap-group-editor/index.d.ts +6 -0
  51. package/dist/src/browser/ldap-group-editor/types.d.ts +58 -0
  52. package/dist/src/browser/ldap-resource-editor/api/ResourceApiClient.d.ts +53 -0
  53. package/dist/src/browser/ldap-resource-editor/cache/CacheManager.d.ts +65 -0
  54. package/dist/src/browser/ldap-resource-editor/index.d.ts +6 -0
  55. package/dist/src/browser/ldap-resource-editor/types.d.ts +78 -0
  56. package/dist/src/browser/ldap-tree-viewer/LdapTreeViewer.d.ts +26 -0
  57. package/dist/src/browser/ldap-tree-viewer/api/LdapApiClient.d.ts +30 -0
  58. package/dist/src/browser/ldap-tree-viewer/components/TreeNode.d.ts +17 -0
  59. package/dist/src/browser/ldap-tree-viewer/components/TreeRoot.d.ts +14 -0
  60. package/dist/src/browser/ldap-tree-viewer/index.d.ts +7 -0
  61. package/dist/src/browser/ldap-tree-viewer/store/Store.d.ts +18 -0
  62. package/dist/src/browser/ldap-tree-viewer/store/actions.d.ts +28 -0
  63. package/dist/src/browser/ldap-tree-viewer/store/reducers.d.ts +7 -0
  64. package/dist/src/browser/ldap-tree-viewer/types.d.ts +34 -0
  65. package/dist/src/browser/ldap-unit-editor/LdapUnitEditor.d.ts +27 -0
  66. package/dist/src/browser/ldap-unit-editor/api/UnitApiClient.d.ts +24 -0
  67. package/dist/src/browser/ldap-unit-editor/components/MoveUnitModal.d.ts +28 -0
  68. package/dist/src/browser/ldap-unit-editor/components/UnitPropertyEditor.d.ts +28 -0
  69. package/dist/src/browser/ldap-unit-editor/components/UnitTree.d.ts +18 -0
  70. package/dist/src/browser/ldap-unit-editor/index.d.ts +6 -0
  71. package/dist/src/browser/ldap-unit-editor/types.d.ts +55 -0
  72. package/dist/src/browser/ldap-user-editor/LdapUserEditor.d.ts +32 -0
  73. package/dist/src/browser/ldap-user-editor/api/UserApiClient.d.ts +55 -0
  74. package/dist/src/browser/ldap-user-editor/cache/CacheManager.d.ts +65 -0
  75. package/dist/src/browser/ldap-user-editor/components/MoveUserModal.d.ts +28 -0
  76. package/dist/src/browser/ldap-user-editor/components/PointerField.d.ts +22 -0
  77. package/dist/src/browser/ldap-user-editor/components/UserEditor.d.ts +46 -0
  78. package/dist/src/browser/ldap-user-editor/components/UserList.d.ts +24 -0
  79. package/dist/src/browser/ldap-user-editor/components/UserTree.d.ts +24 -0
  80. package/dist/src/browser/ldap-user-editor/index.d.ts +7 -0
  81. package/dist/src/browser/ldap-user-editor/types.d.ts +67 -0
  82. package/dist/src/browser/shared/components/Modal.d.ts +38 -0
  83. package/dist/src/browser/shared/components/StatusMessage.d.ts +48 -0
  84. package/dist/src/browser/shared/index.d.ts +10 -0
  85. package/dist/src/browser/shared/types/index.d.ts +65 -0
  86. package/dist/src/browser/shared/utils/dom.d.ts +33 -0
  87. package/dist/src/browser/shared/utils/form.d.ts +28 -0
  88. package/dist/src/browser/shared/utils/schema.d.ts +29 -0
  89. package/dist/src/config/args.d.ts +128 -0
  90. package/dist/src/config/schema.d.ts +18 -0
  91. package/dist/src/hooks.d.ts +76 -0
  92. package/dist/src/lib/auth/base.d.ts +9 -0
  93. package/dist/src/lib/authz/base.d.ts +63 -0
  94. package/dist/src/lib/expressFormatedResponses.d.ts +26 -0
  95. package/dist/src/lib/ldapActions.d.ts +91 -0
  96. package/dist/src/lib/parseConfig.d.ts +23 -0
  97. package/dist/src/lib/utils.d.ts +73 -0
  98. package/dist/src/logger/winston.d.ts +3 -0
  99. package/dist/src/plugins/auth/authzLinid1.d.ts +46 -0
  100. package/dist/src/plugins/auth/authzPerBranch.d.ts +59 -0
  101. package/dist/src/plugins/auth/crowdsec.d.ts +29 -0
  102. package/dist/src/plugins/auth/llng.d.ts +8 -0
  103. package/dist/src/plugins/auth/openidconnect.d.ts +11 -0
  104. package/dist/src/plugins/auth/rateLimit.d.ts +24 -0
  105. package/dist/src/plugins/auth/token.d.ts +17 -0
  106. package/dist/src/plugins/configApi.d.ts +24 -0
  107. package/dist/src/plugins/demo/helloworld.d.ts +12 -0
  108. package/dist/src/plugins/ldap/bulkImport.d.ts +48 -0
  109. package/dist/src/plugins/ldap/departmentSync.d.ts +35 -0
  110. package/dist/src/plugins/ldap/externalUsersInGroups.d.ts +21 -0
  111. package/dist/src/plugins/ldap/flatGeneric.d.ts +31 -0
  112. package/dist/src/plugins/ldap/groups.d.ts +95 -0
  113. package/dist/src/plugins/ldap/onChange.d.ts +32 -0
  114. package/dist/src/plugins/ldap/organization.d.ts +65 -0
  115. package/dist/src/plugins/ldap/trash.d.ts +52 -0
  116. package/dist/src/plugins/static.d.ts +7 -0
  117. package/dist/src/plugins/twake/calendarResources.d.ts +31 -0
  118. package/dist/src/plugins/twake/james.d.ts +83 -0
  119. package/dist/src/plugins/weblogs.d.ts +8 -0
  120. package/docs/DEVELOPER_GUIDE.md +298 -0
  121. package/docs/OPENAPI.md +440 -0
  122. package/docs/PLUGIN_DEPENDENCIES.md +612 -0
  123. package/docs/README.md +466 -0
  124. package/docs/api/REFERENCE.md +983 -0
  125. package/docs/api/REST_API.md +1237 -0
  126. package/docs/authentication.md +469 -0
  127. package/docs/authzLinid1.md +521 -0
  128. package/docs/authzPerBranch.md +596 -0
  129. package/docs/browser/LIBRARIES.md +1196 -0
  130. package/docs/examples/EXAMPLES.md +1659 -0
  131. package/docs/examples/twake-mailbox-type-nomenclature.ldif +37 -0
  132. package/docs/examples/twake-mailbox-type-schema-amendment.ldif +30 -0
  133. package/docs/jamesTeamMailboxes.md +343 -0
  134. package/docs/ldapBulkImport.md +722 -0
  135. package/docs/ldapExternalUsersInGroups.md +325 -0
  136. package/docs/ldapFlatGeneric.md +444 -0
  137. package/docs/ldapGroups.md +589 -0
  138. package/docs/ldapOrganizations.md +659 -0
  139. package/docs/ldapTrash.md +260 -0
  140. package/docs/linagora.png +0 -0
  141. package/docs/onChange.md +318 -0
  142. package/docs/plugins/DEVELOPMENT.md +1219 -0
  143. package/docs/presenterm-fr.md +721 -0
  144. package/docs/presenterm.md +721 -0
  145. package/docs/schemas/SCHEMAS.md +1212 -0
  146. package/docs/static.md +410 -0
  147. package/docs/twakeCalendarResources.md +148 -0
  148. package/docs/twakeJames.md +661 -0
  149. package/docs/weblogs.md +399 -0
  150. package/examples/web/ldap-group-editor.html +113 -0
  151. package/examples/web/ldap-tree-viewer.html +368 -0
  152. package/examples/web/ldap-unit-editor.html +108 -0
  153. package/examples/web/ldap-user-editor.html +412 -0
  154. package/package.json +458 -0
  155. package/static/browser/common-demo.css +450 -0
  156. package/static/browser/ldap-group-editor/LdapGroupEditor.js +207 -0
  157. package/static/browser/ldap-group-editor/LdapGroupEditor.js.map +1 -0
  158. package/static/browser/ldap-group-editor/api/GroupApiClient.js +112 -0
  159. package/static/browser/ldap-group-editor/api/GroupApiClient.js.map +1 -0
  160. package/static/browser/ldap-group-editor/components/CreateGroupModal.js +212 -0
  161. package/static/browser/ldap-group-editor/components/CreateGroupModal.js.map +1 -0
  162. package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js +497 -0
  163. package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js.map +1 -0
  164. package/static/browser/ldap-group-editor/components/GroupTree.js +153 -0
  165. package/static/browser/ldap-group-editor/components/GroupTree.js.map +1 -0
  166. package/static/browser/ldap-group-editor/components/MoveGroupModal.js +235 -0
  167. package/static/browser/ldap-group-editor/components/MoveGroupModal.js.map +1 -0
  168. package/static/browser/ldap-group-editor/index.js +2 -0
  169. package/static/browser/ldap-group-editor/index.js.map +1 -0
  170. package/static/browser/ldap-group-editor/types.js +2 -0
  171. package/static/browser/ldap-group-editor/types.js.map +1 -0
  172. package/static/browser/ldap-group-editor.esm.js +1399 -0
  173. package/static/browser/ldap-group-editor.esm.js.map +1 -0
  174. package/static/browser/ldap-group-editor.js +1407 -0
  175. package/static/browser/ldap-group-editor.js.map +1 -0
  176. package/static/browser/ldap-group-editor.min.js +2 -0
  177. package/static/browser/ldap-group-editor.min.js.map +1 -0
  178. package/static/browser/ldap-resource-editor/api/ResourceApiClient.js +226 -0
  179. package/static/browser/ldap-resource-editor/api/ResourceApiClient.js.map +1 -0
  180. package/static/browser/ldap-resource-editor/cache/CacheManager.js +123 -0
  181. package/static/browser/ldap-resource-editor/cache/CacheManager.js.map +1 -0
  182. package/static/browser/ldap-resource-editor/index.js +2 -0
  183. package/static/browser/ldap-resource-editor/index.js.map +1 -0
  184. package/static/browser/ldap-resource-editor/types.js +2 -0
  185. package/static/browser/ldap-resource-editor/types.js.map +1 -0
  186. package/static/browser/ldap-tree-viewer/LdapTreeViewer.js +187 -0
  187. package/static/browser/ldap-tree-viewer/LdapTreeViewer.js.map +1 -0
  188. package/static/browser/ldap-tree-viewer/api/LdapApiClient.js +50 -0
  189. package/static/browser/ldap-tree-viewer/api/LdapApiClient.js.map +1 -0
  190. package/static/browser/ldap-tree-viewer/components/TreeNode.js +140 -0
  191. package/static/browser/ldap-tree-viewer/components/TreeNode.js.map +1 -0
  192. package/static/browser/ldap-tree-viewer/components/TreeRoot.js +98 -0
  193. package/static/browser/ldap-tree-viewer/components/TreeRoot.js.map +1 -0
  194. package/static/browser/ldap-tree-viewer/index.js +2 -0
  195. package/static/browser/ldap-tree-viewer/index.js.map +1 -0
  196. package/static/browser/ldap-tree-viewer/store/Store.js +29 -0
  197. package/static/browser/ldap-tree-viewer/store/Store.js.map +1 -0
  198. package/static/browser/ldap-tree-viewer/store/actions.js +53 -0
  199. package/static/browser/ldap-tree-viewer/store/actions.js.map +1 -0
  200. package/static/browser/ldap-tree-viewer/store/reducers.js +79 -0
  201. package/static/browser/ldap-tree-viewer/store/reducers.js.map +1 -0
  202. package/static/browser/ldap-tree-viewer/types.js +2 -0
  203. package/static/browser/ldap-tree-viewer/types.js.map +1 -0
  204. package/static/browser/ldap-tree-viewer.css +2 -0
  205. package/static/browser/ldap-tree-viewer.css.map +1 -0
  206. package/static/browser/ldap-tree-viewer.esm.js +606 -0
  207. package/static/browser/ldap-tree-viewer.esm.js.map +1 -0
  208. package/static/browser/ldap-tree-viewer.js +614 -0
  209. package/static/browser/ldap-tree-viewer.js.map +1 -0
  210. package/static/browser/ldap-tree-viewer.min.js +2 -0
  211. package/static/browser/ldap-tree-viewer.min.js.map +1 -0
  212. package/static/browser/ldap-unit-editor/LdapUnitEditor.js +119 -0
  213. package/static/browser/ldap-unit-editor/LdapUnitEditor.js.map +1 -0
  214. package/static/browser/ldap-unit-editor/api/UnitApiClient.js +95 -0
  215. package/static/browser/ldap-unit-editor/api/UnitApiClient.js.map +1 -0
  216. package/static/browser/ldap-unit-editor/components/MoveUnitModal.js +295 -0
  217. package/static/browser/ldap-unit-editor/components/MoveUnitModal.js.map +1 -0
  218. package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js +321 -0
  219. package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js.map +1 -0
  220. package/static/browser/ldap-unit-editor/components/UnitTree.js +197 -0
  221. package/static/browser/ldap-unit-editor/components/UnitTree.js.map +1 -0
  222. package/static/browser/ldap-unit-editor/index.js +2 -0
  223. package/static/browser/ldap-unit-editor/index.js.map +1 -0
  224. package/static/browser/ldap-unit-editor/types.js +2 -0
  225. package/static/browser/ldap-unit-editor/types.js.map +1 -0
  226. package/static/browser/ldap-unit-editor.esm.js +1013 -0
  227. package/static/browser/ldap-unit-editor.esm.js.map +1 -0
  228. package/static/browser/ldap-unit-editor.js +1021 -0
  229. package/static/browser/ldap-unit-editor.js.map +1 -0
  230. package/static/browser/ldap-unit-editor.min.js +2 -0
  231. package/static/browser/ldap-unit-editor.min.js.map +1 -0
  232. package/static/browser/ldap-user-editor/LdapUserEditor.js +184 -0
  233. package/static/browser/ldap-user-editor/LdapUserEditor.js.map +1 -0
  234. package/static/browser/ldap-user-editor/api/UserApiClient.js +209 -0
  235. package/static/browser/ldap-user-editor/api/UserApiClient.js.map +1 -0
  236. package/static/browser/ldap-user-editor/cache/CacheManager.js +123 -0
  237. package/static/browser/ldap-user-editor/cache/CacheManager.js.map +1 -0
  238. package/static/browser/ldap-user-editor/components/MoveUserModal.js +302 -0
  239. package/static/browser/ldap-user-editor/components/MoveUserModal.js.map +1 -0
  240. package/static/browser/ldap-user-editor/components/PointerField.js +152 -0
  241. package/static/browser/ldap-user-editor/components/PointerField.js.map +1 -0
  242. package/static/browser/ldap-user-editor/components/UserEditor.js +608 -0
  243. package/static/browser/ldap-user-editor/components/UserEditor.js.map +1 -0
  244. package/static/browser/ldap-user-editor/components/UserList.js +181 -0
  245. package/static/browser/ldap-user-editor/components/UserList.js.map +1 -0
  246. package/static/browser/ldap-user-editor/components/UserTree.js +164 -0
  247. package/static/browser/ldap-user-editor/components/UserTree.js.map +1 -0
  248. package/static/browser/ldap-user-editor/index.js +2 -0
  249. package/static/browser/ldap-user-editor/index.js.map +1 -0
  250. package/static/browser/ldap-user-editor/types.js +2 -0
  251. package/static/browser/ldap-user-editor/types.js.map +1 -0
  252. package/static/browser/ldap-user-editor.css +2 -0
  253. package/static/browser/ldap-user-editor.css.map +1 -0
  254. package/static/browser/ldap-user-editor.esm.js +1897 -0
  255. package/static/browser/ldap-user-editor.esm.js.map +1 -0
  256. package/static/browser/ldap-user-editor.js +1905 -0
  257. package/static/browser/ldap-user-editor.js.map +1 -0
  258. package/static/browser/ldap-user-editor.min.js +2 -0
  259. package/static/browser/ldap-user-editor.min.js.map +1 -0
  260. package/static/browser/shared/components/Modal.js +121 -0
  261. package/static/browser/shared/components/Modal.js.map +1 -0
  262. package/static/browser/shared/components/StatusMessage.js +119 -0
  263. package/static/browser/shared/components/StatusMessage.js.map +1 -0
  264. package/static/browser/shared/index.js +7 -0
  265. package/static/browser/shared/index.js.map +1 -0
  266. package/static/browser/shared/types/index.js +37 -0
  267. package/static/browser/shared/types/index.js.map +1 -0
  268. package/static/browser/shared/utils/dom.js +78 -0
  269. package/static/browser/shared/utils/dom.js.map +1 -0
  270. package/static/browser/shared/utils/form.js +234 -0
  271. package/static/browser/shared/utils/form.js.map +1 -0
  272. package/static/browser/shared/utils/schema.js +121 -0
  273. package/static/browser/shared/utils/schema.js.map +1 -0
  274. package/static/browser/shared.esm.js +694 -0
  275. package/static/browser/shared.esm.js.map +1 -0
  276. package/static/browser/shared.js +723 -0
  277. package/static/browser/shared.js.map +1 -0
  278. package/static/browser/shared.min.js +2 -0
  279. package/static/browser/shared.min.js.map +1 -0
  280. package/static/schemas/ad/computers.json +92 -0
  281. package/static/schemas/ad/groups.json +55 -0
  282. package/static/schemas/ad/organizations.json +66 -0
  283. package/static/schemas/ad/users.json +116 -0
  284. package/static/schemas/standard/devices.json +72 -0
  285. package/static/schemas/standard/groups.json +54 -0
  286. package/static/schemas/standard/organizations.json +49 -0
  287. package/static/schemas/standard/users.json +86 -0
  288. package/static/schemas/twake/groups.json +75 -0
  289. package/static/schemas/twake/nomenclature/twakeAccountStatus.json +31 -0
  290. package/static/schemas/twake/nomenclature/twakeDeliveryMode.json +31 -0
  291. package/static/schemas/twake/nomenclature/twakeListType.json +31 -0
  292. package/static/schemas/twake/nomenclature/twakeMailboxType.json +31 -0
  293. package/static/schemas/twake/nomenclature/twakeTitle.json +31 -0
  294. package/static/schemas/twake/organizations.json +52 -0
  295. package/static/schemas/twake/positions.json +32 -0
  296. package/static/schemas/twake/users.json +112 -0
@@ -0,0 +1,694 @@
1
+ /**
2
+ * DOM utility functions
3
+ * @module browser/shared/utils/dom
4
+ */
5
+ /**
6
+ * Escape HTML special characters to prevent XSS attacks
7
+ * Reference: https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html
8
+ */
9
+ function escapeHtml(text) {
10
+ if (text == null)
11
+ return '';
12
+ const map = {
13
+ '&': '&',
14
+ '<': '&lt;',
15
+ '>': '&gt;',
16
+ '"': '&quot;',
17
+ "'": '&#x27;',
18
+ '/': '&#x2F;',
19
+ };
20
+ return String(text).replace(/[&<>"'/]/g, m => map[m]);
21
+ }
22
+ /**
23
+ * Convert camelCase or snake_case to Title Case
24
+ */
25
+ function toTitleCase(text) {
26
+ const label = text.replace(/([A-Z])/g, ' $1').replace(/_/g, ' ');
27
+ return label.charAt(0).toUpperCase() + label.slice(1);
28
+ }
29
+ /**
30
+ * Create an HTML element with attributes and children
31
+ */
32
+ function createElement(tag, attributes = {}, children = []) {
33
+ const element = document.createElement(tag);
34
+ for (const [key, value] of Object.entries(attributes)) {
35
+ if (key === 'className') {
36
+ element.className = value;
37
+ }
38
+ else {
39
+ element.setAttribute(key, value);
40
+ }
41
+ }
42
+ for (const child of children) {
43
+ if (typeof child === 'string') {
44
+ element.appendChild(document.createTextNode(child));
45
+ }
46
+ else {
47
+ element.appendChild(child);
48
+ }
49
+ }
50
+ return element;
51
+ }
52
+ /**
53
+ * Set innerHTML safely (assumes content has been properly escaped)
54
+ */
55
+ function setInnerHTML(element, html) {
56
+ element.innerHTML = html;
57
+ }
58
+ /**
59
+ * Get element by ID with type safety
60
+ */
61
+ function getElementByIdSafe(id) {
62
+ return document.getElementById(id);
63
+ }
64
+ /**
65
+ * Query selector with type safety
66
+ */
67
+ function querySelectorSafe(selector, parent = document) {
68
+ return parent.querySelector(selector);
69
+ }
70
+ /**
71
+ * Query selector all with type safety
72
+ */
73
+ function querySelectorAllSafe(selector, parent = document) {
74
+ return parent.querySelectorAll(selector);
75
+ }
76
+
77
+ /**
78
+ * Form utility functions
79
+ * @module browser/shared/utils/form
80
+ */
81
+ /**
82
+ * Get user-friendly field label from field name
83
+ */
84
+ function getFieldLabel(fieldName, attribute, customLabels) {
85
+ // Check custom labels first
86
+ if (customLabels && customLabels[fieldName]) {
87
+ return customLabels[fieldName];
88
+ }
89
+ // Common label mappings
90
+ const labelMap = {
91
+ ou: 'Unit Name',
92
+ l: 'Location',
93
+ telephoneNumber: 'Phone Number',
94
+ facsimileTelephoneNumber: 'Fax Number',
95
+ postalAddress: 'Postal Address',
96
+ twakeDepartmentPath: 'Department Path',
97
+ twakeLocalAdminLink: 'Local Administrators',
98
+ twakeMailboxType: 'Mailbox Type',
99
+ };
100
+ if (labelMap[fieldName]) {
101
+ return labelMap[fieldName];
102
+ }
103
+ // Convert to title case
104
+ return toTitleCase(fieldName);
105
+ }
106
+ /**
107
+ * Generate HTML for a form field based on schema attribute
108
+ */
109
+ async function generateFormField(fieldName, attribute, options) {
110
+ const label = getFieldLabel(fieldName, attribute, options?.customLabels);
111
+ const required = attribute.required ? 'required' : '';
112
+ const labelClass = attribute.required ? 'required' : '';
113
+ let fieldHtml = '';
114
+ if (attribute.type === 'pointer') {
115
+ // Pointer field - load options and display select
116
+ const branch = attribute.branch?.[0];
117
+ if (branch && options?.pointerOptionsLoader) {
118
+ const pointerOptions = await options.pointerOptionsLoader(branch);
119
+ fieldHtml = `
120
+ <div class="form-group">
121
+ <label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
122
+ <select id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" class="form-select" ${required}>
123
+ <option value="">Select...</option>
124
+ ${pointerOptions
125
+ .map(opt => `
126
+ <option value="${escapeHtml(opt.dn)}">${escapeHtml(opt.label)}</option>
127
+ `)
128
+ .join('')}
129
+ </select>
130
+ ${fieldName === 'twakeMailboxType' ? '<div class="help-text">Select "mailingList" or "teamMailbox" to enable mail functionality</div>' : ''}
131
+ </div>
132
+ `;
133
+ }
134
+ else {
135
+ // Fallback if no branch defined
136
+ fieldHtml = `
137
+ <div class="form-group">
138
+ <label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
139
+ <input type="text" id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required} />
140
+ <div class="help-text">Enter DN (Distinguished Name)</div>
141
+ </div>
142
+ `;
143
+ }
144
+ }
145
+ else if (attribute.type === 'array' &&
146
+ attribute.items?.type === 'pointer') {
147
+ // Array of pointers - will be handled dynamically
148
+ const branch = attribute.items.branch?.[0];
149
+ if (branch) {
150
+ fieldHtml = `
151
+ <div class="form-group">
152
+ <label class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
153
+ <div class="array-field pointer-array-field" data-field="${escapeHtml(fieldName)}" data-branch="${escapeHtml(branch)}">
154
+ <button type="button" class="btn btn-secondary add-pointer-item" data-field="${escapeHtml(fieldName)}">
155
+ <span class="material-icons">add</span>
156
+ Add ${escapeHtml(label)}
157
+ </button>
158
+ </div>
159
+ <input type="hidden" name="${escapeHtml(fieldName)}" id="${escapeHtml(fieldName)}-hidden" value="" />
160
+ </div>
161
+ `;
162
+ }
163
+ else {
164
+ // Fallback
165
+ fieldHtml = `
166
+ <div class="form-group">
167
+ <label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
168
+ <textarea id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required}></textarea>
169
+ <div class="help-text">Enter one DN per line</div>
170
+ </div>
171
+ `;
172
+ }
173
+ }
174
+ else if (attribute.type === 'array' && attribute.items?.type === 'string') {
175
+ // Array field - use textarea
176
+ fieldHtml = `
177
+ <div class="form-group">
178
+ <label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
179
+ <textarea id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required}></textarea>
180
+ <div class="help-text">Enter one value per line</div>
181
+ </div>
182
+ `;
183
+ }
184
+ else if (attribute.type === 'number' || attribute.type === 'integer') {
185
+ // Number field
186
+ fieldHtml = `
187
+ <div class="form-group">
188
+ <label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
189
+ <input type="number" id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required} />
190
+ </div>
191
+ `;
192
+ }
193
+ else if (attribute.type === 'string') {
194
+ // String field - use input with appropriate type
195
+ const type = fieldName.toLowerCase().includes('password') ||
196
+ fieldName.toLowerCase().includes('pwd')
197
+ ? 'password'
198
+ : fieldName.toLowerCase().includes('mail')
199
+ ? 'email'
200
+ : fieldName.toLowerCase().includes('phone') ||
201
+ fieldName.toLowerCase().includes('telephone')
202
+ ? 'tel'
203
+ : 'text';
204
+ fieldHtml = `
205
+ <div class="form-group">
206
+ <label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
207
+ <input type="${escapeHtml(type)}" id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required} />
208
+ </div>
209
+ `;
210
+ }
211
+ return fieldHtml;
212
+ }
213
+ /**
214
+ * Process form data and convert types based on schema
215
+ */
216
+ function processFormData(formData, schema) {
217
+ const data = {};
218
+ // Collect form data
219
+ for (const [key, value] of formData) {
220
+ if (data[key]) {
221
+ // Multiple values for the same key - convert to array
222
+ if (!Array.isArray(data[key])) {
223
+ data[key] = [data[key]];
224
+ }
225
+ data[key].push(value);
226
+ }
227
+ else {
228
+ data[key] = value;
229
+ }
230
+ }
231
+ // Process special field types based on schema
232
+ for (const [fieldName, attribute] of Object.entries(schema)) {
233
+ if (!data[fieldName])
234
+ continue;
235
+ if (attribute.type === 'array' && typeof data[fieldName] === 'string') {
236
+ // Split by newlines and filter empty lines
237
+ data[fieldName] = data[fieldName]
238
+ .split('\n')
239
+ .map(line => line.trim())
240
+ .filter(line => line.length > 0);
241
+ }
242
+ else if ((attribute.type === 'number' || attribute.type === 'integer') &&
243
+ typeof data[fieldName] === 'string') {
244
+ // Convert to number
245
+ data[fieldName] =
246
+ attribute.type === 'integer'
247
+ ? parseInt(data[fieldName], 10)
248
+ : parseFloat(data[fieldName]);
249
+ }
250
+ }
251
+ // Clean up: remove empty strings, null values, and empty arrays
252
+ const cleanedData = {};
253
+ for (const [key, value] of Object.entries(data)) {
254
+ if (value === '' || value === null || value === undefined) {
255
+ continue;
256
+ }
257
+ if (Array.isArray(value) && value.length === 0) {
258
+ continue;
259
+ }
260
+ cleanedData[key] = value;
261
+ }
262
+ return cleanedData;
263
+ }
264
+ /**
265
+ * Collect values from pointer array fields
266
+ */
267
+ function collectPointerArrayValues(container) {
268
+ const values = {};
269
+ const pointerArrayFields = container.querySelectorAll('.pointer-array-field');
270
+ pointerArrayFields.forEach(field => {
271
+ const fieldName = field.getAttribute('data-field');
272
+ if (!fieldName)
273
+ return;
274
+ const selects = field.querySelectorAll('.pointer-array-select');
275
+ const fieldValues = Array.from(selects)
276
+ .map(select => select.value)
277
+ .filter(val => val); // Remove empty values
278
+ if (fieldValues.length > 0) {
279
+ values[fieldName] = fieldValues;
280
+ }
281
+ });
282
+ return values;
283
+ }
284
+ /**
285
+ * Group schema fields by category
286
+ */
287
+ function groupSchemaFields(schema, categorizer) {
288
+ const groups = new Map();
289
+ for (const [fieldName, attribute] of Object.entries(schema)) {
290
+ if (attribute.fixed)
291
+ continue;
292
+ if (fieldName === 'objectClass')
293
+ continue;
294
+ const category = categorizer
295
+ ? categorizer(fieldName, attribute)
296
+ : attribute.group || 'default';
297
+ if (category === null)
298
+ continue; // Skip this field
299
+ if (!groups.has(category)) {
300
+ groups.set(category, []);
301
+ }
302
+ groups.get(category).push([fieldName, attribute]);
303
+ }
304
+ return groups;
305
+ }
306
+
307
+ /**
308
+ * Schema utility functions
309
+ * @module browser/shared/utils/schema
310
+ */
311
+ /**
312
+ * Cache for pointer options to avoid repeated API calls
313
+ */
314
+ const pointerOptionsCache = new Map();
315
+ /**
316
+ * Load pointer options from API
317
+ */
318
+ async function loadPointerOptions(branch, apiLoader, useCache = true) {
319
+ if (useCache && pointerOptionsCache.has(branch)) {
320
+ return pointerOptionsCache.get(branch);
321
+ }
322
+ try {
323
+ const options = await apiLoader(branch);
324
+ if (useCache) {
325
+ pointerOptionsCache.set(branch, options);
326
+ }
327
+ return options;
328
+ }
329
+ catch (error) {
330
+ console.error('Failed to load pointer options for branch:', branch, error);
331
+ return [];
332
+ }
333
+ }
334
+ /**
335
+ * Clear pointer options cache
336
+ */
337
+ function clearPointerOptionsCache() {
338
+ pointerOptionsCache.clear();
339
+ }
340
+ /**
341
+ * Replace placeholders in schema branches
342
+ */
343
+ function replacePlaceholders(value, replacements) {
344
+ const replace = (str) => {
345
+ let result = str;
346
+ for (const [key, val] of Object.entries(replacements)) {
347
+ result = result.replace(new RegExp(key, 'g'), val);
348
+ }
349
+ return result;
350
+ };
351
+ if (Array.isArray(value)) {
352
+ return value.map(replace);
353
+ }
354
+ return replace(value);
355
+ }
356
+ /**
357
+ * Replace placeholders in schema attributes
358
+ */
359
+ function replaceSchemaPlaceholders(schema, replacements) {
360
+ const processValue = (value) => {
361
+ if (typeof value === 'string') {
362
+ return replacePlaceholders(value, replacements);
363
+ }
364
+ if (Array.isArray(value)) {
365
+ return value.map(processValue);
366
+ }
367
+ if (value && typeof value === 'object') {
368
+ return processObject(value);
369
+ }
370
+ return value;
371
+ };
372
+ const processObject = (obj) => {
373
+ const result = {};
374
+ for (const [key, value] of Object.entries(obj)) {
375
+ result[key] = processValue(value);
376
+ }
377
+ return result;
378
+ };
379
+ return processObject(schema);
380
+ }
381
+ /**
382
+ * Fetch organization path from API
383
+ */
384
+ async function getOrganizationPath(orgDn, apiBaseUrl) {
385
+ try {
386
+ const response = await fetch(`${apiBaseUrl}/api/v1/ldap/organizations/${encodeURIComponent(orgDn)}`);
387
+ if (!response.ok) {
388
+ console.error('Failed to fetch organization:', response.status);
389
+ return orgDn;
390
+ }
391
+ const org = await response.json();
392
+ // Use twakeDepartmentPath if available (Twake schema)
393
+ if (org.twakeDepartmentPath) {
394
+ const pathValue = Array.isArray(org.twakeDepartmentPath)
395
+ ? org.twakeDepartmentPath[0]
396
+ : org.twakeDepartmentPath;
397
+ return pathValue;
398
+ }
399
+ // Fallback to ou name for non-Twake schemas
400
+ if (org.ou) {
401
+ const ouValue = Array.isArray(org.ou) ? org.ou[0] : org.ou;
402
+ return ouValue;
403
+ }
404
+ // Last resort: use DN
405
+ return orgDn;
406
+ }
407
+ catch (error) {
408
+ console.error('Failed to get organization info:', error);
409
+ return orgDn;
410
+ }
411
+ }
412
+ /**
413
+ * Load schema from URL or object
414
+ */
415
+ async function loadSchema(schemaSource) {
416
+ if (typeof schemaSource === 'string') {
417
+ const response = await fetch(schemaSource);
418
+ if (!response.ok) {
419
+ throw new Error(`Failed to load schema from ${schemaSource}`);
420
+ }
421
+ return (await response.json());
422
+ }
423
+ return schemaSource;
424
+ }
425
+
426
+ /**
427
+ * Reusable Modal component
428
+ * @module browser/shared/components/Modal
429
+ */
430
+ class Modal {
431
+ constructor(options) {
432
+ this.overlay = null;
433
+ this.options = options;
434
+ this.overlay = getElementByIdSafe(options.id);
435
+ if (!this.overlay) {
436
+ throw new Error(`Modal overlay with id "${options.id}" not found`);
437
+ }
438
+ this.attachEventListeners();
439
+ }
440
+ attachEventListeners() {
441
+ if (!this.overlay)
442
+ return;
443
+ // Close button
444
+ const closeBtn = this.overlay.querySelector('.close-button');
445
+ if (closeBtn) {
446
+ closeBtn.addEventListener('click', () => this.close());
447
+ }
448
+ // Cancel button
449
+ const cancelBtn = this.overlay.querySelector('[data-modal-cancel]');
450
+ if (cancelBtn) {
451
+ cancelBtn.addEventListener('click', () => this.close());
452
+ }
453
+ // Click outside to close
454
+ this.overlay.addEventListener('click', e => {
455
+ if (e.target === this.overlay) {
456
+ this.close();
457
+ }
458
+ });
459
+ // Escape key to close
460
+ document.addEventListener('keydown', e => {
461
+ if (e.key === 'Escape' && this.isOpen()) {
462
+ this.close();
463
+ }
464
+ });
465
+ }
466
+ open() {
467
+ if (this.overlay) {
468
+ this.overlay.classList.add('active');
469
+ document.body.style.overflow = 'hidden'; // Prevent background scroll
470
+ }
471
+ }
472
+ close() {
473
+ if (this.overlay) {
474
+ this.overlay.classList.remove('active');
475
+ document.body.style.overflow = ''; // Restore scroll
476
+ if (this.options.onClose) {
477
+ this.options.onClose();
478
+ }
479
+ }
480
+ }
481
+ isOpen() {
482
+ return this.overlay?.classList.contains('active') || false;
483
+ }
484
+ getFormElement() {
485
+ return this.overlay?.querySelector('form') || null;
486
+ }
487
+ resetForm() {
488
+ const form = this.getFormElement();
489
+ if (form) {
490
+ form.reset();
491
+ }
492
+ }
493
+ setContent(html) {
494
+ const contentEl = this.overlay?.querySelector('[data-modal-content]');
495
+ if (contentEl) {
496
+ contentEl.innerHTML = html;
497
+ }
498
+ }
499
+ setTitle(title) {
500
+ const titleEl = this.overlay?.querySelector('.modal-header h2');
501
+ if (titleEl) {
502
+ titleEl.textContent = title;
503
+ }
504
+ }
505
+ /**
506
+ * Create modal HTML structure
507
+ */
508
+ static createModalHTML(options) {
509
+ const formId = options.formId || `${options.id}-form`;
510
+ const submitLabel = options.submitLabel || 'Submit';
511
+ return `
512
+ <div class="modal-overlay" id="${escapeHtml(options.id)}">
513
+ <div class="modal">
514
+ <div class="modal-header">
515
+ <h2>${escapeHtml(options.title)}</h2>
516
+ <button class="close-button" type="button">
517
+ <span class="material-icons">close</span>
518
+ </button>
519
+ </div>
520
+ <form id="${escapeHtml(formId)}">
521
+ <div data-modal-content></div>
522
+ <div class="modal-footer">
523
+ <button type="button" class="secondary" data-modal-cancel>
524
+ Cancel
525
+ </button>
526
+ <button type="submit" class="primary">${escapeHtml(submitLabel)}</button>
527
+ </div>
528
+ </form>
529
+ </div>
530
+ </div>
531
+ `;
532
+ }
533
+ /**
534
+ * Create and inject modal into DOM
535
+ */
536
+ static create(options) {
537
+ const html = Modal.createModalHTML(options);
538
+ document.body.insertAdjacentHTML('beforeend', html);
539
+ return new Modal(options);
540
+ }
541
+ }
542
+
543
+ /**
544
+ * Status message component for displaying notifications
545
+ * @module browser/shared/components/StatusMessage
546
+ */
547
+ class StatusMessage {
548
+ constructor(options) {
549
+ this.container = null;
550
+ this.timeoutId = null;
551
+ this.options = {
552
+ duration: 5000,
553
+ dismissible: true,
554
+ ...options,
555
+ };
556
+ this.container = getElementByIdSafe(options.containerId);
557
+ if (!this.container) {
558
+ throw new Error(`Status container with id "${options.containerId}" not found`);
559
+ }
560
+ }
561
+ /**
562
+ * Show a status message
563
+ */
564
+ show(message, type = 'info') {
565
+ if (!this.container)
566
+ return;
567
+ // Clear any existing timeout
568
+ if (this.timeoutId !== null) {
569
+ window.clearTimeout(this.timeoutId);
570
+ this.timeoutId = null;
571
+ }
572
+ const iconMap = {
573
+ error: 'error_outline',
574
+ success: 'check_circle',
575
+ info: 'info',
576
+ warning: 'warning',
577
+ };
578
+ const icon = iconMap[type] || 'info';
579
+ let html = `
580
+ <div class="status-message ${escapeHtml(type)}">
581
+ <span class="material-icons">${icon}</span>
582
+ <span class="status-message-text">${escapeHtml(message)}</span>
583
+ `;
584
+ if (this.options.dismissible) {
585
+ html += `
586
+ <button class="status-message-close" type="button" aria-label="Close">
587
+ <span class="material-icons">close</span>
588
+ </button>
589
+ `;
590
+ }
591
+ html += '</div>';
592
+ this.container.innerHTML = html;
593
+ // Attach close button handler
594
+ if (this.options.dismissible) {
595
+ const closeBtn = this.container.querySelector('.status-message-close');
596
+ if (closeBtn) {
597
+ closeBtn.addEventListener('click', () => this.hide());
598
+ }
599
+ }
600
+ // Auto-hide after duration
601
+ if (this.options.duration && this.options.duration > 0) {
602
+ this.timeoutId = window.setTimeout(() => {
603
+ this.hide();
604
+ }, this.options.duration);
605
+ }
606
+ }
607
+ /**
608
+ * Show error message
609
+ */
610
+ error(message) {
611
+ this.show(message, 'error');
612
+ }
613
+ /**
614
+ * Show success message
615
+ */
616
+ success(message) {
617
+ this.show(message, 'success');
618
+ }
619
+ /**
620
+ * Show info message
621
+ */
622
+ info(message) {
623
+ this.show(message, 'info');
624
+ }
625
+ /**
626
+ * Show warning message
627
+ */
628
+ warning(message) {
629
+ this.show(message, 'warning');
630
+ }
631
+ /**
632
+ * Hide the status message
633
+ */
634
+ hide() {
635
+ if (this.container) {
636
+ this.container.innerHTML = '';
637
+ }
638
+ if (this.timeoutId !== null) {
639
+ window.clearTimeout(this.timeoutId);
640
+ this.timeoutId = null;
641
+ }
642
+ }
643
+ /**
644
+ * Get the container element
645
+ */
646
+ getContainer() {
647
+ return this.container;
648
+ }
649
+ }
650
+ /**
651
+ * Simple functional API for showing status messages
652
+ */
653
+ function showStatus(containerId, message, type = 'info', duration = 5000) {
654
+ const statusMessage = new StatusMessage({ containerId, duration });
655
+ statusMessage.show(message, type);
656
+ }
657
+
658
+ /**
659
+ * Shared TypeScript types for browser components
660
+ * @module browser/shared/types
661
+ */
662
+ const THEMES = {
663
+ purple: {
664
+ name: 'Purple',
665
+ primaryColor: '#6200ee',
666
+ primaryColorDark: '#3700b3',
667
+ gradientStart: '#667eea',
668
+ gradientEnd: '#764ba2',
669
+ },
670
+ blue: {
671
+ name: 'Blue',
672
+ primaryColor: '#185a9d',
673
+ primaryColorDark: '#0d3a6b',
674
+ gradientStart: '#43cea2',
675
+ gradientEnd: '#185a9d',
676
+ },
677
+ pink: {
678
+ name: 'Pink',
679
+ primaryColor: '#f5576c',
680
+ primaryColorDark: '#d43d50',
681
+ gradientStart: '#f093fb',
682
+ gradientEnd: '#f5576c',
683
+ },
684
+ green: {
685
+ name: 'Green',
686
+ primaryColor: '#00c853',
687
+ primaryColorDark: '#009624',
688
+ gradientStart: '#56ab2f',
689
+ gradientEnd: '#a8e063',
690
+ },
691
+ };
692
+
693
+ export { Modal, StatusMessage, THEMES, clearPointerOptionsCache, collectPointerArrayValues, createElement, escapeHtml, generateFormField, getElementByIdSafe, getFieldLabel, getOrganizationPath, groupSchemaFields, loadPointerOptions, loadSchema, processFormData, querySelectorAllSafe, querySelectorSafe, replacePlaceholders, replaceSchemaPlaceholders, setInnerHTML, showStatus, toTitleCase };
694
+ //# sourceMappingURL=shared.esm.js.map