aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/consultation/admin.php91
-rw-r--r--app/controllers/consultation/consultation_controller.php6
-rw-r--r--app/views/consultation/admin/block-responsibilities.php50
-rw-r--r--app/views/consultation/admin/create.php25
-rw-r--r--app/views/consultation/admin/edit.php34
-rw-r--r--app/views/consultation/admin/edit_room.php21
-rw-r--r--app/views/consultation/admin/index.php9
-rw-r--r--app/views/consultation/block-description.php20
-rw-r--r--db/migrations/1.269_fix_missing_consultation_events.php7
-rw-r--r--db/migrations/5.1.7_consultation_multiple_responsible_ranges.php99
-rw-r--r--lib/classes/ConsultationMailer.php14
-rw-r--r--lib/models/CalendarEvent.class.php22
-rw-r--r--lib/models/ConsultationBlock.php94
-rw-r--r--lib/models/ConsultationBooking.php29
-rw-r--r--lib/models/ConsultationEvent.php34
-rw-r--r--lib/models/ConsultationResponsibility.php126
-rw-r--r--lib/models/ConsultationSlot.php121
-rw-r--r--lib/models/Course.class.php2
-rw-r--r--lib/models/Institute.class.php191
-rw-r--r--lib/models/StatusgruppeUser.php4
-rw-r--r--lib/models/Statusgruppen.php17
-rw-r--r--lib/visual.inc.php4
-rw-r--r--resources/assets/stylesheets/less/lists.less6
23 files changed, 721 insertions, 305 deletions
diff --git a/app/controllers/consultation/admin.php b/app/controllers/consultation/admin.php
index 12c77a7..0ca50b3 100644
--- a/app/controllers/consultation/admin.php
+++ b/app/controllers/consultation/admin.php
@@ -140,11 +140,11 @@ class Consultation_AdminController extends ConsultationController
$block = new ConsultationBlock();
$block->range = $this->range;
- $this->responsible = $block->responsible_persons;
+ $this->responsible = $block->getPossibleResponsibilites();
} elseif ($this->range instanceof Institute) {
$block = new ConsultationBlock();
$block->range = $this->range;
- $this->responsible = $block->responsible_persons;
+ $this->responsible = $block->getPossibleResponsibilites();
}
}
@@ -182,10 +182,20 @@ class Consultation_AdminController extends ConsultationController
$block->confirmation_text = trim(Request::get('confirmation-text')) ?: null;
$block->note = Request::get('note');
$block->size = Request::int('size', 1);
- $block->teacher_id = Request::option('teacher_id') ?: null;
$block->createSlots(Request::int('duration'));
$stored += $block->store();
+
+ // Store block responsibilites
+ foreach (Request::getArray('responsibilities') as $type => $ids) {
+ foreach ($ids as $id) {
+ ConsultationResponsibility::create([
+ 'block_id' => $block->id,
+ 'range_id' => $id,
+ 'range_type' => $type,
+ ]);
+ }
+ }
}
} catch (OverlapException $e) {
$this->keepRequest();
@@ -211,13 +221,9 @@ class Consultation_AdminController extends ConsultationController
$this->relocate('consultation/admin');
}
- public function note_action($block_id, $slot_id = null, $page = 0)
+ public function note_action($block_id, $slot_id, $page = 0)
{
- if ($slot_id) {
- PageLayout::setTitle(_('Anmerkung zu diesem Termin bearbeiten'));
- } else {
- PageLayout::setTitle(_('Anmerkung zu diesem Block bearbeiten'));
- }
+ PageLayout::setTitle(_('Anmerkung zu diesem Termin bearbeiten'));
$this->block = $this->loadBlock($block_id);
$this->slot_id = $slot_id;
@@ -228,20 +234,10 @@ class Consultation_AdminController extends ConsultationController
$note = trim(Request::get('note'));
- $changed = false;
- if ($slot_id) {
- $slot = $this->block->slots->find($slot_id);
- $slot->note = $note;
- $changed = $slot->store();
- } else {
- $this->block->note = $note;
- foreach ($this->block->slots as $slot) {
- $slot->note = '';
- }
- $changed = $this->block->store();
- }
- if ($changed) {
- PageLayout::postSuccess(_('Der Block wurde bearbeitet'));
+ $slot = $this->block->slots->find($slot_id);
+ $slot->note = $note;
+ if ($slot->store()) {
+ PageLayout::postSuccess(_('Die Anmerkung wurde bearbeitet'));
}
if ($this->block->is_expired) {
@@ -344,20 +340,60 @@ class Consultation_AdminController extends ConsultationController
}
}
- public function edit_room_action($block_id, $page = 0)
+ public function edit_action($block_id, $page = 0)
{
- PageLayout::setTitle(_('Ort des Blocks bearbeiten'));
+ PageLayout::setTitle(_('Block bearbeiten'));
$this->block = $this->loadBlock($block_id);
$this->page = $page;
+
+ $this->responsible = false;
+ if ($this->block->range instanceof Course || $this->block->range instanceof Institute) {
+ $this->responsible = $this->block->getPossibleResponsibilites();
+ }
}
- public function store_room_action($block_id, $page = 0)
+ public function store_edited_action($block_id, $page = 0)
{
CSRFProtection::verifyUnsafeRequest();
$this->block = $this->loadBlock($block_id);
- $this->block->room = Request::get('room');
+ $this->block->room = trim(Request::get('room'));
+ $this->block->note = trim(Request::get('note'));
+
+ foreach ($this->block->slots as $slot) {
+ $slot->note = '';
+ }
+
+ // Store block responsibilites
+ $responsibilities = array_merge(
+ ['user' => [], 'statusgroup' => [], 'institute' => []],
+ Request::getArray('responsibilities')
+ );
+ foreach ($responsibilities as $type => $ids) {
+ $of_type = $this->block->responsibilities->filter(function ($responsibility) use ($type) {
+ return $responsibility->range_type === $type;
+ });
+
+ // Delete removed responsibilites
+ $of_type->each(function ($responsibility) use ($ids) {
+ if (!in_array($responsibility->range_id, $ids)) {
+ $responsibility->delete();
+ }
+ });
+ // Add new responsibilities
+ foreach ($ids as $id) {
+ if (!$of_type->findOneBy('range_id', $id)) {
+ ConsultationResponsibility::create([
+ 'block_id' => $this->block->id,
+ 'range_id' => $id,
+ 'range_type' => $type,
+ ]);
+ }
+ }
+ }
+
+
$this->block->store();
PageLayout::postSuccess(_('Der Block wurde gespeichert.'));
@@ -577,7 +613,6 @@ class Consultation_AdminController extends ConsultationController
function ($slot) use (&$deleted) {
$index = $slot->is_expired ? 'expired' : 'current';
- $slot->removeEvent();
$deleted[$index] += $slot->delete();
},
"JOIN consultation_blocks USING (block_id) WHERE range_id = ? AND range_type = ?",
diff --git a/app/controllers/consultation/consultation_controller.php b/app/controllers/consultation/consultation_controller.php
index c69ab16..eba81ff 100644
--- a/app/controllers/consultation/consultation_controller.php
+++ b/app/controllers/consultation/consultation_controller.php
@@ -77,6 +77,12 @@ abstract class ConsultationController extends AuthenticatedController
$this->flash['request'] = Request::getInstance()->getIterator()->getArrayCopy();
}
+ /**
+ * @param $block_id
+ *
+ * @return ConsultationBlock|ConsultationBlock[]
+ * @throws AccessDeniedException
+ */
protected function loadBlock($block_id)
{
if (is_array($block_id)) {
diff --git a/app/views/consultation/admin/block-responsibilities.php b/app/views/consultation/admin/block-responsibilities.php
new file mode 100644
index 0000000..ea3531a
--- /dev/null
+++ b/app/views/consultation/admin/block-responsibilities.php
@@ -0,0 +1,50 @@
+<?php
+$block = $block ?? false;
+$selected = function ($type, $id) use ($block) {
+ if (!$block ) {
+ return '';
+ }
+ $matched = $block->responsibilities->filter(function ($responsibility) use ($type, $id) {
+ return $responsibility->range_type === $type && $responsibility->range_id === $id;
+ });
+ return count($matched) > 0 ? 'selected' : '';
+}
+?>
+<? if (!empty($responsible['users'])): ?>
+ <label>
+ <?= _('Durchführende Person(en)') ?>
+ <select name="responsibilities[user][]" multiple class="nested-select">
+ <? foreach ($responsible['users'] as $user): ?>
+ <option value="<?= htmlReady($user->id) ?>" <?= $selected('user', $user->id) ?>>
+ <?= htmlReady($user->getFullName()) ?>
+ </option>
+ <? endforeach; ?>
+ </select>
+ </label>
+<? endif; ?>
+
+<? if (!empty($responsible['groups'])): ?>
+ <label>
+ <?= _('Durchführende Gruppe(n)') ?>
+ <select name="responsibilities[statusgroup][]" multiple class="nested-select">
+ <? foreach ($responsible['groups'] as $group): ?>
+ <option value="<?= htmlReady($group->id) ?>" <?= $selected('statusgroup', $group->id) ?>>
+ <?= htmlReady($group->getName()) ?>
+ </option>
+ <? endforeach; ?>
+ </select>
+ </label>
+<? endif; ?>
+
+<? if (!empty($responsible['institutes'])): ?>
+ <label>
+ <?= _('Durchführende Einrichtung(en)') ?>
+ <select name="responsibilities[institute][]" multiple class="nested-select">
+ <? foreach ($responsible['institutes'] as $institute): ?>
+ <option value="<?= htmlReady($institute->id) ?>" <?= $selected('institute', $institute->id) ?>>
+ <?= htmlReady($institute->getFullname()) ?>
+ </option>
+ <? endforeach; ?>
+ </select>
+ </label>
+<? endif; ?>
diff --git a/app/views/consultation/admin/create.php b/app/views/consultation/admin/create.php
index 4630cee..d8727ef 100644
--- a/app/views/consultation/admin/create.php
+++ b/app/views/consultation/admin/create.php
@@ -36,7 +36,7 @@ $intervals = [
<fieldset>
<legend>
- <?= _('Neue Terminblöcke anlegen') ?>
+ <?= _('Ort und Zeit') ?>
</legend>
<label>
@@ -119,19 +119,18 @@ $intervals = [
<input required type="text" name="size" id="size"
min="1" max="50" value="<?= Request::int('size', 1) ?>">
</label>
+ </fieldset>
- <? if ($responsible): ?>
- <label>
- <?= _('Durchführende Person') ?>
- <select name="teacher_id">
- <option value=""></option>
- <? foreach ($responsible as $user): ?>
- <option value="<?= htmlReady($user->id) ?>">
- <?= htmlReady($user->getFullName()) ?>
- </option>
- <? endforeach; ?>
- </select>
- <? endif; ?>
+<? if ($responsible): ?>
+ <fieldset>
+ <legend><?= _('Durchführende Person(en), Gruppe(n) oder Einrichtung(en)') ?></legend>
+
+ <?= $this->render_partial('consultation/admin/block-responsibilities.php', compact('responsible')) ?>
+ </fieldset>
+<? endif; ?>
+
+ <fieldset>
+ <legend><?= _('Weitere Einstellungen') ?></legend>
<label>
<?= _('Information zu den Terminen in diesem Block') ?>
diff --git a/app/views/consultation/admin/edit.php b/app/views/consultation/admin/edit.php
new file mode 100644
index 0000000..6f1911a
--- /dev/null
+++ b/app/views/consultation/admin/edit.php
@@ -0,0 +1,34 @@
+<form action="<?= $controller->store_edited($block, $page) ?>" method="post" class="default">
+ <?= CSRFProtection::tokenTag() ?>
+
+ <?= MessageBox::info(
+ _('Das Ändern der Informationen wird auch alle Termine dieses Blocks ändern.')
+ )->hideClose() ?>
+
+ <fieldset>
+ <legend><?= _('Terminblock bearbeiten') ?></legend>
+
+ <label>
+ <span class="required"><?= _('Ort') ?></span>
+ <input required type="text" name="room" placeholder="<?= _('Ort') ?>"
+ value="<?= htmlReady($block->room) ?>">
+ </label>
+
+ <label>
+ <?=_('Information zu den Terminen in diesem Block') ?> (<?= _('Öffentlich einsehbar') ?>)
+ <textarea name="note"><?= htmlReady($block->note ) ?></textarea>
+ </label>
+
+ <? if ($responsible): ?>
+ <?= $this->render_partial('consultation/admin/block-responsibilities.php', compact('responsible', 'block')) ?>
+ <? endif; ?>
+ </fieldset>
+
+ <footer data-dialog-button>
+ <?= Studip\Button::createAccept(_('Speichern')) ?>
+ <?= Studip\LinkButton::createCancel(
+ _('Abbrechen'),
+ $controller->indexURL($page)
+ ) ?>
+ </footer>
+</form>
diff --git a/app/views/consultation/admin/edit_room.php b/app/views/consultation/admin/edit_room.php
index 6c52436..e69de29 100644
--- a/app/views/consultation/admin/edit_room.php
+++ b/app/views/consultation/admin/edit_room.php
@@ -1,21 +0,0 @@
-<form action="<?= $controller->store_room($block, $page) ?>" method="post" class="default">
- <?= CSRFProtection::tokenTag() ?>
-
- <fieldset>
- <legend><?= _('Ort des Terminblocks bearbeiten') ?></legend>
-
- <label>
- <span class="required"><?= _('Ort') ?></span>
- <input required type="text" name="room" placeholder="<?= _('Ort') ?>"
- value="<?= htmlReady($block->room) ?>">
- </label>
- </fieldset>
-
- <footer data-dialog-button>
- <?= Studip\Button::createAccept(_('Speichern')) ?>
- <?= Studip\LinkButton::createCancel(
- _('Abbrechen'),
- $controller->indexURL($page)
- ) ?>
- </footer>
-</form>
diff --git a/app/views/consultation/admin/index.php b/app/views/consultation/admin/index.php
index 5d0a7da..6ee7550 100644
--- a/app/views/consultation/admin/index.php
+++ b/app/views/consultation/admin/index.php
@@ -45,13 +45,8 @@
</th>
<th class="actions">
<?= ActionMenu::get()->addLink(
- $controller->edit_roomURL($block['block'], $page),
- _('Raum bearbeiten'),
- Icon::create('edit'),
- ['data-dialog' => 'size=auto']
- )->addLink(
- $controller->noteURL($block['block'], 0, $page),
- _('Information bearbeiten'),
+ $controller->editURL($block['block'], 0, $page),
+ _('Bearbeiten'),
Icon::create('edit'),
['data-dialog' => 'size=auto']
)->addLink(
diff --git a/app/views/consultation/block-description.php b/app/views/consultation/block-description.php
index df3d0bd..4d037ff 100644
--- a/app/views/consultation/block-description.php
+++ b/app/views/consultation/block-description.php
@@ -6,13 +6,6 @@
date('H:i', $block->end)
) ?>
-<? if ($block->teacher): ?>
-/
-<a href="<?= URLHelper::getLink('dispatch.php/profile', ['username' => $block->teacher->username]) ?>">
- <?= htmlReady($block->teacher->getFullName()) ?>
-</a>
-<? endif; ?>
-
(<?= formatLinks($block->room) ?>)
<? if ($block->show_participants): ?>
@@ -20,6 +13,19 @@
<?= tooltipIcon(_('Die Namen der buchenden Person sind sichtbar')) ?>
<? endif; ?>
+<? if (count($block->responsibilities) > 0): ?>
+<br>
+<ul class="narrow list-csv">
+<? foreach ($block->responsibilities as $responsibility): ?>
+ <li>
+ <a href="<?= URLHelper::getLink($responsibility->getURL(), [], true) ?>">
+ <?= htmlReady($responsibility->getName()) ?>
+ </a>
+ </li>
+<? endforeach; ?>
+</ul>
+<? endif; ?>
+
<? if ($block->note): ?>
<br>
<small>
diff --git a/db/migrations/1.269_fix_missing_consultation_events.php b/db/migrations/1.269_fix_missing_consultation_events.php
index 1432458..a0548e0 100644
--- a/db/migrations/1.269_fix_missing_consultation_events.php
+++ b/db/migrations/1.269_fix_missing_consultation_events.php
@@ -26,7 +26,7 @@ class FixMissingConsultationEvents extends Migration
// has code changes for Stud.IP 5.0 this will fail but we can neglect
// that since the event is already updated.
try {
- $slot->updateEvent();
+ $slot->updateEvents();
} catch (Exception $e) {
}
},
@@ -41,10 +41,11 @@ class LegacyConsultationSlot extends ConsultationSlot
* Updates the teacher event that belongs to the slot. This will either be
* set to be unoccupied, occupied by only one user or by a group of user.
*/
- public function updateEvent()
+ public function updateEvents()
{
if (count($this->bookings) === 0 && !$this->block->calendar_events) {
- return $this->removeEvent();
+ $this->events->delete();
+ return;
}
$teacher = User::find($this->block->teacher_id);
diff --git a/db/migrations/5.1.7_consultation_multiple_responsible_ranges.php b/db/migrations/5.1.7_consultation_multiple_responsible_ranges.php
new file mode 100644
index 0000000..1daf474
--- /dev/null
+++ b/db/migrations/5.1.7_consultation_multiple_responsible_ranges.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * @see https://gitlab.studip.de/studip/studip/-/issues/132
+ */
+final class ConsultationMultipleResponsibleRanges extends Migration
+{
+ public function description()
+ {
+ return 'Adjust database to allow multiple responsible ranges for consultations';
+ }
+
+ protected function up()
+ {
+ $query = "CREATE TABLE IF NOT EXISTS `consultation_responsibilities` (
+ `block_id` INT(11) UNSIGNED NOT NULL,
+ `range_id` CHAR(32) CHARSET latin1 COLLATE latin1_bin NOT NULL,
+ `range_type` ENUM('user', 'institute', 'statusgroup') CHARSET latin1 COLLATE latin1_bin NOT NULL,
+ `mkdate` INT(11) UNSIGNED NOT NULL,
+ PRIMARY KEY (`block_id`, `range_id`, `range_type`)
+ )";
+ DBManager::get()->exec($query);
+
+ $query = "CREATE TABLE IF NOT EXISTS `consultation_events` (
+ `slot_id` INT(11) UNSIGNED NOT NULL,
+ `user_id` CHAR(32) CHARSET latin1 COLLATE latin1_bin NOT NULL,
+ `mkdate` INT(11) UNSIGNED NOT NULL,
+ PRIMARY KEY (`slot_id`, `user_id`)
+ )";
+ DBManager::get()->exec($query);
+
+ $query = "INSERT IGNORE INTO `consultation_responsibilities` (
+ `block_id`, `range_id`, `range_type`, `mkdate`
+ )
+ SELECT `block_id`, `teacher_id`, 'user', UNIX_TIMESTAMP()
+ FROM `consultation_blocks`
+ WHERE `teacher_id` IS NOT NULL";
+ DBManager::get()->exec($query);
+
+ $query = "INSERT IGNORE INTO `consultation_events` (
+ `slot_id`, `user_id`, `event_id`, `mkdate`
+ )
+ SELECT `slot_id`, `teacher_id`, `teacher_event_id`, UNIX_TIMESTAMP()
+ FROM `consultation_blocks`
+ JOIN `consultation_slots` USING (`block_id`)
+ WHERE `teacher_event_id` IS NOT NULL";
+ DBManager::get()->exec($query);
+
+ $query = "ALTER TABLE `consultation_blocks`
+ DROP COLUMN `teacher_id`";
+ DBManager::get()->exec($query);
+
+ $query = "ALTER TABLE `consultation_slots`
+ DROP COLUMN `teacher_event_id`";
+ DBManager::get()->exec($query);
+ }
+
+ protected function down()
+ {
+ $query = "ALTER TABLE `consultation_slots`
+ ADD COLUMN `teacher_event_id` CHAR(32) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL AFTER `note`";
+ DBManager::get()->exec($query);
+
+ $query = "ALTER TABLE `consultation_blocks`
+ ADD COLUMN `teacher_id` CHAR(32) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL";
+ DBManager::get()->exec($query);
+
+ $query = "UPDATE `consultation_slots` AS cs
+ JOIN `consultation_events` AS ce USING (`slot_id`)
+ JOIN `consultation_blocks` AS cb USING (`block_id`)
+ SET cs.`teacher_event_id` = ce.`event_id`
+ WHERE cb.`range_type` = 'user'
+ AND cs.`slot_id` IN (
+ SELECT `slot_id`
+ FROM `consultation_events`
+ GROUP BY `slot_id`
+ HAVING COUNT(*) = 1
+ )";
+ DBManager::get()->exec($query);
+
+ $query = "UPDATE `consultation_blocks` AS cb
+ JOIN `consultation_responsibilities` AS cr USING (`block_id`)
+ SET cb.`teacher_id` = cr.`range_id`
+ WHERE cb.`block_id` IN (
+ SELECT `block_id`
+ FROM `consultation_responsibilities` AS cr2
+ JOIN `consultation_blocks` AS cb USING (`block_id`)
+ WHERE cr2.`range_type` = 'user'
+ GROUP BY `block_id`
+ HAVING COUNT(DISTINCT cr.`range_id`) = 1
+ )";
+ DBManager::get()->exec($query);
+
+ $query = "DROP TABLE IF EXISTS `consultation_events`";
+ DBManager::get()->exec($query);
+
+ $query = "DROP TABLE IF EXISTS `consultation_responsibilities`";
+ DBManager::get()->exec($query);
+ }
+}
diff --git a/lib/classes/ConsultationMailer.php b/lib/classes/ConsultationMailer.php
index 007bb47..b9713dc 100644
--- a/lib/classes/ConsultationMailer.php
+++ b/lib/classes/ConsultationMailer.php
@@ -54,9 +54,13 @@ class ConsultationMailer
*
* @param ConsultationBooking $booking The booking
*/
- public static function sendBookingMessageToTeacher(ConsultationBooking $booking)
+ public static function sendBookingMessageToResponsibilities(ConsultationBooking $booking)
{
foreach ($booking->slot->block->responsible_persons as $user) {
+ if ($user->id === $GLOBALS['user']->id) {
+ continue;
+ }
+
self::sendMessage(
$user,
$booking,
@@ -94,7 +98,7 @@ class ConsultationMailer
self::sendMessage(
$receiver,
$booking->slot,
- sprintf(_('Grund des Termins bei bearbeitet'), $booking->slot->block->range_display),
+ sprintf(_('Grund des Termins bei %s bearbeitet'), $booking->slot->block->range_display),
$booking->reason
);
}
@@ -105,9 +109,13 @@ class ConsultationMailer
* @param ConsultationBooking $booking The booking
* @param String $reason Reason of the cancelation
*/
- public static function sendCancelMessageToTeacher(ConsultationBooking $booking, $reason = '')
+ public static function sendCancelMessageToResponsibilities(ConsultationBooking $booking, $reason = '')
{
foreach ($booking->slot->block->responsible_persons as $user) {
+ if ($user->id === $GLOBALS['user']->id) {
+ continue;
+ }
+
self::sendMessage(
$user,
$booking,
diff --git a/lib/models/CalendarEvent.class.php b/lib/models/CalendarEvent.class.php
index f0a4a87..a61922c 100644
--- a/lib/models/CalendarEvent.class.php
+++ b/lib/models/CalendarEvent.class.php
@@ -69,10 +69,10 @@ class CalendarEvent extends SimpleORMap implements Event, PrivacyObject
'foreign_key' => 'event_id',
'assoc_foreign_key' => 'student_event_id',
];
- $config['belongs_to']['consultation_slot'] = [
- 'class_name' => ConsultationSlot::class,
+ $config['has_many']['consultation_events'] = [
+ 'class_name' => ConsultationEvent::class,
'foreign_key' => 'event_id',
- 'assoc_foreign_key' => 'teacher_event_id',
+ 'assoc_foreign_key' => 'event_id',
];
$config['additional_fields']['type'] = true;
$config['additional_fields']['name'] = true;
@@ -88,10 +88,7 @@ class CalendarEvent extends SimpleORMap implements Event, PrivacyObject
$event->consultation_booking->student_event_id = null;
$event->consultation_booking->store();
}
- if ($event->consultation_slot) {
- $event->consultation_slot->teacher_event_id = null;
- $event->consultation_slot->store();
- }
+ $event->consultation_events->delete();
};
parent::configure($config);
@@ -1018,21 +1015,18 @@ class CalendarEvent extends SimpleORMap implements Event, PrivacyObject
}
/**
- *
- * TODO remove! not used?
- *
- * @return type
+ * @return string
*/
public function getName()
{
switch ($this->type) {
case 'user':
- return $this->user->getFullname();
+ return (string) $this->user->getFullname();
case 'sem':
- return $this->course->name;
+ return (string) $this->course->name;
case 'inst':
case 'fak':
- return $this->institute->name;
+ return (string) $this->institute->name;
}
}
diff --git a/lib/models/ConsultationBlock.php b/lib/models/ConsultationBlock.php
index bb83515..d644629 100644
--- a/lib/models/ConsultationBlock.php
+++ b/lib/models/ConsultationBlock.php
@@ -24,7 +24,8 @@
* @property bool has_bookings computed column
* @property Range range computed column
* @property SimpleORMapCollection slots has_many ConsultationSlot
- * @property User teacher belongs_to User
+ * @property ConsultationResponsibility[] responsibilities has_many ConsultationResponsibility
+ * @property User[] responsible_persons
*/
class ConsultationBlock extends SimpleORMap implements PrivacyObject
{
@@ -36,16 +37,18 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject
{
$config['db_table'] = 'consultation_blocks';
- $config['belongs_to']['teacher'] = [
- 'class_name' => User::class,
- 'foreign_key' => 'teacher_id',
- ];
$config['has_many']['slots'] = [
'class_name' => ConsultationSlot::class,
'assoc_foreign_key' => 'block_id',
'on_store' => 'store',
'on_delete' => 'delete',
];
+ $config['has_many']['responsibilities'] = [
+ 'class_name' => ConsultationResponsibility::class,
+ 'assoc_foreign_key' => 'block_id',
+ 'on_delete' => 'delete',
+ 'order_by' => "ORDER BY range_type = 'user' DESC, range_type = 'statusgroup' DESC",
+ ];
$config['additional_fields']['range'] = [
'set' => function ($block, $field, Range $range) {
@@ -61,31 +64,11 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject
return $block->range->getFullName() . ' <' . $block->range->email . '>';
}
if ($block->range instanceof Course || $block->range instanceof Institute) {
- $display = $block->range->getFullName();
- if ($block->teacher) {
- $display .= ' (' . $block->teacher->getFullName() . ')';
- }
- return $display;
- }
-
- throw new Exception('Not implemented yet');
- };
- $config['additional_fields']['responsible_persons']['get'] = function ($block) {
- if ($block->range instanceof User) {
- return [$block->range];
- }
- if ($block->range instanceof Course && $block->teacher) {
- return [$block->teacher];
- }
-
- if ($block->range instanceof Course) {
- return $block->range->getMembersWithStatus('tutor dozent', true)->pluck('user');
+ return sprintf(_('Veranstaltung: %s'), $block->range->getFullName());
}
if ($block->range instanceof Institute) {
- return $block->range->members->filter(function ($member) {
- return in_array($member->inst_perms, ['tutor', 'dozent']);
- })->pluck('user');
+ return sprintf(_('Einrichtung: %s'), $block->range->getFullname());
}
throw new Exception('Not implemented yet');
@@ -103,6 +86,32 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject
});
};
+ $config['additional_fields']['responsible_persons']['get'] = function (ConsultationBlock $block) {
+ if (count($block->responsibilities) !== 0) {
+ $result = [];
+ foreach (array_merge(...$block->responsibilities->getUsers()) as $user) {
+ $result[$user->id] = $user;
+ }
+ return array_values($result);
+ }
+
+ if ($block->range instanceof User) {
+ return [$block->range];
+ }
+ if ($block->range instanceof Course) {
+ return ConsultationResponsibility::getCourseResponsibilities($block->range);
+ }
+ if ($block->range instanceof Institute) {
+ return ConsultationResponsibility::getInstituteResponsibilites($block->range);
+ }
+
+ throw new Exception('Unknown range type');
+ };
+
+ $config['registered_callbacks']['after_store'][] = function (ConsultationBlock $block) {
+ $block->slots->updateEvents();
+ };
+
parent::configure($config);
}
@@ -274,6 +283,37 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject
}
/**
+ *
+ */
+ public function getPossibleResponsibilites()
+ {
+ if ($this->range instanceof User) {
+ return [
+ 'users' => [$this->range]
+ ];
+ }
+
+ if ($this->range instanceof Course) {
+ return [
+ 'users' => $this->range->getMembersWithStatus('tutor dozent', true)->pluck('user'),
+ ];
+ }
+
+ if ($this->range instanceof Institute) {
+ $users = $this->range->members->filter(function ($member) {
+ return in_array($member->inst_perms, ['tutor', 'dozent']);
+ })->pluck('user');
+
+ $groups = $this->range->status_groups;
+ $institutes = $this->range->sub_institutes;
+
+ return compact('users', 'groups', 'institutes');
+ }
+
+ throw new Exception('Not implemented yet');
+ }
+
+ /**
* Export available data of a given user into a storage object
* (an instance of the StoredUserData class) for that user.
*
diff --git a/lib/models/ConsultationBooking.php b/lib/models/ConsultationBooking.php
index 37756e1..1f269c4 100644
--- a/lib/models/ConsultationBooking.php
+++ b/lib/models/ConsultationBooking.php
@@ -40,7 +40,8 @@ class ConsultationBooking extends SimpleORMap implements PrivacyObject
'on_delete' => 'delete',
];
- $config['registered_callbacks']['before_create'][] = function ($booking) {
+ // Create student event
+ $config['registered_callbacks']['before_create'][] = function (ConsultationBooking $booking) {
setTempLanguage($booking->user_id);
$event = $booking->slot->createEvent($booking->user);
@@ -57,41 +58,37 @@ class ConsultationBooking extends SimpleORMap implements PrivacyObject
$booking->student_event_id = $event->id;
};
- $config['registered_callbacks']['after_create'][] = function ($booking) {
+ $config['registered_callbacks']['after_create'][] = function (ConsultationBooking $booking) {
ConsultationMailer::sendBookingMessageToUser($booking);
-
- $responsible_persons = $booking->slot->block->responsible_persons;
- if (!in_array($GLOBALS['user']->id, $responsible_persons)) {
- ConsultationMailer::sendBookingMessageToTeacher($booking);
- }
+ ConsultationMailer::sendBookingMessageToResponsibilities($booking);
};
- $config['registered_callbacks']['before_store'][] = function ($booking) {
+ $config['registered_callbacks']['before_store'][] = function (ConsultationBooking $booking) {
if (!$booking->isNew() && $booking->isFieldDirty('reason')) {
if ($GLOBALS['user']->id !== $booking->user_id) {
ConsultationMailer::sendReasonMessage($booking,$booking->user);
}
$responsible_persons = $booking->slot->block->responsible_persons;
- if (!in_array($GLOBALS['user']->id, $responsible_persons)) {
- foreach ($responsible_persons as $user) {
+ foreach ($responsible_persons as $user) {
+ if ($GLOBALS['user']->id !== $user->id) {
ConsultationMailer::sendReasonMessage($booking, $user);
}
}
}
};
- $config['registered_callbacks']['after_store'][] = function ($booking) {
+ $config['registered_callbacks']['after_store'][] = function (ConsultationBooking $booking) {
if ($booking->event) {
$booking->event->description = $booking->reason;
$booking->event->store();
}
- $booking->slot->updateEvent();
+ $booking->slot->updateEvents();
};
- $config['registered_callbacks']['after_delete'][] = function ($booking) {
- $booking->slot->updateEvent();
+ $config['registered_callbacks']['after_delete'][] = function (ConsultationBooking $booking) {
+ $booking->slot->updateEvents();
};
parent::configure($config);
@@ -103,9 +100,7 @@ class ConsultationBooking extends SimpleORMap implements PrivacyObject
ConsultationMailer::sendCancelMessageToUser($this, $reason);
}
- if (!in_array($GLOBALS['user']->id, $this->slot->block->responsible_persons)) {
- ConsultationMailer::sendCancelMessageToTeacher($this, $reason);
- }
+ ConsultationMailer::sendCancelMessageToResponsibilities($this, $reason);
return $this->delete() ? 1 : 0;
}
diff --git a/lib/models/ConsultationEvent.php b/lib/models/ConsultationEvent.php
new file mode 100644
index 0000000..d02e2d8
--- /dev/null
+++ b/lib/models/ConsultationEvent.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
+ * @license GPL2 or any later version
+ * @since Stud.IP 5.1
+ *
+ * @property int slot_id database column
+ * @property int id alias column for slot_id
+ * @property string user_id database column
+ * @property string event_id database column
+ * @property int mkdate database column
+ * @property ConsultationSlot slot belongs_to ConsultationSlot
+ * @property EventData event belongs_to Event
+ */
+class ConsultationEvent extends SimpleORMap
+{
+ protected static function configure($config = [])
+ {
+ $config['db_table'] = 'consultation_events';
+
+ $config['belongs_to']['slot'] = [
+ 'class_name' => ConsultationSlot::class,
+ 'foreign_key' => 'slot_id',
+ ];
+ $config['belongs_to']['event'] = [
+ 'class_name' => EventData::class,
+ 'foreign_key' => 'event_id',
+ 'assoc_foreign_key' => 'event_id',
+ 'on_delete' => 'delete',
+ ];
+
+ parent::configure($config);
+ }
+}
diff --git a/lib/models/ConsultationResponsibility.php b/lib/models/ConsultationResponsibility.php
new file mode 100644
index 0000000..9634d41
--- /dev/null
+++ b/lib/models/ConsultationResponsibility.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
+ * @license GPL2 or any later version
+ * @since Stud.IP 5.1
+ *
+ * @property int block_id database column
+ * @property int id alias column for block_id
+ * @property string range_id database column
+ * @property string range_type database column
+ * @property int mkdate database column
+ */
+class ConsultationResponsibility extends SimpleORMap
+{
+ protected static function configure($config = [])
+ {
+ $config['db_table'] = 'consultation_responsibilities';
+
+ $config['belongs_to']['block'] = [
+ 'class_name' => ConsultationBlock::class,
+ 'foreign_key' => 'block_id',
+ ];
+
+ parent::configure($config);
+ }
+
+ /**
+ * Returns the name of the associated responsibility.
+ *
+ * @return string
+ * @throws Exception
+ */
+ public function getName()
+ {
+ if ($this->range_type === 'user') {
+ return User::find($this->range_id)->getFullName();
+ }
+ if ($this->range_type === 'statusgroup') {
+ return Statusgruppen::find($this->range_id)->getName();
+ }
+ if ($this->range_type === 'institute') {
+ return Institute::find($this->range_id)->getFullName();
+ }
+ throw new Exception('Unknown range type');
+ }
+
+ /**
+ * Returns an url to the associated responsibility.
+ *
+ * @return string
+ * @throws Exception
+ */
+ public function getURL()
+ {
+ if ($this->range_type === 'user') {
+ $user = User::find($this->range_id);
+ return URLHelper::getURL('dispatch.php/profile', ['username' => $user->username], true);
+ }
+ // TODO: Check if staff tab is activated and link to that
+ if ($this->range_type === 'statusgroup') {
+ $institute = Statusgruppen::find($this->range_id)->institute;
+ return URLHelper::getURL('dispatch.php/institute/overview', ['auswahl' => $institute->id], true);
+ }
+ if ($this->range_type === 'institute') {
+ return URLHelper::getURL('dispatch.php/institute/overview', ['auswahl' => $this->range_id], true);
+ }
+ throw new Exception('Unknown range type');
+ }
+
+ /**
+ * Returns all users belonging to the associated responsibility.
+ *
+ * @return array
+ * @throws Exception
+ */
+ public function getUsers()
+ {
+ if ($this->range_type === 'user') {
+ return [User::find($this->range_id)];
+ }
+ if ($this->range_type === 'statusgroup') {
+ $group = Statusgruppen::find($this->range_id);
+ return self::getStatusgroupResponsibilities($group);
+ }
+ if ($this->range_type === 'institute') {
+ $institute = Institute::find($this->range_id);
+ return self::getInstituteResponsibilites($institute);
+ }
+ throw new Exception('Unknown range type');
+ }
+
+ /**
+ * Returns all responsible users for a course.
+ *
+ * @param Course $course
+ * @return array
+ */
+ public static function getCourseResponsibilities(Course $course)
+ {
+ return $course->getMembersWithStatus('tutor dozent', true)->pluck('user');
+ }
+
+ /**
+ * Returns all responsible users for a status group.
+ *
+ * @param Statusgruppen $group
+ * @return array
+ */
+ public static function getStatusgroupResponsibilities(Statusgruppen $group)
+ {
+ return $group->members->pluck('user');
+ }
+
+ /**
+ * Returns all responsible users for an institute.
+ *
+ * @param Institute $institute
+ * @return array
+ */
+ public static function getInstituteResponsibilites(Institute $institute)
+ {
+ return $institute->members->filter(function (InstituteMember $member) {
+ return in_array($member->inst_perms, ['tutor', 'dozent']);
+ })->pluck('user');
+ }
+}
diff --git a/lib/models/ConsultationSlot.php b/lib/models/ConsultationSlot.php
index fd3b05a..1177fce 100644
--- a/lib/models/ConsultationSlot.php
+++ b/lib/models/ConsultationSlot.php
@@ -11,10 +11,9 @@
* @property string start_time database column
* @property string end_time database column
* @property string note database column
- * @property string teacher_event_id database column
* @property SimpleORMapCollection bookings has_many ConsultationBooking
* @property ConsultationBlock block belongs_to ConsultationBlock
- * @property EventData event has_one EventData
+ * @property SimpleORMapCollection events has_many EventData
*/
class ConsultationSlot extends SimpleORMap
{
@@ -30,24 +29,20 @@ class ConsultationSlot extends SimpleORMap
'class_name' => ConsultationBlock::class,
'foreign_key' => 'block_id',
];
- $config['has_one']['event'] = [
- 'class_name' => EventData::class,
- 'foreign_key' => 'teacher_event_id',
- 'assoc_foreign_key' => 'event_id',
- 'on_delete' => 'delete',
- ];
$config['has_many']['bookings'] = [
'class_name' => ConsultationBooking::class,
'assoc_foreign_key' => 'slot_id',
'on_store' => 'store',
'on_delete' => 'delete',
];
+ $config['has_many']['events'] = [
+ 'class_name' => ConsultationEvent::class,
+ 'assoc_foreign_key' => 'slot_id',
+ 'on_delete' => 'delete',
+ ];
- $config['registered_callbacks']['before_create'][] = function ($slot) {
- if ($slot->block->calendar_events && $slot->block->range_type === 'user') {
- $slot->teacher_event_id = $slot->createEvent($slot->block->range)->id;
- $slot->updateEvent();
- }
+ $config['registered_callbacks']['before_create'][] = function (ConsultationSlot $slot) {
+ $slot->updateEvents();
};
$config['registered_callbacks']['after_delete'][] = function ($slot) {
$block = $slot->block;
@@ -196,69 +191,81 @@ class ConsultationSlot extends SimpleORMap
* Updates the teacher event that belongs to the slot. This will either be
* set to be unoccupied, occupied by only one user or by a group of user.
*/
- public function updateEvent()
+ public function updateEvents()
{
- if ($this->block->range_type !== 'user') {
- return;
- }
-
// If no range is associated, remove the event
if (!$this->block->range) {
- return $this->removeEvent();
+ $this->events->delete();
+ return;
}
if (count($this->bookings) === 0 && !$this->block->calendar_events) {
- return $this->removeEvent();
+ $this->events->delete();
+ return;
}
- $event = $this->event;
- if (!$event) {
- $event = $this->createEvent($this->block->range);
+ // Get responsible user ids
+ $responsible_ids = array_map(
+ function (User $user) {
+ return $user->id;
+ },
+ $this->block->responsible_persons
+ );
- $this->teacher_event_id = $event->id;
- $this->store();
+ // Remove events for no longer responsible users
+ foreach ($this->events as $event) {
+ if (!in_array($event->user_id, $responsible_ids)) {
+ $event->delete();
+ }
}
- setTempLanguage($this->block->range_id);
+ // Add events for missing responsible users
+ $missing = array_diff($responsible_ids, $this->events->pluck('user_id'));
+ foreach ($missing as $user_id) {
+ $event = $this->createEvent(User::find($user_id));
+ ConsultationEvent::create([
+ 'slot_id' => $this->id,
+ 'user_id' => $user_id,
+ 'event_id' => $event->id,
+ ]);
+ }
+
+ // Reset relation in order to account to the above changes
+ $this->resetRelation('events');
+
+ foreach ($this->events as $event) {
+ setTempLanguage($event->user_id);
- if (count($this->bookings) > 0) {
- $event->category_intern = 1;
+ if (count($this->bookings) > 0) {
+ $event->event->category_intern = 1;
- if (count($this->bookings) === 1) {
- $booking = $this->bookings->first();
+ if (count($this->bookings) === 1) {
+ $booking = $this->bookings->first();
- $event->summary = sprintf(
- _('Termin mit %s'),
- $booking->user->getFullName()
- );
- $event->description = $booking->reason;
+ $event->event->summary = sprintf(
+ _('Termin mit %s'),
+ $booking->user->getFullName()
+ );
+ $event->event->description = $booking->reason;
+ } else {
+ $event->event->summary = sprintf(
+ _('Termin mit %u Personen'),
+ count($this->bookings)
+ );
+ $event->event->description = implode("\n\n----\n\n", $this->bookings->map(function ($booking) {
+ return "- {$booking->user->getFullName()}:\n{$booking->reason}";
+ }));
+ }
} else {
- $event->summary = sprintf(
- _('Termin mit %u Personen'),
- count($this->bookings)
- );
- $event->description = implode("\n\n----\n\n", $this->bookings->map(function ($booking) {
- return "- {$booking->user->getFullName()}:\n{$booking->reason}";
- }));
+ $event->event->category_intern = 9;
+ $event->event->summary = _('Freier Termin');
+ $event->event->description = _('Dieser Termin ist noch nicht belegt.');
}
- } else {
- $event->category_intern = 9;
- $event->summary = _('Freier Termin');
- $event->description = _('Dieser Termin ist noch nicht belegt.');
- }
- restoreLanguage();
+ $event->event->store();
- $event->store();
- }
-
- public function removeEvent()
- {
- if ($this->event) {
- $this->event->delete();
+ restoreLanguage();
- $this->teacher_event_id = null;
- $this->store();
}
}
}
diff --git a/lib/models/Course.class.php b/lib/models/Course.class.php
index b958a4b..f543c88 100644
--- a/lib/models/Course.class.php
+++ b/lib/models/Course.class.php
@@ -449,7 +449,7 @@ class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, Fe
*
* @param String|Array $status the status to filter with
* @param bool $as_collection return collection instead of array?
- * @return Array an array of all those members.
+ * @return Array|SimpleCollection an array of all those members.
*/
public function getMembersWithStatus($status, $as_collection = false)
{
diff --git a/lib/models/Institute.class.php b/lib/models/Institute.class.php
index 86120c1..743931c 100644
--- a/lib/models/Institute.class.php
+++ b/lib/models/Institute.class.php
@@ -41,6 +41,87 @@
class Institute extends SimpleORMap implements Range
{
+ protected static function configure($config = [])
+ {
+ $config['db_table'] = 'Institute';
+ $config['additional_fields']['is_fak']['get'] = 'isFaculty';
+
+ $config['has_many']['members'] = [
+ 'class_name' => InstituteMember::class,
+ 'assoc_func' => 'findByInstitute',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ ];
+ $config['has_many']['home_courses'] = [
+ 'class_name' => Course::class,
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ ];
+ $config['has_many']['sub_institutes'] = [
+ 'class_name' => Institute::class,
+ 'assoc_foreign_key' => 'fakultaets_id',
+ 'assoc_func' => 'findByFaculty',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ ];
+ $config['has_many']['datafields'] = [
+ 'class_name' => DatafieldEntryModel::class,
+ 'assoc_foreign_key' =>
+ function($model,$params) {
+ $model->setValue('range_id', $params[0]->id);
+ },
+ 'assoc_func' => 'findByModel',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ 'foreign_key' =>
+ function($i) {
+ return [$i];
+ }
+ ];
+ $config['belongs_to']['faculty'] = [
+ 'class_name' => Institute::class,
+ 'foreign_key' => 'fakultaets_id',
+ ];
+ $config['has_and_belongs_to_many']['courses'] = [
+ 'class_name' => Course::class,
+ 'thru_table' => 'seminar_inst',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ ];
+ $config['has_many']['scm'] = [
+ 'class_name' => StudipScmEntry::class,
+ 'assoc_foreign_key' => 'range_id',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ ];
+ $config['has_many']['status_groups'] = [
+ 'class_name' => Statusgruppen::class,
+ 'assoc_foreign_key' => 'range_id',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ 'order_by' => 'ORDER BY position ASC',
+ ];
+ $config['has_many']['blubberthreads'] = [
+ 'class_name' => BlubberThread::class,
+ 'assoc_func' => 'findByInstitut',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ ];
+ $config['has_many']['tools'] = [
+ 'class_name' => ToolActivation::class,
+ 'assoc_foreign_key' => 'range_id',
+ 'order_by' => 'ORDER BY position',
+ 'on_delete' => 'delete',
+ ];
+ $config['additional_fields']['all_status_groups']['get'] = function ($institute) {
+ return Statusgruppen::findAllByRangeId($institute->id, true);
+ };
+
+ $config['i18n_fields']['name'] = true;
+ $config['registered_callbacks']['after_create'][] = 'setDefaultTools';
+
+ parent::configure($config);
+ }
/**
* Returns the currently active course or false if none is active.
@@ -60,7 +141,7 @@ class Institute extends SimpleORMap implements Range
* @param string $fakultaets_id
* @return array
*/
- static function findByFaculty($fakultaets_id)
+ public static function findByFaculty($fakultaets_id)
{
return self::findBySQL("fakultaets_id=? AND fakultaets_id <> institut_id ORDER BY Name ASC", [$fakultaets_id]);
}
@@ -69,7 +150,7 @@ class Institute extends SimpleORMap implements Range
* returns an array of all institutes ordered by faculties and name
* @return array
*/
- static function getInstitutes()
+ public static function getInstitutes()
{
$db = DBManager::get();
$result = $db->query("SELECT Institute.Institut_id, Institute.Name, IF(Institute.Institut_id=Institute.fakultaets_id,1,0) AS is_fak " .
@@ -82,10 +163,12 @@ class Institute extends SimpleORMap implements Range
/**
* returns an array of all institutes to which the given user belongs,
* ordered by faculties and name. The user role for each institute is included
+ *
* @param string $user_id if omitted, the current user is used
+ *
* @return array
*/
- static function getMyInstitutes($user_id = NULL)
+ public static function getMyInstitutes($user_id = NULL)
{
global $perm, $user;
if (!$user_id) {
@@ -125,94 +208,9 @@ class Institute extends SimpleORMap implements Range
return $result;
}
- /**
- *
- */
- protected static function configure($config = [])
- {
- $config['db_table'] = 'Institute';
- $config['additional_fields']['is_fak']['get'] = 'isFaculty';
-
- $config['has_many']['members'] = [
- 'class_name' => 'InstituteMember',
- 'assoc_func' => 'findByInstitute',
- 'on_delete' => 'delete',
- 'on_store' => 'store',
- ];
- $config['has_many']['home_courses'] = [
- 'class_name' => 'Course',
- 'on_delete' => 'delete',
- 'on_store' => 'store',
- ];
- $config['has_many']['sub_institutes'] = [
- 'class_name' => 'Institute',
- 'assoc_foreign_key' => 'fakultaets_id',
- 'assoc_func' => 'findByFaculty',
- 'on_delete' => 'delete',
- 'on_store' => 'store',
- ];
- $config['has_many']['datafields'] = [
- 'class_name' => 'DatafieldEntryModel',
- 'assoc_foreign_key' =>
- function($model,$params) {
- $model->setValue('range_id', $params[0]->id);
- },
- 'assoc_func' => 'findByModel',
- 'on_delete' => 'delete',
- 'on_store' => 'store',
- 'foreign_key' =>
- function($i) {
- return [$i];
- }
- ];
- $config['belongs_to']['faculty'] = [
- 'class_name' => 'Institute',
- 'foreign_key' => 'fakultaets_id',
- ];
- $config['has_and_belongs_to_many']['courses'] = [
- 'class_name' => 'Course',
- 'thru_table' => 'seminar_inst',
- 'on_delete' => 'delete',
- 'on_store' => 'store',
- ];
- $config['has_many']['scm'] = [
- 'class_name' => 'StudipScmEntry',
- 'assoc_foreign_key' => 'range_id',
- 'on_delete' => 'delete',
- 'on_store' => 'store',
- ];
- $config['has_many']['status_groups'] = [
- 'class_name' => 'Statusgruppen',
- 'assoc_foreign_key' => 'range_id',
- 'on_delete' => 'delete',
- 'on_store' => 'store',
- 'order_by' => 'ORDER BY position ASC',
- ];
- $config['has_many']['blubberthreads'] = [
- 'class_name' => 'BlubberThread',
- 'assoc_func' => 'findByInstitut',
- 'on_delete' => 'delete',
- 'on_store' => 'store',
- ];
- $config['has_many']['tools'] = [
- 'class_name' => 'ToolActivation',
- 'assoc_foreign_key' => 'range_id',
- 'order_by' => 'ORDER BY position',
- 'on_delete' => 'delete'
- ];
- $config['additional_fields']['all_status_groups']['get'] = function ($institute) {
- return Statusgruppen::findAllByRangeId($institute->id, true);
- };
-
- $config['i18n_fields']['name'] = true;
- $config['registered_callbacks']['after_create'][] = 'setDefaultTools';
-
- parent::configure($config);
- }
-
- function isFaculty()
+ public function isFaculty()
{
- return $this->fakultaets_id == $this->institut_id;
+ return $this->fakultaets_id === $this->institut_id;
}
/**
@@ -221,7 +219,8 @@ class Institute extends SimpleORMap implements Range
* @param string formatting template name
* @return string Fullname
*/
- public function getFullname($format = 'default') {
+ public function getFullname($format = 'default'): string
+ {
$template['type-name'] = '%2$s: %1$s';
if ($format === 'default' || !isset($template[$format])) {
$format = 'type-name';
@@ -240,7 +239,7 @@ class Institute extends SimpleORMap implements Range
*
* @return string
*/
- public function describeRange()
+ public function describeRange(): string
{
return _('Einrichtung');
}
@@ -250,7 +249,7 @@ class Institute extends SimpleORMap implements Range
*
* @return string
*/
- public function getRangeType()
+ public function getRangeType(): string
{
return 'institute';
}
@@ -280,7 +279,7 @@ class Institute extends SimpleORMap implements Range
* @return bool
* @todo Check permissions
*/
- public function isAccessibleToUser($user_id = null)
+ public function isAccessibleToUser($user_id = null): bool
{
return true;
}
@@ -292,7 +291,7 @@ class Institute extends SimpleORMap implements Range
* @return bool
* @todo Check permissions
*/
- public function isEditableByUser($user_id = null)
+ public function isEditableByUser($user_id = null): bool
{
if ($user_id === null) {
$user_id = $GLOBALS['user']->id;
@@ -332,7 +331,7 @@ class Institute extends SimpleORMap implements Range
* @param $name string name of tool / plugin
* @return bool
*/
- public function isToolActive($name)
+ public function isToolActive($name): bool
{
$plugin = PluginEngine::getPlugin($name);
return $plugin && $this->tools->findOneby('plugin_id', $plugin->getPluginId());
diff --git a/lib/models/StatusgruppeUser.php b/lib/models/StatusgruppeUser.php
index cf0b85b..04b42b6 100644
--- a/lib/models/StatusgruppeUser.php
+++ b/lib/models/StatusgruppeUser.php
@@ -30,11 +30,11 @@ class StatusgruppeUser extends SimpleORMap implements PrivacyObject
{
$config['db_table'] = 'statusgruppe_user';
$config['belongs_to']['group'] = [
- 'class_name' => 'Statusgruppen',
+ 'class_name' => Statusgruppen::class,
'foreign_key' => 'statusgruppe_id',
];
$config['belongs_to']['user'] = [
- 'class_name' => 'User',
+ 'class_name' => User::class,
'foreign_key' => 'user_id',
];
diff --git a/lib/models/Statusgruppen.php b/lib/models/Statusgruppen.php
index 1c0927e..16cd3d7 100644
--- a/lib/models/Statusgruppen.php
+++ b/lib/models/Statusgruppen.php
@@ -33,6 +33,9 @@
* @property string children computed column
* @property SimpleORMapCollection members has_many StatusgruppeUser
* @property Statusgruppen parent belongs_to Statusgruppen
+ * @property Course course belongs_to course
+ * @property Institute institute belongs_to institute
+ * @property User user belongs_to user
*/
class Statusgruppen extends SimpleORMap implements PrivacyObject
{
@@ -42,36 +45,36 @@ class Statusgruppen extends SimpleORMap implements PrivacyObject
{
$config['db_table'] = 'statusgruppen';
$config['has_many']['members'] = [
- 'class_name' => 'StatusgruppeUser',
+ 'class_name' => StatusgruppeUser::class,
'assoc_foreign_key' => 'statusgruppe_id',
'on_delete' => 'delete',
'order_by' => 'ORDER BY position ASC',
];
$config['has_and_belongs_to_many']['dates'] = [
- 'class_name' => 'CourseDate',
+ 'class_name' => CourseDate::class,
'thru_table' => 'termin_related_groups',
'order_by' => 'ORDER BY date',
'on_delete' => 'delete', // TODO: This might cause trouble
'on_store' => 'store'
];
$config['belongs_to']['parent'] = [
- 'class_name' => 'Statusgruppen',
+ 'class_name' => Statusgruppen::class,
'foreign_key' => 'range_id',
];
$config['belongs_to']['course'] = [
- 'class_name' => 'Course',
+ 'class_name' => Course::class,
'foreign_key' => 'range_id',
];
$config['belongs_to']['institute'] = [
- 'class_name' => 'Institute',
+ 'class_name' => Institute::class,
'foreign_key' => 'range_id',
];
$config['belongs_to']['user'] = [
- 'class_name' => 'User',
+ 'class_name' => User::class,
'foreign_key' => 'range_id',
];
$config['has_one']['blubberthread'] = [
- 'class_name' => 'BlubberStatusgruppeThread',
+ 'class_name' => BlubberStatusgruppeThread::class,
'on_store' => 'store',
'on_delete' => 'delete'
];
diff --git a/lib/visual.inc.php b/lib/visual.inc.php
index ae1f9d8..303987d 100644
--- a/lib/visual.inc.php
+++ b/lib/visual.inc.php
@@ -635,10 +635,10 @@ function tooltip2($text, $with_alt = TRUE, $with_popup = FALSE) {
* @param bool $important render icon in "important" style
* @param bool $html tooltip text is HTML content
*/
-function tooltipIcon($text, $important = false, $html = false)
+function tooltipIcon($text, $important = false, $html = false): string
{
if (!trim($text)) {
- return;
+ return '';
}
// render tooltip
diff --git a/resources/assets/stylesheets/less/lists.less b/resources/assets/stylesheets/less/lists.less
index 4e10367..20ed994 100644
--- a/resources/assets/stylesheets/less/lists.less
+++ b/resources/assets/stylesheets/less/lists.less
@@ -48,6 +48,12 @@ ol {
content: "";
}
}
+
+ &.narrow {
+ > li {
+ display: inline-flex;
+ }
+ }
}
.list-pipe-separated {
.list-inline();