Compare commits
3 Commits
91334a46b5
...
e2fb3ec165
| Author | SHA1 | Date | |
|---|---|---|---|
| e2fb3ec165 | |||
| 988aafce39 | |||
| be252e49e2 |
@@ -10,7 +10,6 @@ Desktop.ini
|
|||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
.phpunit.cache/
|
.phpunit.cache/
|
||||||
/coverage/
|
/coverage/
|
||||||
/amd/build/
|
|
||||||
grunt/
|
grunt/
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# MeetLet Adobe Connect Bridge — Marketplace Edition
|
# MeetLet Adobe Connect Bridge — Marketplace Edition
|
||||||
|
|
||||||
**Component:** `mod_meetletacbfree`
|
**Component:** `mod_meetletacbfree`
|
||||||
**Release:** 1.0.9-marketplace
|
**Release:** 1.1.4
|
||||||
**License:** [GNU GPL v3 or later](COPYING.txt)
|
**License:** [GNU GPL v3 or later](COPYING.txt)
|
||||||
|
|
||||||
Connect Moodle to Adobe Connect for live classes, single sign-on (SSO), recordings, and shared meeting content — without a separate Windows agent or commercial license.
|
Connect Moodle to Adobe Connect for live classes, single sign-on (SSO), recordings, and shared meeting content — without a separate Windows agent or commercial license.
|
||||||
@@ -34,7 +34,7 @@ This edition is free to install and use. It includes the core classroom workflow
|
|||||||
| **Activity defaults** | Site-wide meeting language, wait-for-host, and tab visibility with per-activity overrides |
|
| **Activity defaults** | Site-wide meeting language, wait-for-host, and tab visibility with per-activity overrides |
|
||||||
| **Backup & restore** | Standard Moodle 2 backup API support for course copies and restores |
|
| **Backup & restore** | Standard Moodle 2 backup API support for course copies and restores |
|
||||||
| **Privacy API** | GDPR-ready privacy provider for exported and deleted user data |
|
| **Privacy API** | GDPR-ready privacy provider for exported and deleted user data |
|
||||||
| **Optional site registration** | Transparent opt-in registration with MeetLet for update notices and support statistics (plugin works fully without it) |
|
| **Plugin activation** | Voluntary administrator action with MeetLet; required to unlock meeting features (SSO join, recordings, content, and related tools) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
//
|
|
||||||
// Moodle is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Moodle is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ajax endpoint: run one Adobe Connect connection-test step.
|
|
||||||
*
|
|
||||||
* @package mod_meetletacbfree
|
|
||||||
* @copyright 2026 MeetLet
|
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
||||||
*/
|
|
||||||
|
|
||||||
define('AJAX_SCRIPT', true);
|
|
||||||
|
|
||||||
require_once('../../../config.php');
|
|
||||||
|
|
||||||
use mod_meetletacbfree\local\conntest_runner;
|
|
||||||
|
|
||||||
require_login();
|
|
||||||
require_capability('moodle/site:config', context_system::instance());
|
|
||||||
require_sesskey();
|
|
||||||
|
|
||||||
\mod_meetletacbfree\registration\manager::require_registered();
|
|
||||||
|
|
||||||
$step = required_param('step', PARAM_ALPHA);
|
|
||||||
$result = conntest_runner::run_step($step);
|
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
|
||||||
echo json_encode($result);
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ajax endpoint: activate / recheck / deactivate MeetLet site registration.
|
|
||||||
*
|
|
||||||
* @package mod_meetletacbfree
|
|
||||||
* @copyright 2026 MeetLet
|
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
||||||
*/
|
|
||||||
|
|
||||||
define('AJAX_SCRIPT', true);
|
|
||||||
|
|
||||||
require_once('../../../config.php');
|
|
||||||
|
|
||||||
use mod_meetletacbfree\registration\error_mapper;
|
|
||||||
use mod_meetletacbfree\registration\manager;
|
|
||||||
use mod_meetletacbfree\registration\registration_exception;
|
|
||||||
|
|
||||||
require_login();
|
|
||||||
require_capability('moodle/site:config', context_system::instance());
|
|
||||||
require_sesskey();
|
|
||||||
|
|
||||||
$action = required_param('action', PARAM_ALPHA);
|
|
||||||
$apiurl = optional_param('registration_api_url', '', PARAM_URL);
|
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ($apiurl !== '') {
|
|
||||||
set_config(manager::CONFIG_API_URL, rtrim($apiurl, '/'), 'meetletacbfree');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($action === 'activate') {
|
|
||||||
manager::activate();
|
|
||||||
echo json_encode([
|
|
||||||
'success' => true,
|
|
||||||
'message' => get_string('registration_activated', 'meetletacbfree'),
|
|
||||||
'summary' => manager::status_summary(),
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($action === 'recheck' || $action === 'refresh' || $action === 'validate') {
|
|
||||||
manager::recheck(true);
|
|
||||||
echo json_encode([
|
|
||||||
'success' => true,
|
|
||||||
'message' => get_string('registration_rechecked', 'meetletacbfree'),
|
|
||||||
'summary' => manager::status_summary(),
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($action === 'deactivate') {
|
|
||||||
manager::deactivate();
|
|
||||||
echo json_encode([
|
|
||||||
'success' => true,
|
|
||||||
'message' => get_string('registration_deactivated', 'meetletacbfree'),
|
|
||||||
'summary' => manager::status_summary(),
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($action === 'status') {
|
|
||||||
echo json_encode([
|
|
||||||
'success' => true,
|
|
||||||
'summary' => manager::status_summary(),
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new moodle_exception('invalidparameter');
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
$ex = registration_exception::from_throwable($e);
|
|
||||||
$mapped = error_mapper::from_exception($ex);
|
|
||||||
echo json_encode([
|
|
||||||
'success' => false,
|
|
||||||
'error' => $mapped['message'] ?? $ex->get_api_message(),
|
|
||||||
'error_code' => $ex->get_error_code(),
|
|
||||||
'request_id' => $ex->get_request_id(),
|
|
||||||
'summary' => manager::status_summary(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<?php
|
|
||||||
// This file is part of Moodle - http://moodle.org/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset site access settings (role mapping or feature matrix) to built-in defaults.
|
|
||||||
*
|
|
||||||
* @package mod_meetletacbfree
|
|
||||||
* @copyright 2026 MeetLet
|
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
||||||
*/
|
|
||||||
|
|
||||||
define('AJAX_SCRIPT', true);
|
|
||||||
|
|
||||||
require_once('../../../config.php');
|
|
||||||
|
|
||||||
use mod_meetletacbfree\local\featureaccess;
|
|
||||||
use mod_meetletacbfree\local\roles;
|
|
||||||
|
|
||||||
require_login();
|
|
||||||
require_capability('moodle/site:config', context_system::instance());
|
|
||||||
require_sesskey();
|
|
||||||
|
|
||||||
\mod_meetletacbfree\registration\manager::require_registered();
|
|
||||||
|
|
||||||
$section = required_param('section', PARAM_ALPHA);
|
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ($section === 'rolemapping') {
|
|
||||||
$rules = roles::builtin_site_defaults();
|
|
||||||
$json = json_encode($rules, JSON_UNESCAPED_UNICODE);
|
|
||||||
set_config('default_participants', $json, 'meetletacbfree');
|
|
||||||
echo json_encode([
|
|
||||||
'success' => true,
|
|
||||||
'section' => 'rolemapping',
|
|
||||||
'matrix' => roles::matrix_from_rules($rules),
|
|
||||||
'message' => get_string('reset_success_rolemapping', 'meetletacbfree'),
|
|
||||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($section === 'featureaccess') {
|
|
||||||
$matrix = featureaccess::builtin_defaults();
|
|
||||||
$json = json_encode($matrix, JSON_UNESCAPED_UNICODE);
|
|
||||||
set_config('featureaccess', $json, 'meetletacbfree');
|
|
||||||
echo json_encode([
|
|
||||||
'success' => true,
|
|
||||||
'section' => 'featureaccess',
|
|
||||||
'matrix' => $matrix,
|
|
||||||
'message' => get_string('reset_success_featureaccess', 'meetletacbfree'),
|
|
||||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new moodle_exception('invalidparameter');
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
$error = $e->getMessage();
|
|
||||||
if ($e instanceof moodle_exception) {
|
|
||||||
$info = get_exception_info($e);
|
|
||||||
if (!empty($info->message)) {
|
|
||||||
$error = (string)$info->message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo json_encode([
|
|
||||||
'success' => false,
|
|
||||||
'error' => $error !== '' ? $error : get_string('reset_error', 'meetletacbfree'),
|
|
||||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
||||||
}
|
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module mod_meetletacbfree/featureaccess_matrix
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
define([], function() {
|
||||||
|
const bindMatrix = (matrix) => {
|
||||||
|
if (!matrix || matrix.getAttribute('data-access-bound') === '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
matrix.setAttribute('data-access-bound', '1');
|
||||||
|
|
||||||
|
const setGroup = (checks, forceOn) => {
|
||||||
|
const list = Array.from(checks).filter((el) => !el.disabled);
|
||||||
|
if (!list.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const turnOn = (typeof forceOn === 'boolean')
|
||||||
|
? forceOn
|
||||||
|
: list.some((el) => !el.checked);
|
||||||
|
list.forEach((el) => {
|
||||||
|
if (el.checked !== turnOn) {
|
||||||
|
el.checked = turnOn;
|
||||||
|
el.dispatchEvent(new Event('change', {bubbles: true}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
matrix.addEventListener('click', (e) => {
|
||||||
|
const btn = e.target.closest('[data-access-toggle]');
|
||||||
|
if (!btn || !matrix.contains(btn) || matrix.classList.contains('is-disabled')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
const mode = btn.getAttribute('data-access-toggle');
|
||||||
|
if (mode === 'row') {
|
||||||
|
const role = btn.getAttribute('data-role');
|
||||||
|
setGroup(matrix.querySelectorAll('.meetlet-access-check[data-role="' + role + '"]'));
|
||||||
|
} else if (mode === 'col') {
|
||||||
|
const feature = btn.getAttribute('data-feature');
|
||||||
|
setGroup(matrix.querySelectorAll('.meetlet-access-check[data-feature="' + feature + '"]'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
document.querySelectorAll('[data-meetlet-access-matrix]').forEach(bindMatrix);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {init: init};
|
||||||
|
});
|
||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module mod_meetletacbfree/meetlet_modal_scroll
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
define([], function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var scrollLockY = 0;
|
||||||
|
var guardsActive = false;
|
||||||
|
|
||||||
|
function scrollbarWidth() {
|
||||||
|
return Math.max(0, window.innerWidth - document.documentElement.clientWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
function focusNoScroll(el) {
|
||||||
|
if (!el || typeof el.focus !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
el.focus({preventScroll: true});
|
||||||
|
} catch (e) {
|
||||||
|
el.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreScroll() {
|
||||||
|
if (scrollLockY > 0) {
|
||||||
|
window.scrollTo(0, scrollLockY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScrollGuard() {
|
||||||
|
if (scrollLockY && window.scrollY !== scrollLockY) {
|
||||||
|
window.scrollTo(0, scrollLockY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMoveGuard(e) {
|
||||||
|
if (e.target.closest && e.target.closest('.meetlet-modal__dialog')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindGuards() {
|
||||||
|
if (guardsActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
guardsActive = true;
|
||||||
|
window.addEventListener('scroll', onScrollGuard, true);
|
||||||
|
window.addEventListener('wheel', onMoveGuard, {passive: false, capture: true});
|
||||||
|
window.addEventListener('touchmove', onMoveGuard, {passive: false, capture: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
function unbindGuards() {
|
||||||
|
if (!guardsActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
guardsActive = false;
|
||||||
|
window.removeEventListener('scroll', onScrollGuard, true);
|
||||||
|
window.removeEventListener('wheel', onMoveGuard, true);
|
||||||
|
window.removeEventListener('touchmove', onMoveGuard, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lockPageScroll() {
|
||||||
|
if (document.documentElement.classList.contains('meetlet-modal-open')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scrollLockY = window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||||
|
var pad = scrollbarWidth();
|
||||||
|
if (pad > 0) {
|
||||||
|
document.body.style.paddingRight = pad + 'px';
|
||||||
|
}
|
||||||
|
document.documentElement.classList.add('meetlet-modal-open');
|
||||||
|
restoreScroll();
|
||||||
|
if (typeof window.requestAnimationFrame === 'function') {
|
||||||
|
window.requestAnimationFrame(restoreScroll);
|
||||||
|
}
|
||||||
|
bindGuards();
|
||||||
|
}
|
||||||
|
|
||||||
|
function unlockPageScroll() {
|
||||||
|
if (!document.documentElement.classList.contains('meetlet-modal-open')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unbindGuards();
|
||||||
|
var y = scrollLockY;
|
||||||
|
document.documentElement.classList.remove('meetlet-modal-open');
|
||||||
|
document.body.style.paddingRight = '';
|
||||||
|
scrollLockY = 0;
|
||||||
|
if (y) {
|
||||||
|
window.scrollTo(0, y);
|
||||||
|
if (typeof window.requestAnimationFrame === 'function') {
|
||||||
|
window.requestAnimationFrame(function() {
|
||||||
|
window.scrollTo(0, y);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: function() {},
|
||||||
|
lock: lockPageScroll,
|
||||||
|
unlock: unlockPageScroll,
|
||||||
|
focusNoScroll: focusNoScroll
|
||||||
|
};
|
||||||
|
});
|
||||||
Vendored
+504
@@ -0,0 +1,504 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module mod_meetletacbfree/modform
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(['core/str', 'core/notification', 'core/templates', 'core/ajax'], function(Str, Notification, Templates, Ajax) {
|
||||||
|
function init() {
|
||||||
|
const ELEMENT_SELECTOR = {
|
||||||
|
participantData: () => document.querySelector('[data-participant-data]'),
|
||||||
|
participantList: () => document.getElementsByName('participants')[0],
|
||||||
|
participantTable: () => document.getElementById('participant_list_table'),
|
||||||
|
participantSelectionType: () => document.getElementsByName('meetlet_participant_selection_type')[0],
|
||||||
|
participantSelection: () => document.getElementsByName('meetlet_participant_selection')[0],
|
||||||
|
participantAddButton: () => document.getElementsByName('meetlet_participant_selection_add')[0],
|
||||||
|
};
|
||||||
|
|
||||||
|
const getParticipantList = () => {
|
||||||
|
const value = ELEMENT_SELECTOR.participantList().value;
|
||||||
|
return value ? JSON.parse(value) : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const participantListUpdate = (list) => {
|
||||||
|
ELEMENT_SELECTOR.participantList().value = JSON.stringify(list);
|
||||||
|
};
|
||||||
|
|
||||||
|
const participantRemove = (selectionTypeValue, selectionValue) => {
|
||||||
|
const pList = getParticipantList();
|
||||||
|
const id = 'participant_list_tr_' + selectionTypeValue + '-' + selectionValue;
|
||||||
|
const table = ELEMENT_SELECTOR.participantTable();
|
||||||
|
for (let i = 0; i < pList.length; i++) {
|
||||||
|
if (pList[i].selectiontype === selectionTypeValue &&
|
||||||
|
String(pList[i].selectionid) === String(selectionValue)) {
|
||||||
|
pList.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < table.rows.length; i++) {
|
||||||
|
if (table.rows[i].id === id) {
|
||||||
|
table.deleteRow(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
participantListUpdate(pList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const participantListRoleUpdate = (type, id) => {
|
||||||
|
const select = document.querySelector('#participant_list_tr_' + type + '-' + id + ' .participant-select');
|
||||||
|
const pList = getParticipantList();
|
||||||
|
for (let i = 0; i < pList.length; i++) {
|
||||||
|
if (pList[i].selectiontype === type && String(pList[i].selectionid) === String(id)) {
|
||||||
|
pList[i].role = select.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
participantListUpdate(pList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const participantAddToForm = async(selectionTypeValue, selectionValue, selectedRole, canRemove) => {
|
||||||
|
const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
|
||||||
|
const [shost, spresenter, sparticipant] = await Promise.all([
|
||||||
|
Str.get_string('role_host', 'meetletacbfree'),
|
||||||
|
Str.get_string('role_presenter', 'meetletacbfree'),
|
||||||
|
Str.get_string('role_participant', 'meetletacbfree'),
|
||||||
|
]);
|
||||||
|
const roles = {
|
||||||
|
host: {id: 'host', label: shost},
|
||||||
|
presenter: {id: 'presenter', label: spresenter},
|
||||||
|
participant: {id: 'participant', label: sparticipant},
|
||||||
|
};
|
||||||
|
if (roles[selectedRole]) {
|
||||||
|
roles[selectedRole].isselected = true;
|
||||||
|
} else {
|
||||||
|
roles.participant.isselected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const listTable = document.querySelector('#participant_list_table tbody');
|
||||||
|
const templateContext = {
|
||||||
|
selectiontypevalue: selectionTypeValue,
|
||||||
|
selectionvalue: selectionValue,
|
||||||
|
participanttype: participantData[selectionTypeValue].name,
|
||||||
|
participantvalue: (selectionTypeValue !== 'all')
|
||||||
|
? participantData[selectionTypeValue].children[selectionValue].name : null,
|
||||||
|
roles: Object.values(roles),
|
||||||
|
canRemove: canRemove,
|
||||||
|
};
|
||||||
|
const {html, js} = await Templates.renderForPromise('mod_meetletacbfree/participant_form_add', templateContext);
|
||||||
|
const newNode = Templates.appendNodeContents(listTable, html, js)[0];
|
||||||
|
newNode.querySelector('.participant-select').addEventListener('change', () => {
|
||||||
|
participantListRoleUpdate(selectionTypeValue, selectionValue);
|
||||||
|
});
|
||||||
|
const removeNode = newNode.querySelector('.remove-button');
|
||||||
|
if (removeNode) {
|
||||||
|
removeNode.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
participantRemove(selectionTypeValue, selectionValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Notification.exception(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const participantAddFromCurrentSelection = () => {
|
||||||
|
const selectionType = ELEMENT_SELECTOR.participantSelectionType();
|
||||||
|
const selection = ELEMENT_SELECTOR.participantSelection();
|
||||||
|
const pList = getParticipantList();
|
||||||
|
for (let i = 0; i < pList.length; i++) {
|
||||||
|
if (pList[i].selectiontype === selectionType.value &&
|
||||||
|
String(pList[i].selectionid) === String(selection.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const role = 'participant';
|
||||||
|
pList.push({
|
||||||
|
selectiontype: selectionType.value,
|
||||||
|
selectionid: selection.value,
|
||||||
|
role: role,
|
||||||
|
});
|
||||||
|
participantAddToForm(selectionType.value, selection.value, role, true);
|
||||||
|
participantListUpdate(pList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSelectionFromType = (currentTypeSelect) => {
|
||||||
|
const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
|
||||||
|
const participantSelect = ELEMENT_SELECTOR.participantSelection();
|
||||||
|
while (participantSelect.firstChild) {
|
||||||
|
participantSelect.removeChild(participantSelect.firstChild);
|
||||||
|
}
|
||||||
|
if (currentTypeSelect.value === 'all') {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.text = '---------------';
|
||||||
|
option.value = 'all';
|
||||||
|
participantSelect.add(option);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const options = Object.values(participantData[currentTypeSelect.value].children || {});
|
||||||
|
options.forEach((opt) => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.text = opt.name;
|
||||||
|
option.value = opt.id;
|
||||||
|
participantSelect.add(option);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const participantListInit = () => {
|
||||||
|
const participantData = JSON.parse(ELEMENT_SELECTOR.participantData().dataset.participantData);
|
||||||
|
getParticipantList().forEach((participant) => {
|
||||||
|
if (participant.selectiontype === 'all' ||
|
||||||
|
(participantData[participant.selectiontype] &&
|
||||||
|
typeof participantData[participant.selectiontype].children[participant.selectionid] !== 'undefined')) {
|
||||||
|
participantAddToForm(participant.selectiontype, participant.selectionid, participant.role, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncAccessMatrix = () => {
|
||||||
|
// Moodle advcheckbox renders a hidden input with the same name first — use the real checkbox.
|
||||||
|
const override = document.querySelector('#id_featureaccess_override') ||
|
||||||
|
document.querySelector('input[type="checkbox"][name="featureaccess_override"]');
|
||||||
|
const matrix = document.querySelector('.meetlet-access-matrix');
|
||||||
|
if (!override || !matrix) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const enabled = !!override.checked;
|
||||||
|
matrix.classList.toggle('is-disabled', !enabled);
|
||||||
|
matrix.querySelectorAll('input.meetlet-access-check').forEach((el) => {
|
||||||
|
el.disabled = !enabled;
|
||||||
|
});
|
||||||
|
matrix.querySelectorAll('.meetlet-access-toggle').forEach((el) => {
|
||||||
|
el.disabled = !enabled;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const accessOverride = document.querySelector('#id_featureaccess_override') ||
|
||||||
|
document.querySelector('input[type="checkbox"][name="featureaccess_override"]');
|
||||||
|
if (accessOverride) {
|
||||||
|
accessOverride.addEventListener('change', syncAccessMatrix);
|
||||||
|
accessOverride.addEventListener('click', syncAccessMatrix);
|
||||||
|
syncAccessMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
const setDatetimeFields = (basename, unix) => {
|
||||||
|
if (!unix) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const d = new Date(unix * 1000);
|
||||||
|
const set = (suffix, value) => {
|
||||||
|
const el = document.querySelector('[name="' + basename + '[' + suffix + ']"]');
|
||||||
|
if (el) {
|
||||||
|
el.value = String(value);
|
||||||
|
el.dispatchEvent(new Event('change', {bubbles: true}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
set('year', d.getFullYear());
|
||||||
|
set('month', d.getMonth() + 1);
|
||||||
|
set('day', d.getDate());
|
||||||
|
set('hour', d.getHours());
|
||||||
|
set('minute', d.getMinutes());
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyExistingMeeting = () => {
|
||||||
|
const picker = document.querySelector('[data-meeting-picker]');
|
||||||
|
const metaEl = document.querySelector('[data-meetings-meta]');
|
||||||
|
const scoInput = document.querySelector('#id_existingscoid') || document.querySelector('input[name="existingscoid"]');
|
||||||
|
const linkmode = document.querySelector('#id_linkmode') || document.querySelector('[name="linkmode"]');
|
||||||
|
const nameInput = document.querySelector('#id_name') || document.querySelector('[name="name"]');
|
||||||
|
if (!picker || !scoInput || !linkmode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggle = picker.querySelector('[data-meeting-toggle]');
|
||||||
|
const panel = picker.querySelector('[data-meeting-panel]');
|
||||||
|
const searchInput = picker.querySelector('[data-meeting-search]');
|
||||||
|
const listEl = picker.querySelector('[data-meeting-list]');
|
||||||
|
const hint = picker.querySelector('[data-meeting-hint]');
|
||||||
|
const labelEl = picker.querySelector('[data-meeting-label]');
|
||||||
|
const readonly = picker.getAttribute('data-readonly') === '1';
|
||||||
|
const strChoose = picker.getAttribute('data-str-choose') || '---';
|
||||||
|
const strLoading = picker.getAttribute('data-str-loading') || '...';
|
||||||
|
const strEmpty = picker.getAttribute('data-str-empty') || '';
|
||||||
|
const courseid = parseInt(picker.getAttribute('data-courseid') || '0', 10);
|
||||||
|
|
||||||
|
let meta = {};
|
||||||
|
try {
|
||||||
|
meta = JSON.parse((metaEl && metaEl.getAttribute('data-meetings-meta')) || '{}');
|
||||||
|
} catch (e) {
|
||||||
|
meta = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
let loadedOnce = false;
|
||||||
|
let searchTimer = null;
|
||||||
|
let requestSeq = 0;
|
||||||
|
|
||||||
|
const setOpen = (open) => {
|
||||||
|
if (!panel || !toggle || readonly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (open) {
|
||||||
|
panel.removeAttribute('hidden');
|
||||||
|
toggle.setAttribute('aria-expanded', 'true');
|
||||||
|
picker.classList.add('is-open');
|
||||||
|
if (searchInput) {
|
||||||
|
searchInput.focus();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panel.setAttribute('hidden', 'hidden');
|
||||||
|
toggle.setAttribute('aria-expanded', 'false');
|
||||||
|
picker.classList.remove('is-open');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const applySelected = (scoid) => {
|
||||||
|
const info = meta[String(scoid)];
|
||||||
|
if (!info || !nameInput || String(linkmode.value) !== '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (info.name) {
|
||||||
|
nameInput.value = info.name;
|
||||||
|
nameInput.dispatchEvent(new Event('input', {bubbles: true}));
|
||||||
|
nameInput.dispatchEvent(new Event('change', {bubbles: true}));
|
||||||
|
}
|
||||||
|
if (info.starttime) {
|
||||||
|
setDatetimeFields('starttime', info.starttime);
|
||||||
|
}
|
||||||
|
if (info.endtime) {
|
||||||
|
const endEnable = document.querySelector('[name="endtimeenable"]');
|
||||||
|
if (endEnable && !endEnable.checked) {
|
||||||
|
endEnable.checked = true;
|
||||||
|
endEnable.dispatchEvent(new Event('change', {bubbles: true}));
|
||||||
|
}
|
||||||
|
setDatetimeFields('endtime', info.endtime);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectMeeting = (m) => {
|
||||||
|
const scoid = String(m.scoid || '0');
|
||||||
|
scoInput.value = scoid;
|
||||||
|
meta[scoid] = {
|
||||||
|
name: m.name || '',
|
||||||
|
urlpath: m.urlpath || '',
|
||||||
|
starttime: m.starttime || 0,
|
||||||
|
endtime: m.endtime || 0,
|
||||||
|
};
|
||||||
|
if (metaEl) {
|
||||||
|
metaEl.setAttribute('data-meetings-meta', JSON.stringify(meta));
|
||||||
|
}
|
||||||
|
if (labelEl) {
|
||||||
|
labelEl.textContent = m.label || m.name || strChoose;
|
||||||
|
}
|
||||||
|
if (listEl) {
|
||||||
|
listEl.querySelectorAll('.meetlet-combobox__option').forEach((btn) => {
|
||||||
|
btn.classList.toggle('is-selected', btn.getAttribute('data-scoid') === scoid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setOpen(false);
|
||||||
|
applySelected(scoid);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderList = (meetings) => {
|
||||||
|
if (!listEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selected = String(scoInput.value || '0');
|
||||||
|
listEl.innerHTML = '';
|
||||||
|
const rows = meetings || [];
|
||||||
|
if (!rows.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rows.forEach((m) => {
|
||||||
|
const scoid = String(m.scoid);
|
||||||
|
meta[scoid] = {
|
||||||
|
name: m.name || '',
|
||||||
|
urlpath: m.urlpath || '',
|
||||||
|
starttime: m.starttime || 0,
|
||||||
|
endtime: m.endtime || 0,
|
||||||
|
};
|
||||||
|
const li = document.createElement('li');
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.type = 'button';
|
||||||
|
btn.className = 'meetlet-combobox__option' + (scoid === selected ? ' is-selected' : '');
|
||||||
|
btn.setAttribute('data-scoid', scoid);
|
||||||
|
btn.textContent = m.label || m.name || scoid;
|
||||||
|
btn.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
selectMeeting(m);
|
||||||
|
});
|
||||||
|
li.appendChild(btn);
|
||||||
|
listEl.appendChild(li);
|
||||||
|
});
|
||||||
|
if (metaEl) {
|
||||||
|
metaEl.setAttribute('data-meetings-meta', JSON.stringify(meta));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchMeetings = (query) => {
|
||||||
|
if (String(linkmode.value) !== '1' || readonly) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
const seq = ++requestSeq;
|
||||||
|
if (hint) {
|
||||||
|
hint.textContent = strLoading;
|
||||||
|
}
|
||||||
|
return Ajax.call([{
|
||||||
|
methodname: 'mod_meetletacbfree_search_server_meetings',
|
||||||
|
args: {
|
||||||
|
query: query || '',
|
||||||
|
limit: 10,
|
||||||
|
courseid: courseid,
|
||||||
|
},
|
||||||
|
}])[0].then((res) => {
|
||||||
|
if (seq !== requestSeq) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const meetings = (res && res.meetings) ? res.meetings : [];
|
||||||
|
renderList(meetings);
|
||||||
|
if (hint) {
|
||||||
|
hint.textContent = meetings.length ? '' : strEmpty;
|
||||||
|
}
|
||||||
|
loadedOnce = true;
|
||||||
|
return res;
|
||||||
|
}).catch((err) => {
|
||||||
|
if (hint) {
|
||||||
|
hint.textContent = (err && err.message) ? err.message : strEmpty;
|
||||||
|
}
|
||||||
|
Notification.exception(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (toggle) {
|
||||||
|
toggle.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (readonly || String(linkmode.value) !== '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const willOpen = panel.hasAttribute('hidden');
|
||||||
|
setOpen(willOpen);
|
||||||
|
if (willOpen) {
|
||||||
|
fetchMeetings(searchInput ? String(searchInput.value || '').trim() : '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchInput) {
|
||||||
|
searchInput.addEventListener('input', () => {
|
||||||
|
window.clearTimeout(searchTimer);
|
||||||
|
searchTimer = window.setTimeout(() => {
|
||||||
|
fetchMeetings(String(searchInput.value || '').trim());
|
||||||
|
}, 350);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!picker.contains(e.target)) {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
linkmode.addEventListener('change', () => {
|
||||||
|
if (String(linkmode.value) === '1') {
|
||||||
|
loadedOnce = false;
|
||||||
|
if (!readonly) {
|
||||||
|
setOpen(true);
|
||||||
|
fetchMeetings('');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setOpen(false);
|
||||||
|
scoInput.value = '0';
|
||||||
|
if (labelEl) {
|
||||||
|
labelEl.textContent = strChoose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (String(linkmode.value) === '1' && !readonly) {
|
||||||
|
// Prefetch quietly so first open is fast; do not force panel open on edit.
|
||||||
|
fetchMeetings('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
applyExistingMeeting();
|
||||||
|
|
||||||
|
const syncSettingOverrideInputs = () => {
|
||||||
|
document.querySelectorAll('[data-meetlet-setting-radio]').forEach((input) => {
|
||||||
|
if (input.getAttribute('data-meetlet-setting-bound') === '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
input.setAttribute('data-meetlet-setting-bound', '1');
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
const baseName = input.name.replace(/_state$/, '');
|
||||||
|
const hidden = document.querySelector('input[type="hidden"][name="' + baseName + '"]');
|
||||||
|
if (hidden && input.checked) {
|
||||||
|
if (baseName === 'participantsmode') {
|
||||||
|
hidden.value = input.value;
|
||||||
|
} else {
|
||||||
|
hidden.value = input.value === '2' ? '1' : '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syncParticipantOverride();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const participantModeInput = () => document.querySelector('input[name="participantsmode_state"]:checked');
|
||||||
|
|
||||||
|
const syncParticipantOverride = () => {
|
||||||
|
const mode = participantModeInput();
|
||||||
|
const enabled = !mode || mode.value === '1';
|
||||||
|
const container = document.querySelector('#fitem_id_static_participant_list');
|
||||||
|
if (container) {
|
||||||
|
container.classList.toggle('is-disabled', !enabled);
|
||||||
|
}
|
||||||
|
const participantForm = document.querySelector('.meetlet-participant-form') || container;
|
||||||
|
if (participantForm) {
|
||||||
|
participantForm.querySelectorAll('select, button, input.participant-select').forEach((el) => {
|
||||||
|
el.disabled = !enabled;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const table = ELEMENT_SELECTOR.participantTable();
|
||||||
|
if (table) {
|
||||||
|
table.querySelectorAll('select, button').forEach((el) => {
|
||||||
|
el.disabled = !enabled;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
syncSettingOverrideInputs();
|
||||||
|
syncParticipantOverride();
|
||||||
|
|
||||||
|
const typeSelect = ELEMENT_SELECTOR.participantSelectionType();
|
||||||
|
const addButton = ELEMENT_SELECTOR.participantAddButton();
|
||||||
|
if (!typeSelect || !addButton || !ELEMENT_SELECTOR.participantData()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
typeSelect.addEventListener('change', (e) => updateSelectionFromType(e.target));
|
||||||
|
addButton.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
participantAddFromCurrentSelection();
|
||||||
|
});
|
||||||
|
document.querySelectorAll('input[name="participantsmode_state"]').forEach((input) => {
|
||||||
|
input.addEventListener('change', syncParticipantOverride);
|
||||||
|
});
|
||||||
|
updateSelectionFromType(typeSelect);
|
||||||
|
participantListInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: init
|
||||||
|
};
|
||||||
|
});
|
||||||
+418
@@ -0,0 +1,418 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module mod_meetletacbfree/settings_access_reset
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(['core/ajax', 'mod_meetletacbfree/meetlet_modal_scroll'], function(Ajax, MeetletModalScroll) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var modalState = {
|
||||||
|
open: false,
|
||||||
|
resolve: null,
|
||||||
|
lastFocus: null,
|
||||||
|
onKey: null,
|
||||||
|
mode: 'confirm',
|
||||||
|
pendingSection: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
function modalScroll() {
|
||||||
|
return MeetletModalScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
function byId(id) {
|
||||||
|
return document.getElementById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStrings(root) {
|
||||||
|
if (!root) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.parse(root.getAttribute('data-strings') || '{}') || {};
|
||||||
|
} catch (e) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sectionContainer(section) {
|
||||||
|
if (section === 'rolemapping') {
|
||||||
|
return document.querySelector('[data-meetlet-rolemapping]');
|
||||||
|
}
|
||||||
|
if (section === 'featureaccess') {
|
||||||
|
return document.querySelector('[data-meetlet-access-matrix]');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyRoleMatrix(container, matrix) {
|
||||||
|
if (!container || !matrix) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object.keys(matrix).forEach(function (rowkey) {
|
||||||
|
var row = container.querySelector('[data-role-row="' + rowkey + '"]');
|
||||||
|
if (!row) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var val = matrix[rowkey];
|
||||||
|
var radio = row.querySelector('input[type="radio"][value="' + val + '"]');
|
||||||
|
if (radio) {
|
||||||
|
radio.checked = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyFeatureMatrix(container, matrix) {
|
||||||
|
if (!container || !matrix) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
container.querySelectorAll('.meetlet-access-check').forEach(function (cb) {
|
||||||
|
var role = cb.getAttribute('data-role');
|
||||||
|
var feature = cb.getAttribute('data-feature');
|
||||||
|
var on = matrix[role] && matrix[role][feature];
|
||||||
|
cb.checked = !!on;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderModalMessage(bodyEl, mode, message) {
|
||||||
|
if (!bodyEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bodyEl.textContent = '';
|
||||||
|
if (mode === 'busy') {
|
||||||
|
var wrap = document.createElement('div');
|
||||||
|
wrap.className = 'meetlet-modal__busy';
|
||||||
|
var spinner = document.createElement('div');
|
||||||
|
spinner.className = 'meetlet-modal__spinner';
|
||||||
|
spinner.setAttribute('aria-hidden', 'true');
|
||||||
|
var text = document.createElement('p');
|
||||||
|
text.className = 'meetlet-modal__lead';
|
||||||
|
text.textContent = message || '';
|
||||||
|
wrap.appendChild(spinner);
|
||||||
|
wrap.appendChild(text);
|
||||||
|
bodyEl.appendChild(wrap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var lead = document.createElement('p');
|
||||||
|
lead.className = 'meetlet-modal__lead';
|
||||||
|
lead.textContent = message || '';
|
||||||
|
bodyEl.appendChild(lead);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal(confirmed) {
|
||||||
|
var modal = byId('meetlet-access-reset-modal');
|
||||||
|
if (!modal || !modalState.open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modal.setAttribute('hidden', 'hidden');
|
||||||
|
modal.style.display = 'none';
|
||||||
|
modal.classList.remove('is-busy');
|
||||||
|
modal.setAttribute('aria-busy', 'false');
|
||||||
|
modalState.open = false;
|
||||||
|
modalScroll().unlock();
|
||||||
|
if (modalState.onKey) {
|
||||||
|
document.removeEventListener('keydown', modalState.onKey, true);
|
||||||
|
modalState.onKey = null;
|
||||||
|
}
|
||||||
|
var resolve = modalState.resolve;
|
||||||
|
modalState.resolve = null;
|
||||||
|
modalState.pendingSection = null;
|
||||||
|
if (resolve) {
|
||||||
|
resolve(!!confirmed);
|
||||||
|
}
|
||||||
|
if (modalState.lastFocus && typeof modalState.lastFocus.focus === 'function') {
|
||||||
|
modalScroll().focusNoScroll(modalState.lastFocus);
|
||||||
|
}
|
||||||
|
modalState.lastFocus = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openModal(opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
var mode = opts.mode === 'alert' ? 'alert' : (opts.mode === 'busy' ? 'busy' : 'confirm');
|
||||||
|
var modal = byId('meetlet-access-reset-modal');
|
||||||
|
if (!modal) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modalState.open) {
|
||||||
|
if (modalState.mode === 'busy' && mode === 'alert') {
|
||||||
|
modalState.resolve = null;
|
||||||
|
} else {
|
||||||
|
closeModal(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var str = opts.strings || {};
|
||||||
|
var titleEl = byId('meetlet-access-reset-modal-title');
|
||||||
|
var bodyEl = byId('meetlet-access-reset-modal-body');
|
||||||
|
var confirmBtn = byId('meetlet-access-reset-modal-confirm');
|
||||||
|
var cancelBtn = modal.querySelector('[data-meetlet-modal-dismiss].btn');
|
||||||
|
var closeBtn = modal.querySelector('.meetlet-modal__close');
|
||||||
|
|
||||||
|
if (titleEl) {
|
||||||
|
titleEl.textContent = opts.title || '';
|
||||||
|
}
|
||||||
|
renderModalMessage(bodyEl, mode, opts.message || '');
|
||||||
|
modal.classList.toggle('is-busy', mode === 'busy');
|
||||||
|
modal.setAttribute('aria-busy', mode === 'busy' ? 'true' : 'false');
|
||||||
|
|
||||||
|
if (confirmBtn) {
|
||||||
|
confirmBtn.textContent = opts.confirmLabel || (mode === 'alert' ? (str.modal_ok || 'OK') : (str.confirm_reset || 'Reset'));
|
||||||
|
confirmBtn.className = opts.confirmClass || (mode === 'alert' ? 'btn btn-primary' : 'btn btn-outline-danger');
|
||||||
|
confirmBtn.hidden = mode === 'busy';
|
||||||
|
confirmBtn.disabled = mode === 'busy';
|
||||||
|
confirmBtn.style.display = mode === 'busy' ? 'none' : '';
|
||||||
|
}
|
||||||
|
if (cancelBtn) {
|
||||||
|
cancelBtn.textContent = str.modal_cancel || 'Cancel';
|
||||||
|
cancelBtn.hidden = mode !== 'confirm';
|
||||||
|
cancelBtn.style.display = mode !== 'confirm' ? 'none' : '';
|
||||||
|
}
|
||||||
|
if (closeBtn) {
|
||||||
|
if (str.modal_close) {
|
||||||
|
closeBtn.setAttribute('aria-label', str.modal_close);
|
||||||
|
}
|
||||||
|
closeBtn.hidden = mode === 'busy';
|
||||||
|
closeBtn.style.display = mode === 'busy' ? 'none' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = document.documentElement.getAttribute('dir') || 'ltr';
|
||||||
|
modal.setAttribute('dir', dir);
|
||||||
|
var dialog = modal.querySelector('.meetlet-modal__dialog');
|
||||||
|
if (dialog) {
|
||||||
|
dialog.setAttribute('dir', dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.mode = mode;
|
||||||
|
if (!modalState.lastFocus) {
|
||||||
|
modalState.lastFocus = document.activeElement;
|
||||||
|
}
|
||||||
|
modalScroll().lock();
|
||||||
|
modal.removeAttribute('hidden');
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
modalState.open = true;
|
||||||
|
|
||||||
|
if (modalState.onKey) {
|
||||||
|
document.removeEventListener('keydown', modalState.onKey, true);
|
||||||
|
}
|
||||||
|
modalState.onKey = function (e) {
|
||||||
|
if (!modalState.open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
if (modalState.mode === 'busy') {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
closeModal(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.key !== 'Tab' || modalState.mode === 'busy') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var focusable = modal.querySelectorAll(
|
||||||
|
'button:not([disabled]):not([hidden]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
||||||
|
);
|
||||||
|
var visible = [];
|
||||||
|
for (var i = 0; i < focusable.length; i++) {
|
||||||
|
if (focusable[i].style.display === 'none') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visible.push(focusable[i]);
|
||||||
|
}
|
||||||
|
if (!visible.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var first = visible[0];
|
||||||
|
var last = visible[visible.length - 1];
|
||||||
|
if (e.shiftKey && document.activeElement === first) {
|
||||||
|
e.preventDefault();
|
||||||
|
modalScroll().focusNoScroll(last);
|
||||||
|
} else if (!e.shiftKey && document.activeElement === last) {
|
||||||
|
e.preventDefault();
|
||||||
|
modalScroll().focusNoScroll(first);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', modalState.onKey, true);
|
||||||
|
|
||||||
|
if (mode !== 'busy' && confirmBtn && !confirmBtn.hidden) {
|
||||||
|
window.setTimeout(function () {
|
||||||
|
modalScroll().focusNoScroll(confirmBtn);
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'busy') {
|
||||||
|
modalState.resolve = null;
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
modalState.resolve = resolve;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindModal() {
|
||||||
|
var modal = byId('meetlet-access-reset-modal');
|
||||||
|
if (!modal || modal.dataset.bound === '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modal.dataset.bound = '1';
|
||||||
|
modal.addEventListener('click', function (e) {
|
||||||
|
if (modalState.mode === 'busy') {
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.target.closest('[data-meetlet-modal-dismiss]')) {
|
||||||
|
e.preventDefault();
|
||||||
|
closeModal(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var confirmBtn = byId('meetlet-access-reset-modal-confirm');
|
||||||
|
if (confirmBtn) {
|
||||||
|
confirmBtn.addEventListener('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (modalState.mode === 'busy') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
closeModal(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function postReset(root, section, str) {
|
||||||
|
return Ajax.call([{
|
||||||
|
methodname: 'mod_meetletacbfree_reset_access_settings',
|
||||||
|
args: {section: section}
|
||||||
|
}])[0].then(function(data) {
|
||||||
|
if (data && data.matrixjson) {
|
||||||
|
try {
|
||||||
|
data.matrix = JSON.parse(data.matrixjson);
|
||||||
|
} catch (e) {
|
||||||
|
data.matrix = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmCopy(section, str) {
|
||||||
|
if (section === 'rolemapping') {
|
||||||
|
return {
|
||||||
|
title: str.confirm_rolemapping_title || '',
|
||||||
|
message: str.confirm_rolemapping || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
title: str.confirm_featureaccess_title || '',
|
||||||
|
message: str.confirm_featureaccess || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function successCopy(section, str, serverMessage) {
|
||||||
|
if (serverMessage) {
|
||||||
|
return serverMessage;
|
||||||
|
}
|
||||||
|
if (section === 'rolemapping') {
|
||||||
|
return str.success_rolemapping || '';
|
||||||
|
}
|
||||||
|
return str.success_featureaccess || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleResetClick(btn, root, str) {
|
||||||
|
var section = btn.getAttribute('data-meetlet-reset-section') || '';
|
||||||
|
if (!section) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var copy = confirmCopy(section, str);
|
||||||
|
openModal({
|
||||||
|
mode: 'confirm',
|
||||||
|
strings: str,
|
||||||
|
title: copy.title,
|
||||||
|
message: copy.message,
|
||||||
|
confirmClass: 'btn btn-outline-danger',
|
||||||
|
}).then(function (confirmed) {
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openModal({
|
||||||
|
mode: 'busy',
|
||||||
|
strings: str,
|
||||||
|
title: str.busy || '',
|
||||||
|
message: str.busy || '',
|
||||||
|
});
|
||||||
|
btn.disabled = true;
|
||||||
|
postReset(root, section, str).then(function (data) {
|
||||||
|
btn.disabled = false;
|
||||||
|
if (!data || !data.success) {
|
||||||
|
openModal({
|
||||||
|
mode: 'alert',
|
||||||
|
strings: str,
|
||||||
|
title: str.error || 'Error',
|
||||||
|
message: (data && data.error) || str.error || 'Error',
|
||||||
|
confirmClass: 'btn btn-primary',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var container = sectionContainer(section);
|
||||||
|
if (section === 'rolemapping') {
|
||||||
|
applyRoleMatrix(container, data.matrix);
|
||||||
|
} else {
|
||||||
|
applyFeatureMatrix(container, data.matrix);
|
||||||
|
}
|
||||||
|
openModal({
|
||||||
|
mode: 'alert',
|
||||||
|
strings: str,
|
||||||
|
title: copy.title,
|
||||||
|
message: successCopy(section, str, data.message),
|
||||||
|
confirmClass: 'btn btn-primary',
|
||||||
|
});
|
||||||
|
}).catch(function () {
|
||||||
|
btn.disabled = false;
|
||||||
|
openModal({
|
||||||
|
mode: 'alert',
|
||||||
|
strings: str,
|
||||||
|
title: str.error || 'Error',
|
||||||
|
message: str.error || 'Error',
|
||||||
|
confirmClass: 'btn btn-primary',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var root = byId('meetlet-access-reset-root');
|
||||||
|
if (!root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var str = parseStrings(root);
|
||||||
|
bindModal();
|
||||||
|
document.addEventListener('click', function (e) {
|
||||||
|
var btn = e.target.closest('[data-meetlet-reset-section]');
|
||||||
|
if (!btn || btn.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
handleResetClick(btn, root, str);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init: init};
|
||||||
|
});
|
||||||
Vendored
+324
@@ -0,0 +1,324 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module mod_meetletacbfree/settings_conntest
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(['core/ajax'], function(Ajax) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function ready(fn) {
|
||||||
|
if (document.readyState !== 'loading') {
|
||||||
|
fn();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function el(tag, cls) {
|
||||||
|
var node = document.createElement(tag);
|
||||||
|
if (cls) {
|
||||||
|
node.className = cls;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function text(tag, cls, value) {
|
||||||
|
var node = el(tag, cls);
|
||||||
|
node.textContent = value || '';
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLabels(raw) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(raw || '{}') || {};
|
||||||
|
} catch (e) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setProgress(root, current, total, strings) {
|
||||||
|
var fill = root.querySelector('[data-meetlet-progress-fill]');
|
||||||
|
var label = root.querySelector('[data-meetlet-progress-label]');
|
||||||
|
var pct = total ? Math.round((current / total) * 100) : 0;
|
||||||
|
if (fill) {
|
||||||
|
fill.style.width = pct + '%';
|
||||||
|
}
|
||||||
|
if (label) {
|
||||||
|
label.textContent = strings.progress
|
||||||
|
.replace('{$a->current}', String(current))
|
||||||
|
.replace('{$a->total}', String(total))
|
||||||
|
.replace('{$a->percent}', String(pct));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCard(index, total, step, label, strings) {
|
||||||
|
var wrap = el('div', 'meetlet-conntest__card meetlet-conntest__card--pending');
|
||||||
|
wrap.setAttribute('data-step', step);
|
||||||
|
wrap.setAttribute('data-index', String(index));
|
||||||
|
|
||||||
|
var top = el('div', 'meetlet-conntest__rowtop');
|
||||||
|
top.appendChild(text('span', 'meetlet-conntest__num', String(index + 1)));
|
||||||
|
top.appendChild(el('span', 'meetlet-dot meetlet-dot--pending'));
|
||||||
|
top.appendChild(text('span', 'meetlet-conntest__label', label));
|
||||||
|
top.appendChild(text('span', 'meetlet-pill meetlet-pill--pending', strings.waiting));
|
||||||
|
wrap.appendChild(top);
|
||||||
|
wrap.appendChild(text('div', 'meetlet-conntest__detail meetlet-conntest__card-detail', ''));
|
||||||
|
return wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCardState(card, state, strings, detail) {
|
||||||
|
card.className = 'meetlet-conntest__card meetlet-conntest__card--' + state;
|
||||||
|
var dot = card.querySelector('.meetlet-dot');
|
||||||
|
var pill = card.querySelector('.meetlet-pill');
|
||||||
|
var detailNode = card.querySelector('.meetlet-conntest__card-detail');
|
||||||
|
if (dot) {
|
||||||
|
dot.className = 'meetlet-dot meetlet-dot--' + state;
|
||||||
|
}
|
||||||
|
if (pill) {
|
||||||
|
pill.className = 'meetlet-pill meetlet-pill--' + state;
|
||||||
|
if (state === 'pending') {
|
||||||
|
pill.textContent = strings.waiting;
|
||||||
|
} else if (state === 'running') {
|
||||||
|
pill.textContent = strings.runningstep;
|
||||||
|
} else if (state === 'ok') {
|
||||||
|
pill.textContent = strings.ok;
|
||||||
|
} else if (state === 'fail') {
|
||||||
|
pill.textContent = strings.fail;
|
||||||
|
} else if (state === 'skipped') {
|
||||||
|
pill.textContent = strings.skipped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (detailNode) {
|
||||||
|
detailNode.textContent = detail || '';
|
||||||
|
detailNode.style.display = detail ? '' : 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSubResults(card, subresults, strings) {
|
||||||
|
var wrap = card.querySelector('[data-meetlet-subresults]');
|
||||||
|
if (!wrap) {
|
||||||
|
wrap = el('div', 'meetlet-conntest__subs');
|
||||||
|
wrap.setAttribute('data-meetlet-subresults', '1');
|
||||||
|
card.appendChild(wrap);
|
||||||
|
}
|
||||||
|
wrap.innerHTML = '';
|
||||||
|
if (!subresults || !subresults.length) {
|
||||||
|
wrap.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wrap.style.display = '';
|
||||||
|
subresults.forEach(function (sub) {
|
||||||
|
var substate = 'fail';
|
||||||
|
var pillText = strings.fail;
|
||||||
|
if (sub.skipped) {
|
||||||
|
substate = 'skipped';
|
||||||
|
pillText = strings.skipped;
|
||||||
|
} else if (sub.ok) {
|
||||||
|
substate = 'ok';
|
||||||
|
pillText = strings.ok;
|
||||||
|
}
|
||||||
|
var subcard = el('div', 'meetlet-conntest__sub meetlet-conntest__sub--' + substate);
|
||||||
|
var top = el('div', 'meetlet-conntest__rowtop');
|
||||||
|
top.appendChild(el('span', 'meetlet-dot meetlet-dot--' + substate));
|
||||||
|
top.appendChild(text('span', 'meetlet-conntest__sublabel', sub.label || sub.id || ''));
|
||||||
|
top.appendChild(text('span', 'meetlet-pill meetlet-pill--' + substate, pillText));
|
||||||
|
subcard.appendChild(top);
|
||||||
|
if (sub.detail) {
|
||||||
|
subcard.appendChild(text('div', 'meetlet-conntest__detail', sub.detail));
|
||||||
|
}
|
||||||
|
wrap.appendChild(subcard);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var SAVE_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">'
|
||||||
|
+ '<path fill="currentColor" d="M17 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V7l-4-4zm-5 16a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm3-10H5V5h10v4z"/>'
|
||||||
|
+ '</svg>';
|
||||||
|
|
||||||
|
document.querySelectorAll('#adminsettings .offset-sm-3').forEach(function (el) {
|
||||||
|
if (!el.querySelector('button.btn-primary, input.btn-primary[type="submit"]')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
el.classList.remove('offset-sm-3', 'col-sm-3', 'col-md-3');
|
||||||
|
el.classList.add('col-12', 'meetlet-savebar');
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll(
|
||||||
|
'#adminsettings button.btn-primary[type="submit"],'
|
||||||
|
+ ' #adminsettings input.btn-primary[type="submit"]'
|
||||||
|
).forEach(function (btn) {
|
||||||
|
if (btn.getAttribute('data-meetlet-save-icon') === '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
btn.setAttribute('data-meetlet-save-icon', '1');
|
||||||
|
btn.classList.add('meetlet-btn-with-icon');
|
||||||
|
if (btn.tagName === 'INPUT') {
|
||||||
|
var replacement = document.createElement('button');
|
||||||
|
replacement.type = 'submit';
|
||||||
|
replacement.name = btn.name || 'submitbutton';
|
||||||
|
replacement.value = btn.value || '';
|
||||||
|
replacement.id = btn.id || '';
|
||||||
|
replacement.className = btn.className;
|
||||||
|
replacement.setAttribute('data-meetlet-save-icon', '1');
|
||||||
|
replacement.innerHTML = SAVE_ICON
|
||||||
|
+ '<span class="meetlet-btn-with-icon__label">'
|
||||||
|
+ (btn.value || btn.getAttribute('value') || 'Save changes')
|
||||||
|
+ '</span>';
|
||||||
|
btn.parentNode.replaceChild(replacement, btn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (btn.querySelector('svg')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var label = (btn.textContent || '').trim() || 'Save changes';
|
||||||
|
btn.textContent = '';
|
||||||
|
btn.insertAdjacentHTML('afterbegin', SAVE_ICON);
|
||||||
|
var span = document.createElement('span');
|
||||||
|
span.className = 'meetlet-btn-with-icon__label';
|
||||||
|
span.textContent = label;
|
||||||
|
btn.appendChild(span);
|
||||||
|
});
|
||||||
|
|
||||||
|
var root = document.getElementById('meetlet-acb-conntest');
|
||||||
|
if (!root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var btn = root.querySelector('[data-meetlet-run-test]');
|
||||||
|
var grid = root.querySelector('[data-meetlet-test-grid]');
|
||||||
|
var status = root.querySelector('[data-meetlet-test-status]');
|
||||||
|
var progressWrap = root.querySelector('[data-meetlet-progress]');
|
||||||
|
if (!btn || !grid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var steps = (root.getAttribute('data-steps') || 'login,folders,user,sso,cleanup')
|
||||||
|
.split(',')
|
||||||
|
.map(function (s) { return s.trim(); })
|
||||||
|
.filter(Boolean);
|
||||||
|
var labels = parseLabels(root.getAttribute('data-step-labels'));
|
||||||
|
var strings = {
|
||||||
|
ok: root.getAttribute('data-str-ok') || 'OK',
|
||||||
|
fail: root.getAttribute('data-str-fail') || 'Fail',
|
||||||
|
running: root.getAttribute('data-str-running') || 'Running…',
|
||||||
|
idle: root.getAttribute('data-str-idle') || '',
|
||||||
|
waiting: root.getAttribute('data-str-waiting') || 'Waiting',
|
||||||
|
runningstep: root.getAttribute('data-str-runningstep') || 'Testing',
|
||||||
|
skipped: root.getAttribute('data-str-skipped') || 'Skipped',
|
||||||
|
progress: root.getAttribute('data-str-progress') || 'Step {$a->current} of {$a->total} ({$a->percent}%)'
|
||||||
|
};
|
||||||
|
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
if (btn.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
btn.disabled = true;
|
||||||
|
grid.innerHTML = '';
|
||||||
|
if (progressWrap) {
|
||||||
|
progressWrap.hidden = false;
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
status.textContent = strings.running;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cards = {};
|
||||||
|
steps.forEach(function (step, index) {
|
||||||
|
var label = labels[step] || step;
|
||||||
|
var card = buildCard(index, steps.length, step, label, strings);
|
||||||
|
setCardState(card, 'pending', strings, '');
|
||||||
|
grid.appendChild(card);
|
||||||
|
cards[step] = card;
|
||||||
|
});
|
||||||
|
setProgress(root, 0, steps.length, strings);
|
||||||
|
|
||||||
|
(async function () {
|
||||||
|
var skipToCleanup = false;
|
||||||
|
var completed = 0;
|
||||||
|
for (var i = 0; i < steps.length; i++) {
|
||||||
|
var step = steps[i];
|
||||||
|
var card = cards[step];
|
||||||
|
|
||||||
|
if (skipToCleanup && step !== 'cleanup') {
|
||||||
|
setCardState(card, 'skipped', strings, '');
|
||||||
|
completed++;
|
||||||
|
setProgress(root, completed, steps.length, strings);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCardState(card, 'running', strings, '');
|
||||||
|
setProgress(root, completed, steps.length, strings);
|
||||||
|
|
||||||
|
var result = await new Promise(function(resolve, reject) {
|
||||||
|
Ajax.call([{
|
||||||
|
methodname: 'mod_meetletacbfree_run_conntest_step',
|
||||||
|
args: {step: step}
|
||||||
|
}])[0].then(resolve).catch(reject);
|
||||||
|
});
|
||||||
|
setCardState(
|
||||||
|
card,
|
||||||
|
result.ok ? 'ok' : 'fail',
|
||||||
|
strings,
|
||||||
|
result.detail || ''
|
||||||
|
);
|
||||||
|
if (result.subresults && result.subresults.length) {
|
||||||
|
setSubResults(card, result.subresults, strings);
|
||||||
|
}
|
||||||
|
completed++;
|
||||||
|
setProgress(root, completed, steps.length, strings);
|
||||||
|
await sleep(220);
|
||||||
|
|
||||||
|
if (result.abort || (result.done && step !== 'cleanup')) {
|
||||||
|
skipToCleanup = true;
|
||||||
|
if (result.done && step !== 'cleanup') {
|
||||||
|
// Mark remaining non-cleanup as skipped, then stop if no cleanup needed.
|
||||||
|
for (var j = i + 1; j < steps.length; j++) {
|
||||||
|
if (steps[j] === 'cleanup') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
setCardState(cards[steps[j]], 'skipped', strings, '');
|
||||||
|
completed++;
|
||||||
|
}
|
||||||
|
// Still run cleanup if present later in loop via skipToCleanup.
|
||||||
|
}
|
||||||
|
if (step === 'cleanup') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setProgress(root, steps.length, steps.length, strings);
|
||||||
|
})().catch(function (err) {
|
||||||
|
var firstPending = grid.querySelector('.meetlet-conntest__card--pending, .meetlet-conntest__card--running');
|
||||||
|
if (firstPending) {
|
||||||
|
setCardState(firstPending, 'fail', strings, String(err && err.message ? err.message : err));
|
||||||
|
}
|
||||||
|
}).finally(function () {
|
||||||
|
btn.disabled = false;
|
||||||
|
if (status) {
|
||||||
|
status.textContent = strings.idle;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init: init};
|
||||||
|
});
|
||||||
+336
@@ -0,0 +1,336 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module mod_meetletacbfree/settings_registration
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(['core/ajax', 'mod_meetletacbfree/meetlet_modal_scroll'], function(Ajax, MeetletModalScroll) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function byId(id) {
|
||||||
|
return document.getElementById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFlash(el, ok, message) {
|
||||||
|
if (!el) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
el.className = 'alert mt-3 ' + (ok ? 'alert-success' : 'alert-danger');
|
||||||
|
el.textContent = message || '';
|
||||||
|
el.classList.toggle('d-none', !message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActiveUi(active) {
|
||||||
|
var inactive = byId('meetlet-registration-actions-inactive');
|
||||||
|
var activeEl = byId('meetlet-registration-actions-active');
|
||||||
|
var product = byId('meetlet-registration-product');
|
||||||
|
if (inactive) {
|
||||||
|
inactive.classList.toggle('d-none', !!active);
|
||||||
|
}
|
||||||
|
if (activeEl) {
|
||||||
|
activeEl.classList.toggle('d-none', !active);
|
||||||
|
}
|
||||||
|
if (product) {
|
||||||
|
product.classList.toggle('d-none', !active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSummary(summary) {
|
||||||
|
if (!summary) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var badge = byId('meetlet-registration-status-badge');
|
||||||
|
var cfg = byId('meetlet-registration-config');
|
||||||
|
if (badge) {
|
||||||
|
badge.className = 'badge meetlet-license-badge '
|
||||||
|
+ (summary.active ? 'meetlet-license-badge--active' : 'meetlet-license-badge--inactive');
|
||||||
|
badge.textContent = summary.status_label
|
||||||
|
|| (summary.active
|
||||||
|
? (cfg && cfg.dataset.labelActive) || 'Active'
|
||||||
|
: (cfg && cfg.dataset.labelInactive) || 'Inactive');
|
||||||
|
}
|
||||||
|
var domain = byId('meetlet-registration-domain');
|
||||||
|
if (domain && cfg && cfg.dataset.labelDomain) {
|
||||||
|
domain.textContent = cfg.dataset.labelDomain.replace('__V__', summary.domain || '—');
|
||||||
|
}
|
||||||
|
var installation = byId('meetlet-registration-installation');
|
||||||
|
if (installation && cfg && cfg.dataset.labelInstallation) {
|
||||||
|
installation.textContent = cfg.dataset.labelInstallation.replace('__V__', summary.installation_id || '—');
|
||||||
|
}
|
||||||
|
var product = byId('meetlet-registration-product');
|
||||||
|
if (product) {
|
||||||
|
product.textContent = summary.product_label || '';
|
||||||
|
}
|
||||||
|
var err = byId('meetlet-registration-error');
|
||||||
|
if (err) {
|
||||||
|
if (summary.error) {
|
||||||
|
err.textContent = summary.error;
|
||||||
|
err.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
err.textContent = '';
|
||||||
|
err.classList.add('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setActiveUi(!!summary.active);
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentApiUrl(cfg) {
|
||||||
|
var input = cfg && cfg.dataset.apiInput ? document.getElementById(cfg.dataset.apiInput) : null;
|
||||||
|
if (input && input.value) {
|
||||||
|
return input.value.trim();
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var modalState = {
|
||||||
|
open: false,
|
||||||
|
resolve: null,
|
||||||
|
lastFocus: null,
|
||||||
|
onKey: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
function ensureModalOnBody(modal) {
|
||||||
|
if (modal && modal.parentNode !== document.body) {
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
}
|
||||||
|
return modal;
|
||||||
|
}
|
||||||
|
|
||||||
|
function modalScroll() {
|
||||||
|
return MeetletModalScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeRegistrationModal(result) {
|
||||||
|
var modal = byId('meetlet-registration-modal');
|
||||||
|
if (!modal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modal.setAttribute('hidden', 'hidden');
|
||||||
|
modal.style.display = 'none';
|
||||||
|
modalScroll().unlock();
|
||||||
|
if (modalState.onKey) {
|
||||||
|
document.removeEventListener('keydown', modalState.onKey, true);
|
||||||
|
modalState.onKey = null;
|
||||||
|
}
|
||||||
|
modalState.open = false;
|
||||||
|
var resolve = modalState.resolve;
|
||||||
|
modalState.resolve = null;
|
||||||
|
if (modalState.lastFocus) {
|
||||||
|
modalScroll().focusNoScroll(modalState.lastFocus);
|
||||||
|
}
|
||||||
|
modalState.lastFocus = null;
|
||||||
|
if (typeof resolve === 'function') {
|
||||||
|
resolve(!!result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRegistrationModal(opts) {
|
||||||
|
var modal = ensureModalOnBody(byId('meetlet-registration-modal'));
|
||||||
|
var cfg = byId('meetlet-registration-config');
|
||||||
|
if (!modal) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
if (modalState.open) {
|
||||||
|
closeRegistrationModal(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var title = byId('meetlet-registration-modal-title');
|
||||||
|
var body = byId('meetlet-registration-modal-body');
|
||||||
|
var confirmBtn = byId('meetlet-registration-modal-confirm');
|
||||||
|
var tpl = opts.templateId ? document.getElementById(opts.templateId) : null;
|
||||||
|
if (!title || !body || !confirmBtn) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
title.textContent = opts.title || '';
|
||||||
|
if (tpl) {
|
||||||
|
body.innerHTML = '';
|
||||||
|
if (tpl.content && tpl.content.cloneNode) {
|
||||||
|
body.appendChild(tpl.content.cloneNode(true));
|
||||||
|
} else {
|
||||||
|
body.innerHTML = tpl.innerHTML;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
body.innerHTML = '';
|
||||||
|
}
|
||||||
|
confirmBtn.textContent = opts.confirmLabel || '';
|
||||||
|
confirmBtn.className = 'btn ' + (opts.danger ? 'btn-outline-danger' : 'btn-primary');
|
||||||
|
|
||||||
|
modalState.lastFocus = document.activeElement;
|
||||||
|
modalScroll().lock();
|
||||||
|
modal.removeAttribute('hidden');
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
modalState.open = true;
|
||||||
|
|
||||||
|
modalState.onKey = function (e) {
|
||||||
|
if (!modalState.open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
closeRegistrationModal(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.key !== 'Tab') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var focusable = modal.querySelectorAll(
|
||||||
|
'button:not([disabled]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
||||||
|
);
|
||||||
|
if (!focusable.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var first = focusable[0];
|
||||||
|
var last = focusable[focusable.length - 1];
|
||||||
|
if (e.shiftKey && document.activeElement === first) {
|
||||||
|
e.preventDefault();
|
||||||
|
modalScroll().focusNoScroll(last);
|
||||||
|
} else if (!e.shiftKey && document.activeElement === last) {
|
||||||
|
e.preventDefault();
|
||||||
|
modalScroll().focusNoScroll(first);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', modalState.onKey, true);
|
||||||
|
|
||||||
|
window.setTimeout(function () {
|
||||||
|
modalScroll().focusNoScroll(confirmBtn);
|
||||||
|
}, 20);
|
||||||
|
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
modalState.resolve = resolve;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function postAction(action) {
|
||||||
|
var cfg = byId('meetlet-registration-config');
|
||||||
|
var flash = byId('meetlet-registration-flash');
|
||||||
|
if (!cfg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setFlash(flash, true, '…');
|
||||||
|
Ajax.call([{
|
||||||
|
methodname: 'mod_meetletacbfree_registration_action',
|
||||||
|
args: {
|
||||||
|
action: action,
|
||||||
|
registration_api_url: currentApiUrl(cfg)
|
||||||
|
}
|
||||||
|
}])[0].then(function(data) {
|
||||||
|
data.summary = {
|
||||||
|
active: !!data.active,
|
||||||
|
status: data.status,
|
||||||
|
status_label: data.status_label,
|
||||||
|
product_label: data.product_label,
|
||||||
|
domain: data.domain,
|
||||||
|
installation_id: data.installation_id,
|
||||||
|
can_deactivate: !!data.can_deactivate
|
||||||
|
};
|
||||||
|
if (data.summary) {
|
||||||
|
renderSummary(data.summary);
|
||||||
|
}
|
||||||
|
if (data.success) {
|
||||||
|
setFlash(flash, true, data.message);
|
||||||
|
if (action === 'activate' || action === 'deactivate') {
|
||||||
|
window.setTimeout(function() {
|
||||||
|
window.location.reload();
|
||||||
|
}, 600);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setFlash(flash, false, data.error || 'Request failed');
|
||||||
|
}
|
||||||
|
}).catch(function(err) {
|
||||||
|
setFlash(flash, false, err && err.message ? err.message : 'Request failed');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindModalChrome() {
|
||||||
|
var modal = byId('meetlet-registration-modal');
|
||||||
|
if (!modal || modal.dataset.bound === '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modal.dataset.bound = '1';
|
||||||
|
modal.addEventListener('click', function (e) {
|
||||||
|
if (e.target.closest('[data-meetlet-modal-dismiss]')) {
|
||||||
|
e.preventDefault();
|
||||||
|
closeRegistrationModal(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var confirmBtn = byId('meetlet-registration-modal-confirm');
|
||||||
|
if (confirmBtn) {
|
||||||
|
confirmBtn.addEventListener('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
closeRegistrationModal(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindDeactivateButton(btn) {
|
||||||
|
if (!btn || btn.dataset.bound === '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var cfg = byId('meetlet-registration-config');
|
||||||
|
btn.dataset.bound = '1';
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
openRegistrationModal({
|
||||||
|
title: (cfg && cfg.dataset.confirmDeactivateTitle) || '',
|
||||||
|
templateId: 'meetlet-registration-tpl-deactivate',
|
||||||
|
confirmLabel: (cfg && cfg.dataset.confirmDeactivateOk) || '',
|
||||||
|
danger: true,
|
||||||
|
}).then(function (ok) {
|
||||||
|
if (ok) {
|
||||||
|
postAction('deactivate');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bind() {
|
||||||
|
var activate = byId('meetlet-registration-activate');
|
||||||
|
var recheck = byId('meetlet-registration-recheck');
|
||||||
|
var deactivate = byId('meetlet-registration-deactivate');
|
||||||
|
var cfg = byId('meetlet-registration-config');
|
||||||
|
if ((!activate && !recheck) || (activate && activate.dataset.bound === '1')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bindModalChrome();
|
||||||
|
if (activate) {
|
||||||
|
activate.dataset.bound = '1';
|
||||||
|
activate.addEventListener('click', function () {
|
||||||
|
openRegistrationModal({
|
||||||
|
title: (cfg && cfg.dataset.confirmActivateTitle) || '',
|
||||||
|
templateId: 'meetlet-registration-tpl-activate',
|
||||||
|
confirmLabel: (cfg && cfg.dataset.confirmActivateOk) || '',
|
||||||
|
danger: false,
|
||||||
|
}).then(function (ok) {
|
||||||
|
if (ok) {
|
||||||
|
postAction('activate');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (recheck) {
|
||||||
|
recheck.dataset.bound = '1';
|
||||||
|
recheck.addEventListener('click', function () {
|
||||||
|
postAction('recheck');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
bindDeactivateButton(deactivate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init: bind};
|
||||||
|
});
|
||||||
Vendored
+160
@@ -0,0 +1,160 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module mod_meetletacbfree/settings_toggles
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
define([], function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function enhanceInput(input) {
|
||||||
|
if (!input || input.getAttribute('data-meetlet-switch') === '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (input.type !== 'checkbox') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
input.setAttribute('data-meetlet-switch', '1');
|
||||||
|
input.classList.add('meetlet-switch__input');
|
||||||
|
|
||||||
|
var compact = input.classList.contains('meetlet-access-check');
|
||||||
|
var switchClass = compact ? 'meetlet-switch meetlet-switch--compact' : 'meetlet-switch';
|
||||||
|
|
||||||
|
var track = document.createElement('span');
|
||||||
|
track.className = 'meetlet-switch__track';
|
||||||
|
track.setAttribute('aria-hidden', 'true');
|
||||||
|
var thumb = document.createElement('span');
|
||||||
|
thumb.className = 'meetlet-switch__thumb';
|
||||||
|
track.appendChild(thumb);
|
||||||
|
|
||||||
|
var parentLabel = input.closest('label');
|
||||||
|
if (parentLabel && parentLabel.contains(input)) {
|
||||||
|
parentLabel.classList.add('meetlet-switch');
|
||||||
|
if (compact) {
|
||||||
|
parentLabel.classList.add('meetlet-switch--compact');
|
||||||
|
}
|
||||||
|
if (input.nextSibling) {
|
||||||
|
parentLabel.insertBefore(track, input.nextSibling);
|
||||||
|
} else {
|
||||||
|
parentLabel.appendChild(track);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrap = document.createElement('label');
|
||||||
|
wrap.className = switchClass;
|
||||||
|
if (input.id) {
|
||||||
|
wrap.setAttribute('for', input.id);
|
||||||
|
}
|
||||||
|
input.parentNode.insertBefore(wrap, input);
|
||||||
|
wrap.appendChild(input);
|
||||||
|
wrap.appendChild(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
var adminRoot = document.querySelector('#adminsettings');
|
||||||
|
if (adminRoot) {
|
||||||
|
adminRoot.querySelectorAll('.form-setting input[type="checkbox"]').forEach(enhanceInput);
|
||||||
|
}
|
||||||
|
document.querySelectorAll('.meetlet-access-check').forEach(enhanceInput);
|
||||||
|
var form = document.getElementById('mform1') || document.querySelector('form.mform');
|
||||||
|
if (form) {
|
||||||
|
form.querySelectorAll(
|
||||||
|
' input[name="autooffline"], input[name="showzip"], input[name="showmp4"],' +
|
||||||
|
' input[name="showmakeoffline"], input[name="showrecordingstab"], input[name="showcontenttab"],' +
|
||||||
|
' input[name="featureaccess_override"], input[name="default_waitforhost"],' +
|
||||||
|
' input[name="waitforhost"], input[name="https"]'
|
||||||
|
).forEach(enhanceInput);
|
||||||
|
}
|
||||||
|
bindHourPicker();
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedHours(wrap) {
|
||||||
|
var hours = [];
|
||||||
|
wrap.querySelectorAll('.meetlet-hourchip.is-on').forEach(function (btn) {
|
||||||
|
hours.push(parseInt(btn.getAttribute('data-hour'), 10));
|
||||||
|
});
|
||||||
|
hours.sort(function (a, b) { return a - b; });
|
||||||
|
return hours;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyHours(wrap, list) {
|
||||||
|
var set = {};
|
||||||
|
(list || []).forEach(function (h) { set[String(h)] = true; });
|
||||||
|
wrap.querySelectorAll('.meetlet-hourchip').forEach(function (btn) {
|
||||||
|
var on = !!set[btn.getAttribute('data-hour')];
|
||||||
|
btn.classList.toggle('is-on', on);
|
||||||
|
btn.setAttribute('aria-pressed', on ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
var input = wrap.querySelector('[data-meetlet-hours-input]');
|
||||||
|
if (input) {
|
||||||
|
input.value = selectedHours(wrap).join(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncHoursEnabled() {
|
||||||
|
var sel = document.querySelector('select[name="s_meetletacbfree_agent_dispatch"]')
|
||||||
|
|| document.getElementById('id_s_meetletacbfree_agent_dispatch');
|
||||||
|
var wrap = document.querySelector('[data-meetlet-hours-wrap]');
|
||||||
|
if (!wrap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var item = wrap.closest('.form-item');
|
||||||
|
var windowMode = !sel || sel.value === 'window';
|
||||||
|
wrap.classList.toggle('is-dimmed', !windowMode);
|
||||||
|
if (item) {
|
||||||
|
item.classList.toggle('is-dimmed', !windowMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindHourPicker() {
|
||||||
|
var wrap = document.querySelector('[data-meetlet-hours-wrap]');
|
||||||
|
if (!wrap || wrap.getAttribute('data-bound') === '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wrap.setAttribute('data-bound', '1');
|
||||||
|
wrap.addEventListener('click', function (e) {
|
||||||
|
var chip = e.target.closest('.meetlet-hourchip');
|
||||||
|
if (chip && wrap.contains(chip)) {
|
||||||
|
e.preventDefault();
|
||||||
|
chip.classList.toggle('is-on');
|
||||||
|
chip.setAttribute('aria-pressed', chip.classList.contains('is-on') ? 'true' : 'false');
|
||||||
|
applyHours(wrap, selectedHours(wrap));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var preset = e.target.closest('.meetlet-hourpreset');
|
||||||
|
if (preset && wrap.contains(preset)) {
|
||||||
|
e.preventDefault();
|
||||||
|
var raw = preset.getAttribute('data-hours') || '';
|
||||||
|
var list = raw === '' ? [] : raw.split(',').map(function (x) { return parseInt(x, 10); });
|
||||||
|
applyHours(wrap, list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var sel = document.querySelector('select[name="s_meetletacbfree_agent_dispatch"]')
|
||||||
|
|| document.getElementById('id_s_meetletacbfree_agent_dispatch');
|
||||||
|
if (sel) {
|
||||||
|
sel.addEventListener('change', syncHoursEnabled);
|
||||||
|
if (document.documentElement.getAttribute('dir') === 'rtl') {
|
||||||
|
sel.setAttribute('dir', 'rtl');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syncHoursEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init: run};
|
||||||
|
});
|
||||||
Vendored
+1052
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,25 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MeetLet feature-access matrix: row/column "select all" toggles.
|
* @module mod_meetletacbfree/featureaccess_matrix
|
||||||
* Page-local (admin settings + mod form). Not AMD.
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
(function() {
|
|
||||||
|
define([], function() {
|
||||||
const bindMatrix = (matrix) => {
|
const bindMatrix = (matrix) => {
|
||||||
if (!matrix || matrix.getAttribute('data-access-bound') === '1') {
|
if (!matrix || matrix.getAttribute('data-access-bound') === '1') {
|
||||||
return;
|
return;
|
||||||
@@ -46,9 +63,5 @@
|
|||||||
document.querySelectorAll('[data-meetlet-access-matrix]').forEach(bindMatrix);
|
document.querySelectorAll('[data-meetlet-access-matrix]').forEach(bindMatrix);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (document.readyState === 'loading') {
|
return {init: init};
|
||||||
document.addEventListener('DOMContentLoaded', init);
|
});
|
||||||
} else {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
@@ -1,19 +1,32 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared scroll lock for MeetLet modals.
|
* @module mod_meetletacbfree/meetlet_modal_scroll
|
||||||
* Uses body overflow + scroll guards — no position:fixed on body (avoids white-page flash).
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
*
|
|
||||||
* @package mod_meetletacbfree
|
|
||||||
* @copyright 2026 MeetLet
|
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
(function (global) {
|
|
||||||
|
define([], function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var scrollLockY = 0;
|
var scrollLockY = 0;
|
||||||
var guardsActive = false;
|
var guardsActive = false;
|
||||||
|
|
||||||
function scrollbarWidth() {
|
function scrollbarWidth() {
|
||||||
return Math.max(0, global.innerWidth - document.documentElement.clientWidth);
|
return Math.max(0, window.innerWidth - document.documentElement.clientWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusNoScroll(el) {
|
function focusNoScroll(el) {
|
||||||
@@ -29,13 +42,13 @@
|
|||||||
|
|
||||||
function restoreScroll() {
|
function restoreScroll() {
|
||||||
if (scrollLockY > 0) {
|
if (scrollLockY > 0) {
|
||||||
global.scrollTo(0, scrollLockY);
|
window.scrollTo(0, scrollLockY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScrollGuard() {
|
function onScrollGuard() {
|
||||||
if (scrollLockY && global.scrollY !== scrollLockY) {
|
if (scrollLockY && window.scrollY !== scrollLockY) {
|
||||||
global.scrollTo(0, scrollLockY);
|
window.scrollTo(0, scrollLockY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,9 +64,9 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
guardsActive = true;
|
guardsActive = true;
|
||||||
global.addEventListener('scroll', onScrollGuard, true);
|
window.addEventListener('scroll', onScrollGuard, true);
|
||||||
global.addEventListener('wheel', onMoveGuard, {passive: false, capture: true});
|
window.addEventListener('wheel', onMoveGuard, {passive: false, capture: true});
|
||||||
global.addEventListener('touchmove', onMoveGuard, {passive: false, capture: true});
|
window.addEventListener('touchmove', onMoveGuard, {passive: false, capture: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
function unbindGuards() {
|
function unbindGuards() {
|
||||||
@@ -61,24 +74,24 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
guardsActive = false;
|
guardsActive = false;
|
||||||
global.removeEventListener('scroll', onScrollGuard, true);
|
window.removeEventListener('scroll', onScrollGuard, true);
|
||||||
global.removeEventListener('wheel', onMoveGuard, true);
|
window.removeEventListener('wheel', onMoveGuard, true);
|
||||||
global.removeEventListener('touchmove', onMoveGuard, true);
|
window.removeEventListener('touchmove', onMoveGuard, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function lockPageScroll() {
|
function lockPageScroll() {
|
||||||
if (document.documentElement.classList.contains('meetlet-modal-open')) {
|
if (document.documentElement.classList.contains('meetlet-modal-open')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scrollLockY = global.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
scrollLockY = window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
||||||
var pad = scrollbarWidth();
|
var pad = scrollbarWidth();
|
||||||
if (pad > 0) {
|
if (pad > 0) {
|
||||||
document.body.style.paddingRight = pad + 'px';
|
document.body.style.paddingRight = pad + 'px';
|
||||||
}
|
}
|
||||||
document.documentElement.classList.add('meetlet-modal-open');
|
document.documentElement.classList.add('meetlet-modal-open');
|
||||||
restoreScroll();
|
restoreScroll();
|
||||||
if (typeof global.requestAnimationFrame === 'function') {
|
if (typeof window.requestAnimationFrame === 'function') {
|
||||||
global.requestAnimationFrame(restoreScroll);
|
window.requestAnimationFrame(restoreScroll);
|
||||||
}
|
}
|
||||||
bindGuards();
|
bindGuards();
|
||||||
}
|
}
|
||||||
@@ -93,18 +106,19 @@
|
|||||||
document.body.style.paddingRight = '';
|
document.body.style.paddingRight = '';
|
||||||
scrollLockY = 0;
|
scrollLockY = 0;
|
||||||
if (y) {
|
if (y) {
|
||||||
global.scrollTo(0, y);
|
window.scrollTo(0, y);
|
||||||
if (typeof global.requestAnimationFrame === 'function') {
|
if (typeof window.requestAnimationFrame === 'function') {
|
||||||
global.requestAnimationFrame(function () {
|
window.requestAnimationFrame(function() {
|
||||||
global.scrollTo(0, y);
|
window.scrollTo(0, y);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global.MeetletModalScroll = {
|
return {
|
||||||
|
init: function() {},
|
||||||
lock: lockPageScroll,
|
lock: lockPageScroll,
|
||||||
unlock: unlockPageScroll,
|
unlock: unlockPageScroll,
|
||||||
focusNoScroll: focusNoScroll,
|
focusNoScroll: focusNoScroll
|
||||||
};
|
};
|
||||||
}(typeof window !== 'undefined' ? window : this));
|
});
|
||||||
@@ -1,7 +1,26 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MeetLet mod form participants — page-local only (not AMD).
|
* @module mod_meetletacbfree/modform
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
require(['core/str', 'core/notification', 'core/templates', 'core/ajax'], function(Str, Notification, Templates, Ajax) {
|
|
||||||
|
define(['core/str', 'core/notification', 'core/templates', 'core/ajax'], function(Str, Notification, Templates, Ajax) {
|
||||||
|
function init() {
|
||||||
const ELEMENT_SELECTOR = {
|
const ELEMENT_SELECTOR = {
|
||||||
participantData: () => document.querySelector('[data-participant-data]'),
|
participantData: () => document.querySelector('[data-participant-data]'),
|
||||||
participantList: () => document.getElementsByName('participants')[0],
|
participantList: () => document.getElementsByName('participants')[0],
|
||||||
@@ -477,4 +496,9 @@ require(['core/str', 'core/notification', 'core/templates', 'core/ajax'], functi
|
|||||||
});
|
});
|
||||||
updateSelectionFromType(typeSelect);
|
updateSelectionFromType(typeSelect);
|
||||||
participantListInit();
|
participantListInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: init
|
||||||
|
};
|
||||||
});
|
});
|
||||||
@@ -1,14 +1,28 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset-to-default for site role mapping and feature-access matrix (admin settings).
|
* @module mod_meetletacbfree/settings_access_reset
|
||||||
*
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
* @package mod_meetletacbfree
|
|
||||||
* @copyright 2026 MeetLet
|
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
(function () {
|
|
||||||
|
define(['core/ajax', 'mod_meetletacbfree/meetlet_modal_scroll'], function(Ajax, MeetletModalScroll) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var modalState = {
|
var modalState = {
|
||||||
open: false,
|
open: false,
|
||||||
resolve: null,
|
resolve: null,
|
||||||
lastFocus: null,
|
lastFocus: null,
|
||||||
@@ -18,15 +32,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
function modalScroll() {
|
function modalScroll() {
|
||||||
return window.MeetletModalScroll || {
|
return MeetletModalScroll;
|
||||||
lock: function () {},
|
|
||||||
unlock: function () {},
|
|
||||||
focusNoScroll: function (el) {
|
|
||||||
if (el && typeof el.focus === 'function') {
|
|
||||||
el.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function byId(id) {
|
function byId(id) {
|
||||||
@@ -292,22 +298,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function postReset(root, section, str) {
|
function postReset(root, section, str) {
|
||||||
var endpoint = root.getAttribute('data-endpoint') || '';
|
return Ajax.call([{
|
||||||
var sesskey = root.getAttribute('data-sesskey') || '';
|
methodname: 'mod_meetletacbfree_reset_access_settings',
|
||||||
if (!endpoint || !sesskey) {
|
args: {section: section}
|
||||||
return Promise.reject(new Error(str.error || 'Error'));
|
}])[0].then(function(data) {
|
||||||
|
if (data && data.matrixjson) {
|
||||||
|
try {
|
||||||
|
data.matrix = JSON.parse(data.matrixjson);
|
||||||
|
} catch (e) {
|
||||||
|
data.matrix = null;
|
||||||
}
|
}
|
||||||
var url = endpoint + (endpoint.indexOf('?') >= 0 ? '&' : '?') +
|
}
|
||||||
'sesskey=' + encodeURIComponent(sesskey) +
|
return data;
|
||||||
'§ion=' + encodeURIComponent(section);
|
|
||||||
return fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'same-origin',
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
},
|
|
||||||
}).then(function (resp) {
|
|
||||||
return resp.json();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,9 +414,5 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === 'loading') {
|
return {init: init};
|
||||||
document.addEventListener('DOMContentLoaded', init);
|
});
|
||||||
} else {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
@@ -1,7 +1,28 @@
|
|||||||
(function () {
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @module mod_meetletacbfree/settings_conntest
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(['core/ajax'], function(Ajax) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function ready(fn) {
|
function ready(fn) {
|
||||||
if (document.readyState !== 'loading') {
|
if (document.readyState !== 'loading') {
|
||||||
fn();
|
fn();
|
||||||
} else {
|
} else {
|
||||||
@@ -131,7 +152,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(function () {
|
function init() {
|
||||||
var SAVE_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">'
|
var SAVE_ICON = '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">'
|
||||||
+ '<path fill="currentColor" d="M17 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V7l-4-4zm-5 16a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm3-10H5V5h10v4z"/>'
|
+ '<path fill="currentColor" d="M17 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V7l-4-4zm-5 16a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm3-10H5V5h10v4z"/>'
|
||||||
+ '</svg>';
|
+ '</svg>';
|
||||||
@@ -191,9 +212,6 @@
|
|||||||
if (!btn || !grid) {
|
if (!btn || !grid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var endpoint = root.getAttribute('data-endpoint');
|
|
||||||
var sesskey = root.getAttribute('data-sesskey');
|
|
||||||
var steps = (root.getAttribute('data-steps') || 'login,folders,user,sso,cleanup')
|
var steps = (root.getAttribute('data-steps') || 'login,folders,user,sso,cleanup')
|
||||||
.split(',')
|
.split(',')
|
||||||
.map(function (s) { return s.trim(); })
|
.map(function (s) { return s.trim(); })
|
||||||
@@ -250,15 +268,12 @@
|
|||||||
setCardState(card, 'running', strings, '');
|
setCardState(card, 'running', strings, '');
|
||||||
setProgress(root, completed, steps.length, strings);
|
setProgress(root, completed, steps.length, strings);
|
||||||
|
|
||||||
var url = endpoint +
|
var result = await new Promise(function(resolve, reject) {
|
||||||
(endpoint.indexOf('?') === -1 ? '?' : '&') +
|
Ajax.call([{
|
||||||
'step=' + encodeURIComponent(step) +
|
methodname: 'mod_meetletacbfree_run_conntest_step',
|
||||||
'&sesskey=' + encodeURIComponent(sesskey);
|
args: {step: step}
|
||||||
var response = await fetch(url, {
|
}])[0].then(resolve).catch(reject);
|
||||||
credentials: 'same-origin',
|
|
||||||
headers: { 'Accept': 'application/json' }
|
|
||||||
});
|
});
|
||||||
var result = await response.json();
|
|
||||||
setCardState(
|
setCardState(
|
||||||
card,
|
card,
|
||||||
result.ok ? 'ok' : 'fail',
|
result.ok ? 'ok' : 'fail',
|
||||||
@@ -303,5 +318,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
})();
|
|
||||||
|
return {init: init};
|
||||||
|
});
|
||||||
@@ -1,14 +1,28 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline Ajax site registration on plugin settings tab (Marketplace free edition).
|
* @module mod_meetletacbfree/settings_registration
|
||||||
*
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
* @package mod_meetletacbfree
|
|
||||||
* @copyright 2026 MeetLet
|
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
(function () {
|
|
||||||
|
define(['core/ajax', 'mod_meetletacbfree/meetlet_modal_scroll'], function(Ajax, MeetletModalScroll) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function byId(id) {
|
function byId(id) {
|
||||||
return document.getElementById(id);
|
return document.getElementById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,15 +112,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function modalScroll() {
|
function modalScroll() {
|
||||||
return window.MeetletModalScroll || {
|
return MeetletModalScroll;
|
||||||
lock: function () {},
|
|
||||||
unlock: function () {},
|
|
||||||
focusNoScroll: function (el) {
|
|
||||||
if (el && typeof el.focus === 'function') {
|
|
||||||
el.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeRegistrationModal(result) {
|
function closeRegistrationModal(result) {
|
||||||
@@ -217,37 +223,37 @@
|
|||||||
if (!cfg) {
|
if (!cfg) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var body = new URLSearchParams();
|
|
||||||
body.set('action', action);
|
|
||||||
body.set('sesskey', cfg.dataset.sesskey || '');
|
|
||||||
body.set('registration_api_url', currentApiUrl(cfg));
|
|
||||||
|
|
||||||
setFlash(flash, true, '…');
|
setFlash(flash, true, '…');
|
||||||
fetch(cfg.dataset.ajaxurl, {
|
Ajax.call([{
|
||||||
method: 'POST',
|
methodname: 'mod_meetletacbfree_registration_action',
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
args: {
|
||||||
credentials: 'same-origin',
|
action: action,
|
||||||
body: body.toString(),
|
registration_api_url: currentApiUrl(cfg)
|
||||||
})
|
}
|
||||||
.then(function (res) {
|
}])[0].then(function(data) {
|
||||||
return res.json();
|
data.summary = {
|
||||||
})
|
active: !!data.active,
|
||||||
.then(function (data) {
|
status: data.status,
|
||||||
|
status_label: data.status_label,
|
||||||
|
product_label: data.product_label,
|
||||||
|
domain: data.domain,
|
||||||
|
installation_id: data.installation_id,
|
||||||
|
can_deactivate: !!data.can_deactivate
|
||||||
|
};
|
||||||
if (data.summary) {
|
if (data.summary) {
|
||||||
renderSummary(data.summary);
|
renderSummary(data.summary);
|
||||||
}
|
}
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
setFlash(flash, true, data.message);
|
setFlash(flash, true, data.message);
|
||||||
if (action === 'activate' || action === 'deactivate') {
|
if (action === 'activate' || action === 'deactivate') {
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function() {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}, 600);
|
}, 600);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setFlash(flash, false, data.error || 'Request failed');
|
setFlash(flash, false, data.error || 'Request failed');
|
||||||
}
|
}
|
||||||
})
|
}).catch(function(err) {
|
||||||
.catch(function (err) {
|
|
||||||
setFlash(flash, false, err && err.message ? err.message : 'Request failed');
|
setFlash(flash, false, err && err.message ? err.message : 'Request failed');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -326,9 +332,5 @@
|
|||||||
bindDeactivateButton(deactivate);
|
bindDeactivateButton(deactivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === 'loading') {
|
return {init: bind};
|
||||||
document.addEventListener('DOMContentLoaded', bind);
|
});
|
||||||
} else {
|
|
||||||
bind();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
@@ -1,10 +1,28 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace Moodle checkboxes with MeetLet pill switches (check / cross).
|
* @module mod_meetletacbfree/settings_toggles
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
(function () {
|
|
||||||
|
define([], function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function enhanceInput(input) {
|
function enhanceInput(input) {
|
||||||
if (!input || input.getAttribute('data-meetlet-switch') === '1') {
|
if (!input || input.getAttribute('data-meetlet-switch') === '1') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -138,9 +156,5 @@
|
|||||||
syncHoursEnabled();
|
syncHoursEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === 'loading') {
|
return {init: run};
|
||||||
document.addEventListener('DOMContentLoaded', run);
|
});
|
||||||
} else {
|
|
||||||
run();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
@@ -1,8 +1,26 @@
|
|||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MeetLet activity view — page-local only (not AMD).
|
* @module mod_meetletacbfree/view
|
||||||
* Loaded only on mod/meetletacbfree/view.php via $PAGE->requires->js().
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
require(['core/ajax', 'core/notification', 'core/str'], function(Ajax, Notification, Str) {
|
|
||||||
|
define(['core/ajax', 'core/notification', 'core/str'], function(Ajax, Notification, Str) {
|
||||||
|
function init() {
|
||||||
const SELECTORS = {
|
const SELECTORS = {
|
||||||
config: '[data-meetlet-config]',
|
config: '[data-meetlet-config]',
|
||||||
app: '.meetlet-app',
|
app: '.meetlet-app',
|
||||||
@@ -1026,4 +1044,9 @@ require(['core/ajax', 'core/notification', 'core/str'], function(Ajax, Notificat
|
|||||||
|
|
||||||
startStatusTicker();
|
startStatusTicker();
|
||||||
loadTab('details', false);
|
loadTab('details', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: init
|
||||||
|
};
|
||||||
});
|
});
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
namespace mod_meetletacbfree\admin;
|
namespace mod_meetletacbfree\admin;
|
||||||
|
|
||||||
use mod_meetletacbfree\local\icons;
|
use mod_meetletacbfree\local\icons;
|
||||||
use moodle_url;
|
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
@@ -65,7 +64,7 @@ class access_reset_ui {
|
|||||||
/**
|
/**
|
||||||
* Modal + config root (render once on the access settings tab).
|
* Modal + config root (render once on the access settings tab).
|
||||||
*/
|
*/
|
||||||
public static function page_assets(moodle_url $ajaxurl): string {
|
public static function page_assets(): string {
|
||||||
if (self::$assetsrendered) {
|
if (self::$assetsrendered) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -75,8 +74,6 @@ class access_reset_ui {
|
|||||||
$html = \html_writer::div('', '', [
|
$html = \html_writer::div('', '', [
|
||||||
'id' => 'meetlet-access-reset-root',
|
'id' => 'meetlet-access-reset-root',
|
||||||
'hidden' => 'hidden',
|
'hidden' => 'hidden',
|
||||||
'data-endpoint' => $ajaxurl->out(false),
|
|
||||||
'data-sesskey' => sesskey(),
|
|
||||||
'data-strings' => json_encode($strings, JSON_UNESCAPED_UNICODE),
|
'data-strings' => json_encode($strings, JSON_UNESCAPED_UNICODE),
|
||||||
]);
|
]);
|
||||||
$html .= self::modal_html();
|
$html .= self::modal_html();
|
||||||
|
|||||||
@@ -45,16 +45,9 @@ class setting_registration extends admin_setting {
|
|||||||
$summary = manager::status_summary();
|
$summary = manager::status_summary();
|
||||||
$active = !empty($summary['active']);
|
$active = !empty($summary['active']);
|
||||||
$candeactivate = !empty($summary['can_deactivate']);
|
$candeactivate = !empty($summary['can_deactivate']);
|
||||||
$ajaxurl = new \moodle_url('/mod/meetletacbfree/ajax/registration.php');
|
|
||||||
$scrolljsurl = new \moodle_url('/mod/meetletacbfree/javascript/meetlet_modal_scroll.js', [
|
|
||||||
'v' => '2026090146',
|
|
||||||
]);
|
|
||||||
$jsurl = new \moodle_url('/mod/meetletacbfree/javascript/settings_registration.js', [
|
|
||||||
'v' => '2026090146',
|
|
||||||
]);
|
|
||||||
$privacytaburl = manager::privacy_tab_url();
|
$privacytaburl = manager::privacy_tab_url();
|
||||||
$PAGE->requires->js($scrolljsurl);
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/meetlet_modal_scroll', 'init');
|
||||||
$PAGE->requires->js($jsurl);
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/settings_registration', 'init');
|
||||||
|
|
||||||
$statusbadge = \html_writer::span(
|
$statusbadge = \html_writer::span(
|
||||||
s($summary['status_label'] ?? ($active
|
s($summary['status_label'] ?? ($active
|
||||||
@@ -169,8 +162,6 @@ class setting_registration extends admin_setting {
|
|||||||
\html_writer::div('', 'mt-3 d-none', ['id' => 'meetlet-registration-flash']) .
|
\html_writer::div('', 'mt-3 d-none', ['id' => 'meetlet-registration-flash']) .
|
||||||
\html_writer::div('', '', [
|
\html_writer::div('', '', [
|
||||||
'id' => 'meetlet-registration-config',
|
'id' => 'meetlet-registration-config',
|
||||||
'data-ajaxurl' => $ajaxurl->out(false),
|
|
||||||
'data-sesskey' => sesskey(),
|
|
||||||
'data-api-input' => 'id_s_meetletacbfree_registration_api_url',
|
'data-api-input' => 'id_s_meetletacbfree_registration_api_url',
|
||||||
'data-label-active' => get_string('registration_status_active', 'meetletacbfree'),
|
'data-label-active' => get_string('registration_status_active', 'meetletacbfree'),
|
||||||
'data-label-inactive' => get_string('registration_status_inactive', 'meetletacbfree'),
|
'data-label-inactive' => get_string('registration_status_inactive', 'meetletacbfree'),
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ class settingspage_tabs extends \theme_boost_admin_settingspage_tabs {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function output_html() {
|
public function output_html() {
|
||||||
return $this->herohtml . parent::output_html();
|
global $PAGE;
|
||||||
|
if ($PAGE) {
|
||||||
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/meetlet_modal_scroll', 'init');
|
||||||
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/settings_registration', 'init');
|
||||||
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/settings_conntest', 'init');
|
||||||
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/settings_toggles', 'init');
|
||||||
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/featureaccess_matrix', 'init');
|
||||||
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/settings_access_reset', 'init');
|
||||||
|
}
|
||||||
|
// Scope MeetLet admin CSS under this root (Marketplace CSS guideline).
|
||||||
|
// dir attribute is not processed by Moodle RTLCSS (unlike styles.css values).
|
||||||
|
$attrs = right_to_left() ? ['dir' => 'rtl'] : [];
|
||||||
|
return \html_writer::div(
|
||||||
|
$this->herohtml . parent::output_html(),
|
||||||
|
'meetlet-admin-settings',
|
||||||
|
$attrs
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ class client {
|
|||||||
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
|
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
|
||||||
}
|
}
|
||||||
if ($this->https) {
|
if ($this->https) {
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||||
}
|
}
|
||||||
if (!empty($CFG->proxyhost)) {
|
if (!empty($CFG->proxyhost)) {
|
||||||
@@ -363,7 +363,7 @@ class client {
|
|||||||
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
|
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
|
||||||
}
|
}
|
||||||
if ($this->https) {
|
if ($this->https) {
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||||
}
|
}
|
||||||
if (!empty($CFG->proxyhost)) {
|
if (!empty($CFG->proxyhost)) {
|
||||||
|
|||||||
@@ -1386,7 +1386,8 @@ class service {
|
|||||||
CURLOPT_RETURNTRANSFER => true,
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
CURLOPT_HEADER => true,
|
CURLOPT_HEADER => true,
|
||||||
CURLOPT_FOLLOWLOCATION => true,
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
CURLOPT_SSL_VERIFYPEER => false,
|
CURLOPT_SSL_VERIFYPEER => true,
|
||||||
|
CURLOPT_SSL_VERIFYHOST => 2,
|
||||||
CURLOPT_TIMEOUT => 12,
|
CURLOPT_TIMEOUT => 12,
|
||||||
CURLOPT_COOKIE => $cookie !== '' ? ('BREEZESESSION=' . $cookie) : '',
|
CURLOPT_COOKIE => $cookie !== '' ? ('BREEZESESSION=' . $cookie) : '',
|
||||||
]);
|
]);
|
||||||
|
|||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External: activate / recheck / deactivate MeetLet site registration.
|
||||||
|
*
|
||||||
|
* @package mod_meetletacbfree
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mod_meetletacbfree\external;
|
||||||
|
|
||||||
|
use core_external\external_api;
|
||||||
|
use core_external\external_function_parameters;
|
||||||
|
use core_external\external_single_structure;
|
||||||
|
use core_external\external_value;
|
||||||
|
use mod_meetletacbfree\registration\error_mapper;
|
||||||
|
use mod_meetletacbfree\registration\manager;
|
||||||
|
use mod_meetletacbfree\registration\registration_exception;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax-capable registration actions for admin settings.
|
||||||
|
*/
|
||||||
|
class registration_action extends external_api {
|
||||||
|
public static function execute_parameters(): external_function_parameters {
|
||||||
|
return new external_function_parameters([
|
||||||
|
'action' => new external_value(PARAM_ALPHA, 'activate|recheck|deactivate|status'),
|
||||||
|
'registration_api_url' => new external_value(PARAM_URL, 'Optional API base URL', VALUE_DEFAULT, ''),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function execute(string $action, string $registrationapiurl = ''): array {
|
||||||
|
$params = self::validate_parameters(self::execute_parameters(), [
|
||||||
|
'action' => $action,
|
||||||
|
'registration_api_url' => $registrationapiurl,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$context = \context_system::instance();
|
||||||
|
self::validate_context($context);
|
||||||
|
require_capability('moodle/site:config', $context);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($params['registration_api_url'] !== '') {
|
||||||
|
set_config(manager::CONFIG_API_URL, rtrim($params['registration_api_url'], '/'), 'meetletacbfree');
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = '';
|
||||||
|
if ($params['action'] === 'activate') {
|
||||||
|
manager::activate();
|
||||||
|
$message = get_string('registration_activated', 'meetletacbfree');
|
||||||
|
} else if (in_array($params['action'], ['recheck', 'refresh', 'validate'], true)) {
|
||||||
|
manager::recheck(true);
|
||||||
|
$message = get_string('registration_rechecked', 'meetletacbfree');
|
||||||
|
} else if ($params['action'] === 'deactivate') {
|
||||||
|
manager::deactivate();
|
||||||
|
$message = get_string('registration_deactivated', 'meetletacbfree');
|
||||||
|
} else if ($params['action'] === 'status') {
|
||||||
|
$message = '';
|
||||||
|
} else {
|
||||||
|
throw new \moodle_exception('invalidparameter');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::pack_response(true, $message, '');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$ex = registration_exception::from_throwable($e);
|
||||||
|
$mapped = error_mapper::from_exception($ex);
|
||||||
|
return self::pack_response(
|
||||||
|
false,
|
||||||
|
'',
|
||||||
|
(string)($mapped['message'] ?? $ex->get_api_message())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string,mixed>
|
||||||
|
*/
|
||||||
|
protected static function pack_response(bool $success, string $message, string $error): array {
|
||||||
|
$summary = manager::status_summary();
|
||||||
|
return [
|
||||||
|
'success' => $success,
|
||||||
|
'message' => $message,
|
||||||
|
'error' => $error,
|
||||||
|
'active' => !empty($summary['active']),
|
||||||
|
'status' => (string)($summary['status'] ?? ''),
|
||||||
|
'status_label' => (string)($summary['status_label'] ?? ''),
|
||||||
|
'product_label' => (string)($summary['product_label'] ?? ''),
|
||||||
|
'domain' => (string)($summary['domain'] ?? ''),
|
||||||
|
'installation_id' => (string)($summary['installation_id'] ?? ''),
|
||||||
|
'can_deactivate' => !empty($summary['can_deactivate']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function execute_returns(): external_single_structure {
|
||||||
|
return new external_single_structure([
|
||||||
|
'success' => new external_value(PARAM_BOOL, 'Whether the action succeeded'),
|
||||||
|
'message' => new external_value(PARAM_TEXT, 'Success message'),
|
||||||
|
'error' => new external_value(PARAM_TEXT, 'Error message when unsuccessful'),
|
||||||
|
'active' => new external_value(PARAM_BOOL, 'Whether activation is active'),
|
||||||
|
'status' => new external_value(PARAM_ALPHANUMEXT, 'Raw status code'),
|
||||||
|
'status_label' => new external_value(PARAM_TEXT, 'Human status label'),
|
||||||
|
'product_label' => new external_value(PARAM_TEXT, 'Product label'),
|
||||||
|
'domain' => new external_value(PARAM_TEXT, 'Site domain'),
|
||||||
|
'installation_id' => new external_value(PARAM_TEXT, 'Installation id'),
|
||||||
|
'can_deactivate' => new external_value(PARAM_BOOL, 'Whether deactivate is allowed'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External: reset site role mapping or feature-access matrix to defaults.
|
||||||
|
*
|
||||||
|
* @package mod_meetletacbfree
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mod_meetletacbfree\external;
|
||||||
|
|
||||||
|
use core_external\external_api;
|
||||||
|
use core_external\external_function_parameters;
|
||||||
|
use core_external\external_single_structure;
|
||||||
|
use core_external\external_value;
|
||||||
|
use mod_meetletacbfree\local\featureaccess;
|
||||||
|
use mod_meetletacbfree\local\roles;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax-capable reset of access settings defaults.
|
||||||
|
*/
|
||||||
|
class reset_access_settings extends external_api {
|
||||||
|
public static function execute_parameters(): external_function_parameters {
|
||||||
|
return new external_function_parameters([
|
||||||
|
'section' => new external_value(PARAM_ALPHA, 'Section: rolemapping or featureaccess'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function execute(string $section): array {
|
||||||
|
$params = self::validate_parameters(self::execute_parameters(), ['section' => $section]);
|
||||||
|
$context = \context_system::instance();
|
||||||
|
self::validate_context($context);
|
||||||
|
require_capability('moodle/site:config', $context);
|
||||||
|
|
||||||
|
\mod_meetletacbfree\registration\manager::require_registered();
|
||||||
|
|
||||||
|
if ($params['section'] === 'rolemapping') {
|
||||||
|
$rules = roles::builtin_site_defaults();
|
||||||
|
$json = json_encode($rules, JSON_UNESCAPED_UNICODE);
|
||||||
|
set_config('default_participants', $json, 'meetletacbfree');
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'section' => 'rolemapping',
|
||||||
|
'matrixjson' => json_encode(roles::matrix_from_rules($rules), JSON_UNESCAPED_UNICODE),
|
||||||
|
'message' => get_string('reset_success_rolemapping', 'meetletacbfree'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($params['section'] === 'featureaccess') {
|
||||||
|
$matrix = featureaccess::builtin_defaults();
|
||||||
|
$json = json_encode($matrix, JSON_UNESCAPED_UNICODE);
|
||||||
|
set_config('featureaccess', $json, 'meetletacbfree');
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'section' => 'featureaccess',
|
||||||
|
'matrixjson' => json_encode($matrix, JSON_UNESCAPED_UNICODE),
|
||||||
|
'message' => get_string('reset_success_featureaccess', 'meetletacbfree'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \moodle_exception('invalidparameter');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function execute_returns(): external_single_structure {
|
||||||
|
return new external_single_structure([
|
||||||
|
'success' => new external_value(PARAM_BOOL, 'Whether reset succeeded'),
|
||||||
|
'section' => new external_value(PARAM_ALPHA, 'Section that was reset'),
|
||||||
|
'matrixjson' => new external_value(PARAM_RAW, 'JSON-encoded matrix payload'),
|
||||||
|
'message' => new external_value(PARAM_TEXT, 'Success message'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
+97
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External: run one Adobe Connect connection-test step.
|
||||||
|
*
|
||||||
|
* @package mod_meetletacbfree
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mod_meetletacbfree\external;
|
||||||
|
|
||||||
|
use core_external\external_api;
|
||||||
|
use core_external\external_function_parameters;
|
||||||
|
use core_external\external_multiple_structure;
|
||||||
|
use core_external\external_single_structure;
|
||||||
|
use core_external\external_value;
|
||||||
|
use mod_meetletacbfree\local\conntest_runner;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax-capable connection test step runner for admin settings.
|
||||||
|
*/
|
||||||
|
class run_conntest_step extends external_api {
|
||||||
|
public static function execute_parameters(): external_function_parameters {
|
||||||
|
return new external_function_parameters([
|
||||||
|
'step' => new external_value(PARAM_ALPHA, 'Connection test step name'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function execute(string $step): array {
|
||||||
|
$params = self::validate_parameters(self::execute_parameters(), ['step' => $step]);
|
||||||
|
$context = \context_system::instance();
|
||||||
|
self::validate_context($context);
|
||||||
|
require_capability('moodle/site:config', $context);
|
||||||
|
|
||||||
|
\mod_meetletacbfree\registration\manager::require_registered();
|
||||||
|
|
||||||
|
$result = conntest_runner::run_step($params['step']);
|
||||||
|
$subresults = [];
|
||||||
|
if (!empty($result['subresults']) && is_array($result['subresults'])) {
|
||||||
|
foreach ($result['subresults'] as $sub) {
|
||||||
|
$subresults[] = [
|
||||||
|
'label' => (string)($sub['label'] ?? ''),
|
||||||
|
'ok' => !empty($sub['ok']),
|
||||||
|
'detail' => (string)($sub['detail'] ?? ''),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'step' => (string)($result['step'] ?? $params['step']),
|
||||||
|
'label' => (string)($result['label'] ?? ''),
|
||||||
|
'ok' => !empty($result['ok']),
|
||||||
|
'detail' => (string)($result['detail'] ?? ''),
|
||||||
|
'done' => !empty($result['done']),
|
||||||
|
'abort' => !empty($result['abort']),
|
||||||
|
'subresults' => $subresults,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function execute_returns(): external_single_structure {
|
||||||
|
return new external_single_structure([
|
||||||
|
'step' => new external_value(PARAM_ALPHANUMEXT, 'Step id'),
|
||||||
|
'label' => new external_value(PARAM_TEXT, 'Step label'),
|
||||||
|
'ok' => new external_value(PARAM_BOOL, 'Whether the step passed'),
|
||||||
|
'detail' => new external_value(PARAM_RAW, 'Detail message'),
|
||||||
|
'done' => new external_value(PARAM_BOOL, 'Whether the run is finished'),
|
||||||
|
'abort' => new external_value(PARAM_BOOL, 'Whether remaining steps should abort'),
|
||||||
|
'subresults' => new external_multiple_structure(
|
||||||
|
new external_single_structure([
|
||||||
|
'label' => new external_value(PARAM_TEXT, 'Sub-step label'),
|
||||||
|
'ok' => new external_value(PARAM_BOOL, 'Sub-step ok'),
|
||||||
|
'detail' => new external_value(PARAM_RAW, 'Sub-step detail'),
|
||||||
|
]),
|
||||||
|
'Optional nested results',
|
||||||
|
VALUE_DEFAULT,
|
||||||
|
[]
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -161,7 +161,8 @@ class content {
|
|||||||
curl_setopt_array($ch, [
|
curl_setopt_array($ch, [
|
||||||
CURLOPT_FILE => $fp,
|
CURLOPT_FILE => $fp,
|
||||||
CURLOPT_FOLLOWLOCATION => true,
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
CURLOPT_SSL_VERIFYPEER => false,
|
CURLOPT_SSL_VERIFYPEER => true,
|
||||||
|
CURLOPT_SSL_VERIFYHOST => 2,
|
||||||
CURLOPT_TIMEOUT => 300,
|
CURLOPT_TIMEOUT => 300,
|
||||||
CURLOPT_COOKIE => $session !== '' ? ('BREEZESESSION=' . $session) : '',
|
CURLOPT_COOKIE => $session !== '' ? ('BREEZESESSION=' . $session) : '',
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
// This file is part of Moodle - http://moodle.org/
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structured registration API / local client exception.
|
* Structured registration API / local client exception.
|
||||||
*
|
*
|
||||||
* @package mod_meetletacbfree
|
* @package mod_meetletacbfree
|
||||||
* @copyright 2026 MeetLet
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
+21
-1
@@ -1,5 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
// This file is part of Moodle - http://moodle.org/
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standalone Adobe Connect connection test page.
|
||||||
|
*
|
||||||
|
* @package mod_meetletacbfree
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
require_once('../../config.php');
|
require_once('../../config.php');
|
||||||
|
|
||||||
@@ -15,7 +36,6 @@ $PAGE->set_context(context_system::instance());
|
|||||||
$PAGE->set_url(new moodle_url('/mod/meetletacbfree/conntest.php'));
|
$PAGE->set_url(new moodle_url('/mod/meetletacbfree/conntest.php'));
|
||||||
$PAGE->set_title(get_string('conntesttitle', 'meetletacbfree'));
|
$PAGE->set_title(get_string('conntesttitle', 'meetletacbfree'));
|
||||||
$PAGE->set_pagelayout('admin');
|
$PAGE->set_pagelayout('admin');
|
||||||
$PAGE->requires->css(new moodle_url('/mod/meetletacbfree/styles.css', ['v' => '2026081415']));
|
|
||||||
|
|
||||||
$results = [];
|
$results = [];
|
||||||
$push = static function(string $label, bool $ok, string $detail = '') use (&$results): void {
|
$push = static function(string $label, bool $ok, string $detail = '') use (&$results): void {
|
||||||
|
|||||||
@@ -81,4 +81,28 @@ $functions = [
|
|||||||
'ajax' => true,
|
'ajax' => true,
|
||||||
'capabilities' => 'mod/meetletacbfree:host',
|
'capabilities' => 'mod/meetletacbfree:host',
|
||||||
],
|
],
|
||||||
|
'mod_meetletacbfree_run_conntest_step' => [
|
||||||
|
'classname' => 'mod_meetletacbfree\external\run_conntest_step',
|
||||||
|
'methodname' => 'execute',
|
||||||
|
'description' => 'Run one Adobe Connect connection-test step (admin)',
|
||||||
|
'type' => 'write',
|
||||||
|
'ajax' => true,
|
||||||
|
'capabilities' => 'moodle/site:config',
|
||||||
|
],
|
||||||
|
'mod_meetletacbfree_reset_access_settings' => [
|
||||||
|
'classname' => 'mod_meetletacbfree\external\reset_access_settings',
|
||||||
|
'methodname' => 'execute',
|
||||||
|
'description' => 'Reset site role mapping or feature-access matrix to defaults (admin)',
|
||||||
|
'type' => 'write',
|
||||||
|
'ajax' => true,
|
||||||
|
'capabilities' => 'moodle/site:config',
|
||||||
|
],
|
||||||
|
'mod_meetletacbfree_registration_action' => [
|
||||||
|
'classname' => 'mod_meetletacbfree\external\registration_action',
|
||||||
|
'methodname' => 'execute',
|
||||||
|
'description' => 'Activate, recheck, or deactivate MeetLet plugin registration (admin)',
|
||||||
|
'type' => 'write',
|
||||||
|
'ajax' => true,
|
||||||
|
'capabilities' => 'moodle/site:config',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -176,6 +176,9 @@ $string['testsso'] = 'Single sign-on (SSO)';
|
|||||||
$string['testsso_adminok'] = 'Admin SSO succeeded';
|
$string['testsso_adminok'] = 'Admin SSO succeeded';
|
||||||
$string['testsso_userok'] = 'Test-user SSO succeeded';
|
$string['testsso_userok'] = 'Test-user SSO succeeded';
|
||||||
$string['testsso_userwarn'] = 'Test-user SSO warned (admin SSO infrastructure is healthy)';
|
$string['testsso_userwarn'] = 'Test-user SSO warned (admin SSO infrastructure is healthy)';
|
||||||
|
$string['testsso_direct_ok'] = 'Admin SSO from Moodle server succeeded';
|
||||||
|
$string['testsso_direct_fail'] = 'Direct HTTP SSO from Moodle failed — {$a}';
|
||||||
|
$string['testmeeting'] = 'Create and remove test meeting';
|
||||||
|
|
||||||
$string['tabinfo'] = 'Overview';
|
$string['tabinfo'] = 'Overview';
|
||||||
$string['tabdetails'] = 'Session details';
|
$string['tabdetails'] = 'Session details';
|
||||||
@@ -334,6 +337,7 @@ $string['emptytab'] = 'Nothing to show';
|
|||||||
$string['cachedef_folders'] = 'Adobe Connect folder sco-ids';
|
$string['cachedef_folders'] = 'Adobe Connect folder sco-ids';
|
||||||
$string['cachedef_meetinginfo'] = 'Adobe Connect meeting info';
|
$string['cachedef_meetinginfo'] = 'Adobe Connect meeting info';
|
||||||
$string['cachedef_templates'] = 'Adobe Connect meeting templates';
|
$string['cachedef_templates'] = 'Adobe Connect meeting templates';
|
||||||
|
$string['cachedef_license_webhook_nonces'] = 'MeetLet license webhook nonce cache';
|
||||||
$string['tasksync'] = 'Sync MeetLet recordings';
|
$string['tasksync'] = 'Sync MeetLet recordings';
|
||||||
$string['brand'] = 'MeetLet';
|
$string['brand'] = 'MeetLet';
|
||||||
$string['subtitle'] = 'Adobe Connect';
|
$string['subtitle'] = 'Adobe Connect';
|
||||||
@@ -352,6 +356,10 @@ $string['enable_contenttab_desc'] = 'Master switch for the content tab. Roles st
|
|||||||
$string['allow_override_contenttab'] = 'Allow per-activity content tab setting';
|
$string['allow_override_contenttab'] = 'Allow per-activity content tab setting';
|
||||||
$string['allow_override_contenttab_desc'] = 'Teachers may enable/disable the content tab for each activity.';
|
$string['allow_override_contenttab_desc'] = 'Teachers may enable/disable the content tab for each activity.';
|
||||||
$string['showrecordingstab'] = 'Show recordings tab';
|
$string['showrecordingstab'] = 'Show recordings tab';
|
||||||
|
$string['offlinesettings'] = 'Offline session video';
|
||||||
|
$string['autooffline'] = 'Automatically create offline video after the Adobe Connect class ends';
|
||||||
|
$string['autooffline_help'] = 'When this class’s Adobe Connect room had people in it and then becomes empty, recordings are sent for MP4 creation. This is independent of the optional Moodle end time (which only blocks joining).';
|
||||||
|
$string['showzip_help'] = 'When ZIP links are enabled in site settings, use this to turn the ZIP link on or off for this activity only.';
|
||||||
$string['showcontenttab'] = 'Show content tab';
|
$string['showcontenttab'] = 'Show content tab';
|
||||||
$string['activityfeatures'] = 'Links and tabs visibility';
|
$string['activityfeatures'] = 'Links and tabs visibility';
|
||||||
$string['featuredisabled'] = 'This section has been disabled by the site administrator.';
|
$string['featuredisabled'] = 'This section has been disabled by the site administrator.';
|
||||||
@@ -551,6 +559,7 @@ $string['registration_err_misconfigured'] = 'Activation request misconfigured.';
|
|||||||
$string['registration_err_bad_signature'] = 'Invalid authorization signature from MeetLet.';
|
$string['registration_err_bad_signature'] = 'Invalid authorization signature from MeetLet.';
|
||||||
$string['registration_err_network'] = 'Could not reach the MeetLet activation server.';
|
$string['registration_err_network'] = 'Could not reach the MeetLet activation server.';
|
||||||
$string['registration_err_unknown'] = 'An unknown activation error occurred.';
|
$string['registration_err_unknown'] = 'An unknown activation error occurred.';
|
||||||
|
$string['registration_api_error'] = 'MeetLet activation API error: {$a}';
|
||||||
$string['registration_crypto_error'] = 'Activation cryptography error.';
|
$string['registration_crypto_error'] = 'Activation cryptography error.';
|
||||||
$string['registration_sodium_required'] = 'PHP sodium extension is required for plugin activation.';
|
$string['registration_sodium_required'] = 'PHP sodium extension is required for plugin activation.';
|
||||||
$string['task_registration_heartbeat'] = 'MeetLet plugin activation heartbeat';
|
$string['task_registration_heartbeat'] = 'MeetLet plugin activation heartbeat';
|
||||||
|
|||||||
@@ -176,6 +176,9 @@ $string['testsso'] = 'ورود یکپارچه (SSO)';
|
|||||||
$string['testsso_adminok'] = 'SSO مدیر موفق بود';
|
$string['testsso_adminok'] = 'SSO مدیر موفق بود';
|
||||||
$string['testsso_userok'] = 'SSO کاربر آزمایشی موفق بود';
|
$string['testsso_userok'] = 'SSO کاربر آزمایشی موفق بود';
|
||||||
$string['testsso_userwarn'] = 'SSO کاربر آزمایشی هشدار داد (زیرساخت SSO مدیر سالم است)';
|
$string['testsso_userwarn'] = 'SSO کاربر آزمایشی هشدار داد (زیرساخت SSO مدیر سالم است)';
|
||||||
|
$string['testsso_direct_ok'] = 'SSO مدیر از سرور مودل موفق بود';
|
||||||
|
$string['testsso_direct_fail'] = 'SSO مستقیم HTTP از مودل ناموفق بود — {$a}';
|
||||||
|
$string['testmeeting'] = 'ایجاد و حذف جلسه آزمایشی';
|
||||||
|
|
||||||
$string['tabinfo'] = 'نمای کلی';
|
$string['tabinfo'] = 'نمای کلی';
|
||||||
$string['tabdetails'] = 'توضیحات جلسه';
|
$string['tabdetails'] = 'توضیحات جلسه';
|
||||||
@@ -336,6 +339,7 @@ $string['emptytab'] = 'موردی برای نمایش نیست';
|
|||||||
$string['cachedef_folders'] = 'شناسه پوشههای Adobe Connect';
|
$string['cachedef_folders'] = 'شناسه پوشههای Adobe Connect';
|
||||||
$string['cachedef_meetinginfo'] = 'اطلاعات جلسه Adobe Connect';
|
$string['cachedef_meetinginfo'] = 'اطلاعات جلسه Adobe Connect';
|
||||||
$string['cachedef_templates'] = 'قالبهای جلسه Adobe Connect';
|
$string['cachedef_templates'] = 'قالبهای جلسه Adobe Connect';
|
||||||
|
$string['cachedef_license_webhook_nonces'] = 'کش nonce وبهوک لایسنس MeetLet';
|
||||||
$string['tasksync'] = 'همگامسازی ضبطهای MeetLet';
|
$string['tasksync'] = 'همگامسازی ضبطهای MeetLet';
|
||||||
$string['brand'] = 'MeetLet';
|
$string['brand'] = 'MeetLet';
|
||||||
$string['subtitle'] = 'Adobe Connect';
|
$string['subtitle'] = 'Adobe Connect';
|
||||||
@@ -354,6 +358,10 @@ $string['enable_contenttab_desc'] = 'کلید اصلی تب محتوا. نقش
|
|||||||
$string['allow_override_contenttab'] = 'اجازه تنظیم تب محتوا در هر فعالیت';
|
$string['allow_override_contenttab'] = 'اجازه تنظیم تب محتوا در هر فعالیت';
|
||||||
$string['allow_override_contenttab_desc'] = 'مدرس بتواند برای هر جلسه نمایش تب محتوا را جداگانه کنترل کند.';
|
$string['allow_override_contenttab_desc'] = 'مدرس بتواند برای هر جلسه نمایش تب محتوا را جداگانه کنترل کند.';
|
||||||
$string['showrecordingstab'] = 'نمایش تب ضبطها';
|
$string['showrecordingstab'] = 'نمایش تب ضبطها';
|
||||||
|
$string['offlinesettings'] = 'ویدیوی آفلاین جلسه';
|
||||||
|
$string['autooffline'] = 'ساخت خودکار ویدیوی آفلاین پس از پایان کلاس Adobe Connect';
|
||||||
|
$string['autooffline_help'] = 'وقتی اتاق Adobe Connect این کلاس خالی شود (پس از حضور شرکتکنندگان)، ضبطها برای ساخت MP4 ارسال میشوند. این مستقل از زمان پایان اختیاری در مودل است.';
|
||||||
|
$string['showzip_help'] = 'اگر لینک ZIP در تنظیمات سایت فعال باشد، با این گزینه میتوانید لینک ZIP را فقط برای این فعالیت روشن یا خاموش کنید.';
|
||||||
$string['showcontenttab'] = 'نمایش تب محتوا';
|
$string['showcontenttab'] = 'نمایش تب محتوا';
|
||||||
$string['activityfeatures'] = 'نمایش لینکها و تبها';
|
$string['activityfeatures'] = 'نمایش لینکها و تبها';
|
||||||
$string['featuredisabled'] = 'این بخش توسط مدیر سایت غیرفعال شده است.';
|
$string['featuredisabled'] = 'این بخش توسط مدیر سایت غیرفعال شده است.';
|
||||||
@@ -553,6 +561,7 @@ $string['registration_err_misconfigured'] = 'درخواست فعالسازی
|
|||||||
$string['registration_err_bad_signature'] = 'امضای مجوز از MeetLet نامعتبر است.';
|
$string['registration_err_bad_signature'] = 'امضای مجوز از MeetLet نامعتبر است.';
|
||||||
$string['registration_err_network'] = 'اتصال به سرور فعالسازی MeetLet برقرار نشد.';
|
$string['registration_err_network'] = 'اتصال به سرور فعالسازی MeetLet برقرار نشد.';
|
||||||
$string['registration_err_unknown'] = 'خطای ناشناخته در فعالسازی رخ داد.';
|
$string['registration_err_unknown'] = 'خطای ناشناخته در فعالسازی رخ داد.';
|
||||||
|
$string['registration_api_error'] = 'خطای API فعالسازی MeetLet: {$a}';
|
||||||
$string['registration_crypto_error'] = 'خطای رمزنگاری فعالسازی.';
|
$string['registration_crypto_error'] = 'خطای رمزنگاری فعالسازی.';
|
||||||
$string['registration_sodium_required'] = 'افزونه sodium در PHP برای فعالسازی پلاگین لازم است.';
|
$string['registration_sodium_required'] = 'افزونه sodium در PHP برای فعالسازی پلاگین لازم است.';
|
||||||
$string['task_registration_heartbeat'] = 'بررسی روزانه فعالسازی پلاگین MeetLet';
|
$string['task_registration_heartbeat'] = 'بررسی روزانه فعالسازی پلاگین MeetLet';
|
||||||
|
|||||||
+25
-8
@@ -1,5 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
// This file is part of Moodle - http://moodle.org/
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MeetLet activity module form.
|
||||||
|
*
|
||||||
|
* @package mod_meetletacbfree
|
||||||
|
* @copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
require_once($CFG->dirroot . '/course/moodleform_mod.php');
|
require_once($CFG->dirroot . '/course/moodleform_mod.php');
|
||||||
@@ -12,10 +34,6 @@ use mod_meetletacbfree\registration\manager as registration_manager;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* MeetLet mod form.
|
* MeetLet mod form.
|
||||||
*
|
|
||||||
* @package mod_meetletacbfree
|
|
||||||
* @copyright 2026 MeetLet
|
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
||||||
*/
|
*/
|
||||||
class mod_meetletacbfree_mod_form extends moodleform_mod {
|
class mod_meetletacbfree_mod_form extends moodleform_mod {
|
||||||
public function definition() {
|
public function definition() {
|
||||||
@@ -34,10 +52,9 @@ class mod_meetletacbfree_mod_form extends moodleform_mod {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$PAGE->requires->js(new moodle_url('/mod/meetletacbfree/javascript/modform.js'));
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/modform', 'init');
|
||||||
$PAGE->requires->js(new moodle_url('/mod/meetletacbfree/javascript/featureaccess_matrix.js'));
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/featureaccess_matrix', 'init');
|
||||||
$PAGE->requires->js(new moodle_url('/mod/meetletacbfree/javascript/settings_toggles.js'));
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/settings_toggles', 'init');
|
||||||
$PAGE->requires->css(new moodle_url('/mod/meetletacbfree/styles.css', ['v' => '2026081913']));
|
|
||||||
|
|
||||||
$mform->addElement('header', 'general', get_string('general', 'form'));
|
$mform->addElement('header', 'general', get_string('general', 'form'));
|
||||||
$mform->addElement('text', 'name', get_string('meetingname', 'meetletacbfree'), ['size' => '64']);
|
$mform->addElement('text', 'name', get_string('meetingname', 'meetletacbfree'), ['size' => '64']);
|
||||||
|
|||||||
+2
-618
@@ -46,620 +46,6 @@ if ($ADMIN->fulltree) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
$logo = $OUTPUT->image_url('adobeconnect', 'meetletacbfree');
|
$logo = $OUTPUT->image_url('adobeconnect', 'meetletacbfree');
|
||||||
$stylesurl = new moodle_url('/mod/meetletacbfree/styles.css', ['v' => '2026090146']);
|
|
||||||
$jsurl = new moodle_url('/mod/meetletacbfree/javascript/settings_conntest.js', ['v' => '2026082135']);
|
|
||||||
$togglejsurl = new moodle_url('/mod/meetletacbfree/javascript/settings_toggles.js', ['v' => '2026081907']);
|
|
||||||
$matrixjsurl = new moodle_url('/mod/meetletacbfree/javascript/featureaccess_matrix.js');
|
|
||||||
$accessresetjsurl = new moodle_url('/mod/meetletacbfree/javascript/settings_access_reset.js', ['v' => '2026090115']);
|
|
||||||
$resetajaxurl = new moodle_url('/mod/meetletacbfree/ajax/settings_reset.php');
|
|
||||||
$modalscrolljsurl = new moodle_url('/mod/meetletacbfree/javascript/meetlet_modal_scroll.js', ['v' => '2026082239']);
|
|
||||||
$registrationjsurl = new moodle_url('/mod/meetletacbfree/javascript/settings_registration.js', ['v' => '2026090144']);
|
|
||||||
$cronjsurl = null;
|
|
||||||
$ajaxurl = new moodle_url('/mod/meetletacbfree/ajax/conntest.php');
|
|
||||||
|
|
||||||
// Load styles.css explicitly on admin tabs (theme concatenate alone is not enough for switch CSS).
|
|
||||||
$styles = '
|
|
||||||
<link rel="stylesheet" href="' . s($stylesurl->out(false)) . '">
|
|
||||||
<style>
|
|
||||||
.path-admin #adminsettings .settingsform > h2 {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-settings-hero {
|
|
||||||
display:flex; gap:1rem; align-items:center;
|
|
||||||
padding:1.15rem 1.25rem; margin:0 0 1rem;
|
|
||||||
border-radius:16px;
|
|
||||||
background:
|
|
||||||
radial-gradient(700px 180px at 100% 0%, rgba(82,204,163,.22), transparent 55%),
|
|
||||||
linear-gradient(135deg, #00261a 0%, #0a3d2e 60%, #145c45 100%);
|
|
||||||
color:#fff;
|
|
||||||
border:1px solid rgba(82,204,163,.25);
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-settings-hero img {
|
|
||||||
width:56px; height:56px; object-fit:contain;
|
|
||||||
background:rgba(255,255,255,.95); border-radius:12px; padding:6px;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-settings-hero h3 {
|
|
||||||
margin:0 0 .2rem; font-size:1.2rem; font-weight:700; color:#fff;
|
|
||||||
display:flex; flex-wrap:wrap; align-items:center; gap:.45rem .55rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-settings-hero .meetlet-settings-version {
|
|
||||||
display:inline-block;
|
|
||||||
flex:0 0 auto;
|
|
||||||
align-self:center;
|
|
||||||
margin-inline-start:auto;
|
|
||||||
padding:.12rem .55rem;
|
|
||||||
font-size:.72rem; font-weight:650; line-height:1.35;
|
|
||||||
letter-spacing:.02em;
|
|
||||||
border-radius:999px;
|
|
||||||
background:rgba(82,204,163,.18);
|
|
||||||
border:1px solid rgba(82,204,163,.35);
|
|
||||||
color:#b8f5df;
|
|
||||||
white-space:nowrap;
|
|
||||||
direction:ltr;
|
|
||||||
unicode-bidi:isolate;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-settings-hero p {
|
|
||||||
margin:0; opacity:.85; color:#e6faf3;
|
|
||||||
white-space:nowrap;
|
|
||||||
max-width:none;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-settings-copy {
|
|
||||||
min-width:0; flex:1 1 auto;
|
|
||||||
}
|
|
||||||
@media (max-width: 900px) {
|
|
||||||
.path-admin .meetlet-settings-hero p {
|
|
||||||
white-space:normal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.path-admin #adminsettings .nav-tabs {
|
|
||||||
border-bottom:2px solid #cfe8de;
|
|
||||||
gap:.2rem;
|
|
||||||
}
|
|
||||||
.path-admin #adminsettings .nav-tabs .nav-link {
|
|
||||||
border:0 !important;
|
|
||||||
border-radius:6px !important;
|
|
||||||
padding:.45rem .9rem;
|
|
||||||
color:#00261a;
|
|
||||||
font-weight:650;
|
|
||||||
box-shadow:none !important;
|
|
||||||
outline:none !important;
|
|
||||||
}
|
|
||||||
.path-admin #adminsettings .nav-tabs .nav-link:hover {
|
|
||||||
background:#e6faf3; color:#00261a;
|
|
||||||
}
|
|
||||||
.path-admin #adminsettings .nav-tabs .nav-link.active {
|
|
||||||
background:#52cca3; color:#00261a;
|
|
||||||
}
|
|
||||||
.path-admin #adminsettings .nav-tabs .nav-link:focus,
|
|
||||||
.path-admin #adminsettings .nav-tabs .nav-link:focus-visible,
|
|
||||||
.path-admin #adminsettings .nav-tabs .nav-link:active {
|
|
||||||
outline:none !important;
|
|
||||||
box-shadow:none !important;
|
|
||||||
border:0 !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-feature-group {
|
|
||||||
margin:1rem 0 .35rem;
|
|
||||||
padding:1rem 1.1rem .35rem;
|
|
||||||
border:1px solid #cfe8de;
|
|
||||||
border-radius:12px;
|
|
||||||
background:linear-gradient(180deg, #f3fbf8 0%, #ffffff 55%);
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-feature-group h4 {
|
|
||||||
margin:0 0 .25rem; color:#00261a; font-size:1.02rem; font-weight:700;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-feature-group p {
|
|
||||||
margin:0 0 .5rem; color:#4a6b5f; font-size:.9rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-settings-card {
|
|
||||||
background:#fff; border:1px solid #cfe8de; border-radius:14px;
|
|
||||||
padding:1rem 1.15rem; margin:.35rem 0 1rem;
|
|
||||||
}
|
|
||||||
.meetlet-license-badge {
|
|
||||||
font-weight: 700;
|
|
||||||
border-radius: 999px;
|
|
||||||
padding: .35rem .65rem;
|
|
||||||
}
|
|
||||||
.meetlet-license-badge--active {
|
|
||||||
background: var(--ml-green, #52cca3);
|
|
||||||
color: var(--ml-green-deep, #00261a);
|
|
||||||
}
|
|
||||||
.meetlet-license-badge--inactive {
|
|
||||||
background: #fff4d6;
|
|
||||||
color: #5a4700;
|
|
||||||
}
|
|
||||||
.path-admin #meetlet-license-modal.meetlet-modal,
|
|
||||||
.path-admin #meetlet-registration-modal.meetlet-modal,
|
|
||||||
.path-admin #meetlet-cronmon-modal.meetlet-modal,
|
|
||||||
.path-admin #meetlet-access-reset-modal.meetlet-modal {
|
|
||||||
z-index: 20000 !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-access-reset-toolbar {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
margin: 0 0 .65rem;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin .meetlet-access-reset-toolbar {
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
/* Cron videos ops cell: no extra cell/wrapper border (icon buttons keep theirs). */
|
|
||||||
.path-admin .meetlet-cronmon__table td.meetlet-cronmon__dl {
|
|
||||||
border-left: 0 !important;
|
|
||||||
border-right: 0 !important;
|
|
||||||
border-top: 0 !important;
|
|
||||||
background: transparent !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-cronmon__dl .meetlet-iconactions {
|
|
||||||
border: 0 !important;
|
|
||||||
background: transparent !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal--confirm .meetlet-modal__dialog {
|
|
||||||
width: min(480px, 100%);
|
|
||||||
max-height: min(86vh, 640px);
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal--confirm .meetlet-modal__head {
|
|
||||||
padding: 1.2rem 1.15rem 1rem;
|
|
||||||
border-radius: 18px 18px 0 0;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__lead {
|
|
||||||
margin: 0 0 .85rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 650;
|
|
||||||
line-height: 1.65;
|
|
||||||
color: #00261a;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__callout {
|
|
||||||
margin: 0 0 .9rem;
|
|
||||||
padding: .85rem 1rem .7rem;
|
|
||||||
border-radius: 12px;
|
|
||||||
border: 1px solid #e6c56a;
|
|
||||||
background: #fff8e6;
|
|
||||||
color: #5a4700;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__callout-title {
|
|
||||||
margin: 0 0 .4rem;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__limits {
|
|
||||||
margin: 0 0 .55rem;
|
|
||||||
padding-inline-start: 1.2rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__keep {
|
|
||||||
margin: 0;
|
|
||||||
color: #334942;
|
|
||||||
line-height: 1.65;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__foot {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
gap: .6rem;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: .9rem 1.15rem 1.05rem;
|
|
||||||
border-top: 1px solid #cfe8de;
|
|
||||||
background: linear-gradient(180deg, #f7fcfa 0%, #ffffff 70%);
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__foot .btn {
|
|
||||||
border-radius: 10px;
|
|
||||||
font-weight: 700;
|
|
||||||
padding: .5rem 1.15rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__foot .btn-success {
|
|
||||||
background: #52cca3 !important;
|
|
||||||
border-color: #3db892 !important;
|
|
||||||
color: #00261a !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__foot .btn-outline-danger {
|
|
||||||
color: #a33b3b !important;
|
|
||||||
border-color: #d9a0a0 !important;
|
|
||||||
background: #fff !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-modal__foot .btn-outline-danger:hover,
|
|
||||||
.path-admin .meetlet-modal__foot .btn-outline-danger:focus {
|
|
||||||
color: #fff !important;
|
|
||||||
background: #c0392b !important;
|
|
||||||
border-color: #c0392b !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-about {
|
|
||||||
display:grid; gap:1.1rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-about__brand {
|
|
||||||
display:flex; justify-content:center; align-items:center;
|
|
||||||
padding:1.35rem 1.2rem;
|
|
||||||
border-radius:14px;
|
|
||||||
background:#f0f2f4;
|
|
||||||
border:1px solid #e2e6ea;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-about__brand img {
|
|
||||||
max-width:240px; width:100%; height:auto; display:block; margin:0 auto;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-about h4 { margin:0 0 .55rem; color:#00261a; }
|
|
||||||
.path-admin .meetlet-about p {
|
|
||||||
margin:0 0 .85rem; color:#334942; line-height:1.75; text-align:justify;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide { display:grid; gap:1rem; }
|
|
||||||
.path-admin .meetlet-guide__heading { margin:0; color:#00261a; font-size:1.15rem; }
|
|
||||||
.path-admin .meetlet-guide__intro {
|
|
||||||
margin:0; color:#334942; line-height:1.7;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__callout {
|
|
||||||
padding:.85rem 1rem; border-radius:10px; line-height:1.65;
|
|
||||||
border:1px solid #e6c56a; background:#fff8e6; color:#5a4700;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__section {
|
|
||||||
padding:1rem 1.05rem; border-radius:12px;
|
|
||||||
border:1px solid #cfe8de; background:#f7fcfa;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__section--current { background:#fff; }
|
|
||||||
.path-admin .meetlet-guide__section h4 {
|
|
||||||
margin:0 0 .55rem; color:#00261a; font-size:1.02rem; font-weight:700;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__section h5 {
|
|
||||||
margin:.85rem 0 .35rem; color:#0a3d2e; font-size:.95rem; font-weight:700;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__body,
|
|
||||||
.path-admin .meetlet-guide__body p,
|
|
||||||
.path-admin .meetlet-guide__body li {
|
|
||||||
color:#334942; line-height:1.7;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__body p { margin:0 0 .55rem; }
|
|
||||||
.path-admin .meetlet-guide__body ol,
|
|
||||||
.path-admin .meetlet-guide__body ul { margin:0 0 .35rem; padding-inline-start:1.25rem; }
|
|
||||||
.path-admin .meetlet-guide__body li { margin:0 0 .35rem; }
|
|
||||||
.path-admin .meetlet-guide__body code,
|
|
||||||
.path-admin .meetlet-guide__table code {
|
|
||||||
font-size:.88em; padding:.05rem .3rem; border-radius:4px;
|
|
||||||
background:#e6faf3; color:#00261a;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__table {
|
|
||||||
width:100%; margin:.35rem 0 0; border-collapse:collapse;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__table th,
|
|
||||||
.path-admin .meetlet-guide__table td {
|
|
||||||
padding:.45rem .55rem; border:1px solid #cfe8de; vertical-align:top;
|
|
||||||
text-align:start;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__table thead th,
|
|
||||||
.path-admin .meetlet-guide__table tbody th {
|
|
||||||
background:#e6faf3; color:#00261a; font-weight:650;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__code {
|
|
||||||
margin:.35rem 0 0; padding:.75rem .9rem; overflow-x:auto;
|
|
||||||
border-radius:10px; border:1px solid #cfe8de;
|
|
||||||
background:#0a3d2e; color:#e6faf3;
|
|
||||||
font-size:.82rem; line-height:1.45; direction:ltr; text-align:left;
|
|
||||||
white-space:pre;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__code-cell code {
|
|
||||||
font-weight:800; letter-spacing:.02em; direction:ltr; unicode-bidi:isolate;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__table td ol,
|
|
||||||
.path-admin .meetlet-guide__table td ul {
|
|
||||||
margin:.15rem 0 0; padding-inline-start:1.15rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-guide__table td li { margin:0 0 .25rem; }
|
|
||||||
.path-admin .meetlet-hourpicker { max-width: 42rem; }
|
|
||||||
.path-admin .meetlet-hourpicker.is-dimmed,
|
|
||||||
.path-admin #adminsettings .form-item.is-dimmed {
|
|
||||||
opacity: .45;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-hourpicker__tz {
|
|
||||||
margin: 0 0 .55rem; color:#4a6b5f; font-size:.88rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-hourpresets {
|
|
||||||
display:flex; flex-wrap:wrap; gap:.4rem; margin:0 0 .65rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-hourpreset {
|
|
||||||
border:1px solid #9fdcc4; background:#fff; color:#00261a;
|
|
||||||
border-radius:999px; padding:.28rem .7rem; font-size:.8rem; font-weight:650;
|
|
||||||
cursor:pointer;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-hourpreset:hover { background:#e6faf3; }
|
|
||||||
.path-admin .meetlet-hourpreset--ghost { border-color:#cfe8de; color:#4a6b5f; }
|
|
||||||
.path-admin .meetlet-hourgrid {
|
|
||||||
display:grid; grid-template-columns:repeat(8, minmax(0,1fr)); gap:.35rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-hourchip {
|
|
||||||
border:1px solid #cfe8de; background:#f7fcfa; color:#00261a;
|
|
||||||
border-radius:10px; padding:.45rem 0; font-size:.82rem; font-weight:750;
|
|
||||||
cursor:pointer;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-hourchip.is-on {
|
|
||||||
background:#52cca3; border-color:#0d9f6e; color:#00261a;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-hourchip.is-now { box-shadow:0 0 0 2px rgba(13,159,110,.35); }
|
|
||||||
@media (max-width: 720px) {
|
|
||||||
.path-admin .meetlet-hourgrid { grid-template-columns:repeat(6, minmax(0,1fr)); }
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-about__cta-wrap {
|
|
||||||
display:flex; justify-content:center; margin-top:.35rem; padding-top:.35rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-about__cta {
|
|
||||||
display:inline-flex; align-items:center; justify-content:center;
|
|
||||||
min-width:220px; padding:.7rem 1.35rem; border-radius:10px;
|
|
||||||
background:#52cca3; color:#00261a !important;
|
|
||||||
text-decoration:none !important; font-weight:700;
|
|
||||||
border:1px solid #3db892;
|
|
||||||
box-shadow:0 6px 16px rgba(0,38,26,.08);
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-about__cta:hover {
|
|
||||||
background:#6fd9b5; color:#00261a !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-settings-btn {
|
|
||||||
display:inline-flex; align-items:center; justify-content:center; gap:.45rem;
|
|
||||||
padding:.6rem 1.15rem; border-radius:10px; border:0; cursor:pointer;
|
|
||||||
background:#52cca3; color:#00261a !important;
|
|
||||||
text-decoration:none !important; font-weight:700;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-settings-btn:hover { background:#6fd9b5; color:#00261a !important; }
|
|
||||||
.path-admin .meetlet-settings-btn:disabled { opacity:.65; cursor:wait; }
|
|
||||||
.path-admin .meetlet-settings-btn svg {
|
|
||||||
width:1.1rem; height:1.1rem; flex:0 0 auto; display:block;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-license-actions {
|
|
||||||
display:flex; flex-wrap:wrap; gap:.55rem; align-items:center;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-license-actions .btn {
|
|
||||||
display:inline-flex; align-items:center; justify-content:center; gap:.45rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-license-actions .btn svg {
|
|
||||||
width:1.05rem; height:1.05rem; flex:0 0 auto; display:block;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-license-btn--free {
|
|
||||||
background: linear-gradient(180deg, #e6faf3 0%, #d4f5ea 100%) !important;
|
|
||||||
border: 2px solid #52cca3 !important;
|
|
||||||
color: #00261a !important;
|
|
||||||
border-radius: 10px !important;
|
|
||||||
font-weight: 700 !important;
|
|
||||||
padding: .6rem 1.15rem !important;
|
|
||||||
box-shadow: 0 4px 12px rgba(82, 204, 163, 0.18) !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-license-btn--free:hover,
|
|
||||||
.path-admin .meetlet-license-btn--free:focus {
|
|
||||||
background: linear-gradient(180deg, #6fd9b5 0%, #52cca3 100%) !important;
|
|
||||||
border-color: #3db892 !important;
|
|
||||||
color: #00261a !important;
|
|
||||||
box-shadow: 0 6px 16px rgba(82, 204, 163, 0.28) !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest { max-width:none; margin:0; }
|
|
||||||
.path-admin .meetlet-conntest__actions {
|
|
||||||
display:flex; flex-wrap:wrap; gap:.75rem; align-items:center;
|
|
||||||
margin:.85rem 0 1rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__status { color:#4a6b5f; font-size:.9rem; }
|
|
||||||
.path-admin .meetlet-conntest__progress {
|
|
||||||
margin:0 0 1rem; padding:.85rem 1rem;
|
|
||||||
border:1px solid #cfe8de; border-radius:12px; background:#f7fcfa;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__progress-label {
|
|
||||||
display:flex; justify-content:space-between; gap:.75rem;
|
|
||||||
margin:0 0 .45rem; color:#00261a; font-weight:650; font-size:.92rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__progress-track {
|
|
||||||
height:10px; border-radius:999px; background:#d9eee5; overflow:hidden;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__progress-fill {
|
|
||||||
height:100%; width:0; border-radius:999px;
|
|
||||||
background:linear-gradient(90deg, #2f9f7a, #52cca3);
|
|
||||||
transition:width .28s ease;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__num {
|
|
||||||
min-width:1.7rem; height:1.7rem; border-radius:8px;
|
|
||||||
display:inline-flex; align-items:center; justify-content:center;
|
|
||||||
background:#e6faf3; color:#00261a; font-weight:750; font-size:.85rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__card--pending { opacity:.78; background:#fbfefe; }
|
|
||||||
.path-admin .meetlet-conntest__card--running {
|
|
||||||
border-color:#e6b422;
|
|
||||||
box-shadow:0 0 0 2px rgba(230,180,34,.22);
|
|
||||||
background:#fff8e6;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__card--running .meetlet-conntest__num {
|
|
||||||
background:#ffe08a; color:#5c4700;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__card--ok {
|
|
||||||
border-color:#9fdcc4; background:#f3fbf8;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__card--fail {
|
|
||||||
border-color:#ead3d0; background:#fff8f7;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-conntest__card--skipped { opacity:.55; }
|
|
||||||
.path-admin .meetlet-dot--pending { background:#9bb7ac; box-shadow:0 0 0 4px rgba(155,183,172,.18); }
|
|
||||||
.path-admin .meetlet-dot--running {
|
|
||||||
background:#e6b422; box-shadow:0 0 0 4px rgba(230,180,34,.28);
|
|
||||||
animation:meetletPulse 1s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-dot--skipped { background:#b7c4be; box-shadow:none; }
|
|
||||||
.path-admin .meetlet-pill--pending { background:#e8efec; color:#456257; }
|
|
||||||
.path-admin .meetlet-pill--running { background:#ffe08a; color:#5c4700; }
|
|
||||||
.path-admin .meetlet-pill--skipped { background:#eef1f0; color:#667a72; }
|
|
||||||
@keyframes meetletPulse {
|
|
||||||
0%, 100% { transform:scale(1); opacity:1; }
|
|
||||||
50% { transform:scale(1.15); opacity:.75; }
|
|
||||||
}
|
|
||||||
.path-admin #adminsettings form.settingsform {
|
|
||||||
padding-bottom:1rem;
|
|
||||||
}
|
|
||||||
/* Single save row: spacing only — no stacked border/gradient bands. */
|
|
||||||
.path-admin #adminsettings .settingsform > .row:has(.btn-primary),
|
|
||||||
.path-admin #adminsettings .form-buttons,
|
|
||||||
.path-admin #adminsettings .btns-group {
|
|
||||||
margin-top:1.25rem !important;
|
|
||||||
padding:0;
|
|
||||||
border:0;
|
|
||||||
background:transparent;
|
|
||||||
border-radius:0;
|
|
||||||
}
|
|
||||||
.path-admin #adminsettings .meetlet-savebar {
|
|
||||||
margin-top: 1.25rem !important;
|
|
||||||
margin-right: 0 !important;
|
|
||||||
margin-left: 0 !important;
|
|
||||||
margin-inline: 0 !important;
|
|
||||||
flex: 0 0 100% !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: 100% !important;
|
|
||||||
padding: .35rem 0 !important;
|
|
||||||
border: 0 !important;
|
|
||||||
background: transparent !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
@media (min-width: 576px) {
|
|
||||||
.path-admin #adminsettings .meetlet-savebar.offset-sm-3,
|
|
||||||
.path-admin #adminsettings .meetlet-savebar {
|
|
||||||
margin-right: 0 !important;
|
|
||||||
margin-left: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.path-admin #adminsettings .btn-primary,
|
|
||||||
.path-admin #adminsettings input[type="submit"].btn-primary,
|
|
||||||
.path-admin #adminsettings button[type="submit"].btn-primary,
|
|
||||||
.path-admin #adminsettings .meetlet-btn-with-icon.btn-primary {
|
|
||||||
background:#52cca3 !important;
|
|
||||||
border-color:#3db892 !important;
|
|
||||||
color:#00261a !important;
|
|
||||||
font-weight:700 !important;
|
|
||||||
border-radius:10px !important;
|
|
||||||
padding:.55rem 1.35rem !important;
|
|
||||||
box-shadow:none !important;
|
|
||||||
}
|
|
||||||
.path-admin #adminsettings .btn-primary:hover,
|
|
||||||
.path-admin #adminsettings input[type="submit"].btn-primary:hover,
|
|
||||||
.path-admin #adminsettings button[type="submit"].btn-primary:hover {
|
|
||||||
background:#6fd9b5 !important;
|
|
||||||
border-color:#52cca3 !important;
|
|
||||||
color:#00261a !important;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-btn-with-icon {
|
|
||||||
display:inline-flex !important;
|
|
||||||
align-items:center;
|
|
||||||
justify-content:center;
|
|
||||||
gap:.45rem;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-btn-with-icon svg {
|
|
||||||
width:1.1rem;
|
|
||||||
height:1.1rem;
|
|
||||||
flex:0 0 auto;
|
|
||||||
display:block;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-btn-with-icon__label {
|
|
||||||
line-height:1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.path-admin .meetlet-server-group {
|
|
||||||
margin:1.1rem 0 .4rem; padding:1rem 1.1rem .2rem;
|
|
||||||
border:1px solid #cfe8de; border-radius:12px;
|
|
||||||
background:linear-gradient(180deg, #f3fbf8 0%, #ffffff 60%);
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-server-group h4 {
|
|
||||||
margin:0 0 .25rem; color:#00261a; font-size:1.02rem; font-weight:700;
|
|
||||||
}
|
|
||||||
.path-admin .meetlet-server-group p {
|
|
||||||
margin:0 0 .55rem; color:#4a6b5f; font-size:.9rem; line-height:1.55;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RTL alignment for Persian/Arabic admin settings */
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .settingsform,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .tab-content,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item .form-label,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item .form-label.label,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item .form-label > label,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item .form-label .form-shortname,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-description,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-defaultinfo,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting .form-check,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting .checkbox,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting label,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings h3.main,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item .form-label.text-sm-end,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item .form-label.text-end {
|
|
||||||
text-align: right !important;
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-feature-group,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-feature-group h4,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-feature-group p,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-settings-card,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-about,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-about p,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-about h4,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-privacy,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-privacy p,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-privacy h3,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-privacy h4,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-conntest,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-conntest__intro,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-conntest__title,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-conntest__status,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-conntest__progress-label,
|
|
||||||
html[dir="rtl"] .path-admin .meetlet-settings-hero,
|
|
||||||
html[dir="rtl"] .path-admin .meetlet-settings-hero h3,
|
|
||||||
html[dir="rtl"] .path-admin .meetlet-settings-hero .meetlet-settings-version,
|
|
||||||
html[dir="rtl"] .path-admin .meetlet-settings-hero p,
|
|
||||||
html[dir="rtl"] .path-admin .meetlet-settings-copy {
|
|
||||||
direction: rtl;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin .meetlet-settings-hero {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .nav-tabs {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting input[type="text"],
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting input[type="password"],
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting input[type="url"],
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting input[type="number"],
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting select,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting textarea {
|
|
||||||
direction: ltr;
|
|
||||||
text-align: left;
|
|
||||||
unicode-bidi: isolate;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting .form-check,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting .checkbox,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting .form-checkbox {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
gap: .5rem;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item .form-label .form-shortname {
|
|
||||||
direction: ltr;
|
|
||||||
unicode-bidi: isolate;
|
|
||||||
text-align: right !important;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting .form-checkbox {
|
|
||||||
direction: rtl;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-setting .defaultsnext {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .meetlet-savebar,
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-buttons {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script src="' . s($modalscrolljsurl->out(false)) . '" defer></script>
|
|
||||||
<script src="' . s($registrationjsurl->out(false)) . '" defer></script>
|
|
||||||
<script src="' . s($jsurl->out(false)) . '" defer></script>
|
|
||||||
<script src="' . s($togglejsurl->out(false)) . '" defer></script>
|
|
||||||
<script src="' . s($matrixjsurl->out(false)) . '" defer></script>
|
|
||||||
<script src="' . s($accessresetjsurl->out(false)) . '" defer></script>';
|
|
||||||
|
|
||||||
$plugin = new stdClass();
|
$plugin = new stdClass();
|
||||||
include(__DIR__ . '/version.php');
|
include(__DIR__ . '/version.php');
|
||||||
@@ -673,7 +59,7 @@ html[dir="rtl"] .path-admin #adminsettings .form-buttons {
|
|||||||
$versionbadge = $release !== ''
|
$versionbadge = $release !== ''
|
||||||
? html_writer::span(get_string('settingsversion', 'meetletacbfree', $release), 'meetlet-settings-version')
|
? html_writer::span(get_string('settingsversion', 'meetletacbfree', $release), 'meetlet-settings-version')
|
||||||
: '';
|
: '';
|
||||||
$hero = $styles . html_writer::div(
|
$hero = html_writer::div(
|
||||||
html_writer::img($logo, 'Adobe Connect', ['alt' => 'Adobe Connect']) .
|
html_writer::img($logo, 'Adobe Connect', ['alt' => 'Adobe Connect']) .
|
||||||
html_writer::div(
|
html_writer::div(
|
||||||
html_writer::tag('h3', get_string('pluginname', 'meetletacbfree')) .
|
html_writer::tag('h3', get_string('pluginname', 'meetletacbfree')) .
|
||||||
@@ -828,8 +214,6 @@ html[dir="rtl"] .path-admin #adminsettings .form-buttons {
|
|||||||
'meetlet-conntest',
|
'meetlet-conntest',
|
||||||
[
|
[
|
||||||
'id' => 'meetlet-acb-conntest',
|
'id' => 'meetlet-acb-conntest',
|
||||||
'data-endpoint' => $ajaxurl->out(false),
|
|
||||||
'data-sesskey' => sesskey(),
|
|
||||||
'data-steps' => implode(',', conntest_runner::STEPS),
|
'data-steps' => implode(',', conntest_runner::STEPS),
|
||||||
'data-step-labels' => json_encode($steplabels, JSON_UNESCAPED_UNICODE),
|
'data-step-labels' => json_encode($steplabels, JSON_UNESCAPED_UNICODE),
|
||||||
'data-str-ok' => get_string('testok', 'meetletacbfree'),
|
'data-str-ok' => get_string('testok', 'meetletacbfree'),
|
||||||
@@ -923,7 +307,7 @@ html[dir="rtl"] .path-admin #adminsettings .form-buttons {
|
|||||||
$page->add(new admin_setting_heading(
|
$page->add(new admin_setting_heading(
|
||||||
'meetletacbfree/accessresetassets',
|
'meetletacbfree/accessresetassets',
|
||||||
'',
|
'',
|
||||||
\mod_meetletacbfree\admin\access_reset_ui::page_assets($resetajaxurl)
|
\mod_meetletacbfree\admin\access_reset_ui::page_assets()
|
||||||
));
|
));
|
||||||
$settings->add($page);
|
$settings->add($page);
|
||||||
|
|
||||||
|
|||||||
+537
-22
@@ -1185,11 +1185,13 @@
|
|||||||
background: #c0392b;
|
background: #c0392b;
|
||||||
border-color: #c0392b;
|
border-color: #c0392b;
|
||||||
}
|
}
|
||||||
|
/* rtl:begin:ignore */
|
||||||
html[dir="rtl"] .meetlet-modal--confirm .meetlet-modal__title,
|
html[dir="rtl"] .meetlet-modal--confirm .meetlet-modal__title,
|
||||||
html[dir="rtl"] .meetlet-modal--confirm .meetlet-modal__body,
|
html[dir="rtl"] .meetlet-modal--confirm .meetlet-modal__body,
|
||||||
html[dir="rtl"] .meetlet-modal--confirm .meetlet-modal__lead {
|
html[dir="rtl"] .meetlet-modal--confirm .meetlet-modal__lead {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
/* rtl:end:ignore */
|
||||||
/* Marker class on html; lock scroll on body only — overflow+height on html resets scroll position. */
|
/* Marker class on html; lock scroll on body only — overflow+height on html resets scroll position. */
|
||||||
html.meetlet-modal-open body {
|
html.meetlet-modal-open body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -1349,7 +1351,7 @@ html.meetlet-modal-open body {
|
|||||||
transition: background .18s ease;
|
transition: background .18s ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
direction: ltr; /* Keep thumb travel physical LTR; icons stay readable. */
|
/* rtl:ignore */ direction: ltr; /* Keep thumb travel physical LTR; icons stay readable. */
|
||||||
}
|
}
|
||||||
.meetlet-switch__thumb {
|
.meetlet-switch__thumb {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -1405,20 +1407,20 @@ html.meetlet-modal-open body {
|
|||||||
.meetlet-access-cell .meetlet-switch {
|
.meetlet-access-cell .meetlet-switch {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.path-admin #adminsettings .form-item:has(.meetlet-switch) {
|
.meetlet-admin-settings .form-item:has(.meetlet-switch) {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
.path-admin #adminsettings .form-item:has(.meetlet-switch) > .form-setting {
|
.meetlet-admin-settings .form-item:has(.meetlet-switch) > .form-setting {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
/* Toward the label column: left in LTR, right in RTL. */
|
/* Toward the label column: left in LTR, right in RTL. */
|
||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
.path-admin #adminsettings .form-item:has(.meetlet-switch) > .form-label,
|
.meetlet-admin-settings .form-item:has(.meetlet-switch) > .form-label,
|
||||||
.path-admin #adminsettings .form-item:has(.meetlet-switch) > .form-setting {
|
.meetlet-admin-settings .form-item:has(.meetlet-switch) > .form-setting {
|
||||||
padding-top: .4rem;
|
padding-top: .4rem;
|
||||||
}
|
}
|
||||||
.path-admin #adminsettings .form-item:has(.meetlet-switch) .form-label > label {
|
.meetlet-admin-settings .form-item:has(.meetlet-switch) .form-label > label {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
@@ -1426,9 +1428,9 @@ html.meetlet-modal-open body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 22px;
|
min-height: 22px;
|
||||||
}
|
}
|
||||||
.path-admin #adminsettings .form-item:has(.meetlet-switch) .form-check,
|
.meetlet-admin-settings .form-item:has(.meetlet-switch) .form-check,
|
||||||
.path-admin #adminsettings .form-item:has(.meetlet-switch) .checkbox,
|
.meetlet-admin-settings .form-item:has(.meetlet-switch) .checkbox,
|
||||||
.path-admin #adminsettings .form-item:has(.meetlet-switch) .form-checkbox {
|
.meetlet-admin-settings .form-item:has(.meetlet-switch) .form-checkbox {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1441,14 +1443,16 @@ html.meetlet-modal-open body {
|
|||||||
width: auto;
|
width: auto;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item:has(.meetlet-switch) .form-check,
|
/* rtl:begin:ignore */
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item:has(.meetlet-switch) .checkbox,
|
html[dir="rtl"] .meetlet-admin-settings .form-item:has(.meetlet-switch) .form-check,
|
||||||
html[dir="rtl"] .path-admin #adminsettings .form-item:has(.meetlet-switch) .form-checkbox {
|
html[dir="rtl"] .meetlet-admin-settings .form-item:has(.meetlet-switch) .checkbox,
|
||||||
|
html[dir="rtl"] .meetlet-admin-settings .form-item:has(.meetlet-switch) .form-checkbox {
|
||||||
/* Keep switch on the label side (visual right), same as pre-compact layout. */
|
/* Keep switch on the label side (visual right), same as pre-compact layout. */
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
/* rtl:end:ignore */
|
||||||
|
|
||||||
/* Site admin: Moodle role → Adobe Connect role mapping (chip radios). */
|
/* Site admin: Moodle role → Adobe Connect role mapping (chip radios). */
|
||||||
.meetlet-rolemapping-table .meetlet-rolemapping-cell {
|
.meetlet-rolemapping-table .meetlet-rolemapping-cell {
|
||||||
@@ -1637,19 +1641,19 @@ html[dir="rtl"] .path-admin #adminsettings .form-item:has(.meetlet-switch) .form
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Cron & video-delivery dashboard (plugin settings) */
|
/* Cron & video-delivery dashboard (plugin settings) */
|
||||||
.path-admin #adminsettings .form-item:has(#meetlet-cronmon) {
|
.meetlet-admin-settings .form-item:has(#meetlet-cronmon) {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
.path-admin #adminsettings .form-item:has(#meetlet-cronmon) > .form-label,
|
.meetlet-admin-settings .form-item:has(#meetlet-cronmon) > .form-label,
|
||||||
.path-admin #adminsettings .form-item:has(#meetlet-cronmon) .form-description,
|
.meetlet-admin-settings .form-item:has(#meetlet-cronmon) .form-description,
|
||||||
.path-admin #adminsettings .form-item:has(#meetlet-cronmon) .form-defaultinfo,
|
.meetlet-admin-settings .form-item:has(#meetlet-cronmon) .form-defaultinfo,
|
||||||
.path-admin #adminsettings .form-item:has(#meetlet-cronmon) .form-setting > .form-shortname {
|
.meetlet-admin-settings .form-item:has(#meetlet-cronmon) .form-setting > .form-shortname {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
.path-admin #adminsettings .form-item:has(#meetlet-cronmon) > .form-setting {
|
.meetlet-admin-settings .form-item:has(#meetlet-cronmon) > .form-setting {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
float: none;
|
float: none;
|
||||||
@@ -2049,20 +2053,26 @@ html[dir="rtl"] .path-admin #adminsettings .form-item:has(.meetlet-switch) .form
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html[dir="rtl"] .path-admin #adminsettings #id_s_meetletacbfree_agent_dispatch,
|
/* rtl:begin:ignore */
|
||||||
html[dir="rtl"] .path-admin #adminsettings select[name="s_meetletacbfree_agent_dispatch"] {
|
html[dir="rtl"] .meetlet-admin-settings #id_s_meetletacbfree_agent_dispatch,
|
||||||
|
html[dir="rtl"] .meetlet-admin-settings select[name="s_meetletacbfree_agent_dispatch"] {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
html[dir="rtl"] .path-admin #adminsettings #id_s_meetletacbfree_agent_dispatch option,
|
/* rtl:end:ignore */
|
||||||
html[dir="rtl"] .path-admin #adminsettings select[name="s_meetletacbfree_agent_dispatch"] option {
|
/* rtl:begin:ignore */
|
||||||
|
html[dir="rtl"] .meetlet-admin-settings #id_s_meetletacbfree_agent_dispatch option,
|
||||||
|
html[dir="rtl"] .meetlet-admin-settings select[name="s_meetletacbfree_agent_dispatch"] option {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
/* rtl:end:ignore */
|
||||||
|
/* rtl:begin:ignore */
|
||||||
html[dir="rtl"] .path-admin .meetlet-hourpicker,
|
html[dir="rtl"] .path-admin .meetlet-hourpicker,
|
||||||
html[dir="rtl"] .path-admin .meetlet-hourpresets {
|
html[dir="rtl"] .path-admin .meetlet-hourpresets {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
}
|
}
|
||||||
|
/* rtl:end:ignore */
|
||||||
|
|
||||||
.meetlet-gate-msg {
|
.meetlet-gate-msg {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@@ -2200,3 +2210,508 @@ html[dir="rtl"] .path-admin .meetlet-hourpresets {
|
|||||||
.activityiconcontainer .activityicon[src*="mod/meetletacbfree"] {
|
.activityiconcontainer .activityicon[src*="mod/meetletacbfree"] {
|
||||||
filter: none !important;
|
filter: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== MeetLet admin settings (scoped under .meetlet-admin-settings) ===== */
|
||||||
|
.meetlet-admin-settings .settingsform > h2 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-settings-hero {
|
||||||
|
display:flex; gap:1rem; align-items:center;
|
||||||
|
padding:1.15rem 1.25rem; margin:0 0 1rem;
|
||||||
|
border-radius:16px;
|
||||||
|
background:
|
||||||
|
radial-gradient(700px 180px at 100% 0%, rgba(82,204,163,.22), transparent 55%),
|
||||||
|
linear-gradient(135deg, #00261a 0%, #0a3d2e 60%, #145c45 100%);
|
||||||
|
color:#fff;
|
||||||
|
border:1px solid rgba(82,204,163,.25);
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-settings-hero img {
|
||||||
|
width:56px; height:56px; object-fit:contain;
|
||||||
|
background:rgba(255,255,255,.95); border-radius:12px; padding:6px;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-settings-hero h3 {
|
||||||
|
margin:0 0 .2rem; font-size:1.2rem; font-weight:700; color:#fff;
|
||||||
|
display:flex; flex-wrap:wrap; align-items:center; gap:.45rem .55rem;
|
||||||
|
}
|
||||||
|
/* rtl:begin:ignore */
|
||||||
|
.meetlet-admin-settings .meetlet-settings-hero .meetlet-settings-version {
|
||||||
|
display:inline-block;
|
||||||
|
flex:0 0 auto;
|
||||||
|
align-self:center;
|
||||||
|
margin-inline-start:auto;
|
||||||
|
padding:.12rem .55rem;
|
||||||
|
font-size:.72rem; font-weight:650; line-height:1.35;
|
||||||
|
letter-spacing:.02em;
|
||||||
|
border-radius:999px;
|
||||||
|
background:rgba(82,204,163,.18);
|
||||||
|
border:1px solid rgba(82,204,163,.35);
|
||||||
|
color:#b8f5df;
|
||||||
|
white-space:nowrap;
|
||||||
|
direction:ltr;
|
||||||
|
unicode-bidi:isolate;
|
||||||
|
}
|
||||||
|
/* rtl:end:ignore */
|
||||||
|
.meetlet-admin-settings .meetlet-settings-hero p {
|
||||||
|
margin:0; opacity:.85; color:#e6faf3;
|
||||||
|
white-space:nowrap;
|
||||||
|
max-width:none;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-settings-copy {
|
||||||
|
min-width:0; flex:1 1 auto;
|
||||||
|
}
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.meetlet-admin-settings .meetlet-settings-hero p {
|
||||||
|
white-space:normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .nav-tabs {
|
||||||
|
border-bottom:2px solid #cfe8de;
|
||||||
|
gap:.2rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .nav-tabs .nav-link {
|
||||||
|
border:0 !important;
|
||||||
|
border-radius:6px !important;
|
||||||
|
padding:.45rem .9rem;
|
||||||
|
color:#00261a;
|
||||||
|
font-weight:650;
|
||||||
|
box-shadow:none !important;
|
||||||
|
outline:none !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .nav-tabs .nav-link:hover {
|
||||||
|
background:#e6faf3; color:#00261a;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .nav-tabs .nav-link.active {
|
||||||
|
background:#52cca3; color:#00261a;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .nav-tabs .nav-link:focus,
|
||||||
|
.meetlet-admin-settings .nav-tabs .nav-link:focus-visible,
|
||||||
|
.meetlet-admin-settings .nav-tabs .nav-link:active {
|
||||||
|
outline:none !important;
|
||||||
|
box-shadow:none !important;
|
||||||
|
border:0 !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-feature-group {
|
||||||
|
margin:1rem 0 .35rem;
|
||||||
|
padding:1rem 1.1rem .35rem;
|
||||||
|
border:1px solid #cfe8de;
|
||||||
|
border-radius:12px;
|
||||||
|
background:linear-gradient(180deg, #f3fbf8 0%, #ffffff 55%);
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-feature-group h4 {
|
||||||
|
margin:0 0 .25rem; color:#00261a; font-size:1.02rem; font-weight:700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-feature-group p {
|
||||||
|
margin:0 0 .5rem; color:#4a6b5f; font-size:.9rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-settings-card {
|
||||||
|
background:#fff; border:1px solid #cfe8de; border-radius:14px;
|
||||||
|
padding:1rem 1.15rem; margin:.35rem 0 1rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-license-badge {
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: .35rem .65rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-license-badge--active {
|
||||||
|
background: var(--ml-green, #52cca3);
|
||||||
|
color: var(--ml-green-deep, #00261a);
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-license-badge--inactive {
|
||||||
|
background: #fff4d6;
|
||||||
|
color: #5a4700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings #meetlet-license-modal.meetlet-modal,
|
||||||
|
.meetlet-admin-settings #meetlet-registration-modal.meetlet-modal,
|
||||||
|
.meetlet-admin-settings #meetlet-cronmon-modal.meetlet-modal,
|
||||||
|
.meetlet-admin-settings #meetlet-access-reset-modal.meetlet-modal {
|
||||||
|
z-index: 20000 !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-access-reset-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin: 0 0 .65rem;
|
||||||
|
}
|
||||||
|
/* Cron videos ops cell: no extra cell/wrapper border (icon buttons keep theirs). */
|
||||||
|
.meetlet-admin-settings .meetlet-cronmon__table td.meetlet-cronmon__dl {
|
||||||
|
border-left: 0 !important;
|
||||||
|
border-right: 0 !important;
|
||||||
|
border-top: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-cronmon__dl .meetlet-iconactions {
|
||||||
|
border: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal--confirm .meetlet-modal__dialog {
|
||||||
|
width: min(480px, 100%);
|
||||||
|
max-height: min(86vh, 640px);
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal--confirm .meetlet-modal__head {
|
||||||
|
padding: 1.2rem 1.15rem 1rem;
|
||||||
|
border-radius: 18px 18px 0 0;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__lead {
|
||||||
|
margin: 0 0 .85rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 650;
|
||||||
|
line-height: 1.65;
|
||||||
|
color: #00261a;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__callout {
|
||||||
|
margin: 0 0 .9rem;
|
||||||
|
padding: .85rem 1rem .7rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid #e6c56a;
|
||||||
|
background: #fff8e6;
|
||||||
|
color: #5a4700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__callout-title {
|
||||||
|
margin: 0 0 .4rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__limits {
|
||||||
|
margin: 0 0 .55rem;
|
||||||
|
padding-inline-start: 1.2rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__keep {
|
||||||
|
margin: 0;
|
||||||
|
color: #334942;
|
||||||
|
line-height: 1.65;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__foot {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
gap: .6rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: .9rem 1.15rem 1.05rem;
|
||||||
|
border-top: 1px solid #cfe8de;
|
||||||
|
background: linear-gradient(180deg, #f7fcfa 0%, #ffffff 70%);
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__foot .btn {
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: .5rem 1.15rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__foot .btn-success {
|
||||||
|
background: #52cca3 !important;
|
||||||
|
border-color: #3db892 !important;
|
||||||
|
color: #00261a !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__foot .btn-outline-danger {
|
||||||
|
color: #a33b3b !important;
|
||||||
|
border-color: #d9a0a0 !important;
|
||||||
|
background: #fff !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-modal__foot .btn-outline-danger:hover,
|
||||||
|
.meetlet-admin-settings .meetlet-modal__foot .btn-outline-danger:focus {
|
||||||
|
color: #fff !important;
|
||||||
|
background: #c0392b !important;
|
||||||
|
border-color: #c0392b !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-about {
|
||||||
|
display:grid; gap:1.1rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-about__brand {
|
||||||
|
display:flex; justify-content:center; align-items:center;
|
||||||
|
padding:1.35rem 1.2rem;
|
||||||
|
border-radius:14px;
|
||||||
|
background:#f0f2f4;
|
||||||
|
border:1px solid #e2e6ea;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-about__brand img {
|
||||||
|
max-width:240px; width:100%; height:auto; display:block; margin:0 auto;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-about h4 { margin:0 0 .55rem; color:#00261a; }
|
||||||
|
.meetlet-admin-settings .meetlet-about p {
|
||||||
|
margin:0 0 .85rem; color:#334942; line-height:1.75; text-align:start;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide { display:grid; gap:1rem; }
|
||||||
|
.meetlet-admin-settings .meetlet-guide__heading { margin:0; color:#00261a; font-size:1.15rem; }
|
||||||
|
.meetlet-admin-settings .meetlet-guide__intro {
|
||||||
|
margin:0; color:#334942; line-height:1.7;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__callout {
|
||||||
|
padding:.85rem 1rem; border-radius:10px; line-height:1.65;
|
||||||
|
border:1px solid #e6c56a; background:#fff8e6; color:#5a4700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__section {
|
||||||
|
padding:1rem 1.05rem; border-radius:12px;
|
||||||
|
border:1px solid #cfe8de; background:#f7fcfa;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__section--current { background:#fff; }
|
||||||
|
.meetlet-admin-settings .meetlet-guide__section h4 {
|
||||||
|
margin:0 0 .55rem; color:#00261a; font-size:1.02rem; font-weight:700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__section h5 {
|
||||||
|
margin:.85rem 0 .35rem; color:#0a3d2e; font-size:.95rem; font-weight:700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__body,
|
||||||
|
.meetlet-admin-settings .meetlet-guide__body p,
|
||||||
|
.meetlet-admin-settings .meetlet-guide__body li {
|
||||||
|
color:#334942; line-height:1.7;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__body p { margin:0 0 .55rem; }
|
||||||
|
.meetlet-admin-settings .meetlet-guide__body ol,
|
||||||
|
.meetlet-admin-settings .meetlet-guide__body ul { margin:0 0 .35rem; padding-inline-start:1.25rem; }
|
||||||
|
.meetlet-admin-settings .meetlet-guide__body li { margin:0 0 .35rem; }
|
||||||
|
.meetlet-admin-settings .meetlet-guide__body code,
|
||||||
|
.meetlet-admin-settings .meetlet-guide__table code {
|
||||||
|
font-size:.88em; padding:.05rem .3rem; border-radius:4px;
|
||||||
|
background:#e6faf3; color:#00261a;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__table {
|
||||||
|
width:100%; margin:.35rem 0 0; border-collapse:collapse;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__table th,
|
||||||
|
.meetlet-admin-settings .meetlet-guide__table td {
|
||||||
|
padding:.45rem .55rem; border:1px solid #cfe8de; vertical-align:top;
|
||||||
|
text-align:start;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__table thead th,
|
||||||
|
.meetlet-admin-settings .meetlet-guide__table tbody th {
|
||||||
|
background:#e6faf3; color:#00261a; font-weight:650;
|
||||||
|
}
|
||||||
|
/* rtl:begin:ignore */
|
||||||
|
.meetlet-admin-settings .meetlet-guide__code {
|
||||||
|
margin:.35rem 0 0; padding:.75rem .9rem; overflow-x:auto;
|
||||||
|
border-radius:10px; border:1px solid #cfe8de;
|
||||||
|
background:#0a3d2e; color:#e6faf3;
|
||||||
|
font-size:.82rem; line-height:1.45; direction:ltr; text-align:left;
|
||||||
|
white-space:pre;
|
||||||
|
}
|
||||||
|
/* rtl:end:ignore */
|
||||||
|
.meetlet-admin-settings .meetlet-guide__code-cell code {
|
||||||
|
font-weight:800; letter-spacing:.02em; direction:ltr; unicode-bidi:isolate;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__table td ol,
|
||||||
|
.meetlet-admin-settings .meetlet-guide__table td ul {
|
||||||
|
margin:.15rem 0 0; padding-inline-start:1.15rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-guide__table td li { margin:0 0 .25rem; }
|
||||||
|
.meetlet-admin-settings .meetlet-hourpicker { max-width: 42rem; }
|
||||||
|
.meetlet-admin-settings .meetlet-hourpicker.is-dimmed,
|
||||||
|
.meetlet-admin-settings .form-item.is-dimmed {
|
||||||
|
opacity: .45;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-hourpicker__tz {
|
||||||
|
margin: 0 0 .55rem; color:#4a6b5f; font-size:.88rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-hourpresets {
|
||||||
|
display:flex; flex-wrap:wrap; gap:.4rem; margin:0 0 .65rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-hourpreset {
|
||||||
|
border:1px solid #9fdcc4; background:#fff; color:#00261a;
|
||||||
|
border-radius:999px; padding:.28rem .7rem; font-size:.8rem; font-weight:650;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-hourpreset:hover { background:#e6faf3; }
|
||||||
|
.meetlet-admin-settings .meetlet-hourpreset--ghost { border-color:#cfe8de; color:#4a6b5f; }
|
||||||
|
.meetlet-admin-settings .meetlet-hourgrid {
|
||||||
|
display:grid; grid-template-columns:repeat(8, minmax(0,1fr)); gap:.35rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-hourchip {
|
||||||
|
border:1px solid #cfe8de; background:#f7fcfa; color:#00261a;
|
||||||
|
border-radius:10px; padding:.45rem 0; font-size:.82rem; font-weight:750;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-hourchip.is-on {
|
||||||
|
background:#52cca3; border-color:#0d9f6e; color:#00261a;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-hourchip.is-now { box-shadow:0 0 0 2px rgba(13,159,110,.35); }
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.meetlet-admin-settings .meetlet-hourgrid { grid-template-columns:repeat(6, minmax(0,1fr)); }
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-about__cta-wrap {
|
||||||
|
display:flex; justify-content:center; margin-top:.35rem; padding-top:.35rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-about__cta {
|
||||||
|
display:inline-flex; align-items:center; justify-content:center;
|
||||||
|
min-width:220px; padding:.7rem 1.35rem; border-radius:10px;
|
||||||
|
background:#52cca3; color:#00261a !important;
|
||||||
|
text-decoration:none !important; font-weight:700;
|
||||||
|
border:1px solid #3db892;
|
||||||
|
box-shadow:0 6px 16px rgba(0,38,26,.08);
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-about__cta:hover {
|
||||||
|
background:#6fd9b5; color:#00261a !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-settings-btn {
|
||||||
|
display:inline-flex; align-items:center; justify-content:center; gap:.45rem;
|
||||||
|
padding:.6rem 1.15rem; border-radius:10px; border:0; cursor:pointer;
|
||||||
|
background:#52cca3; color:#00261a !important;
|
||||||
|
text-decoration:none !important; font-weight:700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-settings-btn:hover { background:#6fd9b5; color:#00261a !important; }
|
||||||
|
.meetlet-admin-settings .meetlet-settings-btn:disabled { opacity:.65; cursor:wait; }
|
||||||
|
.meetlet-admin-settings .meetlet-settings-btn svg {
|
||||||
|
width:1.1rem; height:1.1rem; flex:0 0 auto; display:block;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-license-actions {
|
||||||
|
display:flex; flex-wrap:wrap; gap:.55rem; align-items:center;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-license-actions .btn {
|
||||||
|
display:inline-flex; align-items:center; justify-content:center; gap:.45rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-license-actions .btn svg {
|
||||||
|
width:1.05rem; height:1.05rem; flex:0 0 auto; display:block;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-license-btn--free {
|
||||||
|
background: linear-gradient(180deg, #e6faf3 0%, #d4f5ea 100%) !important;
|
||||||
|
border: 2px solid #52cca3 !important;
|
||||||
|
color: #00261a !important;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
padding: .6rem 1.15rem !important;
|
||||||
|
box-shadow: 0 4px 12px rgba(82, 204, 163, 0.18) !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-license-btn--free:hover,
|
||||||
|
.meetlet-admin-settings .meetlet-license-btn--free:focus {
|
||||||
|
background: linear-gradient(180deg, #6fd9b5 0%, #52cca3 100%) !important;
|
||||||
|
border-color: #3db892 !important;
|
||||||
|
color: #00261a !important;
|
||||||
|
box-shadow: 0 6px 16px rgba(82, 204, 163, 0.28) !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest { max-width:none; margin:0; }
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__actions {
|
||||||
|
display:flex; flex-wrap:wrap; gap:.75rem; align-items:center;
|
||||||
|
margin:.85rem 0 1rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__status { color:#4a6b5f; font-size:.9rem; }
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__progress {
|
||||||
|
margin:0 0 1rem; padding:.85rem 1rem;
|
||||||
|
border:1px solid #cfe8de; border-radius:12px; background:#f7fcfa;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__progress-label {
|
||||||
|
display:flex; justify-content:space-between; gap:.75rem;
|
||||||
|
margin:0 0 .45rem; color:#00261a; font-weight:650; font-size:.92rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__progress-track {
|
||||||
|
height:10px; border-radius:999px; background:#d9eee5; overflow:hidden;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__progress-fill {
|
||||||
|
height:100%; width:0; border-radius:999px;
|
||||||
|
background:linear-gradient(90deg, #2f9f7a, #52cca3);
|
||||||
|
transition:width .28s ease;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__num {
|
||||||
|
min-width:1.7rem; height:1.7rem; border-radius:8px;
|
||||||
|
display:inline-flex; align-items:center; justify-content:center;
|
||||||
|
background:#e6faf3; color:#00261a; font-weight:750; font-size:.85rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__card--pending { opacity:.78; background:#fbfefe; }
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__card--running {
|
||||||
|
border-color:#e6b422;
|
||||||
|
box-shadow:0 0 0 2px rgba(230,180,34,.22);
|
||||||
|
background:#fff8e6;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__card--running .meetlet-conntest__num {
|
||||||
|
background:#ffe08a; color:#5c4700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__card--ok {
|
||||||
|
border-color:#9fdcc4; background:#f3fbf8;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__card--fail {
|
||||||
|
border-color:#ead3d0; background:#fff8f7;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-conntest__card--skipped { opacity:.55; }
|
||||||
|
.meetlet-admin-settings .meetlet-dot--pending { background:#9bb7ac; box-shadow:0 0 0 4px rgba(155,183,172,.18); }
|
||||||
|
.meetlet-admin-settings .meetlet-dot--running {
|
||||||
|
background:#e6b422; box-shadow:0 0 0 4px rgba(230,180,34,.28);
|
||||||
|
animation:meetletPulse 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-dot--skipped { background:#b7c4be; box-shadow:none; }
|
||||||
|
.meetlet-admin-settings .meetlet-pill--pending { background:#e8efec; color:#456257; }
|
||||||
|
.meetlet-admin-settings .meetlet-pill--running { background:#ffe08a; color:#5c4700; }
|
||||||
|
.meetlet-admin-settings .meetlet-pill--skipped { background:#eef1f0; color:#667a72; }
|
||||||
|
@keyframes meetletPulse {
|
||||||
|
0%, 100% { transform:scale(1); opacity:1; }
|
||||||
|
50% { transform:scale(1.15); opacity:.75; }
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings form.settingsform {
|
||||||
|
padding-bottom:1rem;
|
||||||
|
}
|
||||||
|
/* Single save row: spacing only — no stacked border/gradient bands. */
|
||||||
|
.meetlet-admin-settings .settingsform > .row:has(.btn-primary),
|
||||||
|
.meetlet-admin-settings .form-buttons,
|
||||||
|
.meetlet-admin-settings .btns-group {
|
||||||
|
margin-top:1.25rem !important;
|
||||||
|
padding:0;
|
||||||
|
border:0;
|
||||||
|
background:transparent;
|
||||||
|
border-radius:0;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-savebar {
|
||||||
|
margin-top: 1.25rem !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-inline: 0 !important;
|
||||||
|
flex: 0 0 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
width: 100% !important;
|
||||||
|
padding: .35rem 0 !important;
|
||||||
|
border: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
.meetlet-admin-settings .meetlet-savebar.offset-sm-3,
|
||||||
|
.meetlet-admin-settings .meetlet-savebar {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .btn-primary,
|
||||||
|
.meetlet-admin-settings input[type="submit"].btn-primary,
|
||||||
|
.meetlet-admin-settings button[type="submit"].btn-primary,
|
||||||
|
.meetlet-admin-settings .meetlet-btn-with-icon.btn-primary {
|
||||||
|
background:#52cca3 !important;
|
||||||
|
border-color:#3db892 !important;
|
||||||
|
color:#00261a !important;
|
||||||
|
font-weight:700 !important;
|
||||||
|
border-radius:10px !important;
|
||||||
|
padding:.55rem 1.35rem !important;
|
||||||
|
box-shadow:none !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .btn-primary:hover,
|
||||||
|
.meetlet-admin-settings input[type="submit"].btn-primary:hover,
|
||||||
|
.meetlet-admin-settings button[type="submit"].btn-primary:hover {
|
||||||
|
background:#6fd9b5 !important;
|
||||||
|
border-color:#52cca3 !important;
|
||||||
|
color:#00261a !important;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-btn-with-icon {
|
||||||
|
display:inline-flex !important;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:center;
|
||||||
|
gap:.45rem;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-btn-with-icon svg {
|
||||||
|
width:1.1rem;
|
||||||
|
height:1.1rem;
|
||||||
|
flex:0 0 auto;
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-btn-with-icon__label {
|
||||||
|
line-height:1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meetlet-admin-settings .meetlet-server-group {
|
||||||
|
margin:1.1rem 0 .4rem; padding:1rem 1.1rem .2rem;
|
||||||
|
border:1px solid #cfe8de; border-radius:12px;
|
||||||
|
background:linear-gradient(180deg, #f3fbf8 0%, #ffffff 60%);
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-server-group h4 {
|
||||||
|
margin:0 0 .25rem; color:#00261a; font-size:1.02rem; font-weight:700;
|
||||||
|
}
|
||||||
|
.meetlet-admin-settings .meetlet-server-group p {
|
||||||
|
margin:0 0 .55rem; color:#4a6b5f; font-size:.9rem; line-height:1.55;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
{{!
|
||||||
|
This file is part of Moodle - http://moodle.org/
|
||||||
|
|
||||||
|
Moodle is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Moodle is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
@template mod_meetletacbfree/participant_form_add
|
||||||
|
@copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
@license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
}}
|
||||||
<tr id="participant_list_tr_{{selectiontypevalue}}-{{selectionvalue}}">
|
<tr id="participant_list_tr_{{selectiontypevalue}}-{{selectionvalue}}">
|
||||||
<td class="participant_list_c0"><b><i>{{participanttype}}</i></b></td>
|
<td class="participant_list_c0"><b><i>{{participanttype}}</i></b></td>
|
||||||
<td class="participant_list_c1">{{participantvalue}}</td>
|
<td class="participant_list_c1">{{participantvalue}}</td>
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
{{!
|
||||||
|
This file is part of Moodle - http://moodle.org/
|
||||||
|
|
||||||
|
Moodle is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Moodle is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
@template mod_meetletacbfree/participant_single_select
|
||||||
|
@copyright 2026 MeetLet <info@meetlet.net>
|
||||||
|
@license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
}}
|
||||||
<div class="{{classes}} d-inline-block">
|
<div class="{{classes}} d-inline-block">
|
||||||
{{#label}}
|
{{#label}}
|
||||||
<label for="{{id}}"{{#labelattributes}} {{name}}="{{value}}"{{/labelattributes}}>
|
<label for="{{id}}"{{#labelattributes}} {{name}}="{{value}}"{{/labelattributes}}>
|
||||||
|
|||||||
+2
-2
@@ -25,8 +25,8 @@
|
|||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
$plugin->component = 'mod_meetletacbfree';
|
$plugin->component = 'mod_meetletacbfree';
|
||||||
$plugin->version = 2026090153;
|
$plugin->version = 2026092003;
|
||||||
$plugin->requires = 2024100700; // Moodle 4.5+.
|
$plugin->requires = 2024100700; // Moodle 4.5+.
|
||||||
$plugin->supported = [405, 502]; // 4.5 LTS through 5.2.
|
$plugin->supported = [405, 502]; // 4.5 LTS through 5.2.
|
||||||
$plugin->maturity = MATURITY_STABLE;
|
$plugin->maturity = MATURITY_STABLE;
|
||||||
$plugin->release = '1.1.3';
|
$plugin->release = '1.1.4';
|
||||||
|
|||||||
@@ -48,9 +48,8 @@ $PAGE->set_title(format_string($meetlet->name));
|
|||||||
$PAGE->set_heading(format_string($course->fullname));
|
$PAGE->set_heading(format_string($course->fullname));
|
||||||
$PAGE->set_context($context);
|
$PAGE->set_context($context);
|
||||||
$PAGE->activityheader->disable();
|
$PAGE->activityheader->disable();
|
||||||
$PAGE->requires->css(new moodle_url('/mod/meetletacbfree/styles.css', ['v' => '2026082250']));
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/meetlet_modal_scroll', 'init');
|
||||||
$PAGE->requires->js(new moodle_url('/mod/meetletacbfree/javascript/meetlet_modal_scroll.js', ['v' => '2026082250']));
|
$PAGE->requires->js_call_amd('mod_meetletacbfree/view', 'init');
|
||||||
$PAGE->requires->js(new moodle_url('/mod/meetletacbfree/javascript/view.js', ['v' => '2026082250']));
|
|
||||||
|
|
||||||
if ($cm->groupmode) {
|
if ($cm->groupmode) {
|
||||||
$groupid = groups_get_activity_group($cm, true) ?: $groupid;
|
$groupid = groups_get_activity_group($cm, true) ?: $groupid;
|
||||||
|
|||||||
Reference in New Issue
Block a user