Skip to content
Snippets Groups Projects
functions.php 61 KiB
Newer Older
<?php
# Lifter002: DONE - not applicable
# Lifter003: TEST
# Lifter007: TODO
# Lifter010: DONE - not applicable
/**
 * functions.php
 *
 * The Stud.IP-Core functions. Look to the descriptions to get further details
 *
 * 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      Cornelis Kater <ckater@gwdg.de>
 * @author      Suchi & Berg GmbH <info@data-quest.de>
 * @author      Ralf Stockmann <rstockm@gwdg.de>
 * @author      André Noack <andre.noack@gmx.net>
 * @license     http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
 * @category    Stud.IP
 * @access      public
 * @package     studip_cores
 * @modulegroup library
 * @module      functions.php
 */

// +---------------------------------------------------------------------------+
// This file is part of Stud.IP
// functions.php
// Stud.IP Kernfunktionen
// Copyright (C) 2002 Cornelis Kater <ckater@gwdg.de>, Suchi & Berg GmbH <info@data-quest.de>,
// Ralf Stockmann <rstockm@gwdg.de>, André Noack André Noack <andre.noack@gmx.net>
// +---------------------------------------------------------------------------+
// 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 any later version.
// +---------------------------------------------------------------------------+
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
// +---------------------------------------------------------------------------+


require_once 'lib/object.inc.php';
require_once 'lib/user_visible.inc.php';

/**
 * returns an array containing name and type of the passed objeact
 * denoted by $range_id
 *
 * @global array $SEM_TYPE
 * @global array $INST_TYPE
 * @global array $SEM_TYPE_MISC_NAME
 *
 * @param string $range_id    the id of the object
 * @param string $object_type the type of the object
 *
 * @return array  an array containing name and type of the object
 */
function get_object_name($range_id, $object_type)
{
    global $SEM_TYPE,$INST_TYPE, $SEM_TYPE_MISC_NAME;

    $name = '';
    $type = '';
    if ($object_type == "sem") {
        $query = "SELECT status, Name FROM seminare WHERE Seminar_id = ?";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$range_id]);
        $row = $statement->fetch(PDO::FETCH_ASSOC);

        if (!$row) {
            $name = _('Unbekannt');
            $type = _('Veranstaltung');
        } else {
            $name = $row['Name'];
            $type = $SEM_TYPE[$row['status']]['name'] ?? _('Veranstaltung');
            if ($type === $SEM_TYPE_MISC_NAME) {
                $type = _('Veranstaltung');
            }
        }
    } else if ($object_type == 'inst' || $object_type == 'fak') {
        $query = "SELECT type, Name FROM Institute WHERE Institut_id = ?";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$range_id]);
        $row = $statement->fetch(PDO::FETCH_ASSOC);

        if (!$row) {
            $name = _('Unbekannt');
            $type = _('Einrichtung');
        } else {
            $name = $row['Name'];
            $type = $INST_TYPE[$row['type']]['name'] ?? _('Einrichtung');
        }
    }

    return compact('name', 'type');
}

/**
 * Returns a sorm object for a given range_id
 *
 * @param string the range_id
 * @return bool|SimpleORMap Course/Institute/User/Statusgruppen/
 */
function get_object_by_range_id($range_id) {
    $possible_sorms = "Course Institute User";
    foreach(words($possible_sorms) as $sorm) {
        if ($object = $sorm::find($range_id)) {
            return $object;
        }
    }
    return false;
}

/**
 * This function checks, if there is an open Veranstaltung or Einrichtung
 *
 * @throws CheckObjectException
 *
 * @return void
 */
function checkObject()
{
    if (!Context::get()) {
        throw new CheckObjectException(_('Sie haben kein Objekt gewählt.'));
    }
}


/**
 * This function checks, if given module
 * is allowed for this stud.ip-object.
 *
 * @throws CheckObjectException
 * @param string $module the module to check for
 * @param bool $is_plugin_name is the module name old style ( "wiki","scm") or new style (the name of the plugin)
 * @return StudipModule
 */
