aboutsummaryrefslogtreecommitdiff
path: root/app/controllers/admin/extern.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers/admin/extern.php')
-rw-r--r--app/controllers/admin/extern.php491
1 files changed, 491 insertions, 0 deletions
diff --git a/app/controllers/admin/extern.php b/app/controllers/admin/extern.php
new file mode 100644
index 0000000..41833b1
--- /dev/null
+++ b/app/controllers/admin/extern.php
@@ -0,0 +1,491 @@
+<?php
+/**
+ * extern.php - administration controller for system-wide external pages
+ *
+ * This program 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.
+ *
+ * @author Peter Thienel <thienel@data-quest.de>
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ * @package extern
+ * @since 5.5
+ */
+
+class Admin_ExternController extends AuthenticatedController
+{
+
+ protected $range;
+ protected $template_path;
+
+ /**
+ * @see PluginController::before_filter()
+ */
+ public function before_filter(&$action, &$args)
+ {
+ parent::before_filter($action, $args);
+
+ $this->checkPerm();
+ $this->init();
+ $this->setSidebar();
+ }
+
+ /**
+ * Initialize the controller.
+ */
+ protected function init()
+ {
+ $this->range = 'studip';
+ $nav = Navigation::getItem('admin/locations/external');
+ if ($nav) {
+ $nav->setActive(true);
+ }
+
+ $this->getSystemWideConfigTypes();
+ $this->config_types['PersBrowse'] = [
+ 'name' => _('Personen-Browser'),
+ 'description' => _('Personal der Einrichtungen'),
+ 'icon' => 'person',
+ 'template' => 'admin/extern/extern_config/persbrowse'
+ ];
+
+ $this->fetchPlugins(true);
+
+ PageLayout::setTitle(_('Externe Seiten (Systemweit)'));
+ }
+
+ /**
+ * Action to show index page.
+ */
+ public function index_action()
+ {
+ $configs = [];
+ $count_not_migrated = 0;
+ ExternPageConfig::findEachBySQL(
+ function ($c) use (&$configs, &$count_not_migrated) {
+ $configs[$c->type][] = $c;
+ if (isset($c->conf['not_fixed_after_migration'])) {
+ $count_not_migrated++;
+ }
+ },
+ "range_id = ?", [$this->range]
+ );
+ $this->configs = $configs;
+
+ if ($count_not_migrated > 0) {
+ PageLayout::postInfo(
+ _('Die mit einem Ausrufezeichen versehenen Konfigurationen müssen eventuell überarbeitet werden.'),
+ [
+ _('Der Aufbau der Templates hat sich in der aktuellen Stud.IP-Version geändert.'),
+ _('Nach dem Speichern der gekennzeichneten Templates wird die Markierung entfernt.')
+ ]
+ );
+ }
+ $actions_widget = Sidebar::get()->getWidget('actions');
+ $actions_widget->addLink(
+ _('Konfiguration hochladen'),
+ $this->uploadURL(),
+ Icon::create('upload'),
+ ['data-dialog' => 'size=auto']
+ );
+
+ $this->render_template('institute/extern/index', $this->layout);
+ }
+
+ /**
+ * Shows dialog to select a new page configuration
+ */
+ public function new_action()
+ {
+ PageLayout::setTitle(_('Neue externe Seite anlegen'));
+ $this->render_template('institute/extern/new', $this->layout);
+ }
+
+ /**
+ * Create a new configuration or edit an existing one.
+ *
+ * @param string $type_id The type of the configuration.
+ * @param string $config_id The id of the configuration.
+ * @throws AccessDeniedException
+ * @throws InvalidSecurityTokenException
+ * @throws MethodNotAllowedException
+ */
+ public function edit_action(string $type_id, string $config_id = '')
+ {
+ $this->load($type_id, $config_id);
+ $actions_widget = Sidebar::get()->getWidget('actions');
+ $actions_widget->addLink(
+ _('Vorschau anzeigen'),
+ $this->url_for('extern/index', $this->config->id),
+ Icon::create('log'),
+ ['target' => '_blank']
+ );
+ $actions_widget->addLink(
+ _('Informationen anzeigen'),
+ $this->infoURL($this->config->id),
+ Icon::create('infopage'),
+ ['data-dialog' => 'size=auto']
+ );
+ $actions_widget->addLink(
+ _('Konfiguration herunterladen'),
+ $this->downloadURL($this->config->id),
+ Icon::create('download')
+ );
+ $actions_widget->addLink(
+ _('Konfiguration hochladen'),
+ $this->uploadURL($this->config->id),
+ Icon::create('upload'),
+ ['data-dialog' => 'size=auto']
+ );
+
+ $this->render_template($this->config_types[$type_id]['template'], $this->layout);
+ }
+
+ /**
+ * Action to store an external page with configuration.
+ *
+ * @param string $type_id The type of the external page.
+ * @param string $config_id The id of the configuration.
+ * @throws AccessDeniedException
+ * @throws InvalidSecurityTokenException
+ * @throws MethodNotAllowedException
+ */
+ public function store_action(string $type_id, string $config_id = '')
+ {
+ $this->load($type_id, $config_id);
+ if (Request::isPost()) {
+ CSRFProtection::verifyUnsafeRequest();
+ $this->page->conf = Request::extract(($this->page->getConfigFields()));
+ $this->page->name = Request::get('name');
+ $this->page->description = Request::get('description');
+ $this->page->template = Request::get('template');
+ if ($this->page->store()) {
+ if ($this->page->page_config->isNew()) {
+ PageLayout::postSuccess(sprintf(
+ _('Eine neue externe Seite "%$1s" vom Typ %$2s wurde angelegt.'),
+ htmlReady($this->page->name),
+ htmlReady($this->page->type)
+ ));
+ } else {
+ PageLayout::postSuccess(
+ sprintf(_('Die Konfiguration der externen Seite "%s" wurde gespeichert.'),
+ htmlReady($this->page->name)
+ ));
+ }
+ } else {
+ PageLayout::postInfo(_('Es wurden keine Änderungen vorgenommen'));
+ }
+ if (Request::submitted('store_cancel')) {
+ $this->redirect($this->indexURL(['open_type' => $type_id]));
+ return;
+ }
+ }
+ $this->redirect($this->editURL($type_id, $this->page->id));
+ }
+
+ /**
+ * Loads the configuration to edit or store.
+ *
+ * @param string $type_id The type of the external page.
+ * @param string $config_id The id of the external page.
+ * @throws AccessDeniedException
+ */
+ protected function load(string $type_id, string $config_id = '')
+ {
+ if (!$config_id) {
+ if (!in_array($type_id, array_keys($this->config_types))) {
+ throw new AccessDeniedException();
+ }
+ $this->config = new ExternPageConfig();
+ $this->config->type = $type_id;
+ $this->config->range_id = $this->range;
+ } else {
+ $this->config = ExternPageConfig::find($config_id);
+ if ($this->config->range_id === 'studip') {
+ $GLOBALS['perm']->check('root');
+ $have_perm = true;
+ } else {
+ $have_perm = $GLOBALS['perm']->have_studip_perm('admin', $this->config->range_id);
+ }
+ if (!$this->config || !$have_perm) {
+ throw new AccessDeniedException();
+ }
+ }
+ $this->page = ExternPage::get($this->config);
+ }
+
+ /**
+ * Deletes a configuration.
+ *
+ * @param string $config_id The id of the configuration to delete.
+ * @throws AccessDeniedException
+ * @throws InvalidSecurityTokenException
+ * @throws MethodNotAllowedException
+ */
+ public function delete_action(string $config_id)
+ {
+ $config_type = '';
+ if (Request::isPost()) {
+ CSRFProtection::verifyUnsafeRequest();
+ $config = ExternPageConfig::find($config_id);
+ if ($config->range_id === 'studip') {
+ $GLOBALS['perm']->check('root');
+ $have_perm = true;
+ } else {
+ $have_perm = $GLOBALS['perm']->have_studip_perm('admin', $config->range_id);
+ }
+ if (!$config || !$have_perm) {
+ throw new AccessDeniedException();
+ }
+ if ($config) {
+ $config_name = $config->name;
+ $config_type = $config->type;
+ if ($config->delete()) {
+ PageLayout::postSuccess(
+ sprintf(_('Die Konfiguration "%s" wurde gelöscht.'), htmlReady($config_name))
+ );
+ }
+ }
+ }
+ $this->redirect($this->indexURL(['open_type' => $config_type]));
+ }
+
+ /**
+ * Show dialog with information about the given configuration.
+ *
+ * @param $config_id
+ */
+ public function info_action(string $config_id)
+ {
+ $this->page = ExternPage::get(ExternPageConfig::find($config_id));
+ if ($this->page->author) {
+ $this->author = '<a href="'
+ . URLHelper::getLink('dispatch.php/profile', ['username' => $this->page->author->username])
+ . '">'
+ . htmlReady($this->page->author)
+ . '</a>';
+ } else {
+ $this->author = _('unbekannt');
+ }
+ if ($this->page->editor) {
+ $this->editor = '<a href="'
+ . URLHelper::getLink('dispatch.php/profile', ['username' => $this->page->editor->username])
+ . '">'
+ . htmlReady($this->page->editor)
+ . '</a>';
+ } else {
+ $this->editor = _('unbekannt');
+ }
+ PageLayout::setHelpKeyword('Basis.ExterneSeiten' . $this->page->type);
+ $this->datafields = $this->page->getDataFields();
+ $this->render_template('institute/extern/info', $this->layout);
+ }
+
+ /**
+ * Action to download given configuration.
+ *
+ * @param $config_id
+ */
+ public function download_action(string $config_id)
+ {
+ $config = ExternPageConfig::find($config_id);
+ $perm = $GLOBALS['perm']->have_studip_perm('admin', $config->range_id)
+ || ($GLOBALS['perm']->have_perm('root') && $config->range_id === 'studip');
+ if (!$config || !$perm) {
+ throw new AccessDeniedException();
+ }
+ $this->response->add_header(
+ 'Content-Disposition',
+ 'attachment; ' . encode_header_parameter('filename', $config->name . '.cfg')
+ );
+ $this->render_json($config->toArray('type name description conf template'));
+ }
+
+ /**
+ * Action to create a new configuration from uploaded file.
+ */
+ public function upload_action()
+ {
+ $this->render_template('institute/extern/upload', $this->layout);
+ }
+
+ /**
+ * Action to import a configuration as json file.
+ *
+ * @return void
+ * @throws InvalidSecurityTokenException
+ * @throws MethodNotAllowedException
+ */
+ public function import_action()
+ {
+ if (Request::submitted('import')) {
+ $this->config_name = Request::get('config_name', '');
+ CSRFProtection::verifyUnsafeRequest();
+ $file = $_FILES['config_file'];
+ if (!$file) {
+ PageLayout::postError(_('Es wurde keine Datei ausgewählt!'));
+ $this->redirect($this->uploadURL());
+ return;
+ }
+ $config_data = json_decode(file_get_contents($file['tmp_name']));
+ if (!$config_data) {
+ PageLayout::postError(_('Die Datei hat ein ungültiges Format!'));
+ $this->redirect($this->uploadURL());
+ return;
+ }
+ $config = new ExternPageConfig();
+ $fields = [
+ 'name',
+ 'description',
+ 'conf',
+ 'template',
+ 'type',
+ ];
+ foreach ($fields as $field) {
+ if (isset($config_data->$field)) {
+ $config->$field = $config_data->$field;
+ } else {
+ PageLayout::postError(_('Die Datei kann nicht importiert werden!'));
+ $this->redirect($this->uploadURL());
+ return;
+ }
+ }
+ if (!in_array($config->type, array_keys($this->config_types))) {
+ PageLayout::postError(_('Der Typ der Konfiguration ist ungültig!'));
+ $this->redirect($this->uploadURL());
+ return;
+ }
+ if (trim($this->config_name)) {
+ $config->name = $this->config_name;
+ }
+ $config->range_id = $this->range;
+ $config->author_id = $config->editor_id = $GLOBALS['user']->id;
+ $config->store();
+ PageLayout::postSuccess(
+ sprintf(_('Die Konfiguration "%s" wurde erfolgreich importiert.'),
+ htmlReady($config->name)
+ ));
+ }
+ $this->relocate($this->indexURL(['open_type' => $config->type]));
+ }
+
+ /**
+ * Action to get study areas by search term.
+ *
+ * @param string $search_term The search term.
+ * @return void
+ */
+ public function search_studyareas_action(string $search_term = '')
+ {
+ $search_term = Request::get('term', $search_term);
+ $paths = [];
+ $study_areas = StudipStudyArea::search($search_term);
+ foreach ((array) $study_areas as $study_area) {
+ if ($study_area->isHidden()) {
+ continue;
+ }
+ $path = $study_area->getPath(' > ');
+ $paths[$path] = [
+ 'id' => $study_area->id,
+ 'text' => $path
+ ];
+ }
+ ksort($paths, SORT_LOCALE_STRING);
+
+ $this->render_json([
+ 'results' => array_values($paths)
+ ]);
+ }
+
+ /**
+ * Checks for sufficient rights.
+ *
+ * @throws AccessDeniedException
+ */
+ protected function checkPerm()
+ {
+ if (!$GLOBALS['perm']->have_perm('root')) {
+ throw new AccessDeniedException();
+ }
+ }
+
+ /**
+ * Creates the sidebar menu.
+ */
+ protected function setSidebar()
+ {
+ if ($this->range) {
+ $actions_widget = new ActionsWidget();
+ $actions_widget->addLink(
+ _('Neue externe Seite anlegen'),
+ $this->newURL(),
+ Icon::create('settings'),
+ ['data-dialog' => 'size=870x500']
+ );
+ Sidebar::Get()->addWidget($actions_widget);
+ }
+ }
+
+ /**
+ * Retrofits navigation with plugins.
+ *
+ * @param bool $is_system True to fetch system-wide external page plugins.
+ * @return void
+ */
+ protected function fetchPlugins(bool $is_system): void
+ {
+ $plugins = PluginEngine::getPlugins('ExternPagePlugin');
+ foreach ($plugins as $plugin) {
+ if (
+ $is_system === $plugin->isSystemPage()
+ || !$is_system === $plugin->isInstitutePage()
+ ) {
+ $nav = $plugin->getConfigurationFormNavigation();
+ $this->config_types[$nav->name] = [
+ 'name' => $nav->title,
+ 'description' => $nav->description,
+ 'icon' => $nav->image,
+ 'template' => $plugin->getConfigurationFormTemplate(),
+ ];
+ }
+ }
+ }
+
+ /**
+ * Sets data for system-wide external pages.
+ *
+ * @return void
+ */
+ protected function getSystemWideConfigTypes(): void
+ {
+ $this->config_types = [
+ 'PersonDetails' => [
+ 'name' => _('Personen-Details'),
+ 'description' => _('Details zu einer Person'),
+ 'icon' => 'person2',
+ 'template' => 'institute/extern/extern_config/persondetails'
+ ],
+ 'Courses' => [
+ 'name' => _('Veranstaltungen'),
+ 'description' => _('Liste der Veranstaltungen'),
+ 'icon' => 'seminar',
+ 'template' => 'institute/extern/extern_config/courses'
+ ],
+ 'CourseDetails' => [
+ 'name' => _('Veranst.-Details'),
+ 'description' => _('Details zu einer Veranstaltung'),
+ 'icon' => 'seminar',
+ 'template' => 'institute/extern/extern_config/coursedetails'
+ ],
+ 'Timetable' => [
+ 'name' => _('Termine'),
+ 'description' => _('Zeiten/Termine/Themen der Veranstaltungen'),
+ 'icon' => 'date-cycle',
+ 'template' => 'institute/extern/extern_config/timetable'
+ ]
+ ];
+ }
+
+}