Adobe Connect Bridge free edition (meetletacbfree) v1.1.3

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-01 23:59:56 +03:30
commit 91334a46b5
93 changed files with 21023 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
<?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/>.
/**
* Reset-to-default controls for access settings (role mapping + feature matrix).
*
* @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\admin;
use mod_meetletacbfree\local\icons;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
/**
* Shared reset button, modal, and client strings for the access settings tab.
*/
class access_reset_ui {
/** @var bool */
protected static $assetsrendered = false;
/**
* Toolbar with a section-specific reset button.
*/
public static function toolbar(string $section): string {
if ($section === 'rolemapping') {
$label = get_string('reset_rolemapping', 'meetletacbfree');
} else if ($section === 'featureaccess') {
$label = get_string('reset_featureaccess', 'meetletacbfree');
} else {
return '';
}
return \html_writer::div(
\html_writer::tag('button',
icons::svg('refresh') .
\html_writer::span($label, 'meetlet-btn-with-icon__label'),
[
'type' => 'button',
'class' => 'meetlet-settings-btn meetlet-btn-with-icon meetlet-access-reset-btn',
'data-meetlet-reset-section' => $section,
]
),
'meetlet-access-reset-toolbar'
);
}
/**
* Modal + config root (render once on the access settings tab).
*/
public static function page_assets(moodle_url $ajaxurl): string {
if (self::$assetsrendered) {
return '';
}
self::$assetsrendered = true;
$strings = self::client_strings();
$html = \html_writer::div('', '', [
'id' => 'meetlet-access-reset-root',
'hidden' => 'hidden',
'data-endpoint' => $ajaxurl->out(false),
'data-sesskey' => sesskey(),
'data-strings' => json_encode($strings, JSON_UNESCAPED_UNICODE),
]);
$html .= self::modal_html();
return $html;
}
/**
* @return array<string,string>
*/
public static function client_strings(): array {
return [
'modal_cancel' => get_string('accessreset_modal_cancel', 'meetletacbfree'),
'modal_close' => get_string('accessreset_modal_close', 'meetletacbfree'),
'modal_ok' => get_string('accessreset_modal_ok', 'meetletacbfree'),
'confirm_reset' => get_string('accessreset_confirm_reset', 'meetletacbfree'),
'confirm_rolemapping_title' => get_string('reset_rolemapping_confirm_title', 'meetletacbfree'),
'confirm_rolemapping' => get_string('reset_rolemapping_confirm', 'meetletacbfree'),
'confirm_featureaccess_title' => get_string('reset_featureaccess_confirm_title', 'meetletacbfree'),
'confirm_featureaccess' => get_string('reset_featureaccess_confirm', 'meetletacbfree'),
'success_rolemapping' => get_string('reset_success_rolemapping', 'meetletacbfree'),
'success_featureaccess' => get_string('reset_success_featureaccess', 'meetletacbfree'),
'error' => get_string('reset_error', 'meetletacbfree'),
'busy' => get_string('accessreset_busy', 'meetletacbfree'),
];
}
protected static function modal_html(): string {
return \html_writer::div(
\html_writer::div('', 'meetlet-modal__backdrop', ['data-meetlet-modal-dismiss' => '1']) .
\html_writer::div(
\html_writer::div(
\html_writer::tag('h3', '', [
'class' => 'meetlet-modal__title',
'id' => 'meetlet-access-reset-modal-title',
]) .
\html_writer::tag('button', '×', [
'type' => 'button',
'class' => 'meetlet-modal__close',
'data-meetlet-modal-dismiss' => '1',
'aria-label' => get_string('accessreset_modal_close', 'meetletacbfree'),
]),
'meetlet-modal__head'
) .
\html_writer::div('', 'meetlet-modal__body', [
'id' => 'meetlet-access-reset-modal-body',
]) .
\html_writer::div(
\html_writer::tag('button', get_string('accessreset_modal_cancel', 'meetletacbfree'), [
'type' => 'button',
'class' => 'btn btn-outline-secondary',
'data-meetlet-modal-dismiss' => '1',
]) .
\html_writer::tag('button', get_string('accessreset_confirm_reset', 'meetletacbfree'), [
'type' => 'button',
'class' => 'btn btn-outline-danger',
'id' => 'meetlet-access-reset-modal-confirm',
]),
'meetlet-modal__foot'
),
'meetlet-modal__dialog',
[
'role' => 'dialog',
'aria-modal' => 'true',
'aria-labelledby' => 'meetlet-access-reset-modal-title',
]
),
'meetlet-modal meetlet-modal--confirm',
[
'id' => 'meetlet-access-reset-modal',
'hidden' => 'hidden',
]
);
}
}
+85
View File
@@ -0,0 +1,85 @@
<?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/>.
/**
* Admin setting: site-wide role × feature access matrix.
*
* @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\admin;
use admin_setting;
use mod_meetletacbfree\local\featureaccess;
defined('MOODLE_INTERNAL') || die();
/**
* Stores the site default feature-access JSON used when an activity does not override.
*/
class setting_featureaccess extends admin_setting {
public function __construct() {
parent::__construct(
'meetletacbfree/featureaccess',
get_string('featureaccess', 'meetletacbfree'),
get_string('featureaccess_desc', 'meetletacbfree'),
json_encode(featureaccess::builtin_defaults(), JSON_UNESCAPED_UNICODE)
);
}
public function get_setting() {
return $this->config_read($this->name);
}
public function write_setting($data) {
if (!is_array($data)) {
$data = [];
}
$json = featureaccess::matrix_from_post($data);
return $this->config_write($this->name, $json) ? '' : get_string('errorsetting', 'admin');
}
public function output_html($data, $query = '') {
$matrix = featureaccess::builtin_defaults();
if (is_string($data) && $data !== '') {
$decoded = json_decode($data, true);
if (is_array($decoded) && !empty($decoded)) {
$matrix = featureaccess::normalize($decoded);
}
} else if (is_array($data) && !empty($data)) {
$json = featureaccess::matrix_from_post($data);
$decoded = json_decode($json, true);
if (is_array($decoded)) {
$matrix = featureaccess::normalize($decoded);
}
}
$html = access_reset_ui::toolbar('featureaccess');
$html .= featureaccess::render_matrix_html($matrix, $this->get_full_name());
return format_admin_setting(
$this,
$this->visiblename,
$html,
$this->description,
true,
'',
get_string('featureaccess_usesite', 'meetletacbfree'),
$query
);
}
}
+256
View File
@@ -0,0 +1,256 @@
<?php
// This file is part of Moodle - http://moodle.org/
/**
* Admin UI for voluntary MeetLet site registration (Marketplace free edition).
*
* @package mod_meetletacbfree
* @copyright 2026 MeetLet
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_meetletacbfree\admin;
use admin_setting;
use mod_meetletacbfree\local\icons;
use mod_meetletacbfree\registration\manager;
defined('MOODLE_INTERNAL') || die();
/**
* Transparent online activation panel — no license key in this edition.
*/
class setting_registration extends admin_setting {
public function __construct() {
parent::__construct(
'meetletacbfree/registration_panel',
'',
'',
''
);
$this->nosave = true;
}
public function get_setting() {
return true;
}
public function write_setting($data) {
return '';
}
public function output_html($data, $query = '') {
global $OUTPUT, $PAGE;
$summary = manager::status_summary();
$active = !empty($summary['active']);
$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();
$PAGE->requires->js($scrolljsurl);
$PAGE->requires->js($jsurl);
$statusbadge = \html_writer::span(
s($summary['status_label'] ?? ($active
? get_string('registration_status_active', 'meetletacbfree')
: get_string('registration_status_inactive', 'meetletacbfree'))),
'badge meetlet-license-badge ' . ($active ? 'meetlet-license-badge--active' : 'meetlet-license-badge--inactive'),
['id' => 'meetlet-registration-status-badge']
);
$datalist = \html_writer::tag('ul',
\html_writer::tag('li', get_string('registration_data_item_domain', 'meetletacbfree', s($summary['domain']))) .
\html_writer::tag('li', get_string('registration_data_item_moodle', 'meetletacbfree', s($summary['moodle_release']))) .
\html_writer::tag('li', get_string('registration_data_item_plugin', 'meetletacbfree', s($summary['plugin_release']))) .
\html_writer::tag('li', get_string('registration_data_item_installation', 'meetletacbfree', s($summary['installation_id']))) .
\html_writer::tag('li', get_string('registration_data_item_sitename', 'meetletacbfree', s($summary['site_company']))) .
\html_writer::tag('li', get_string('registration_data_item_email', 'meetletacbfree',
$summary['site_admin_email'] !== '' ? s($summary['site_admin_email']) : get_string('registration_data_optional', 'meetletacbfree'))),
['class' => 'meetlet-registration-data']
);
$why = \html_writer::div(
\html_writer::tag('h4', get_string('registration_why_title', 'meetletacbfree')) .
\html_writer::tag('p', get_string('registration_why', 'meetletacbfree')),
'meetlet-guide__section'
);
$what = \html_writer::div(
\html_writer::tag('h4', get_string('registration_data_title', 'meetletacbfree')) .
\html_writer::tag('p', get_string('registration_data_sent', 'meetletacbfree')) .
$datalist .
\html_writer::tag('p',
get_string(
'registration_privacy',
'meetletacbfree',
\html_writer::link($privacytaburl, get_string('registration_privacy_tablink', 'meetletacbfree'))
),
['class' => 'mb-0']
),
'meetlet-guide__section'
);
$inactiveactions = [
'id' => 'meetlet-registration-actions-inactive',
'class' => 'meetlet-license-actions mt-3' . ($active ? ' d-none' : ''),
];
$activeactions = [
'id' => 'meetlet-registration-actions-active',
'class' => 'meetlet-license-actions mt-3' . ($active ? '' : ' d-none'),
];
$html = \html_writer::div(
$why .
$what .
\html_writer::div(
\html_writer::tag('p', get_string('registration_status_label', 'meetletacbfree') . ' ' .
\html_writer::span($statusbadge, '', ['id' => 'meetlet-registration-status-badge'])) .
\html_writer::tag('p', get_string('registration_domain', 'meetletacbfree', s($summary['domain'])), [
'id' => 'meetlet-registration-domain',
]) .
\html_writer::tag('p', get_string('registration_installation', 'meetletacbfree', s($summary['installation_id'])), [
'id' => 'meetlet-registration-installation',
]) .
\html_writer::tag('p', s($summary['product_label'] ?? ''), [
'id' => 'meetlet-registration-product',
'class' => $active ? '' : 'd-none',
]) .
\html_writer::div(
$summary['error'] ? s($summary['error']) : '',
'alert alert-danger mt-2' . (!empty($summary['error']) ? '' : ' d-none'),
['id' => 'meetlet-registration-error']
),
'meetlet-license-status mb-2'
) .
\html_writer::div(
\html_writer::tag('button',
'<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">'
. '<path fill="currentColor" d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z"/></svg>'
. \html_writer::span(get_string('registration_activate', 'meetletacbfree'), 'meetlet-btn-with-icon__label'),
[
'type' => 'button',
'class' => 'meetlet-settings-btn meetlet-btn-with-icon meetlet-license-btn--free',
'id' => 'meetlet-registration-activate',
'title' => get_string('registration_activate_help', 'meetletacbfree'),
]
),
'',
$inactiveactions
) .
\html_writer::div(
\html_writer::tag('button',
icons::svg('refresh')
. \html_writer::span(get_string('registration_recheck', 'meetletacbfree'), 'meetlet-btn-with-icon__label'),
[
'type' => 'button',
'class' => 'btn btn-secondary meetlet-btn-with-icon',
'id' => 'meetlet-registration-recheck',
]
) .
($candeactivate ? ' ' .
\html_writer::tag('button',
'<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">'
. '<path fill="currentColor" d="M12 2a10 10 0 1 0 10 10h-2a8 8 0 1 1-8-8V2zm1 5v5.6l3.2 1.9-.8 1.4L11 13.2V7h2z"/></svg>'
. \html_writer::span(get_string('registration_deactivate', 'meetletacbfree'), 'meetlet-btn-with-icon__label'),
[
'type' => 'button',
'class' => 'btn btn-outline-danger meetlet-btn-with-icon',
'id' => 'meetlet-registration-deactivate',
]
) : ''),
'',
$activeactions
) .
\html_writer::div('', 'mt-3 d-none', ['id' => 'meetlet-registration-flash']) .
\html_writer::div('', '', [
'id' => 'meetlet-registration-config',
'data-ajaxurl' => $ajaxurl->out(false),
'data-sesskey' => sesskey(),
'data-api-input' => 'id_s_meetletacbfree_registration_api_url',
'data-label-active' => get_string('registration_status_active', 'meetletacbfree'),
'data-label-inactive' => get_string('registration_status_inactive', 'meetletacbfree'),
'data-label-domain' => get_string('registration_domain', 'meetletacbfree', '__V__'),
'data-label-installation' => get_string('registration_installation', 'meetletacbfree', '__V__'),
'data-confirm-activate-title' => get_string('registration_activate_confirm_title', 'meetletacbfree'),
'data-confirm-activate-ok' => get_string('registration_activate_confirm_ok', 'meetletacbfree'),
'data-confirm-deactivate-title' => get_string('registration_deactivate_confirm_title', 'meetletacbfree'),
'data-confirm-deactivate-ok' => get_string('registration_deactivate_confirm_ok', 'meetletacbfree'),
'data-confirm-deactivate' => get_string('registration_deactivate_confirm', 'meetletacbfree'),
'hidden' => 'hidden',
]) .
self::confirm_modal_html(),
'meetlet-registration-panel meetlet-settings-card'
);
return format_admin_setting($this, '', $html, '', true, '', null, $query);
}
protected static function confirm_modal_html(): string {
$activatebody = \html_writer::tag('p', get_string('registration_activate_confirm_lead', 'meetletacbfree'), [
'class' => 'meetlet-modal__lead',
]) .
\html_writer::tag('p', get_string('registration_activate_confirm_data', 'meetletacbfree'), [
'class' => 'meetlet-modal__keep',
]);
$deactivatebody = \html_writer::tag('p', get_string('registration_deactivate_confirm_lead', 'meetletacbfree'), [
'class' => 'meetlet-modal__lead',
]) .
\html_writer::div(
\html_writer::tag('p', get_string('registration_deactivate_confirm_warning', 'meetletacbfree'), [
'class' => 'meetlet-modal__callout-title',
]),
'meetlet-modal__callout'
);
return \html_writer::div(
\html_writer::div('', 'meetlet-modal__backdrop', ['data-meetlet-modal-dismiss' => '1']) .
\html_writer::div(
\html_writer::div(
\html_writer::tag('h3', '', [
'class' => 'meetlet-modal__title',
'id' => 'meetlet-registration-modal-title',
]) .
\html_writer::tag('button', '×', [
'type' => 'button',
'class' => 'meetlet-modal__close',
'data-meetlet-modal-dismiss' => '1',
'aria-label' => get_string('registration_modal_close', 'meetletacbfree'),
]),
'meetlet-modal__head'
) .
\html_writer::div('', 'meetlet-modal__body', ['id' => 'meetlet-registration-modal-body']) .
\html_writer::tag('template', $activatebody, ['id' => 'meetlet-registration-tpl-activate']) .
\html_writer::tag('template', $deactivatebody, ['id' => 'meetlet-registration-tpl-deactivate']) .
\html_writer::div(
\html_writer::tag('button', get_string('cancel', 'moodle'), [
'type' => 'button',
'class' => 'btn btn-secondary',
'data-meetlet-modal-dismiss' => '1',
]) .
\html_writer::tag('button', '', [
'type' => 'button',
'class' => 'btn btn-primary',
'id' => 'meetlet-registration-modal-confirm',
]),
'meetlet-modal__foot'
),
'meetlet-modal__dialog',
[
'role' => 'dialog',
'aria-modal' => 'true',
'aria-labelledby' => 'meetlet-registration-modal-title',
]
),
'meetlet-modal meetlet-modal--confirm',
[
'id' => 'meetlet-registration-modal',
'hidden' => 'hidden',
]
);
}
}
+88
View File
@@ -0,0 +1,88 @@
<?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/>.
/**
* Admin setting: default Moodle role → Adobe Connect role mapping.
*
* @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\admin;
use admin_setting;
use mod_meetletacbfree\local\roles;
defined('MOODLE_INTERNAL') || die();
/**
* Stores default participant rules JSON used for new activities / empty lists.
*/
class setting_rolemapping extends admin_setting {
public function __construct() {
parent::__construct(
'meetletacbfree/default_participants',
get_string('siterolemapping', 'meetletacbfree'),
get_string('siterolemapping_desc', 'meetletacbfree'),
json_encode(roles::builtin_site_defaults(), JSON_UNESCAPED_UNICODE)
);
}
public function get_setting() {
return $this->config_read($this->name);
}
public function write_setting($data) {
if (!is_array($data)) {
$data = [];
}
$json = roles::rules_json_from_matrix_post($data);
return $this->config_write($this->name, $json) ? '' : get_string('errorsetting', 'admin');
}
public function output_html($data, $query = '') {
$rules = roles::builtin_site_defaults();
if (is_string($data) && $data !== '') {
$decoded = json_decode($data, true);
if (is_array($decoded) && !empty($decoded)) {
$rules = roles::normalize_rules($decoded);
}
} else if (is_array($data) && !empty($data) && isset($data[0]) && is_array($data[0])) {
$rules = roles::normalize_rules($data);
} else if (is_array($data) && !empty($data)) {
// Posted radio matrix already (preview / validation).
$json = roles::rules_json_from_matrix_post($data);
$decoded = json_decode($json, true);
if (is_array($decoded)) {
$rules = roles::normalize_rules($decoded);
}
}
$html = access_reset_ui::toolbar('rolemapping');
$html .= roles::render_site_mapping_html($rules, $this->get_full_name());
return format_admin_setting(
$this,
$this->visiblename,
$html,
$this->description,
true,
'',
get_string('siterolemapping_defaultinfo', 'meetletacbfree'),
$query
);
}
}
+49
View File
@@ -0,0 +1,49 @@
<?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/>.
/**
* Admin settings tabs with a fixed hero above the tab bar.
*
* @package mod_meetletacbfree
* @copyright 2026 MeetLet
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_meetletacbfree\admin;
defined('MOODLE_INTERNAL') || die();
/**
* Extends Boost tabbed settings so a hero banner stays above all tabs.
*/
class settingspage_tabs extends \theme_boost_admin_settingspage_tabs {
/** @var string HTML rendered above the tab list. */
protected string $herohtml = '';
/**
* Set hero markup shown above tabs on every tab switch.
*/
public function set_hero(string $html): void {
$this->herohtml = $html;
}
/**
* @return string
*/
public function output_html() {
return $this->herohtml . parent::output_html();
}
}