function checkObjectModule($module, $is_plugin_name = false)
{
    if ($context = Context::get()) {
        if (!$is_plugin_name) {
            $module_name = "Core" . ucfirst($module);
        } else {
            $module_name = $module;
        }

        $studip_module = PluginManager::getInstance()->getPlugin($module_name);
        if (!$studip_module || !$studip_module->isActivated($context->getId())) {
            throw new CheckObjectException(sprintf(_('Das Inhaltselement "%s" ist für dieses Objekt leider nicht verfügbar.'), ucfirst($module)));
        }
        return $studip_module;
    }
}

/**
 * This function closes a opened Veranstaltung or Einrichtung
 *
 * @return void
 */
function closeObject()
{
    Context::close();
}

/**
 * This function determines the type of the passed id
 *
 * The function recognizes the following types at the moment:
 * Einrichtungen, Veranstaltungen, Statusgruppen and Fakultaeten
 *
 * @staticvar array $object_type_cache
 *
 * @param string $id         the id of the object
 * @param array  $check_only an array to narrow the search, may contain
 *                            'sem', 'inst', 'fak', 'group' or 'dokument' (optional)
 *
 * @return string  return "inst" (Einrichtung), "sem" (Veranstaltung),
 *                 "fak" (Fakultaeten), "group" (Statusgruppe), "dokument" (Dateien)
 *
 */
