* @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) { $config = ExternPageConfig::find($config_id); if (!$config) { throw new Exception('ExternPageConfig object not found!'); } $this->page = ExternPage::get($config); if ($this->page->author) { $this->author = '' . htmlReady($this->page->author) . ''; } else { $this->author = _('unbekannt'); } if ($this->page->editor) { $this->editor = '' . htmlReady($this->page->editor) . ''; } 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' ] ]; } }