Commit 6bea3bbc authored by Michael Koch's avatar Michael Koch
Browse files

Merge branch 'master' of git.uni-paderborn.de:asta/raumbuchung into master

parents b90e4717 3c354e02
include config/make.mk
all: install-deps update-deps deploy
.PHONY: all doc
all: install-deps update-deps doc deploy
doc documentation:
@jsdoc -d doc/js js/*
@phpdoc --sourcecode -f backend.php -d php -t doc/php
install install-deps:
@npm install
......
# AStA Raumbuchung
Booking tool for the rooms of the General Students' Committee (AStA) of Paderborn University
## Requirements
* Web server (i.e. Apache, Nginx, lighttpd)
* SMTP mail server (can be hosted somewhere else)
* PHP with `mysqli` and `curl` extensions enabled
* MySQL or MariaDB server
* NodeJS (or at least `npm` to install JS dependencies)
* Git (for PHPMailer submodule)
* [JSDoc](https://github.com/jsdoc/jsdoc) and [phpDocumentator](https://www.phpdoc.org/) for JS and PHP documentation generation
### Optional requirements:
* Make, SSH and rsync for deployment (otherwise copy the files manually to your server)
## Installation
* Clone this repository to your machine
* Create database and user with access to that database on your MySQL server:
```
$ mysql -u root -p
Enter password:
MariaDB [(none)]> CREATE DATABASE raumbuchung;
Query OK, 1 row affected (0.001 sec)
MariaDB [(none)]> CREATE USER 'raumbuchung'@'localhost' IDENTIFIED BY 'topsecret';
Query OK, 0 rows affected (0.003 sec)
MariaDB [(none)]> GRANT ALL PRIVILEGES ON raumbuchung.* TO 'raumbuchung'@'localhost';
Query OK, 0 rows affected (0.003 sec)
MariaDB [(none)]> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.000 sec)
```
* Import [`sql/db.sql`](sql/db.sql) into that database:
```
$ mysql -u raumbuchung -p raumbuchung < sql/db.sql
```
* Copy the contents of [`config/templates`](config/templates) to [`config`](config)
* Edit [`config/backend.json`](config/templates/backend.json) and enter your database, SMTP and (optionally) CalDAV credentials
* Edit [`config/rest.js`](config/templates/rest.js) and replace the backend URL with the path to [`backend.php`](backend.php) on your web server
* Edit [`config/make.mk`](config/templates/make.mk) and enter your deployment credentials:
* `deploy_user`: User on your web server
* `deploy_host`: Hostname or IP of your web server
* `deploy_path`: Path on your web server where Raumbuchung will be installed
* `web_user`: User your web server runs with (i.e. `www-data`)
* `web_group`: Group your web server runs with (i.e.`www-data`)
* Run `make install` (or just `npm install`) to install JS dependencies
* Run `git submodule init` and `git submodule update` to install PHP dependencies
* Run `make deploy` to copy Raumbuchung to your web server (or use whatever tool you like, i.e. FTP)
* Make sure [`config/backend.json`](config/templates/backend.json) and [`config/make.mk`](config/templates/make.mk) aren't accessible from the web (i.e. with `.htaccess` in Apache)
* Open `https://[yourserver]/raumbuchung` in a web browser of your choice and press the Login button on the top-right to create a new administrative user
## Documentation
You can create [JSDoc](https://github.com/jsdoc/jsdoc) and [PHPdoc](https://phpdoc.org) documentation files by running `make doc`.
The created HTML files will be in the `doc` directory.
## License
AStA Raumbuchung 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 2 of the License, or (at your option) any later version.
See `LICENSE` for more information.
\ No newline at end of file
......@@ -64,58 +64,58 @@ if (in_array($http_origin, $safe_origins)) {
header("Access-Control-Allow-Origin: $http_origin");
}
function naughty($e) {
/**
* Tells the calling frontend that the user tried to call a protected function without the correct permissions.
*
* @param string $reason Reason for failure
*/
function naughty($reason) {
echo json_encode(array(
'result' => 'noperm',
'reason' => $e . ": Not allowed to issue command."
'reason' => $reason . ": Not allowed to issue command."
));
clearDBConnection();
exit(1);
}
function fail($e) {
/**
* Tells the calling frontend that a general error happened.
* Also kills both mysqli instances.
*
* @param string $reason Reason for failure
*/
function fail($reason) {
global $m, $m2;
echo json_encode(array(
'result' => 'error',
'reason' => $e . ((is_object($m) && ! empty($m->error)) ? ": " . $m->error : "") . ((is_object($m2) && ! empty($m2->error)) ? "; " . $m2->error : "")
));
clearDBConnection();
exit(1);
}
function softfail($e) {
global $m, $m2;
echo json_encode(array(
'result' => 'softfail',
'reason' => $e . ": " . (is_object($m) ? $m->error : "") . "; " . (is_object($m2) ? $m2->error : "")
'reason' => $reason . ((is_object($m) && ! empty($m->error)) ? ": " . $m->error : "") . ((is_object($m2) && ! empty($m2->error)) ? "; " . $m2->error : "")
));
clearDBConnection();
exit(1);
}
function respond($a) {
echo json_encode($a);
clearDBConnection();
exit(0);
}
function success($r = false) {
/**
* Tells the calling frontend that the API call was a success, with additional content optionally.
*
* @param mixed $content (optional) Additional content to be sent back to the frontend
*/
function success($content = false) {
if ($r !== false) {
respond(array(
if ($content !== false) {
echo json_encode(array(
'result' => 'success',
'content' => $r
'content' => $content
));
} else {
echo json_encode(array('result' => 'success'));
}
respond(array('result' => 'success'));
clearDBConnection();
exit(0);
}
if (sizeof($_POST) > 0) {
......
......@@ -42,6 +42,10 @@
"subject": "[stadtcampus] Your reservation for Lernsprint at Stadtcampus",
"body": "<h2>Stadtcampus-Lernsprint</h2>We've just received your Lernsprint reservation for Stadtcampus."
},
"new_reservation_studyspace_outbound": {
"subject": "[stadtcampus] Your reservation for Study Space at Stadtcampus",
"body": "<h2>AStA Study Space</h2>We've just received your Study Space reservation for Stadtcampus."
},
"new_reservation_refreshments": {
"subject": "[stadtcampus] Refreshments order for reservation \"%s\"",
"body": "<h3>Refreshments order</h3><strong>Reservation:</strong> %s<br /><strong>Client name:</strong> %s<br /><strong>Timeslot:</strong><br /><strong>incl. preparation:</strong> %s-%s<br /><strong>Event:</strong> %s-%s<br/><strong><br /><a href='https://example.com/index.html?reservation_id=%s'>To the reservation details page</a>",
......@@ -80,6 +84,18 @@
"subject": "[stadtcampus] The decision for your reservation for Sprintraum has been reverted.",
"body": "<h2>Sprintraum</h2>Your previously confirmed or denied reservation for Sprintraum has been marked again as undecided.<br /><br /><h4>Reservation details</h4><strong>Room:</strong> %s<br /><strong>Timeslot:</strong> %s-%s"
},
"reservation_confirmed_outbound_ss": {
"subject": "[stadtcampus] Your reservation for Study Space has been confirmed",
"body": "<h2>Study Space</h2>Your reservation for Study Space has been marked as confirmed.<br /><br /><h4>Reservation details:</h4><strong>Timeslot:</strong> %s-%s"
},
"reservation_denied_outbound_ss": {
"subject": "[stadtcampus] Your reservation for Study Space has been denied",
"body": "<h2>Study Space</h2>Your reservation for Study Space has been marked as denied.<br /><br /><h4>Reservation details</h4><strong>Timeslot:</strong> %s-%s"
},
"reservation_undecide_outbound_ss": {
"subject": "[stadtcampus] The decision for your reservation for Study Space has been reverted.",
"body": "<h2>Study Space</h2>Your previously confirmed or denied reservation for Study Space has been marked again as undecided.<br /><br /><h4>Reservation details</h4><strong>Timeslot:</strong> %s-%s"
},
"reservation_refreshment_order_confirmed_outbound": {
"subject": "[stadtcampus] Your refreshment order for Stadtcampus has been accepted.",
"body": "<h2>Stadtcampus</h2>Your refreshment order for Stadtcampus has been accepted.<br /><br /><h4>Reservation details</h4><strong>Subject:</strong> %s<br /><strong>Room:</strong> %s<br /><strong>Timeslot:</strong> %s-%s"
......
......@@ -44,7 +44,7 @@ input:invalid {
font-weight: bold;
}
div#lernsprint_info {
div#lernsprint_info, div#studyspace_info {
border: 1px solid #090909;
border-radius: 10px;
padding: 5px;
......
......@@ -95,5 +95,14 @@
--font-default: 'Source Sans Pro', sans-serif;
--logo: url(../gfx/logo.png);
--logo: url('../gfx/logo.png');
}
/* This should only trigger on Safari. It is needed because it doesn't seem to properly understand relative paths in url(). */
@media not all and (min-resolution: .001dpcm) {
@media {
:root {
--logo: url('gfx/logo.png');
}
}
}
\ No newline at end of file
<html>
<body>
<h1>AStA Raumbuchung - Documentation</h1>
<ul>
<li><a href="./js/index.html">Javascript</a></li>
<li><a href="./php/index.html">PHP</a></li>
</ul>
</body>
</html>
\ No newline at end of file
<div id="frontend">
<div id="frontend_order">
<div class="frontend_info" id="frontend_info_-1">
<p>Hier können Sie R&auml;umlichkeiten des AStA Uni Paderborn buchen.</p>
<p>Hier k&ouml;nnen Sie R&auml;umlichkeiten des AStA Uni Paderborn buchen.</p>
<p>Verf&uuml;gbare R&auml;ume sind derzeit:</p>
<ul>
<li><strong>Veranstaltungsraum</strong> im 6. OG des AStA Stadtcampus (bis zu 130 Pers.)<br />
......@@ -9,6 +9,7 @@
</li>
<li><strong>Seminarraum</strong> im 6. OG des AStA Stadtcampus (bis zu 20 Pers.)</li>
<li>zwei <strong>Lernsprinträume</strong> im 5. OG des AStA Stadtcampus</li>
<li>der <strong>Study Space</strong> im 5. OG des AStA Stadtcampus (bis zu 11 Pers.)</li>
<li>ein <strong>Konferenzraum</strong> im <a href="https://maps.uni-paderborn.de/maps.html?zoom=18&lon=8.76805&lat=51.71025&room=P9&layers=B100000010TT" target="_blank">Gebäude P9</a> auf dem Campus</li>
</ul>
</div>
......@@ -40,6 +41,11 @@
Dieser kann für Besprechungen gebucht werden. Nach Annahme der Buchung erhalten Sie den Schlüssel für den Raum im <a href="https://maps.uni-paderborn.de/maps.html?zoom=18&lon=8.771392&lat=51.708401&room=P9&layers=B100000010TT" target="_blank">AStA Hauptbüro</a> (MEU.210).
</p>
</div>
<div class="frontend_info" id="frontend_info_5" style="display: none;">
<h2>AStA Study Space</h2>
<p>Im 5. Obergeschoss des <strong>AStA Stadtcampus</strong> befindet sich neben den Sprinträumen der <strong>Study Space</strong>.</p>
<p>Hier findest Du mehrere Lernräume, Gruppenarbeitsräume, eine Dachterrasse sowie eine gut ausgestattete Kaffeeküche.</p>
</div>
<p><a href="https://asta.uni-paderborn.de/datenschutzerklaerung/" target="_blank">Datenschutzerkl&auml;rung</a></p>
<hr />
<div id="covid_info" style="display: none;">
......@@ -60,7 +66,15 @@
<tr id="frontend_row_lernsprint_info" style="display: none;">
<td colspan="2">
<div id="lernsprint_info">
<strong>Achtung:</strong> Lernsprinträume dürfen zugunsten der Chancengleichheit nur <strong>1x alle zwei Monate für max. 7 Tage</strong> gebucht werden. Zu häufige Buchungen können zu einer Sperrung für die Räume führen.
<strong>Achtung:</strong> Lernsprintr&auml;ume dürfen zugunsten der Chancengleichheit nur <strong>1x alle zwei Monate für max. 7 Tage</strong> gebucht werden. Zu häufige Buchungen können zu einer Sperrung für die Räume führen.
</div>
</td>
</tr>
<tr id="frontend_row_studyspace_info" style="display: none;">
<td colspan="2">
<div id="studyspace_info">
Die Buchung f&uuml;r den Study Space gilt immer für eine Woche. Aufgrund der Coronabestimmungen dürfen <strong>maximal 11 Personen gleichzeitig</strong> die R&auml;umlichkeiten nutzen.<br />
Wurden bereits 11 Buchungen für die gew&auml;hlte Woche best&auml;tigt, ist eine Anmeldung nicht mehr m&ouml;glich. Es gilt "first come, first serve".
</div>
</td>
</tr>
......@@ -140,4 +154,4 @@
<div id="frontend_calendar_fc" class="cal"></div>
</div>
</div>
</div>
\ No newline at end of file
</div>
......@@ -17,7 +17,8 @@
<option value="denied_upcoming">Abgelehnte Buchungen (kommend)</option>
<option value="denied_total">Abgelehnte Buchungen (alle)</option>
<option value="archived">Vergangene Buchungen</option>
<option value="lernsprint">Lernsprintbuchungen</option>
<option value="lernsprint">Lernsprint-Buchungen</option>
<option value="studyspace">StudySpace-Buchungen</option>
<option value="all">Alle Buchungen</option>
</select>
</div>
......@@ -52,7 +53,7 @@
Von: {{ r.preparation.from }}<br />
Bis: {{ r.preparation.to }}<br /><br />
</span>
<b>{{ r.room.room_type !== 1 ? 'Veranstaltung' : 'Lernsprint' }}:</b><br />
<b>{{ r.room.room_type !== 1 ? 'Veranstaltung' : (r.room.id !== 6 ? 'Lernsprint' : 'Study Space' ) }}:</b><br />
Von: {{ r.event.from }}<br />
Bis: {{ r.event.to }}
</td>
......
......@@ -24,9 +24,9 @@
<td>{{ r.description }}</td>
<td>{{ r.location }}</td>
<td>{{ r.orderable === 1 ? 'ja' : 'nein' }}</td>
<td>{{ r.room_type === 0 ? 'Stadtcampus' : (r.room_type === 1 ? 'Lernsprint' : (r.room_type === 2 ? 'P9' : 'AStA')) }}</td>
<td>{{ r.room_type === 0 ? 'Stadtcampus' : (r.room_type === 1 ? 'Lernsprint' : (r.room_type === 2 ? 'P9' : (r.room_type === 5 ? 'Study Space' : 'AStA'))) }}</td>
<td><span class="calendar_color_block" style="background-color: {{ r.calendar_color }};"></span> / <span class="calendar_color_block" style="background-color: {{ r.calendar_color_confirmed }};"></span></td>
</tr>
</tbody>
</table>
</div>
\ No newline at end of file
</div>
......@@ -19,6 +19,9 @@
* along with AStA Raumbuchung. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Calendar events for backend
*/
let _events = {
'confirmed': [],
'unconfirmed': [],
......@@ -29,6 +32,9 @@ let _events = {
'current': {}
};
/**
* Calendar events for frontend
*/
let _frontendEvents = {
'confirmed': [],
'unconfirmed': [],
......@@ -36,6 +42,9 @@ let _frontendEvents = {
'lernsprint': []
};
/**
* Calendar instances
*/
let _calendars = {
'frontend': false,
'landing': false
......@@ -91,13 +100,13 @@ async function setupLandingCalendar() {
content: "<b>" + info.event._def.extendedProps.reservation.subject + "</b><br />" + info.event._def.extendedProps.room.longname + " (" + info.event._def.extendedProps.room.location + ")" +
(info.event._def.extendedProps.reservation.confirmed === 1 ? '<br /><br /><b>BEST&Auml;TIGT</b>' : '') +
(info.event._def.extendedProps.reservation.denied === 1 ? '<br /><br /><b>ABGELEHNT</b>' : '') +
"<br /><br /><b>Zeitraum " + (info.event._def.extendedProps.room.room_type === 1 ? "Lernsprint" : "Veranstaltung") + ":</b><br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.event.from)) + " bis<br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.event.to)) +
(info.view.type !== "timeGridWeek" && info.event._def.extendedProps.room.room_type !== 1 ?
"<br /><br /><b>Zeitraum " + (info.event._def.extendedProps.room.room_type === 1 ? "Lernsprint" : (info.event._def.extendedProps.room.room_type === 5 ? "StudySpace" : "Veranstaltung")) + ":</b><br />" +
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.event.from)._d) + " bis<br />" +
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.event.to)._d) +
(info.view.type !== "timeGridWeek" && _validateDate(info.event._def.extendedProps.reservation.preparation.from, 'YYYY-MM-DD HH:mm') ?
"<br /><br /><b>Zeitraum inkl. Auf- und Abbau:</b><br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.preparation.from)) + " bis<br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.preparation.to))
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.preparation.from)._d) + " bis<br />" +
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.preparation.to)._d)
: "") +
"<br /><br /><b>Buchender:</b><br />" +
info.event._def.extendedProps.reservation.contact.name + "<br />" +
......@@ -113,8 +122,8 @@ async function setupLandingCalendar() {
$(info.el).tooltip({
content: "<b>" + info.event._def.extendedProps.reservation.subject + "</b>" +
"<br /><br /><b>Zeitraum inkl. Auf- und Abbau:</b><br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.preparation.from)) + " bis<br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.preparation.to)),
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.preparation.from)._d) + " bis<br />" +
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.preparation.to)._d),
track: true
});
}
......@@ -165,12 +174,12 @@ async function setupFrontendCalendar() {
content: "<b>Buchung Nr. " + info.event._def.extendedProps.reservation.id + "</b><br />" + info.event._def.extendedProps.room.longname + " (" + info.event._def.extendedProps.room.location + ")" +
(info.event._def.extendedProps.reservation.confirmed === 1 ? '<br /><br /><b>BEST&Auml;TIGT</b>' : '') +
(info.event._def.extendedProps.reservation.denied === 1 ? '<br /><br /><b>ABGELEHNT</b>' : '') +
(info.view.type === "dayGridMonth" ? "<br /><br /><b>Zeitraum inkl. Auf- und Abbau:</b><br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.preparation.from)) + " bis<br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.preparation.to)) : "") +
"<br /><br /><b>Zeitraum Veranstaltung:</b><br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.event.from)) + " bis<br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.event.to)),
(info.view.type === "dayGridMonth" && _validateDate(info.event._def.extendedProps.reservation.preparation.from, 'YYYY-MM-DD HH:mm') ? "<br /><br /><b>Zeitraum inkl. Auf- und Abbau:</b><br />" +
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.preparation.from)._d) + " bis<br />" +
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.preparation.to)._d) : "") +
"<br /><br /><b>Zeitraum " + (info.event._def.extendedProps.room.room_type === 1 ? "Lernsprint" : (info.event._def.extendedProps.room.room_type === 5 ? "StudySpace" : "Veranstaltung")) + ":</b><br />" +
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.event.from)._d) + " bis<br />" +
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.event.to)._d),
track: true
});
} else if (info.event._def.extendedProps.timeslot === "preparation") {
......@@ -178,8 +187,8 @@ async function setupFrontendCalendar() {
$(info.el).tooltip({
content: "<b>Buchung Nr. " + info.event._def.extendedProps.reservation.id + "</b>" +
"<br /><br /><b>Zeitraum inkl. Auf- und Abbau:</b><br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.preparation.from)) + " bis<br />" +
_getLocalDateTimeShort(new Date(info.event._def.extendedProps.reservation.preparation.to)),
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.preparation.from)._d) + " bis<br />" +
_getLocalDateTimeShort(moment(info.event._def.extendedProps.reservation.preparation.to)._d),
track: true
});
}
......@@ -219,8 +228,8 @@ function fillLandingCalendar() {
_callBackend({
"cmd": "getReservationsFiltered",
"from_preparation_after": _getFormattedDateTime(view.activeStart), // reservations starting during this month
"from_preparation_before": _getFormattedDateTime(view.activeEnd),
"from_event_after": _getFormattedDateTime(view.activeStart), // reservations starting during this month
"from_event_before": _getFormattedDateTime(view.activeEnd),
"confirmed": 0,
"denied": 0,
"rooms": rooms
......@@ -229,8 +238,8 @@ function fillLandingCalendar() {
let r_tmp = r.content;
_callBackend({
"cmd": "getReservationsFiltered",
"to_preparation_after": _getFormattedDateTime(view.activeStart), // reservations ending during this month
"to_preparation_before": _getFormattedDateTime(view.activeEnd),
"to_event_after": _getFormattedDateTime(view.activeStart), // reservations ending during this month
"to_event_before": _getFormattedDateTime(view.activeEnd),
"confirmed": 0,
"denied": 0,
"rooms": rooms
......@@ -241,8 +250,8 @@ function fillLandingCalendar() {
$.each(_events.unconfirmed, (i, e) => {
_calendars.landing.addEvent({
title: e.room.shortname.substring(0,3).toUpperCase() + " - " + e.subject + " (" + e.contact.name + ")",
start: new Date(e.event.from.replace(/-/g, "/")),
end: new Date(e.event.to.replace(/-/g, "/")),
start: moment(e.event.from)._d,
end: moment(e.event.to)._d,
color: e.room.calendar_color,
textColor: _isColorLight(e.room.calendar_color) ? '#000000' : '#ffffff',
extendedProps: {
......@@ -254,8 +263,8 @@ function fillLandingCalendar() {
});
_calendars.landing.addEvent({
title: '',
start: new Date(e.preparation.from.replace(/-/g, "/")),
end: new Date(e.preparation.to.replace(/-/g, "/")),
start: moment(e.preparation.from)._d,
end: moment(e.preparation.to)._d,
color: e.room.calendar_color,
textColor: _isColorLight(e.room.calendar_color) ? '#000000' : '#ffffff',
rendering: 'background',
......@@ -280,8 +289,8 @@ function fillLandingCalendar() {
_callBackend({
"cmd": "getReservationsFiltered",
"from_preparation_after": _getFormattedDateTime(view.activeStart),
"from_preparation_before": _getFormattedDateTime(view.activeEnd),
"from_event_after": _getFormattedDateTime(view.activeStart),
"from_event_before": _getFormattedDateTime(view.activeEnd),
"confirmed": 1,
"denied": 0,
"rooms": rooms
......@@ -290,8 +299,8 @@ function fillLandingCalendar() {
let r_tmp = r.content;
_callBackend({
"cmd": "getReservationsFiltered",
"to_preparation_after": _getFormattedDateTime(view.activeStart),
"to_preparation_before": _getFormattedDateTime(view.activeEnd),
"to_event_after": _getFormattedDateTime(view.activeStart),
"to_event_before": _getFormattedDateTime(view.activeEnd),
"confirmed": 1,
"denied": 0,
"rooms": rooms
......@@ -302,8 +311,8 @@ function fillLandingCalendar() {
$.each(_events.confirmed, (i, e) => {
_calendars.landing.addEvent({
title: e.room.shortname.substring(0,3).toUpperCase() + " - " + e.subject + " (" + e.contact.name + ")",
start: new Date(e.event.from.replace(/-/g, "/")),
end: new Date(e.event.to.replace(/-/g, "/")),
start: moment(e.event.from)._d,
end: moment(e.event.to)._d,
color: e.room.calendar_color_confirmed,
textColor: _isColorLight(e.room.calendar_color_confirmed) ? '#000000' : '#ffffff',
extendedProps: {
......@@ -315,8 +324,8 @@ function fillLandingCalendar() {
});
_calendars.landing.addEvent({
title: '',
start: new Date(e.preparation.from.replace(/-/g, "/")),
end: new Date(e.preparation.to.replace(/-/g, "/")),
start: moment(e.preparation.from)._d,
end: moment(e.preparation.to)._d,
color: e.room.calendar_color_confirmed,
textColor: _isColorLight(e.room.calendar_color_confirmed) ? '#000000' : '#ffffff',
rendering: 'background',
......@@ -364,8 +373,8 @@ function fillFrontendCalendar() {
_callBackend({
"cmd": "getReservationsFiltered",
"from_preparation_after": _getFormattedDateTime(view.activeStart), // reservations starting during this month
"from_preparation_before": _getFormattedDateTime(view.activeEnd),
"from_event_after": _getFormattedDateTime(view.activeStart), // reservations starting during this month
"from_event_before": _getFormattedDateTime(view.activeEnd),
"confirmed": 0,
"denied": 0,
"rooms": rooms,
......@@ -375,8 +384,8 @@ function fillFrontendCalendar() {
let r_tmp = r.content;
_callBackend({
"cmd": "getReservationsFiltered",
"to_preparation_after": _getFormattedDateTime(view.activeStart), // reservations ending during this month
"to_preparation_before": _getFormattedDateTime(view.activeEnd),
"to_event_after": _getFormattedDateTime(view.activeStart), // reservations ending during this month
"to_event_before": _getFormattedDateTime(view.activeEnd),
"confirmed": 0,
"denied": 0,
"rooms": rooms,
......@@ -388,8 +397,8 @@ function fillFrontendCalendar() {
$.each(_frontendEvents.unconfirmed, (i, e) => {
_calendars.frontend.addEvent({
title: e.room.shortname.substring(0,3).toUpperCase() + " - #" + e.id,
start: new Date(e.event.from.replace(/-/g, "/")),
end: new Date(e.event.to.replace(/-/g, "/")),
start: moment(e.event.from)._d,
end: moment(e.event.to)._d,
color: e.room.calendar_color,
textColor: _isColorLight(e.room.calendar_color) ? '#000000' : '#ffffff',
extendedProps: {
......@@ -401,8 +410,8 @@ function fillFrontendCalendar() {
});
_calendars.frontend.addEvent({
title: '',
start: new Date(e.preparation.from.replace(/-/g, "/")),
end: new Date(e.preparation.to.replace(/-/g, "/")),
start: moment(e.preparation.from)._d,
end: moment(e.preparation.to)._d,
color: e.room.calendar_color,
textColor: _isColorLight(e.room.calendar_color) ? '#000000' : '#ffffff',
rendering: 'background',
......@@ -426,8 +435,8 @@ function fillFrontendCalendar() {
_callBackend({
"cmd": "getReservationsFiltered",
"from_preparation_after": _getFormattedDateTime(view.activeStart),
"from_preparation_before": _getFormattedDateTime(view.activeEnd),
"from_event_after": _getFormattedDateTime(view.activeStart),
"from_event_before": _getFormattedDateTime(view.activeEnd),
"confirmed": 1,
"denied": 0,
"rooms": rooms,
......@@ -437,8 +446,8 @@ function fillFrontendCalendar() {
let r_tmp = r.content;
_callBackend({
"cmd": "getReservationsFiltered",
"to_preparation_after": _getFormattedDateTime(view.activeStart),
"to_preparation_before": _getFormattedDateTime(view.activeEnd),
"to_event_after": _getFormattedDateTime(view.activeStart),
"to_event_before": _getFormattedDateTime(view.activeEnd),
"confirmed": 1,
"denied": 0,
"rooms": rooms,
......@@ -449,8 +458,8 @@ function fillFrontendCalendar() {
$.each(_frontendEvents.confirmed, (i, e) => {
_calendars.frontend.addEvent({
title: e.room.shortname.substring(0,3).toUpperCase() + " - #" + e.id,
start: new Date(e.event.from.replace(/-/g, "/")),
end: new Date(e.event.to.replace(/-/g, "/")),
start: moment(e.event.from)._d,
end: moment(e.event.to)._d,
color: e.room.calendar_color_confirmed,
textColor: _isColorLight(e.room.calendar_color_confirmed) ? '#000000' : '#ffffff',
extendedProps: {
......@@ -462,8 +471,8 @@ function fillFrontendCalendar() {
});
_calendars.frontend.addEvent({
title: '',
start: new Date(e.preparation.from.replace(/-/g, "/")),
end: new Date(e.preparation.to.replace(/-/g, "/")),
start: moment(e.preparation.from)._d,
end: moment(e.preparation.to)._d,
color: e.room.calendar_color_confirmed,
textColor: _isColorLight(e.room.calendar_color_confirmed) ? '#000000' : '#ffffff',
rendering: 'background',
......@@ -495,8 +504,8 @@ function exportCalendar(cal) {
const sel = cal.getDate();
$.each(_events.unconfirmed, (i, e) =>
ical.addEvent("Unbestätigte Buchung: \"" + e.subject + "\" in " + e.room.longname + " von " + e.contact.name, "Gebucht von: " + e.contact.name + "\\nTelefon: " + e.contact.phone + "\\nEmail: " + e.contact.email + "\\n\\nKommentar: " + e.comments.user + "\\nBackendkommentar: " + e.comments.internal, e.room.longname + ' (' + e.room.location + ')', e.preparation.from.replace(/-/g, "/"), e.preparation.to.replace(/-/g, "/")));
ical.addEvent("Unbestätigte Buchung: \"" + e.subject + "\" in " + e.room.longname + " von " + e.contact.name, "Gebucht von: " + e.contact.name + "\\nTelefon: " + e.contact.phone + "\\nEmail: " + e.contact.email + "\\n\\nKommentar: " + e.comments.user + "\\nBackendkommentar: " + e.comments.internal, e.room.longname + ' (' + e.room.location + ')', e.preparation.from, e.preparation.to));
$.each(_events.confirmed, (i, e) =>
ical.addEvent("Bestätigte Buchung: \"" + e.subject + "\" in " + e.room.longname + " von " + e.contact.name, "Gebucht von: " + e.contact.name + "\\nTelefon: " + e.contact.phone + "\\nEmail: " + e.contact.email + "\\n\\nKommentar: " + e.comments.user + "\\nBackendkommentar: " + e.comments.internal, e.room.longname + ' (' + e.room.location + ')', e.preparation.from.replace(/-/g, "/"), e.preparation.to.replace(/-/g, "/")));
ical.addEvent("Bestätigte