Merge branch 'MDL-78619-master' of https://github.com/mickhawkins/moodle

This commit is contained in:
Huong Nguyen 2023-09-07 15:19:18 +07:00
commit bd372a63d2
No known key found for this signature in database
GPG Key ID: 40D88AB693A3E72A
18 changed files with 622 additions and 17 deletions

View File

@ -451,6 +451,9 @@ class api {
);
}
// Reload so the currently selected provider is used.
$this->reload();
// Update provider record from form data.
if ($instance !== null) {
$this->communication->get_form_provider()->save_form_data($instance);
@ -514,8 +517,8 @@ class api {
return;
}
// No userids? don't bother doing anything.
if (empty($userids)) {
// No user IDs or this provider does not manage users? No action required.
if (empty($userids) || !$this->communication->supports_user_features()) {
return;
}
@ -542,12 +545,14 @@ class api {
return;
}
if ($this->communication->get_provider() === processor::PROVIDER_NONE) {
$provider = $this->communication->get_provider();
if ($provider === processor::PROVIDER_NONE) {
return;
}
// No user ids? don't bother doing anything.
if (empty($userids)) {
// No user IDs or this provider does not manage users? No action required.
if (empty($userids) || !$this->communication->supports_user_features()) {
return;
}

View File

@ -46,13 +46,12 @@ class create_and_configure_room_task extends adhoc_task {
return;
}
// If the room is created successfully, add members to the room.
if ($communication->get_room_provider()->create_chat_room()) {
// If the room is created successfully, add members to the room if supported by the provider.
if ($communication->get_room_provider()->create_chat_room() && $communication->supports_user_features()) {
add_members_to_room_task::queue(
$communication
);
}
}
/**

View File

@ -0,0 +1,168 @@
<?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/>.
namespace communication_customlink;
use core_communication\processor;
/**
* class communication_feature to handle custom link specific actions.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class communication_feature implements
\core_communication\communication_provider,
\core_communication\room_chat_provider,
\core_communication\form_provider {
/** @var string The database table storing custom link specific data */
protected const CUSTOMLINK_TABLE = 'communication_customlink';
/** @var \cache_application $cache The application cache for this provider. */
protected \cache_application $cache;
/**
* Load the communication provider for the communication API.
*
* @param processor $communication The communication processor object.
* @return communication_feature The communication provider object.
*/
public static function load_for_instance(processor $communication): self {
return new self($communication);
}
/**
* Constructor for communication provider.
*
* @param processor $communication The communication processor object.
*/
private function __construct(
private \core_communication\processor $communication,
) {
$this->cache = \cache::make('communication_customlink', 'customlink');
}
/**
* Create room - room existence managed externally, always return true.
*
* @return boolean
*/
public function create_chat_room(): bool {
return true;
}
/**
* Update room - room existence managed externally, always return true.
*
* @return boolean
*/
public function update_chat_room(): bool {
return true;
}
/**
* Delete room - room existence managed externally, always return true.
*
* @return boolean
*/
public function delete_chat_room(): bool {
return true;
}
/**
* Fetch the URL for this custom link provider.
*
* @return string|null The custom URL, or null if not found.
*/
public function get_chat_room_url(): ?string {
global $DB;
$commid = $this->communication->get_id();
$cachekey = "link_url_{$commid}";
// Attempt to fetch the room URL from the cache.
if ($url = $this->cache->get($cachekey)) {
return $url;
}
// If not found in the cache, fetch the URL from the database.
$url = $DB->get_field(
self::CUSTOMLINK_TABLE,
'url',
['commid' => $commid],
);
// Cache the URL.
$this->cache->set($cachekey, $url);
return $url;
}
public function save_form_data(\stdClass $instance): void {
global $DB;
$commid = $this->communication->get_id();
$cachekey = "link_url_{$commid}";
$newrecord = new \stdClass();
$newrecord->url = $instance->customlinkurl ?? null;
$existingrecord = $DB->get_record(
self::CUSTOMLINK_TABLE,
['commid' => $commid],
'id, url'
);
if (!$existingrecord) {
// Create the record if it does not exist.
$newrecord->commid = $commid;
$DB->insert_record(self::CUSTOMLINK_TABLE, $newrecord);
} else if ($newrecord->url !== $existingrecord->url) {
// Update record if the URL has changed.
$newrecord->id = $existingrecord->id;
$DB->update_record(self::CUSTOMLINK_TABLE, $newrecord);
} else {
// No change made.
return;
}
// Cache the new URL.
$this->cache->set($cachekey, $newrecord->url);
}
public function set_form_data(\stdClass $instance): void {
if (!empty($instance->id) && !empty($this->communication->get_id())) {
$instance->customlinkurl = $this->get_chat_room_url();
}
}
public static function set_form_definition(\MoodleQuickForm $mform): void {
// Custom link description for the communication provider.
$mform->insertElementBefore($mform->createElement('text', 'customlinkurl',
get_string('customlinkurl', 'communication_customlink'),
'maxlength="255" size="40"'), 'addcommunicationoptionshere');
$mform->addHelpButton('customlinkurl', 'customlinkurl', 'communication_customlink');
$mform->setType('customlinkurl', PARAM_URL);
$mform->addRule('customlinkurl', get_string('required'), 'required', null, 'client');
$mform->addRule('customlinkurl', get_string('maximumchars', '', 255), 'maxlength', 255);
$mform->insertElementBefore($mform->createElement('static', 'customlinkurlinfo', '',
get_string('customlinkurlinfo', 'communication_customlink'),
'addcommunicationoptionshere'), 'addcommunicationoptionshere');
}
}

View File

@ -0,0 +1,39 @@
<?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/>.
namespace communication_customlink\privacy;
use core_privacy\local\metadata\null_provider;
/**
* Privacy Subsystem for communication_customlink implementing null_provider.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}

View File

@ -0,0 +1,35 @@
<?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/>.
/**
* Defined caches used internally by the provider.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
defined('MOODLE_INTERNAL') || die();
$definitions = [
'customlink' => [
'mode' => cache_store::MODE_APPLICATION,
'simplekeys' => true,
'simpledata' => true,
],
];

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="communication/provider/customlink/db" VERSION="20230826" COMMENT="Stores the link associated with a custom link communication instance."
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="communication_customlink" COMMENT="Stores the link associated with a custom link communication instance.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="commid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the communication record"/>
<FIELD NAME="url" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="URL being linked to by the provider"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="fk_commid" TYPE="foreign" FIELDS="commid" REFTABLE="communication" REFFIELDS="id" COMMENT="Foreign key for communication reference"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>

View File

@ -0,0 +1,30 @@
<?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/>.
/**
* Strings for component communication_customlink, language 'en'.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['cachedef_customlink'] = 'Custom link data';
$string['customlinkurl'] = 'Custom link URL';
$string['customlinkurl_help'] = 'Provide a link to an existing room from any communication service you would like to make available to participants - such as Microsoft Teams, Slack or Matrix.';
$string['customlinkurlinfo'] = 'The URL of an existing room already set up for this course.';
$string['pluginname'] = 'Custom link';
$string['privacy:metadata'] = 'Custom link communication plugin does not store any personal data.';

View File

@ -0,0 +1,86 @@
@communication @communication_customlink @javascript
Feature: Communication custom link
In order to facilitate easy access to an existing communication platform
As a teacher
I need to be able to make a custom communication link available in my course
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following config values are set as admin:
| enablecommunicationsubsystem | 1 |
Scenario: As a teacher I can configure a custom communication provider for my course
Given I am on the "Course 1" "Course" page logged in as "teacher1"
And "Chat to course participants" "button" should not be visible
When I navigate to "Communication" in current page administration
And the "Communication service" select box should contain "Custom link"
And I should not see "Custom link URL"
And I select "Custom link" from the "Communication service" singleselect
And I should see "Custom link URL"
And I set the following fields to these values:
| communicationroomname | Test URL |
| customlinkurl | #wwwroot#/communication/provider/customlink/tests/behat/fixtures/custom_link_test_page.php |
And I press "Save changes"
Then "Chat to course participants" "button" should be visible
And I click on "Chat to course participants" "button"
# Check the link hits the expected destination.
And I switch to a second window
And I should see "Example messaging service - teacher1" in the "region-main" "region"
And I close all opened windows
# Ensure any communication subsystem tasks have no impact on availability.
And I run all adhoc tasks
And I am on the "Course 1" course page
And "Chat to course participants" "button" should be visible
And I click on "Chat to course participants" "button"
And I switch to a second window
And I should see "Example messaging service - teacher1" in the "region-main" "region"
And I close all opened windows
And I log out
# Confirm student also has access to the custom link.
And I am on the "Course 1" "Course" page logged in as "student1"
And "Chat to course participants" "button" should be visible
And I click on "Chat to course participants" "button"
And I switch to a second window
And I should see "Example messaging service - student1" in the "region-main" "region"
Scenario: As a teacher I can disable and re-enable a custom communication provider for my course
Given I am on the "Course 1" "Course" page logged in as "teacher1"
And "Chat to course participants" "button" should not be visible
When I navigate to "Communication" in current page administration
And I select "Custom link" from the "Communication service" singleselect
And I set the following fields to these values:
| communicationroomname | Test URL |
| customlinkurl | #wwwroot#/communication/provider/customlink/tests/behat/fixtures/custom_link_test_page.php |
And I press "Save changes"
And "Chat to course participants" "button" should be visible
And I run all adhoc tasks
And I navigate to "Communication" in current page administration
And I select "None" from the "Communication service" singleselect
And I press "Save changes"
And "Chat to course participants" "button" should not be visible
And I run all adhoc tasks
And I am on the "Course 1" course page
And "Chat to course participants" "button" should not be visible
And I navigate to "Communication" in current page administration
And I select "Custom link" from the "Communication service" singleselect
And I set the following fields to these values:
| communicationroomname | Test URL |
| customlinkurl | #wwwroot#/communication/provider/customlink/tests/behat/fixtures/custom_link_test_page.php |
And I press "Save changes"
And "Chat to course participants" "button" should be visible
And I run all adhoc tasks
And I am on the "Course 1" course page
And "Chat to course participants" "button" should be visible
And I click on "Chat to course participants" "button"
And I switch to a second window
And I should see "Example messaging service - teacher1" in the "region-main" "region"

View File

@ -0,0 +1,40 @@
<?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/>.
/**
* A page which can be used to represent a messaging service while testing the custom link communication provider.
*
* The current Moodle user's username is listed in the heading to make it easier to confirm the page has been
* opened by the expected user.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../../../../config.php');
defined('BEHAT_SITE_RUNNING') || die();
global $OUTPUT, $PAGE, $USER;
$PAGE->set_url('/communication/provider/customlink/tests/behat/fixtures/custom_link_test_page.php');
require_login();
$PAGE->set_context(core\context\system::instance());
echo $OUTPUT->header();
echo "<h2>Example messaging service - {$USER->username}</h2>";
echo "<p>Imagine this is a wonderful messaging service being accessed directly from a link in Moodle!</p>";
echo $OUTPUT->footer();

View File

@ -0,0 +1,117 @@
<?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/>.
namespace communication_customlink;
use core_communication\processor;
use core_communication\communication_test_helper_trait;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../../../tests/communication_test_helper_trait.php');
/**
* Class communication_feature_test to test the custom link features implemented using the core interfaces.
*
* @package communication_customlink
* @category test
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \communication_customlink\communication_feature
*/
class communication_feature_test extends \advanced_testcase {
use communication_test_helper_trait;
public function setUp(): void {
parent::setUp();
$this->resetAfterTest();
$this->setup_communication_configs();
}
/**
* Test create, update and delete chat room.
*
* @covers ::load_for_instance
*/
public function test_load_for_instance(): void {
$communicationprocessor = $this->get_test_communication_processor();
$instance = communication_feature::load_for_instance($communicationprocessor);
$this->assertInstanceOf('communication_customlink\communication_feature', $instance);
}
/**
* Test create, update and delete chat room.
*
* @covers ::create_chat_room
* @covers ::update_chat_room
* @covers ::delete_chat_room
*/
public function test_create_update_delete_chat_room(): void {
$communicationprocessor = $this->get_test_communication_processor();
// Create, update and delete room should always return true because this provider contains
// a link to a room, but does not manage the existence of the room.
$createroomresult = $communicationprocessor->get_room_provider()->create_chat_room();
$updateroomresult = $communicationprocessor->get_room_provider()->update_chat_room();
$deleteroomresult = $communicationprocessor->get_room_provider()->delete_chat_room();
$this->assertTrue($createroomresult);
$this->assertTrue($updateroomresult);
$this->assertTrue($deleteroomresult);
}
/**
* Test save form data with provider's custom field and fetching with get_chat_room_url().
*
* @covers ::save_form_data
* @covers ::get_chat_room_url
*/
public function test_save_form_data(): void {
$communicationprocessor = $this->get_test_communication_processor();
$customlinkurl = 'https://moodle.org/message/index.php';
$formdatainstance = (object) ['customlinkurl' => $customlinkurl];
// Test the custom link URL is saved and can be retrieved as expected.
$communicationprocessor->get_form_provider()->save_form_data($formdatainstance);
$fetchedurl = $communicationprocessor->get_room_provider()->get_chat_room_url();
$this->assertEquals($customlinkurl, $fetchedurl);
}
/**
* Create a test custom link communication processor object.
*
* @return processor
*/
protected function get_test_communication_processor(): processor {
$course = $this->getDataGenerator()->create_course();
$instanceid = $course->id;
$component = 'core_course';
$instancetype = 'coursecommunication';
$selectedcommunication = 'communication_customlink';
$communicationroomname = 'communicationroom';
$communicationprocessor = processor::create_instance(
$selectedcommunication,
$instanceid,
$component,
$instancetype,
$communicationroomname,
);
return $communicationprocessor;
}
}

View File

@ -0,0 +1,30 @@
<?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/>.
/**
* Version information for communication_customlink.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'communication_customlink';
$plugin->version = 2023082600;
$plugin->requires = 2023082600;
$plugin->maturity = MATURITY_ALPHA;

View File

@ -167,6 +167,8 @@ class communication_feature_test extends \advanced_testcase {
],
);
$provider->reload();
// Then call the provider's update method to actually perform the change.
$provider->update_chat_room();

View File

@ -107,6 +107,7 @@ class behat_transformations extends behat_base {
* @return TableNode The transformed table
*/
public function tablenode_transformations(TableNode $tablenode) {
global $CFG;
// Walk through all values including the optional headers.
$rows = $tablenode->getRows();
foreach ($rows as $rowkey => $row) {
@ -123,6 +124,11 @@ class behat_transformations extends behat_base {
$rows[$rowkey][$colkey] = $this->get_transformed_timestamp($match[1]);
}
}
// Transform wwwroot.
if (preg_match('/#wwwroot#/', $rows[$rowkey][$colkey])) {
$rows[$rowkey][$colkey] = $this->replace_wwwroot($rows[$rowkey][$colkey]);
}
}
}
@ -133,6 +139,18 @@ class behat_transformations extends behat_base {
return $tablenode;
}
/**
* Convert #wwwroot# to the wwwroot config value, so it is
* possible to reference fully qualified URLs within the site.
*
* @Transform /^((.*)#wwwroot#(.*))$/
* @param string $string
* @return string
*/
public function arg_insert_wwwroot(string $string): string {
return $this->replace_wwwroot($string);
}
/**
* Replaces $NASTYSTRING vars for a nasty string.
*
@ -176,4 +194,15 @@ class behat_transformations extends behat_base {
return $time;
}
}
/**
* Replace #wwwroot# with the actual wwwroot config value.
*
* @param string $string String to attempt the replacement in.
* @return string
*/
protected function replace_wwwroot(string $string): string {
global $CFG;
return str_replace('#wwwroot#', $CFG->wwwroot, $string);
}
}

View File

@ -47,7 +47,8 @@ $drawer-bg: darken($body-bg, 5%) !default;
[data-region="drawer"] {
padding: $drawer-padding-x $drawer-padding-y;
}
.jsenabled .btn-footer-popover {
.jsenabled .btn-footer-popover,
.jsenabled .btn-footer-communication {
@include transition(0.2s);
}
}

View File

@ -228,7 +228,8 @@
margin-left: 0;
margin-right: $drawer-right-width;
padding-right: 1rem;
.jsenabled & .btn-footer-popover {
.jsenabled & .btn-footer-popover,
.jsenabled & .btn-footer-communication {
right: calc(#{$drawer-right-width} + 2rem);
}
}

View File

@ -29586,12 +29586,14 @@ span.editinstructions .alert-link {
[data-region=drawer] {
padding: 20px 20px;
}
.jsenabled .btn-footer-popover {
.jsenabled .btn-footer-popover,
.jsenabled .btn-footer-communication {
transition: 0.2s;
}
}
@media (min-width: 576px) and (prefers-reduced-motion: reduce) {
.jsenabled .btn-footer-popover {
.jsenabled .btn-footer-popover,
.jsenabled .btn-footer-communication {
transition: none;
}
}
@ -36653,7 +36655,7 @@ span[data-flexitour=container][x-placement=right] div[data-role=arrow]:after, sp
margin-right: 315px;
padding-right: 1rem;
}
.jsenabled #page.drawers.show-drawer-right .btn-footer-popover {
.jsenabled #page.drawers.show-drawer-right .btn-footer-popover, .jsenabled #page.drawers.show-drawer-right .btn-footer-communication {
right: calc(315px + 2rem);
}
}

View File

@ -38,7 +38,7 @@
<footer id="page-footer" class="footer-popover bg-white">
<div data-region="footer-container-popover">
{{#output.has_communication_links}}
<button onclick="window.open('{{output.communication_url}}', '_blank')" class="btn btn-icon bg-primary text-white icon-no-margin btn-footer-communication" aria-label="{{#str}}communicationroomlink, course{{/str}}">
<button onclick="window.open('{{output.communication_url}}', '_blank', 'noreferrer')" class="btn btn-icon bg-primary text-white icon-no-margin btn-footer-communication" aria-label="{{#str}}communicationroomlink, course{{/str}}">
{{#pix}}t/messages-o, core{{/pix}}
</button>
{{/output.has_communication_links}}

View File

@ -29586,12 +29586,14 @@ span.editinstructions .alert-link {
[data-region=drawer] {
padding: 20px 20px;
}
.jsenabled .btn-footer-popover {
.jsenabled .btn-footer-popover,
.jsenabled .btn-footer-communication {
transition: 0.2s;
}
}
@media (min-width: 576px) and (prefers-reduced-motion: reduce) {
.jsenabled .btn-footer-popover {
.jsenabled .btn-footer-popover,
.jsenabled .btn-footer-communication {
transition: none;
}
}
@ -36587,7 +36589,7 @@ span[data-flexitour=container][x-placement=right] div[data-role=arrow]:after, sp
margin-right: 315px;
padding-right: 1rem;
}
.jsenabled #page.drawers.show-drawer-right .btn-footer-popover {
.jsenabled #page.drawers.show-drawer-right .btn-footer-popover, .jsenabled #page.drawers.show-drawer-right .btn-footer-communication {
right: calc(315px + 2rem);
}
}