function get_object_type($id, $check_only = [])
{
    static $cache = null;

    // Nothing to check
    if (!$id) {
        return false;
    }

    // Id is global
    if ($id === 'studip') {
        return 'global';
    }

    // Initialize cache array
    if ($cache === null) {
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        $cache = new StudipCachedArray('Studip/ObjectTypes');
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
    // No cached entry available? Go ahead and determine type
    if (!isset($cache[$id])) {
        // Tests for specific types
        $tests = [
            "SELECT 'sem' FROM `seminare` WHERE `Seminar_id` = ?" => ['sem'],
            "SELECT IF(`Institut_id` = `fakultaets_id`, 'fak', 'inst') FROM `Institute` WHERE `Institut_id` = ?" => ['inst', 'fak'],
            "SELECT 'date' FROM `termine` WHERE `termin_id` = ?" => ['date'],
            "SELECT 'user' FROM `auth_user_md5` WHERE `user_id` = ?" => ['user'],
            "SELECT 'group' FROM `statusgruppen` WHERE `statusgruppe_id` = ?" => ['group'],
            "SELECT 'dokument' FROM `file_refs` WHERE `id` = ?" => ['dokument'],
            "SELECT 'range_tree' FROM `range_tree` WHERE `item_id` = ?" => ['range_tree'],
        ];

        // If we want to check only for a specific type, order the tests so that
        // these tests will be executed first
        if ($check_only) {
            uasort($tests, function ($a, $b) use ($check_only) {
                return count(array_intersect($b, $check_only)) - count(array_intersect($a, $check_only));
            });
        }
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        // Actually determine type
        $type = null;
        foreach ($tests as $query => $types) {
            $type = DBManager::get()->fetchColumn($query, [$id]);
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
            if ($type) {
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
                break;
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        // Store type
        $cache[$id] = $type ?? false;
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed

    return (!$check_only || in_array($cache[$id], $check_only)) ? $cache[$id] : false;
}

/**
 * This function calculates one of the group colors unique for the semester of
 * the passed timestamp
 *
 * It calculates a unique color number to create the initial entry for a new user in a seminar.
 * It will create a unique number for every semester and will start over, if the max. number
 * (7) is reached.
 *
 * @param integer $sem_start_time the timestamp of the start time from the Semester
 *
 * @return integer  the color number
 *
 */
function select_group($sem_start_time)
{
    //Farben Algorhytmus, erzeugt eindeutige Farbe fuer jedes Semester. Funktioniert ab 2001 die naechsten 1000 Jahre.....
    $year_of_millenium = date ('Y', $sem_start_time) % 1000;
    $index = $year_of_millenium * 2;
    if (date('n', $sem_start_time) > 6)
    return ($index % 7) + 1;
}

/**
 * The function shortens a string, but it uses the first 2/3 and the last 1/3
 *
 * The parts will be divided by a "[...]". The functions is to use like php's
 * mb_substr function.
 *
 * @param string  $what  the original string
 * @param integer $start start pos, 0 is the first pos
 * @param integer $end   end pos
 *
 * @return string
 *
 *
 */
function my_substr($what, $start, $end)
{
    $length = $end - $start;
    $what_length = mb_strlen($what);
    // adding 5 because: mb_strlen("[...]") == 5
    if ($what_length > $length + 5) {
        $what = mb_substr($what, $start, round(($length / 3) * 2))
              . "[...]" . mb_substr($what, $what_length - round($length / 3), $what_length);
    }
    return $what;
}

/**
 * Retrieves the fullname for a given user_id
 *
 * @param string $user_id   if omitted, current user_id is used
 * @param string $format    output format
 * @param bool   $htmlready if true, htmlReady is applied to all output-strings
 *
 * @return string
 */
function get_fullname($user_id = "", $format = "full" , $htmlready = false)
{
    static $cache = [];

    $current_user = User::findCurrent();
        $user_id = $current_user ? $current_user->id : null;
    if ($current_user && $current_user->id === $user_id) {
        $fullname = $current_user->getFullName($format);
        return $htmlready ? htmlReady($fullname) : $fullname;
    }

    $hash = md5($user_id . $format);
    if (!isset($cache[$hash])) {
        $query = "SELECT {$GLOBALS['_fullname_sql'][$format]}
                  FROM auth_user_md5
                  LEFT JOIN user_info USING (user_id)
                  WHERE user_id = ?";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$user_id]);
        $cache[$hash] = $statement->fetchColumn() ?: _('unbekannt');
    }

    return $htmlready ? htmlReady($cache[$hash]) : $cache[$hash];
}

/**
 * Retrieves the fullname for a given username
 *
 * @param string $uname     if omitted, current user_id is used
 * @param string $format    output format
 * @param bool   $htmlready if true, htmlReady is applied to all output-strings
 *
 * @return       string
 */
function get_fullname_from_uname($uname = "", $format = "full", $htmlready = false)
{
    static $cache;
    global $auth, $_fullname_sql;

    if (!$uname) {
        $uname = $auth->auth['uname'];
    }

    $hash = md5($uname . $format);
    if (!isset($cache[$hash])) {
        $query = "SELECT {$_fullname_sql[$format]}
                  FROM auth_user_md5
                  LEFT JOIN user_info USING (user_id)
                  WHERE username = ?";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$uname]);
        $cache[$hash] = $statement->fetchColumn() ?: _('unbekannt');
    }

    return $htmlready ? htmlReady($cache[$hash]) : $cache[$hash];
}

/**
 * Retrieves the username for a given user_id
 *
 * @global object $auth
 * @staticvar array $cache
 *
 * @param string $user_id if omitted, current username will be returned
 *
 * @return string
 *
 */
function get_username($user_id = "")
{
    static $cache = [];
    global $auth;

    if (!$user_id || $user_id === $auth->auth['uid']) {
        return $auth->auth['uname'] ?? '';
    }

    if (!isset($cache[$user_id])) {
        $query = "SELECT username FROM auth_user_md5 WHERE user_id = ?";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$user_id]);
        $cache[$user_id] = $statement->fetchColumn();
    }

    return $cache[$user_id];
}

/**
 * Retrieves the userid for a given username
 *
 * uses global $online array if user is online
 *
 * @global object $auth
 * @staticvar array $cache
 *
 * @param string $username if omitted, current user_id will be returned
 *
 * @return string
 */
function get_userid($username = "")
{
    static $cache = [];
    global $auth;

    if (!$username || $username == $auth->auth['uname']) {
        return $auth->auth['uid'];
    }

    // Read id from database if no cached version is available
    if (!isset($cache[$username])) {
        $query = "SELECT user_id FROM auth_user_md5 WHERE username = ?";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$username]);
        $cache[$username] = $statement->fetchColumn();
    }

    return $cache[$username];
}


/**
 * Return an array containing the nodes of the sem-tree-path
 *
 * @param string $seminar_id the seminar to get the path for
 * @param int    $depth      the depth
 * @param string $delimeter  a string to separate the path parts
 *
 * @return array
 */
