Files
FHC-Core/application/libraries/SearchBarLib.php
T

601 lines
20 KiB
PHP

<?php
/**
* Copyright (C) 2022 fhcomplete.org
*
* 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 3 of the License, or
* (at your option) 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, see <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
use \stdClass as stdClass;
/**
*
*/
class SearchBarLib
{
// Error constats
const ERROR_WRONG_JSON = 'ERR001';
const ERROR_WRONG_SEARCHSTR = 'ERR002';
const ERROR_NO_TYPES = 'ERR003';
const ERROR_WRONG_TYPES = 'ERR004';
const ERROR_NOT_AUTH = 'ERR005';
// List of allowed types of search
const ALLOWED_TYPES = ['mitarbeiter', 'mitarbeiter_ohne_zuordnung', 'organisationunit', 'raum', 'person', 'student','studentStv', 'prestudent', 'document', 'cms'];
const PHOTO_IMG_URL = '/cis/public/bild.php?src=person&person_id=';
private $_ci; // Code igniter instance
/**
* Gets the CI instance and loads model
*/
public function __construct()
{
$this->_ci =& get_instance(); // get code igniter instance
// It is loaded only to have the DB_Model available
$this->_ci->load->model('person/Benutzer_model', 'BenutzerModel');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
/**
* It performes the search of the given search string using the specified search types
*/
public function search($searchstr, $types)
{
// Checks if the given parameters are fine
$search = $this->_checkParameters($searchstr, $types);
// If the check was successful then perform the search
if (isSuccess($search)) $search = $this->_search($searchstr, $types);
return $search; // return the result
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
/**
* Checks:
* - The given searchstr is a not empty string
* - The given types is a not empty array and contains allowed search types
*/
private function _checkParameters($searchstr, $types)
{
// If searchstr is empty
if (isEmptyString($searchstr)) return error(self::ERROR_WRONG_SEARCHSTR);
// If types is not an array or it is empty
if (isEmptyArray($types)) return error(self::ERROR_NO_TYPES);
// If all the elements in types are allowed search types
if (!isEmptyArray(array_diff($types, self::ALLOWED_TYPES))) return error(self::ERROR_WRONG_TYPES);
return success(); // The check is fine!
}
/**
* Loops on types and perform the search of that type using searchstr
* Then it collects all the returned data into an array as property of an object
*/
private function _search($searchstr, $types)
{
// Object to be returned
$result = new stdClass();
$result->data = array();
// For each search type
foreach ($types as $type)
{
// Perform the search and then add the result to data
$result->data = array_merge($result->data, $this->{'_'.$type}($searchstr, $type));
}
return $result;
}
private function _mitarbeiter_ohne_zuordnung($searchstr, $type)
{
$dbModel = new DB_Model();
$sql = '
SELECT
\'employee\' AS renderer,
\''.$type.'\' AS type,
b.uid AS uid,
p.person_id AS person_id,
p.vorname || \' \' || p.nachname AS name,
ARRAY_AGG(DISTINCT(org.bezeichnung)) AS organisationunit_name,
COALESCE(b.alias, b.uid) || \''.'@'.DOMAIN.'\' AS email,
TRIM(COALESCE(k.kontakt, \'\') || \' \' || COALESCE(m.telefonklappe, \'\')) AS phone,
\''.base_url(self::PHOTO_IMG_URL).'\' || p.person_id AS photo_url,
ARRAY_AGG(DISTINCT(stdkst.bezeichnung)) AS standardkostenstelle
FROM public.tbl_mitarbeiter m
JOIN public.tbl_benutzer b ON(b.uid = m.mitarbeiter_uid)
LEFT JOIN (
SELECT \'[\' || ot.bezeichnung || \'] \' || o.bezeichnung AS bezeichnung, bf.uid
FROM public.tbl_benutzerfunktion bf
JOIN public.tbl_organisationseinheit o USING(oe_kurzbz)
JOIN public.tbl_organisationseinheittyp ot USING(organisationseinheittyp_kurzbz)
WHERE bf.funktion_kurzbz = \'kstzuordnung\'
AND (bf.datum_von IS NULL OR bf.datum_von <= NOW())
AND (bf.datum_bis IS NULL OR bf.datum_bis >= NOW())
GROUP BY o.bezeichnung, ot.bezeichnung, bf.uid
) stdkst ON stdkst.uid = b.uid
JOIN public.tbl_person p USING(person_id)
LEFT JOIN (
SELECT \'[\' || ot.bezeichnung || \'] \' || o.bezeichnung AS bezeichnung, bf.uid
FROM public.tbl_benutzerfunktion bf
JOIN public.tbl_organisationseinheit o USING(oe_kurzbz)
JOIN public.tbl_organisationseinheittyp ot USING(organisationseinheittyp_kurzbz)
WHERE bf.funktion_kurzbz = \'oezuordnung\'
AND (bf.datum_von IS NULL OR bf.datum_von <= NOW())
AND (bf.datum_bis IS NULL OR bf.datum_bis >= NOW())
GROUP BY o.bezeichnung, ot.bezeichnung, bf.uid
) org ON org.uid = b.uid
LEFT JOIN (
SELECT kontakt, standort_id
FROM public.tbl_kontakt
WHERE kontakttyp = \'telefon\'
) k ON(k.standort_id = m.standort_id)
WHERE
(stdkst.bezeichnung IS NULL
OR org.bezeichnung IS NULL)
AND (
' .
$this->buildSearchClause(
$dbModel,
array('b.uid', 'p.vorname', 'p.nachname'),
$searchstr
) .
'
)
GROUP BY type, b.uid, p.person_id, name, email, m.telefonklappe, phone
';
$employees = $dbModel->execReadOnlyQuery($sql);
// If something has been found then return it
if (hasData($employees)) return getData($employees);
// Otherwise return an empty array
return array();
}
protected function buildSearchClause(DB_Model $dbModel, array $columns, $searchstr)
{
$searchstr = preg_replace('/[[:punct:]]/', ' ', $searchstr);
$document = implode(' || \' \' || ', $columns);
$query = '\'' . implode(':* & ', explode(' ', trim($searchstr))) . ':*\'';
$reversequery = '\'*:' . implode(' & *:', explode(' ', trim($searchstr))) . '\'';
$nospacequery = '\'' . implode('', explode(' ', trim($searchstr))) . ':*\'';
$searchclause = <<<EOSC
to_tsvector(lower(regexp_replace({$document}, '[[:punct:]]', ' ', 'g'))) @@ to_tsquery(lower({$query}))
OR
to_tsvector(reverse(lower(regexp_replace({$document}, '[[:punct:]]', ' ', 'g')))) @@ to_tsquery(reverse(lower({$reversequery})))
OR
to_tsvector(lower(regexp_replace({$document}, '[[:punct:]]', ' ', 'g'))) @@ to_tsquery(lower({$nospacequery}))
EOSC;
return $searchclause;
}
/**
* Search for employees
*/
private function _mitarbeiter($searchstr, $type)
{
$dbModel = new DB_Model();
$employees = $dbModel->execReadOnlyQuery('
SELECT
\'employee\' AS renderer,
\''.$type.'\' AS type,
b.uid AS uid,
p.person_id AS person_id,
p.vorname || \' \' || p.nachname AS name,
ARRAY_AGG(DISTINCT(org.bezeichnung)) AS organisationunit_name,
COALESCE(b.alias, b.uid) || \''.'@'.DOMAIN.'\' AS email,
TRIM(COALESCE(k.kontakt, \'\') || \' \' || COALESCE(m.telefonklappe, \'\')) AS phone,
\''.base_url(self::PHOTO_IMG_URL).'\' || p.person_id AS photo_url,
ARRAY_AGG(DISTINCT(stdkst.bezeichnung)) AS standardkostenstelle
FROM public.tbl_mitarbeiter m
JOIN public.tbl_benutzer b ON(b.uid = m.mitarbeiter_uid)
JOIN (
SELECT \'[\' || ot.bezeichnung || \'] \' || o.bezeichnung AS bezeichnung, bf.uid
FROM public.tbl_benutzerfunktion bf
JOIN public.tbl_organisationseinheit o USING(oe_kurzbz)
JOIN public.tbl_organisationseinheittyp ot USING(organisationseinheittyp_kurzbz)
WHERE bf.funktion_kurzbz = \'kstzuordnung\'
AND (bf.datum_von IS NULL OR bf.datum_von <= NOW())
AND (bf.datum_bis IS NULL OR bf.datum_bis >= NOW())
GROUP BY o.bezeichnung, ot.bezeichnung, bf.uid
) stdkst ON stdkst.uid = b.uid
JOIN public.tbl_person p USING(person_id)
JOIN (
SELECT \'[\' || ot.bezeichnung || \'] \' || o.bezeichnung AS bezeichnung, bf.uid
FROM public.tbl_benutzerfunktion bf
JOIN public.tbl_organisationseinheit o USING(oe_kurzbz)
JOIN public.tbl_organisationseinheittyp ot USING(organisationseinheittyp_kurzbz)
WHERE bf.funktion_kurzbz = \'oezuordnung\'
AND (bf.datum_von IS NULL OR bf.datum_von <= NOW())
AND (bf.datum_bis IS NULL OR bf.datum_bis >= NOW())
GROUP BY o.bezeichnung, ot.bezeichnung, bf.uid
) org ON org.uid = b.uid
LEFT JOIN (
SELECT kontakt, standort_id
FROM public.tbl_kontakt
WHERE kontakttyp = \'telefon\'
) k ON(k.standort_id = m.standort_id)
WHERE ' .
$this->buildSearchClause(
$dbModel,
array('b.uid', 'p.vorname', 'p.nachname', 'org.bezeichnung', 'stdkst.bezeichnung'),
$searchstr
) .
'
GROUP BY type, b.uid, p.person_id, name, email, m.telefonklappe, phone
');
// If something has been found then return it
if (hasData($employees)) return getData($employees);
// Otherwise return an empty array
return array();
}
/**
* Seach for organisation units
*/
private function _organisationunit($searchstr, $type)
{
$dbModel = new DB_Model();
$ous = $dbModel->execReadOnlyQuery('
SELECT
\'' . $type . '\' AS renderer,
\''.$type.'\' AS type,
o.oe_kurzbz AS oe_kurzbz,
\'[\' || ot.bezeichnung || \'] \' || o.bezeichnung AS name,
oParent.oe_kurzbz AS parentoe_kurzbz,
(CASE WHEN oParent.bezeichnung IS NOT NULL THEN \'[\' || otParent.bezeichnung || \'] \' || oParent.bezeichnung END) AS parentoe_name,
ARRAY_AGG(DISTINCT(bfLeader.uid)) AS leader_uid,
ARRAY_AGG(DISTINCT(bfLeader.vorname || \' \' || bfLeader.nachname)) AS leader_name,
COUNT(bfCount.benutzerfunktion_id) AS number_of_people,
(CASE WHEN o.mailverteiler = TRUE THEN o.oe_kurzbz || \''.'@'.DOMAIN.'\' END) AS mailgroup
FROM public.tbl_organisationseinheit o
JOIN public.tbl_organisationseinheittyp ot USING(organisationseinheittyp_kurzbz)
LEFT JOIN public.tbl_organisationseinheit oParent ON(oParent.oe_kurzbz = o.oe_parent_kurzbz)
LEFT JOIN public.tbl_organisationseinheittyp otParent ON(oParent.organisationseinheittyp_kurzbz = otParent.organisationseinheittyp_kurzbz)
LEFT JOIN (
SELECT benutzerfunktion_id, oe_kurzbz
FROM public.tbl_benutzerfunktion
WHERE funktion_kurzbz = \'oezuordnung\'
AND (datum_von IS NULL OR datum_von <= NOW())
AND (datum_bis IS NULL OR datum_bis >= NOW())
) bfCount ON(bfCount.oe_kurzbz = o.oe_kurzbz)
LEFT JOIN (
SELECT bf.oe_kurzbz, bf.uid, p.vorname, p.nachname
FROM public.tbl_benutzerfunktion bf
JOIN public.tbl_benutzer b USING(uid)
JOIN public.tbl_person p USING(person_id)
WHERE funktion_kurzbz = \'Leitung\'
AND (datum_von IS NULL OR datum_von <= NOW())
AND (datum_bis IS NULL OR datum_bis >= NOW())
AND b.aktiv = TRUE
) bfLeader ON(bfLeader.oe_kurzbz = o.oe_kurzbz)
WHERE
o.aktiv = true
AND (' .
$this->buildSearchClause(
$dbModel,
array('o.oe_kurzbz', 'o.bezeichnung', 'ot.bezeichnung'),
$searchstr
) .
')
GROUP BY type, o.oe_kurzbz, o.bezeichnung, ot.bezeichnung, oParent.oe_kurzbz, oParent.bezeichnung, otParent.bezeichnung
');
// If something has been found
if (hasData($ous))
{
// Loop through the returned dataset
foreach (getData($ous) as $ou)
{
// Create the new property leaders as an empty array
$ou->leaders = array();
// Loop through the found leaders for this organisation unit
for ($i = 0; $i < count($ou->leader_uid); $i++)
{
// If a leader exists for this organisationunit and has a name :D
if (!isEmptyString($ou->leader_uid[$i]) && !isEmptyString($ou->leader_name[$i]))
{
// Empty object that will contains the leader uid and name
$leader = new stdClass();
// Set the properties name and uid
$leader->uid = $ou->leader_uid[$i];
$leader->name = $ou->leader_name[$i];
// Add the leader object to the leaders array
$ou->leaders[] = $leader;
}
}
// Remove the not needed properties leader_uid and leader_name
unset($ou->leader_uid);
unset($ou->leader_name);
}
// Returns the changed dataset
return getData($ous);
}
// Otherwise return an empty array
return array();
}
/**
* Search for persons
*/
private function _person($searchstr, $type)
{
return array();
}
/**
* Search for students
*/
private function _student($searchstr, $type)
{
$dbModel = new DB_Model();
$gesperrtes_foto = base64_encode(file_get_contents(DOC_ROOT.'skin/images/profilbild_dummy.jpg'));
$students = $dbModel->execReadOnlyQuery('
SELECT
\'' . $type . '\' AS renderer,
\''.$type.'\' AS type,
s.student_uid AS uid,
CONCAT(s.student_uid,\'@'.DOMAIN.'\') AS email,
s.matrikelnr,
CONCAT(UPPER(stg.typ),UPPER(stg.kurzbz),\'-\',s.semester,s.verband) as verband,
stg.bezeichnung AS studiengang,
p.person_id AS person_id,
p.vorname || \' \' || p.nachname AS name,
CASE
when s.student_uid = \''.getAuthUID().'\' then p.foto
when p.foto IS NULL then \''.$gesperrtes_foto.'\'
when p.foto_sperre = false then p.foto
else \''.$gesperrtes_foto.'\'
end as foto,
b.aktiv
FROM public.tbl_student s
JOIN public.tbl_studiengang stg USING(studiengang_kz)
JOIN public.tbl_benutzer b ON(b.uid = s.student_uid)
JOIN public.tbl_person p USING(person_id)
LEFT JOIN (
SELECT kontakt, person_id
FROM public.tbl_kontakt
WHERE kontakttyp = \'email\'
) as k USING(person_id)
WHERE
b.aktiv = TRUE
AND (b.uid ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\'
OR p.vorname ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\'
OR p.nachname ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\')
GROUP BY type, s.student_uid, s.matrikelnr, p.person_id, name,
email, p.foto, s.verband, s.semester, stg.bezeichnung,
stg.typ, stg.kurzbz, b.aktiv
ORDER BY b.aktiv DESC, p.nachname ASC, p.vorname ASC
');
// If something has been found then return it
if (hasData($students)) return getData($students);
// Otherwise return an empty array
return array();
}
private function _studentStv($searchstr, $type)
{
$dbModel = new DB_Model();
$students = $dbModel->execReadOnlyQuery('
SELECT
\'student\' AS renderer,
\''.$type.'\' AS type,
s.student_uid AS uid,
s.matrikelnr,
CONCAT(UPPER(stg.typ),UPPER(stg.kurzbz),\'-\',s.semester,s.verband) as verband,
stg.bezeichnung AS studiengang,
p.person_id AS person_id,
p.vorname || \' \' || p.nachname AS name,
k.kontakt AS email,
p.foto,
b.aktiv
FROM public.tbl_student s
JOIN public.tbl_studiengang stg USING(studiengang_kz)
JOIN public.tbl_benutzer b ON(b.uid = s.student_uid)
JOIN public.tbl_person p USING(person_id)
LEFT JOIN (
SELECT kontakt, person_id
FROM public.tbl_kontakt
WHERE kontakttyp = \'email\'
) as k USING(person_id)
WHERE
b.uid ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\'
OR p.vorname ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\'
OR p.nachname ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\'
GROUP BY type, s.student_uid, s.matrikelnr, p.person_id, name,
k.kontakt, p.foto, s.verband, s.semester, stg.bezeichnung,
stg.typ, stg.kurzbz, b.aktiv
ORDER BY b.aktiv DESC, p.nachname ASC, p.vorname ASC
');
// If something has been found then return it
if (hasData($students)) return getData($students);
// Otherwise return an empty array
return array();
}
/**
* Search for prestudents
*/
private function _prestudent($searchstr, $type)
{
$dbModel = new DB_Model();
$prestudent = $dbModel->execReadOnlyQuery('
SELECT
\'' . $type . '\' AS renderer,
\''.$type.'\' AS type,
ps.prestudent_id,
ps.studiengang_kz,
p.person_id AS person_id,
b.uid,
p.vorname || \' \' || p.nachname AS name,
(
SELECT kontakt
FROM public.tbl_kontakt
WHERE kontakttyp = \'email\'
AND person_id = p.person_id
LIMIT 1
) as email,
p.foto,
sg.bezeichnung
FROM public.tbl_prestudent ps
LEFT JOIN public.tbl_student s USING (prestudent_id)
LEFT JOIN public.tbl_benutzer b ON (b.uid = s.student_uid)
JOIN public.tbl_person p ON (p.person_id = ps.person_id)
LEFT JOIN public.tbl_studiengang sg ON (sg.studiengang_kz = ps.studiengang_kz)
WHERE b.uid ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\'
OR p.vorname ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\'
OR p.nachname ILIKE \'%'.$dbModel->escapeLike($searchstr).'%\'
or cast(ps.prestudent_id as text) ILIKE \'%'.$dbModel->escapeLIKE($searchstr).'%\'
GROUP BY type, b.uid, ps.prestudent_id, ps.studiengang_kz, sg.bezeichnung, s.student_uid, s.matrikelnr, p.person_id, name, email, p.foto
');
// If something has been found then return it
if (hasData($prestudent)) return getData($prestudent);
// Otherwise return an empty array
return array();
}
/**
* Search for documents
*/
private function _document($searchstr, $type)
{
return array();
}
/**
* Search for CMSs
*/
private function _cms($searchstr, $type)
{
return array();
}
/**
* Search for rooms
*/
private function _raum($searchstr, $type)
{
$dbModel = new DB_Model();
$rooms = $dbModel->execReadOnlyQuery('
SELECT
\'room\' AS renderer,
\''.$type.'\' AS type,
COALESCE(ort.ort_kurzbz, \'N/A\') as ort_kurzbz,
COALESCE(ort.gebteil, \'N/A\') as building,
COALESCE(ort.ausstattung, \'N/A\') as austattung,
COALESCE(CAST(ort.stockwerk AS VARCHAR), \'N/A\') as floor,
COALESCE(CAST(ort.dislozierung AS VARCHAR), \'N/A\') as room_number,
COALESCE(CAST(ort.content_id AS VARCHAR), \'N/A\') as content_id,
CASE
WHEN standort.plz IS NULL OR standort.ort IS NULL THEN
CASE
WHEN standort.strasse IS NULL THEN
CASE
WHEN ort.stockwerk IS NULL THEN \'N/A\'
ELSE CONCAT(ort.stockwerk,\' Stockwerk\')
END
ELSE
CASE
WHEN ort.stockwerk IS NULL THEN standort.strasse
ELSE CONCAT(standort.strasse,\' / \',ort.stockwerk,\' Stockwerk\')
END
END
ELSE
CASE
WHEN standort.strasse IS NULL THEN
CASE
WHEN ort.stockwerk IS NULL THEN CONCAT(standort.plz,\' \',standort.ort)
ELSE CONCAT(standort.plz,\' \',standort.ort,\' / \',ort.stockwerk,\' Stockwerk\')
END
ELSE
CASE
WHEN ort.stockwerk IS NULL THEN CONCAT(standort.plz,\' \',standort.ort,\' / \',standort.strasse)
ELSE CONCAT(standort.plz,\' \',standort.ort,\', \',standort.strasse,\' / \',ort.stockwerk,\' Stockwerk\')
END
END
END as standort,
CASE
WHEN ort.max_person IS NULL OR ort.arbeitsplaetze IS NULL THEN \'N/A\'
ELSE CONCAT(ort.max_person,\', davon \',ort.arbeitsplaetze,\' PC-Plätze\')
END as sitzplaetze
FROM public.tbl_ort as ort
LEFT JOIN (
select ort,standort_id,strasse, plz
FROM public.tbl_standort
LEFT JOIN public.tbl_adresse USING(adresse_id)
) standort USING(standort_id)
WHERE
ort.aktiv = true
AND
ort.lehre = true
AND (' .
$this->buildSearchClause(
$dbModel,
array('ort.ort_kurzbz', 'ort.bezeichnung'),
$searchstr
) .
')'
);
// If something has been found
if (hasData($rooms))
{
// Returns the dataset
return getData($rooms);
}
// Otherwise return an empty array
return array();
}
}