mod/bigbluebuttonbn/amd/src/modform.js

  1. // This file is part of Moodle - http://moodle.org/
  2. //
  3. // Moodle is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // Moodle is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  15. /**
  16. * JS for the mod_form page on mod_bigbluebuttonbn plugin.
  17. *
  18. * @module mod_bigbluebuttonbn/modform
  19. * @copyright 2021 Blindside Networks Inc
  20. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  21. */
  22. import {getString} from 'core/str';
  23. import Notification from 'core/notification';
  24. import Templates from "core/templates";
  25. /**
  26. * Get all selectors in one place.
  27. *
  28. */
  29. const ELEMENT_SELECTOR = {
  30. instanceTypeSelection: () => document.querySelector('select#id_type'),
  31. instanceTypeProfiles: () => document.querySelector('[data-profile-types]'),
  32. participantData: () => document.querySelector('[data-participant-data]'),
  33. participantList: () => document.getElementsByName('participants')[0],
  34. participantTable: () => document.getElementById('participant_list_table'),
  35. participantSelectionType: () => document.getElementsByName('bigbluebuttonbn_participant_selection_type')[0],
  36. participantSelection: () => document.getElementsByName('bigbluebuttonbn_participant_selection')[0],
  37. participantAddButton: () => document.getElementsByName('bigbluebuttonbn_participant_selection_add')[0],
  38. };
  39. /**
  40. * Initialise the moodle form code.
  41. *
  42. * This will help hide or show items depending on the selection of the instance type.
  43. *
  44. * @method init
  45. * @param {object} info
  46. */
  47. export const init = (info) => {
  48. const selectedType = ELEMENT_SELECTOR.instanceTypeSelection();
  49. const instanceTypeProfiles = JSON.parse(ELEMENT_SELECTOR.instanceTypeProfiles().dataset.profileTypes);
  50. let profileType = info.instanceTypeDefault;
  51. if (selectedType !== null && selectedType.selectedIndex !== -1) {
  52. profileType = selectedType.options[selectedType.selectedIndex].value;
  53. }
  54. const isFeatureEnabled = (profileType, feature) => {
  55. const features = instanceTypeProfiles[profileType].features;
  56. return (features.indexOf(feature) !== -1);
  57. };
  58. applyInstanceTypeProfile(profileType, isFeatureEnabled);
  59. // Change form visible fields depending on the selection.
  60. selectedType.addEventListener('change', (e) => {
  61. applyInstanceTypeProfile(e.target.value, isFeatureEnabled);
  62. });
  63. ELEMENT_SELECTOR.participantSelectionType().addEventListener('change', (e) => {
  64. const currentTypeSelect = e.target;
  65. updateSelectionFromType(currentTypeSelect);
  66. });
  67. ELEMENT_SELECTOR.participantAddButton().addEventListener('click', (e) => {
  68. e.stopPropagation();
  69. e.preventDefault();
  70. participantAddFromCurrentSelection();
  71. });
  72. participantListInit();
  73. };
  74. /**
  75. * Show or hide form element depending on the selected profile
  76. *
  77. * @param {string} profileType
  78. * @param {function} isFeatureEnabled
  79. */
  80. const applyInstanceTypeProfile = (profileType, isFeatureEnabled) => {
  81. let showAll = isFeatureEnabled(profileType, 'all');
  82. const showFieldset = (id, show) => {
  83. // Show room settings validation.
  84. const node = document.querySelector('#' + id);
  85. if (!node) {
  86. return;
  87. }
  88. if (show) {
  89. node.style.display = 'block';
  90. return;
  91. }
  92. node.style.display = 'none';
  93. };
  94. const showInput = (id, show) => {
  95. // Show room settings validation.
  96. const node = document.querySelector('#' + id);
  97. if (!node) {
  98. return;
  99. }
  100. var ancestor = node.closest('div').closest('div');
  101. if (show) {
  102. ancestor.style.display = 'block';
  103. return;
  104. }
  105. ancestor.style.display = 'none';
  106. };
  107. const showFormGroup = (id, show) => {
  108. // Show room settings validation.
  109. const node = document.querySelector('#fgroup_id_' + id);
  110. if (!node) {
  111. return;
  112. }
  113. if (show) {
  114. node.classList.remove('hidden');
  115. return;
  116. }
  117. node.classList.add('hidden');
  118. };
  119. // Show room settings validation.
  120. showFieldset('id_room', showAll ||
  121. isFeatureEnabled(profileType, 'showroom'));
  122. showInput('id_record', showAll ||
  123. isFeatureEnabled(profileType, 'showroom'));
  124. // Show recordings settings validation.
  125. showFieldset('id_recordings', showAll ||
  126. isFeatureEnabled(profileType, 'showrecordings'));
  127. // Show recordings imported settings validation.
  128. showInput('id_recordings_imported', showAll ||
  129. isFeatureEnabled(profileType, 'showrecordings'));
  130. // Show lock settings validation.
  131. showFieldset('id_lock', showAll ||
  132. isFeatureEnabled(profileType, 'lock'));
  133. // Show guest settings validation.
  134. showFieldset('id_guestaccess', showAll ||
  135. isFeatureEnabled(profileType, 'showroom'));
  136. // Preuploadpresentation feature validation.
  137. showFieldset('id_preuploadpresentation', showAll ||
  138. isFeatureEnabled(profileType, 'preuploadpresentation'));
  139. // Participants feature validation.
  140. showFieldset('id_permissions', showAll ||
  141. isFeatureEnabled(profileType, 'permissions'));
  142. // Schedule feature validation.
  143. showFieldset('id_schedule', showAll ||
  144. isFeatureEnabled(profileType, 'schedule'));
  145. // Common module settings validation.
  146. showFieldset('id_modstandardelshdr', showAll ||
  147. isFeatureEnabled(profileType, 'modstandardelshdr'));
  148. // Restrict access validation.
  149. showFieldset('id_availabilityconditionsheader', showAll ||
  150. isFeatureEnabled(profileType, 'availabilityconditionsheader'));
  151. // Tags validation.
  152. showFieldset('id_tagshdr', showAll || isFeatureEnabled(profileType, 'tagshdr'));
  153. // Competencies validation.
  154. showFieldset('id_competenciessection', showAll ||
  155. isFeatureEnabled(profileType, 'competenciessection'));
  156. // Completion validation.
  157. showFieldset('id_activitycompletionheader', showAll ||
  158. isFeatureEnabled(profileType, 'activitycompletionheader'));
  159. // Completion validation.
  160. showFormGroup('completionattendancegroup', showAll ||
  161. isFeatureEnabled(profileType, 'completionattendance'));
  162. // Completion validation.
  163. showFormGroup('completionengagementgroup', showAll ||
  164. isFeatureEnabled(profileType, 'completionengagement'));
  165. };
  166. /**
  167. * Init the participant list
  168. */
  169. const participantListInit = () => {
  170. const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
  171. const participantList = getParticipantList();
  172. participantList.forEach(participant => {
  173. const selectionTypeValue = participant.selectiontype;
  174. const selectionValue = participant.selectionid;
  175. const selectionRole = participant.role;
  176. if (participant.selectiontype === 'all' ||
  177. typeof participantData[participant.selectiontype].children[participant.selectionid] !== 'undefined') {
  178. // Add it to the form, but don't add the delete button if it is the first item.
  179. participantAddToForm(selectionTypeValue, selectionValue, selectionRole, true).then();
  180. }
  181. });
  182. };
  183. /**
  184. * Add rows to the participant list depending on the current selection.
  185. *
  186. * @param {string} selectionTypeValue
  187. * @param {string} selectionValue
  188. * @param {string} selectedRole
  189. * @param {boolean} canRemove
  190. * @returns {Promise<void>}
  191. */
  192. const participantAddToForm = async(selectionTypeValue, selectionValue, selectedRole, canRemove) => {
  193. const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
  194. const sviewer = await getString('mod_form_field_participant_bbb_role_viewer', 'mod_bigbluebuttonbn');
  195. const smoderator = await getString('mod_form_field_participant_bbb_role_moderator', 'mod_bigbluebuttonbn');
  196. let roles = {
  197. viewer: {'id': 'viewer', label: sviewer},
  198. moderator: {'id': 'moderator', label: smoderator}
  199. };
  200. roles[selectedRole].isselected = true;
  201. try {
  202. const listTable = document.querySelector('#participant_list_table tbody');
  203. const templateContext = {
  204. 'selectiontypevalue': selectionTypeValue,
  205. 'selectionvalue': selectionValue,
  206. 'participanttype': participantData[selectionTypeValue].name,
  207. 'participantvalue':
  208. (selectionTypeValue !== 'all') ?
  209. participantData[selectionTypeValue].children[selectionValue].name : null,
  210. 'roles': Object.values(roles),
  211. 'canRemove': canRemove
  212. };
  213. const {html, js} = await Templates.renderForPromise('mod_bigbluebuttonbn/participant_form_add', templateContext);
  214. const newNode = Templates.appendNodeContents(listTable, html, js)[0];
  215. newNode.querySelector('.participant-select').addEventListener('change', () => {
  216. participantListRoleUpdate(selectionTypeValue, selectionValue);
  217. });
  218. // Now add the callbacks: participantListRoleUpdate() and participantRemove().
  219. const removeNode = newNode.querySelector('.remove-button');
  220. if (removeNode) {
  221. removeNode
  222. .addEventListener('click', () => {
  223. participantRemove(selectionTypeValue, selectionValue);
  224. });
  225. }
  226. } catch (e) {
  227. Notification.exception(e);
  228. }
  229. };
  230. /*
  231. */
  232. /**
  233. * Update the related form element with the list value.
  234. *
  235. * @param {object} list
  236. */
  237. const participantListUpdate = (list) => {
  238. const participantList = ELEMENT_SELECTOR.participantList();
  239. participantList.value = JSON.stringify(list);
  240. };
  241. /**
  242. *
  243. * @returns {any}
  244. */
  245. const getParticipantList = () => {
  246. const participantListValue = ELEMENT_SELECTOR.participantList().value;
  247. if (participantListValue) {
  248. return JSON.parse(participantListValue);
  249. }
  250. return [];
  251. };
  252. /**
  253. * Remove participant both in the table/form and in the form element.
  254. *
  255. * @param {string} selectionTypeValue
  256. * @param {string} selectionValue
  257. */
  258. const participantRemove = (selectionTypeValue, selectionValue) => {
  259. const pList = getParticipantList();
  260. const id = 'participant_list_tr_' + selectionTypeValue + '-' + selectionValue;
  261. const participantListTable = ELEMENT_SELECTOR.participantTable();
  262. const selectionid = (selectionValue === '' ? null : selectionValue);
  263. for (let i = 0; i < pList.length; i++) {
  264. if (pList[i].selectiontype === selectionTypeValue &&
  265. pList[i].selectionid === selectionid) {
  266. pList.splice(i, 1);
  267. }
  268. }
  269. // Remove from the form.
  270. for (let i = 0; i < participantListTable.rows.length; i++) {
  271. if (participantListTable.rows[i].id === id) {
  272. participantListTable.deleteRow(i);
  273. }
  274. }
  275. // Update value in the form.
  276. participantListUpdate(pList);
  277. };
  278. /**
  279. * Role update
  280. *
  281. * @param {string} type
  282. * @param {string} id
  283. */
  284. const participantListRoleUpdate = (type, id) => {
  285. // Update in memory.
  286. const participantListRoleSelection = document.querySelector(`#participant_list_tr_${type}-${id} .participant-select`);
  287. const pList = getParticipantList();
  288. for (var i = 0; i < pList.length; i++) {
  289. if (pList[i].selectiontype === type && pList[i].selectionid === id) {
  290. pList[i].role = participantListRoleSelection.value;
  291. }
  292. }
  293. // Update in the form.
  294. participantListUpdate(pList);
  295. };
  296. /**
  297. * Add participant from the currently selected options
  298. */
  299. const participantAddFromCurrentSelection = () => {
  300. let selectionType = ELEMENT_SELECTOR.participantSelectionType();
  301. let selection = ELEMENT_SELECTOR.participantSelection();
  302. const pList = getParticipantList();
  303. // Lookup to see if it has been added already.
  304. for (var i = 0; i < pList.length; i++) {
  305. if (pList[i].selectiontype === selectionType.value &&
  306. pList[i].selectionid === selection.value) {
  307. return;
  308. }
  309. }
  310. pList.push({
  311. "selectiontype": selectionType.value,
  312. "selectionid": selection.value,
  313. "role": "viewer"
  314. });
  315. // Add it to the form.
  316. participantAddToForm(selectionType.value, selection.value, 'viewer', true).then();
  317. // Update in the form.
  318. participantListUpdate(pList);
  319. };
  320. /**
  321. * Update selectable options when changing types
  322. *
  323. * @param {HTMLNode} currentTypeSelect
  324. */
  325. const updateSelectionFromType = (currentTypeSelect) => {
  326. const createNewOption = (selectItem, label, value) => {
  327. const option = document.createElement('option');
  328. option.text = label;
  329. option.value = value;
  330. selectItem.add(option);
  331. };
  332. const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
  333. // Clear all selection items.
  334. const participantSelect = ELEMENT_SELECTOR.participantSelection();
  335. while (participantSelect.firstChild) {
  336. participantSelect.removeChild(participantSelect.firstChild);
  337. }
  338. // Add options depending on the selection.
  339. if (currentTypeSelect.selectedIndex !== -1) {
  340. const options = Object.values(participantData[currentTypeSelect.value].children);
  341. options.forEach(option => {
  342. createNewOption(participantSelect, option.name, option.id);
  343. });
  344. if (currentTypeSelect.value === 'all') {
  345. createNewOption(participantSelect, '---------------', 'all');
  346. participantSelect.disabled = true;
  347. } else {
  348. participantSelect.disabled = false;
  349. }
  350. }
  351. };