Skip to content

Commit

Permalink
Merge pull request #292 from ferishili/issue-280
Browse files Browse the repository at this point in the history
Default Room + Widget, fixes #280 & #281
  • Loading branch information
ferishili authored Aug 19, 2021
2 parents 8965bbc + 9f20d35 commit fb28644
Show file tree
Hide file tree
Showing 14 changed files with 385 additions and 8 deletions.
33 changes: 31 additions & 2 deletions MeetingPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@

use Meetings\AppFactory;
use Meetings\RouteMap;
use Meetings\WidgetHandler;

require_once 'compat/StudipVersion.php';

class MeetingPlugin extends StudIPPlugin implements StandardPlugin, SystemPlugin
class MeetingPlugin extends StudIPPlugin implements PortalPlugin, StandardPlugin, SystemPlugin
{
const GETTEXT_DOMAIN = 'meetings';
const NAVIGATION_ITEM_NAME = 'video-conferences';
Expand Down Expand Up @@ -481,8 +482,36 @@ public static function getCourseTypeName($server_course_type)
return '';
}

/**
* Return the template for the widget.
*
* @return Flexi_PhpTemplate The template containing the widget contents
*/
public function getPortalTemplate()
{
return false;
require_once __DIR__ . '/vendor/autoload.php';

// We need to use "nobody" rights for Upload Slides,
// but in here we have to prevent that right,
// in order to not to show the template in login page and so on.
if ('nobody' === $GLOBALS['user']->id) {
return;
}

$template_factory = new Flexi_TemplateFactory(__DIR__ . "/templates");
$template = $template_factory->open("index.php");

$template->set_attribute('items', WidgetHandler::getMeetingsForWidget());

$texts = [
'empty' => $this->_('Derzeit finden keine Meetings in den gebuchten Kursen statt.'),
'current' => $this->_('Derzeitige Meetings'),
'upcoming' => $this->_('Kommende Meetings'),
'to_course' => $this->_('Zur Meeting-Liste'),
'to_meeting' => $this->_('Direkt zum Meeting')
];
$template->set_attribute('texts', $texts);

return $template;
}
}
36 changes: 36 additions & 0 deletions lib/MeetingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,40 @@ public function checkGroupPermission($group_id, $cid)
&& $perm->have_studip_perm('tutor', $cid, $user->id)
);
}

/**
* Selects/Deselect a room as default for a course.
* When a room is selected as default, other default room will be deselected automatically.
*
* @param string $meeting_id room id
* @param string $cid course id
* @param int $is_default the default flag
*/
public function manageCourseDefaultRoom($meeting_id, $cid, $is_default) {

$meetingCourse = new MeetingCourse([$meeting_id, $cid]);
$meetingCourse->is_default = $is_default;
$meetingCourse->store();

// Check for other records.
$otherCourseMeetings = MeetingCourse::findBySQL('course_id = ? AND meeting_id != ?', [$cid, $meeting_id]);
// Make sure there is no other default room if there are other records and we select this room as default.
if (!empty($otherCourseMeetings) && $is_default == 1) {
// Loop through all other courseMeeting records.
foreach ($otherCourseMeetings as $meetingCourse) {
$meetingCourse->is_default = 0;
$meetingCourse->store();
}
}
}

/**
* When There is only one room in course, this method helps to auto select it as default.
*
* @param MeetingCourse $meetingCourse the meeting course object
*/
public function autoSelectCourseDefaultRoom(MeetingCourse $meetingCourse) {
$meetingCourse->is_default = 1;
$meetingCourse->store();
}
}
4 changes: 4 additions & 0 deletions lib/Routes/Rooms/RoomAdd.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ public function __invoke(Request $request, Response $response, $args)
$meeting->remote_id = $meetingParameters->getRemoteId();
$meeting->store();

// Handel course default room.
$is_default = isset($json['is_default']) ? $json['is_default'] : 0;
$this->manageCourseDefaultRoom($meeting->id, $json['cid'], $is_default);

$message = [
'text' => I18N::_('Raum wurde erfolgreich erstellt.'),
'type' => 'success'
Expand Down
5 changes: 5 additions & 0 deletions lib/Routes/Rooms/RoomEdit.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public function __invoke(Request $request, Response $response, $args)
$meetingCourse->group_id = ((empty($json['group_id']) ? null : $json['group_id']));
$meetingCourse->store();
}

// Handel course default room.
$is_default = isset($json['is_default']) ? intval($json['is_default']) : 0;
$this->manageCourseDefaultRoom($room_id, $json['cid'], $is_default);

$meeting = $meetingCourse->meeting;
$meeting->name = $name;
!isset($json['recordingUrl']) ?: $meeting->recording_url = utf8_decode($json['recording_url']);
Expand Down
10 changes: 10 additions & 0 deletions lib/Routes/Rooms/RoomsList.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public function __invoke(Request $request, Response $response, $args)