function get_sem_tree_path($seminar_id, $depth = false, $delimeter = ">")
{
    $the_tree = TreeAbstract::GetInstance("StudipSemTree");
    $view = DbView::getView('sem_tree');
    $view->params[0] = $seminar_id;
    $rs = $view->get_query("view:SEMINAR_SEM_TREE_GET_IDS");
    while ($rs->next_record()){
        $ret[$rs->f('sem_tree_id')] = $the_tree->getShortPath($rs->f('sem_tree_id'), null, $delimeter, $depth ? $depth - 1 : 0);
    }
    return $ret;
}

/**
 * check_and_set_date
 *
 * Checks if given date is valid and sets field in array accordingly.
 * (E.g. $admin_admission_data['admission_enddate'])
 *
 * @param mixed $tag    day or placeholder for day
 * @param mixed $monat  month or placeholder for month
 * @param mixed $jahr   year or placeholder for year
 * @param mixed $stunde hours or placeholder for hours
 * @param mixed $minute minutes or placeholder for minutes
 * @param array &$arr   Reference to array to update. If NULL, only check is performed
 * @param mixed $field  Name of field in array to be set
 *
 * @return bool  true if date was valid, false else
 */
function check_and_set_date($tag, $monat, $jahr, $stunde, $minute, &$arr, $field)
{
    $check = true; // everything ok?
    if (($jahr>0) && ($jahr<100))
        $jahr=$jahr+2000;

    if ($monat == _("mm")) $monat=0;
    if ($tag == _("tt")) $tag=0;
    if ($jahr == _("jjjj")) $jahr=0;
    //if ($stunde == _("hh")) $stunde=0;
    if ($minute == _("mm")) $minute=0;

    if (($monat) && ($tag) && ($jahr)) {
        if ($stunde==_("hh")) {
            $check = false;
        }

        if ((!checkdate((int)$monat, (int)$tag, (int)$jahr) && ((int)$monat) && ((int)$tag) && ((int)$jahr))) {
            $check = false;
        }

        if (($stunde > 24) || ($minute > 59)
            || ($stunde == 24 && $minute > 0) ) {
            $check = false;
        }

        if ($stunde == 24) {
            $stunde = 23;
            $minute = 59;
        }

        if ($arr) {
            if ($check) {
                $arr[$field] = mktime((int)$stunde,(int)$minute, 0,$monat,$tag,$jahr);
            } else {
                $arr[$field] = -1;
            }
        }
    }
    return $check;
}

/**
 * reset the order-positions for the lecturers in the passed seminar,
 * starting at the passed position
 *
 * @param string $s_id     the seminar to work on
 * @param int    $position the position to start with
 * @deprecated since Stud.IP 5.3
 *
 * @return void
 */
function re_sort_dozenten($s_id, $position)
{
    $query = "UPDATE seminar_user
              SET position = position - 1
              WHERE Seminar_id = ? AND status = 'dozent' AND position > ?";
    $statement = DBManager::get()->prepare($query);
    $statement->execute([$s_id, $position]);
}

/**
 * reset the order-positions for the tutors in the passed seminar,
 * starting at the passed position
 *
 * @param string $s_id     the seminar to work on
 * @param int    $position the position to start with
 * @deprecated since Stud.IP 5.3
 *
 * @return void
 */
function re_sort_tutoren($s_id, $position)
{
    $query = "UPDATE seminar_user
              SET position = position - 1
              WHERE Seminar_id = ? AND status = 'tutor' AND position > ?";
    $statement = DBManager::get()->prepare($query);
    $statement->execute([$s_id, $position]);
}

/**
 * return the highest position-number increased by one for the
 * passed user-group in the passed seminar
 *
 * @param string $status     can be on of 'tutor', 'dozent', ...
 * @param string $seminar_id the seminar to work on
 * @deprecated since Stud.IP 5.3
 *
 * @return int  the next available position
 */
function get_next_position($status, $seminar_id)
{
    $query = "SELECT MAX(position) + 1
              FROM seminar_user
              WHERE Seminar_id = ? AND status = ?";
    $statement = DBManager::get()->prepare($query);
    $statement->execute([$seminar_id, $status]);

    return $statement->fetchColumn() ?: 0;
}

/**
 * converts a string to a float, depending on the locale
 *
 * @param string $str the string to convert to float
 *
 * @return float the string casted to float
 */
