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. showFormGroup('completionattendancegroup', showAll ||
  158. isFeatureEnabled(profileType, 'completionattendance'));
  159. // Completion validation.
  160. showFormGroup('completionengagementgroup', showAll ||
  161. isFeatureEnabled(profileType, 'completionengagement'));
  162. };
  163. /**
  164. * Init the participant list
  165. */
  166. const participantListInit = () => {
  167. const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
  168. const participantList = getParticipantList();
  169. participantList.forEach(participant => {
  170. const selectionTypeValue = participant.selectiontype;
  171. const selectionValue = participant.selectionid;
  172. const selectionRole = participant.role;
  173. if (participant.selectiontype === 'all' ||
  174. typeof participantData[participant.selectiontype].children[participant.selectionid] !== 'undefined') {
  175. // Add it to the form, but don't add the delete button if it is the first item.
  176. participantAddToForm(selectionTypeValue, selectionValue, selectionRole, true).then();
  177. }
  178. });
  179. };
  180. /**
  181. * Add rows to the participant list depending on the current selection.
  182. *
  183. * @param {string} selectionTypeValue
  184. * @param {string} selectionValue
  185. * @param {string} selectedRole
  186. * @param {boolean} canRemove
  187. * @returns {Promise<void>}
  188. */
  189. const participantAddToForm = async(selectionTypeValue, selectionValue, selectedRole, canRemove) => {
  190. const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
  191. const sviewer = await getString('mod_form_field_participant_bbb_role_viewer', 'mod_bigbluebuttonbn');
  192. const smoderator = await getString('mod_form_field_participant_bbb_role_moderator', 'mod_bigbluebuttonbn');
  193. let roles = {
  194. viewer: {'id': 'viewer', label: sviewer},
  195. moderator: {'id': 'moderator', label: smoderator}
  196. };
  197. roles[selectedRole].isselected = true;
  198. try {
  199. const listTable = document.querySelector('#participant_list_table tbody');
  200. const templateContext = {
  201. 'selectiontypevalue': selectionTypeValue,
  202. 'selectionvalue': selectionValue,
  203. 'participanttype': participantData[selectionTypeValue].name,
  204. 'participantvalue':
  205. (selectionTypeValue !== 'all') ?
  206. participantData[selectionTypeValue].children[selectionValue].name : null,
  207. 'roles': Object.values(roles),
  208. 'canRemove': canRemove
  209. };
  210. const {html, js} = await Templates.renderForPromise('mod_bigbluebuttonbn/participant_form_add', templateContext);
  211. const newNode = Templates.appendNodeContents(listTable, html, js)[0];
  212. newNode.querySelector('.participant-select').addEventListener('change', () => {
  213. participantListRoleUpdate(selectionTypeValue, selectionValue);
  214. });
  215. // Now add the callbacks: participantListRoleUpdate() and participantRemove().
  216. const removeNode = newNode.querySelector('.remove-button');
  217. if (removeNode) {
  218. removeNode
  219. .addEventListener('click', () => {
  220. participantRemove(selectionTypeValue, selectionValue);
  221. });
  222. }
  223. } catch (e) {
  224. Notification.exception(e);
  225. }
  226. };
  227. /*
  228. */
  229. /**
  230. * Update the related form element with the list value.
  231. *
  232. * @param {object} list
  233. */
  234. const participantListUpdate = (list) => {
  235. const participantList = ELEMENT_SELECTOR.participantList();
  236. participantList.value = JSON.stringify(list);
  237. };
  238. /**
  239. *
  240. * @returns {any}
  241. */
  242. const getParticipantList = () => {
  243. const participantListValue = ELEMENT_SELECTOR.participantList().value;
  244. if (participantListValue) {
  245. return JSON.parse(participantListValue);
  246. }
  247. return [];
  248. };
  249. /**
  250. * Remove participant both in the table/form and in the form element.
  251. *
  252. * @param {string} selectionTypeValue
  253. * @param {string} selectionValue
  254. */
  255. const participantRemove = (selectionTypeValue, selectionValue) => {
  256. const pList = getParticipantList();
  257. const id = 'participant_list_tr_' + selectionTypeValue + '-' + selectionValue;
  258. const participantListTable = ELEMENT_SELECTOR.participantTable();
  259. const selectionid = (selectionValue === '' ? null : selectionValue);
  260. for (let i = 0; i < pList.length; i++) {
  261. if (pList[i].selectiontype === selectionTypeValue &&
  262. pList[i].selectionid === selectionid) {
  263. pList.splice(i, 1);
  264. }
  265. }
  266. // Remove from the form.
  267. for (let i = 0; i < participantListTable.rows.length; i++) {
  268. if (participantListTable.rows[i].id === id) {
  269. participantListTable.deleteRow(i);
  270. }
  271. }
  272. // Update value in the form.
  273. participantListUpdate(pList);
  274. };
  275. /**
  276. * Role update
  277. *
  278. * @param {string} type
  279. * @param {string} id
  280. */
  281. const participantListRoleUpdate = (type, id) => {
  282. // Update in memory.
  283. const participantListRoleSelection = document.querySelector(`#participant_list_tr_${type}-${id} .participant-select`);
  284. const pList = getParticipantList();
  285. for (var i = 0; i < pList.length; i++) {
  286. if (pList[i].selectiontype === type && pList[i].selectionid === id) {
  287. pList[i].role = participantListRoleSelection.value;
  288. }
  289. }
  290. // Update in the form.
  291. participantListUpdate(pList);
  292. };
  293. /**
  294. * Add participant from the currently selected options
  295. */
  296. const participantAddFromCurrentSelection = () => {
  297. let selectionType = ELEMENT_SELECTOR.participantSelectionType();
  298. let selection = ELEMENT_SELECTOR.participantSelection();
  299. const pList = getParticipantList();
  300. // Lookup to see if it has been added already.
  301. for (var i = 0; i < pList.length; i++) {
  302. if (pList[i].selectiontype === selectionType.value &&
  303. pList[i].selectionid === selection.value) {
  304. return;
  305. }
  306. }
  307. pList.push({
  308. "selectiontype": selectionType.value,
  309. "selectionid": selection.value,
  310. "role": "viewer"
  311. });
  312. // Add it to the form.
  313. participantAddToForm(selectionType.value, selection.value, 'viewer', true).then();
  314. // Update in the form.
  315. participantListUpdate(pList);
  316. };
  317. /**
  318. * Update selectable options when changing types
  319. *
  320. * @param {HTMLNode} currentTypeSelect
  321. */
  322. const updateSelectionFromType = (currentTypeSelect) => {
  323. const createNewOption = (selectItem, label, value) => {
  324. const option = document.createElement('option');
  325. option.text = label;
  326. option.value = value;
  327. selectItem.add(option);
  328. };
  329. const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
  330. // Clear all selection items.
  331. const participantSelect = ELEMENT_SELECTOR.participantSelection();
  332. while (participantSelect.firstChild) {
  333. participantSelect.removeChild(participantSelect.firstChild);
  334. }
  335. // Add options depending on the selection.
  336. if (currentTypeSelect.selectedIndex !== -1) {
  337. const options = Object.values(participantData[currentTypeSelect.value].children);
  338. options.forEach(option => {
  339. createNewOption(participantSelect, option.name, option.id);
  340. });
  341. if (currentTypeSelect.value === 'all') {
  342. createNewOption(participantSelect, '---------------', 'all');
  343. participantSelect.disabled = true;
  344. } else {
  345. participantSelect.disabled = false;
  346. }
  347. }
  348. };