if ($perm->have_studip_perm('tutor', $cid)) {
$meeting_course_list_raw = MeetingCourse::findByCourseId($cid);

// Default Room:
// In case there is only one room and it is not default, we forcefully select the room as default.
if (count($meeting_course_list_raw) == 1 && $meeting_course_list_raw[0]->is_default == 0) {
$this->autoSelectCourseDefaultRoom($meeting_course_list_raw[0]);
}
} else {
$meeting_course_list_raw = MeetingCourse::findActiveByCourseId($cid);
}
Expand Down Expand Up @@ -170,6 +176,10 @@ public function __invoke(Request $request, Response $response, $args)
throw new Error(implode ("\n", $error_messages), $error_code);
}
} else {
// Sort the list based on default. We want to push the default room to the top.
$defaults = array_column($course_rooms_list, 'is_default');
array_multisort($defaults, SORT_DESC, $course_rooms_list);

return $this->createResponse($course_rooms_list, $response);
}
}
Expand Down
132 changes: 132 additions & 0 deletions lib/WidgetHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

namespace Meetings;

use ElanEv\Model\MeetingCourse;
// use CourseMember;
use CourseDate;
use Course;
use PluginEngine;

/**
* WidgetHandler.php - contains function for MeetingPlugin widget handling.
*
* @author Farbod Zamani Broujeni (zamani@elan-ev.de)
*/
class WidgetHandler
{
/**
* Get all current and up-coming meetings
*
* @param array $userCourses list of courses which user is participated and has a default meeting room
* @return array
*/
public static function getMeetingsForWidget()
{
global $user, $perm;

// Handle Admin users.
if ($perm->have_perm('admin') || $perm->have_perm('root')) {
$courses = Course::findBySQL(
'INNER JOIN vc_meeting_course AS mc ON seminar_id = mc.course_id
WHERE mc.is_default = 1 AND mc.active = 1'
);
} else {
$courses = Course::findBySQL(
'INNER JOIN seminar_user AS su USING(Seminar_id)
INNER JOIN vc_meeting_course AS mc ON seminar_id = mc.course_id
WHERE su.user_id = ? AND mc.is_default = 1 AND mc.active = 1 ORDER BY mkdate ASC',
[$user->id]
);
}

$widgetItemsArray = self::getTodaysMeetings($courses);

return $widgetItemsArray;
}

/**
* Sort the meetings which are happening or will be happened today.
*
* @param array $courses list of courses which user is participated and has a default meeting room
* @return array
*/
private function getTodaysMeetings($courses) {
if (empty($courses)) {
return [];
}

$currents = [];
$upcomings = [];

$whereCurrent = 'range_id = ? AND date <= ? AND end_time >= ?';
$whereUpcoming = 'range_id = ? AND date > ? AND end_time < ? ORDER BY date ASC';
$now = strtotime('now');
$tonight = strtotime('tomorrow midnight');

foreach ($courses as $course) {
$currentSessionDate = CourseDate::findOneBySQL($whereCurrent,
[
$course->seminar_id,
$now,
$now,
]);
if ($currentSessionDate) {
$widgetItem = self::prepareWidgetItems($course, $currentSessionDate);
if (!empty($widgetItem)) {
$currents[] = $widgetItem;
}
// Here we stop the process by simply passing the itteration.
continue;
}

$upcomingSessionDate = CourseDate::findOneBySQL($whereUpcoming,
[
$course->seminar_id,
$now,
$tonight,
]);
if ($upcomingSessionDate) {
$upcomings[] = self::prepareWidgetItems($course, $upcomingSessionDate);
}
}

$widgetItemsArray = [];

if (!empty($currents)) {
$widgetItemsArray['current'] = $currents;
}

if (!empty($upcomings)) {
$widgetItemsArray['upcoming'] = $upcomings;
}

return $widgetItemsArray;
}

/**
* Prepare the widget item.
*
* @param Course $course course object
* @param CourseDate $courseDate course date object
* @return array
*/
private function prepareWidgetItems(Course $course, CourseDate $courseDate) {
$meetingCourse = MeetingCourse::findOneBySQL('course_id = ? AND is_default = 1', [$course->seminar_id]);
$widgetItem = [
'item_title' => $course->name . ': ' . _('Heute') . date(", H:i", $courseDate->date) . " - " . date("H:i", $courseDate->end_time),
'course_id' => $course->seminar_id,
'course_title' => $course->name,
'termin_id' => $courseDate->termin_id,
'termin_start' => $courseDate->date,
'termin_end' => $courseDate->end_time,
'termin_fullname' => $courseDate->getFullname('verbose'),
'meeing_name' => $meetingCourse->meeting->name,
'meeting_id' => $meetingCourse->meeting->id,
'meeting_course_url' => PluginEngine::getURL('meetingplugin', ['cid' => $course->id], 'index', true),
'meeting_join_url' => PluginEngine::getURL('meetingplugin', [], "api/rooms/join/{$course->id}/{$meetingCourse->meeting->id}")
];

return $widgetItem;
}
}
49 changes: 49 additions & 0 deletions migrations/039_add_default_column_to_meeting_course_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/**
* Adding default column to the meeting course table to be used it in widget and appoinments.
*
* @author Farbod Zamani Broujeni (zamani@elan-ev.de)
*/
class AddDefaultColumnToMeetingCourseTable extends Migration
{
/**
* {@inheritdoc}
*/
public function description()
{
return 'Adding is_default column to the meeting course table to be used it in widget and appoinments.';
}

/**
* {@inheritdoc}
*/
public function up()
{
$db = DBManager::get();
$db->exec(
'ALTER TABLE
vc_meeting_course
ADD COLUMN
is_default TINYINT NOT NULL DEFAULT 0 AFTER active'
);

SimpleORMap::expireTableScheme();
}

/**
* {@inheritdoc}
*/
public function down()
{
$db = DBManager::get();
$db->exec(
'ALTER TABLE
vc_meeting_course
DROP COLUMN
is_default'
);

SimpleORMap::expireTableScheme();
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"translate": "gettext-extract --attribute v-i18n --attribute v-translate --output locale/en/LC_MESSAGES/meetings_js.pot $(find vueapp -type f -name '*.vue') && sh translate.sh && gettext-compile --output vueapp/i18n/translations.json locale/en/LC_MESSAGES/meetings.po",
"prod": "webpack --mode=\"production\"",
"prezip": "npm install && npm run prod && composer install --no-dev",
"zip": "zip -r Meetings-V$npm_package_version.zip app assets compat Driver images lib locale migrations static vendor LICENSE README.md bootstrap.php MeetingPlugin.php plugin.manifest"
"zip": "zip -r Meetings-V$npm_package_version.zip app assets compat Driver images lib templates locale migrations static vendor LICENSE README.md bootstrap.php MeetingPlugin.php plugin.manifest"
},
"author": "ELAN e.V. (tgloeggl@uos.de)",
"dependencies": {
Expand Down
66 changes: 66 additions & 0 deletions templates/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<article class="studip">
<? if (count($items)): ?>
<? if (isset($items['current'])): ?>
<article class="studip toggle open <?= ContentBoxHelper::classes('meeting-widget-current') ?>" id="meeting-widget-current">
<header>
<h1>
<a href="<?= ContentBoxHelper::href('meeting-widget-current') ?>">
<?= htmlReady($texts['current']) ?>
</a>
</h1>
</header>
<? foreach ($items['current'] as $item): ?>
<article class="studip toggle">
<header>
<h1 onclick="window.open('<?= $item['meeting_join_url'] ?>', '_self');return false;">
<?= Icon::create('chat', 'inactive')->asImg(['class' => 'text-bottom']) ?>
<?= htmlReady($item['item_title']) ?>
</h1>
<nav>
<a href="<?= $item['meeting_join_url'] ?>" title="<?= htmlReady($texts['to_meeting']) ?>" target="_blank">
<?= Icon::create('door-enter', 'clickable')->asImg(['class' => 'text-bottom']) ?>
</a>
<a href="<?= $item['meeting_course_url'] ?>" title="<?= htmlReady($texts['to_course']) ?>">
<?= Icon::create('seminar', 'clickable')->asImg(['class' => 'text-bottom']) ?>
</a>
</nav>
</header>
</article>
<? endforeach; ?>
</article>
<? endif; ?>
<? if (isset($items['upcoming'])): ?>
<article class="studip toggle <?= \ContentBoxHelper::classes('meeting-widget-upcoming') ?>" id="meeting-widget-upcoming">
<header>
<h1>
<a href="<?= \ContentBoxHelper::href('meeting-widget-upcoming') ?>">
<?= htmlReady($texts['upcoming']) ?>
</a>
</h1>
</header>
<? foreach ($items['upcoming'] as $item): ?>
<article class="studip toggle">
<header>
<h1 onclick="window.open('<?= $item['meeting_join_url'] ?>', '_self');return false;">
<?= Icon::create('chat', 'inactive')->asImg(['class' => 'text-bottom']) ?>
<?= htmlReady($item['item_title']) ?>
</h1>
<nav>
<a href="<?= $item['meeting_join_url'] ?>" target="_blank">
<?= Icon::create('door-enter', 'clickable')->asImg(['class' => 'text-bottom']) ?>
</a>
<a href="<?= $item['meeting_course_url'] ?>">
<?= Icon::create('seminar', 'clickable')->asImg(['class' => 'text-bottom']) ?>
</a>
</nav>
</header>
</article>
<? endforeach; ?>
</article>
<? endif; ?>
<? else: ?>
<section>
<?= htmlReady($texts['empty']) ?>
</section>
<? endif; ?>
</article>
Loading

0 comments on commit fb28644

Please sign in to comment.