function StringToFloat($str)
{
    $str = mb_substr((string)$str,0,13);
    $locale = localeconv();
    $from = ($locale["thousands_sep"] ? $locale["thousands_sep"] : ',');
    $to = ($locale["decimal_point"] ? $locale["decimal_point"] : '.');
    if(mb_strstr($str, $from)){
        $conv_str = str_replace($from, $to, $str);
        $my_float = (float)$conv_str;
        if ($conv_str === (string)$my_float) return $my_float;
    }
    return (float)$str;
}

/**
 * check which perms the currently logged in user had in the
 * passed archived seminar
 *
 * @global array $perm
 * @global object $auth
 * @staticvar array $archiv_perms
 *
 * @param string $seminar_id the seminar in the archive
 *
 * @return string the perm the user had
 */
function archiv_check_perm($seminar_id)
{
    static $archiv_perms;

    $u_id = $GLOBALS['user']->id;

    // root darf sowieso ueberall dran
    if ($GLOBALS['perm']->have_perm('root')) {
        return 'admin';
    }

    if (!is_array($archiv_perms)){
        $query = "SELECT seminar_id, status FROM archiv_user WHERE user_id = ?";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$u_id]);
        $archiv_perms = $statement->fetchGrouped(PDO::FETCH_COLUMN);

        if ($GLOBALS['perm']->have_perm('admin')) {
            $query = "SELECT archiv.seminar_id, 'admin'
                      FROM user_inst
                      INNER JOIN archiv ON (heimat_inst_id = institut_id)
                      WHERE user_inst.user_id = ? AND user_inst.inst_perms = 'admin'";
            $statement = DBManager::get()->prepare($query);
            $statement->execute([$u_id]);
            $temp_perms = $statement->fetchGrouped(PDO::FETCH_COLUMN);

            $archiv_perms = array_merge($archiv_perms, $temp_perms);
        }
        if ($GLOBALS['perm']->is_fak_admin()) {
            $query = "SELECT archiv.seminar_id, 'admin'
                      FROM user_inst
                      INNER JOIN Institute ON (user_inst.institut_id = Institute.fakultaets_id)
                      INNER JOIN archiv ON (archiv.heimat_inst_id = Institute.institut_id)
                      WHERE user_inst.user_id = ? AND user_inst.inst_perms = 'admin'";
            $statement = DBManager::get()->prepare($query);
            $statement->execute([$u_id]);
            $temp_perms = $statement->fetchGrouped(PDO::FETCH_COLUMN);

            $archiv_perms = array_merge($archiv_perms, $temp_perms);
        }
    }
    return $archiv_perms[$seminar_id] ?? '';
}

/**
 * retrieve a list of all online users
 *
 * @global object $user
 * @global array  $_fullname_sql
 *
 * @param int    $active_time filter: the time in minutes until last life-sign
 * @param string $name_format format the fullname shall have
 *
 * @return array
 */
function get_users_online($active_time = 5, $name_format = 'full_rev')
{
    if (!isset($GLOBALS['_fullname_sql'][$name_format])) {
        $sql_fullname = array_keys($GLOBALS['_fullname_sql']);
        $name_format = reset($sql_fullname);
    }

    $query = "SELECT a.username AS temp, a.username, {$GLOBALS['_fullname_sql'][$name_format]} AS name,
                     ABS(CAST(UNIX_TIMESTAMP() AS SIGNED) - CAST(last_lifesign AS SIGNED)) AS last_action,
                     a.user_id, IF(owner_id IS NOT NULL, 1, 0) AS is_buddy, " . get_vis_query('a', 'online') . " AS is_visible,
                     a.visible
              FROM user_online uo
              JOIN auth_user_md5 a ON (a.user_id = uo.user_id)
              LEFT JOIN user_info ON (user_info.user_id = uo.user_id)
              LEFT JOIN user_visibility ON (user_visibility.user_id = uo.user_id)
              LEFT JOIN contact ON (owner_id = ? AND contact.user_id = a.user_id)
              WHERE last_lifesign > ? AND uo.user_id <> ?
              ORDER BY {$GLOBALS['_fullname_sql'][$name_format]} ASC";
    $statement = DBManager::get()->prepare($query);
    $statement->execute([
        $GLOBALS['user']->id,
        time() - $active_time * 60,
        $GLOBALS['user']->id,
    ]);
    $online = $statement->fetchGrouped();

    // measure users online
    if ($active_time === 10) {
        Metrics::gauge('core.users_online', sizeof($online));
    }

    return $online;
}

/**
 * get the number of currently online users
 *
 * @param int $active_time filter: the time in minutes until last life-sign
 *
 * @return int
 */
function get_users_online_count($active_time = 10)
{
    $cache = StudipCacheFactory::getCache();
    $online_count = $cache->read("online_count/{$active_time}");
    if ($online_count === false) {
        $query = "SELECT COUNT(*) FROM user_online
                  WHERE last_lifesign > ?";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([time() - $active_time * 60]);
        $online_count = $statement->fetchColumn();
        $cache->write("online_count/{$active_time}", $online_count, 180);
    }
    if ($GLOBALS['user']->id && $GLOBALS['user']->id !== 'nobody') {
        --$online_count;
    }
    return $online_count > 0 ? $online_count : 0;
}

/**
 * return a studip-ticket
 *
 * @return string a unique id referring to a newly created ticket
 */
function get_ticket()
{
    return Seminar_Session::get_ticket();
}

/**
 * check if the passed ticket is valid
 *
 * @param string $studipticket the ticket-id to check
 *
 * @return bool
 */
function check_ticket($studipticket)
{
    return Seminar_Session::check_ticket($studipticket);
}

/**
 * searches
 *
 * @global array $perm
 * @global object $user
 * @global array $_fullname_sql
 *
 * @param string $search_str  optional search-string
 * @param string $search_user optional user to search for
 * @param bool   $show_sem    if true, the seminar is added to the result
 *
 * @return array
 */
function search_range($search_str = false, $search_user = false, $show_sem = true)
{
    global $_fullname_sql;

    // Helper function that obtains the correct name for an entity taking
    // in account whether the semesters should be displayed or not
    $formatName = function ($row) use ($show_sem) {
        $name = $row['Name'];
        if ($show_sem) {
            $name = sprintf('%s (%s%s)',
                            $name,
                            $row['startsem'],
                            $row['startsem'] != $row['endsem'] ? ' - ' . $row['endsem'] : '');
        }
        return $name;
    };

    $search_result = [];
    $show_sem_sql1 = ", s.start_time, (SELECT semester_data.name FROM semester_data WHERE s.start_time >= semester_data.`beginn` AND s.start_time <= semester_data.`ende` LIMIT 1) AS startsem, IF(semester_courses.semester_id IS NULL, '"._("unbegrenzt")."', (SELECT semester_data.name FROM semester_data LEFT JOIN semester_courses USING (semester_id) WHERE semester_courses.course_id = s.Seminar_id ORDER BY semester_data.`beginn` DESC LIMIT 1)) AS endsem ";
    $show_sem_sql2 = "LEFT JOIN semester_courses ON (semester_courses.course_id = s.Seminar_id) ";


    if ($search_str && $GLOBALS['perm']->have_perm('root')) {
        if ($search_user) {
            $query = "SELECT user_id, CONCAT({$_fullname_sql['full']}, ' (', username, ')') AS name
                      FROM auth_user_md5 AS a
                      LEFT JOIN user_info USING (user_id)
                      WHERE CONCAT(Vorname, ' ', Nachname, ' ', username) LIKE CONCAT('%', ?, '%')
                      ORDER BY Nachname, Vorname";
            $statement = DBManager::get()->prepare($query);
            $statement->execute([$search_str]);
            while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
                $search_result[$row['user_id']] = [
                    'type' => 'user',
                    'name' => $row['name'],
                ];
            }
        }

        $_hidden = _('(versteckt)');
        $query = "SELECT Seminar_id, IF(s.visible = 0, CONCAT(s.Name, ' {$_hidden}'), s.Name) AS Name %s
                  FROM seminare AS s %s
                  WHERE s.Name LIKE CONCAT('%%', ?, '%%')
                  GROUP BY s.Seminar_id
                  ORDER BY start_time DESC, Name";
        $query = $show_sem
               ? sprintf($query, $show_sem_sql1, $show_sem_sql2)
               : sprintf($query, '', '');
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$search_str]);
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
            $search_result[$row['Seminar_id']] = [
                'type'      => 'sem',
                'name'      => $formatName($row),
                'starttime' => $row['start_time'],
                'startsem'  => $row['startsem'],
            ];
        }

        $query = "SELECT Institut_id, Name, IF(Institut_id = fakultaets_id, 'fak', 'inst') AS type
                  FROM Institute
                  WHERE Name LIKE CONCAT('%', ?, '%')
                  ORDER BY Name";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$search_str]);
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
            $search_result[$row['Institut_id']] = [
                'type' => $row['type'],
                'name' => $row['Name'],
            ];
        }
    } elseif ($search_str && $GLOBALS['perm']->have_perm('admin')) {
        $_hidden = _('(versteckt)');
        $query = "SELECT s.Seminar_id, IF(s.visible = 0, CONCAT(s.Name, ' {$_hidden}'), s.Name) AS Name %s
                  FROM user_inst AS a
                  JOIN seminare AS s USING (Institut_id) %s
                  WHERE a.user_id = ? AND a.inst_perms = 'admin' AND s.Name LIKE CONCAT('%%', ?, '%%')
                  ORDER BY start_time";
        $query = $show_sem
               ? sprintf($query, $show_sem_sql1, $show_sem_sql2)
               : sprintf($query, '', '');
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$GLOBALS['user']->id, $search_str]);
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
            $search_result[$row['Seminar_id']] = [
                'type'      => 'sem',
                'name'      => $formatName($row),
                'starttime' => $row['start_time'],
                'startsem'  => $row['startsem'],
            ];
        }

        $query = "SELECT b.Institut_id, b.Name
                  FROM user_inst AS a
                  JOIN Institute AS b USING (Institut_id)
                  WHERE a.user_id = ? AND a.inst_perms = 'admin'
                    AND a.institut_id != b.fakultaets_id AND b.Name LIKE CONCAT('%', ?, '%')
                  ORDER BY Name";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$GLOBALS['user']->id, $search_str]);
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
            $search_result[$row['Institut_id']] = [
                'type' => 'inst',
                'name' => $row['Name'],
            ];
        }
        if ($GLOBALS['perm']->is_fak_admin()) {
            $_hidden = _('(versteckt)');
            $query = "SELECT s.Seminar_id, IF(s.visible = 0, CONCAT(s.Name, ' {$_hidden}'), s.Name) AS Name %s
                      FROM user_inst AS a
                      JOIN Institute AS b ON (a.Institut_id = b.Institut_id AND b.Institut_id = b.fakultaets_id)
                      JOIN Institute AS c ON (c.fakultaets_id = b.Institut_id AND c.fakultaets_id != c.Institut_id)
                      JOIN seminare AS s ON (s.Institut_id = c.Institut_id) %s
                      WHERE a.user_id = ? AND a.inst_perms = 'admin'
                        AND s.Name LIKE CONCAT('%%', ?, '%%')
                      ORDER BY start_time DESC, Name";
            $query = $show_sem
                   ? sprintf($query, $show_sem_sql1, $show_sem_sql2)
                   : sprintf($query, '', '');
            $statement = DBManager::get()->prepare($query);
            $statement->execute([$GLOBALS['user']->id, $search_str]);
            while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
                $search_result[$row['Seminar_id']] = [
                    'type'      => 'sem',
                    'name'      => $formatName($row),
                    'starttime' => $row['start_time'],
                    'startsem'  => $row['startsem'],
                ];
            }

            $query = "SELECT c.Institut_id, c.Name
                      FROM user_inst AS a
                      JOIN Institute AS b ON (a.Institut_id = b.Institut_id AND b.Institut_id = b.fakultaets_id)
                      JOIN Institute AS c ON (c.fakultaets_id = b.institut_id AND c.fakultaets_id != c.institut_id)
                      WHERE a.user_id = ? AND a.inst_perms = 'admin'
                        AND c.Name LIKE CONCAT('%', ?, '%')
                      ORDER BY Name";
            $statement = DBManager::get()->prepare($query);
            $statement->execute([$GLOBALS['user']->id, $search_str]);
            while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
                $search_result[$row['Institut_id']] = [
                    'type' => 'inst',
                    'name' => $row['Name'],
                ];
            }

            $query = "SELECT b.Institut_id, b.Name
                      FROM user_inst AS a
                      JOIN Institute AS b ON (a.Institut_id = b.Institut_id AND b.Institut_id = b.fakultaets_id)
                      WHERE a.user_id = ? AND a.inst_perms = 'admin'
                        AND b.Name LIKE CONCAT('%', ?, '%')
                      ORDER BY Name";
            $statement = DBManager::get()->prepare($query);
            $statement->execute([$GLOBALS['user']->id, $search_str]);
            while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
                $search_result[$row['Institut_id']] = [
                    'type' => 'inst',
                    'name' => $row['Name'],
                ];
            }
        }
    } elseif ($GLOBALS['perm']->have_perm('tutor') || $GLOBALS['perm']->have_perm('autor')) {
        // autors my also have evaluations and news in studygroups with proper rights
        $_hidden = _('(versteckt)');
        $query = "SELECT s.Seminar_id, IF(s.visible = 0, CONCAT(s.Name, ' {$_hidden}'), s.Name) AS Name %s
                  FROM seminar_user AS a
                  JOIN seminare AS s USING (Seminar_id) %s
                  WHERE a.user_id = ? AND a.status IN ('tutor', 'dozent')
                  ORDER BY start_time DESC, Name";
        $query = $show_sem
               ? sprintf($query, $show_sem_sql1, $show_sem_sql2)
               : sprintf($query, '', '');
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$GLOBALS['user']->id]);
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
            $search_result[$row['Seminar_id']] = [
                'type'      => 'sem',
                'name'      => $formatName($row),
                'starttime' => $row['start_time'],
                'startsem'  => $row['startsem'],
            ];
        }

        $query = "SELECT Institut_id, b.Name,
                         IF (Institut_id = fakultaets_id, 'fak', 'inst') AS type
                  FROM user_inst AS a
                  JOIN Institute AS b USING (Institut_id)
                  WHERE a.user_id = ? AND a.inst_perms IN ('dozent','tutor')
                  ORDER BY Name";
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$GLOBALS['user']->id]);
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
            $search_result[$row['Institut_id']] = [
                'name' => $row['Name'],
                'type' => $row['type'],
            ];
        }
    }

    if (Config::get()->DEPUTIES_ENABLE) {
        $_hidden = _('(versteckt)');
        $_deputy = _('Vertretung');
        $query = "SELECT s.Seminar_id,
                         CONCAT(IF(s.visible = 0, CONCAT(s.Name, ' {$_hidden}'), s.Name), ' [{$_deputy}]') AS Name %s
                  FROM seminare AS s
                  JOIN deputies AS d ON (s.Seminar_id = d.range_id) %s
                  WHERE d.user_id = ?
                  ORDER BY s.start_time DESC, Name";
        $query = $show_sem
               ? sprintf($query, $show_sem_sql1, $show_sem_sql2)
               : sprintf($query, '', '');
        $statement = DBManager::get()->prepare($query);
        $statement->execute([$GLOBALS['user']->id]);
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
            $search_result[$row['Seminar_id']] = [
                'type'      => 'sem',
                'name'      => $formatName($row),
                'starttime' => $row['start_time'],
                'startsem'  => $row['startsem'],
            ];
        }
        if (Deputy::isEditActivated()) {
            $query = "SELECT a.user_id, a.username, 'user' AS type,
                             CONCAT({$_fullname_sql['full']}, ' (', username, ')') AS name
                      FROM auth_user_md5 AS a
                      JOIN user_info USING (user_id)
                      JOIN deputies AS d ON (a.user_id = d.range_id)
                      WHERE d.user_id = ?
                      ORDER BY name ASC";
            $statement = DBManager::get()->prepare($query);
            $statement->execute([
                $GLOBALS['user']->id
            ]);
            while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
                $search_result[$row['user_id']] = $row;
            }
        }
    }

    return $search_result ?: null;
}

/**
 * format_help_url($keyword)
 * returns URL for given help keyword
 *
 * @param string $keyword the help-keyword
 *
 * @return string the help-url
 */
function format_help_url($keyword)
{
    // all help urls need short language tag (de, en)