mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-01 12:19:28 +00:00
Fist Draft
This commit is contained in:
@@ -0,0 +1,664 @@
|
||||
<?php
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
|
||||
// TODO(chris): permissions
|
||||
// TODO(chris): foto
|
||||
|
||||
|
||||
$config['person'] = [
|
||||
'primarykey' => 'person_id',
|
||||
'table' => 'public.tbl_person',
|
||||
'searchfields' => [
|
||||
'uid' => [
|
||||
'comparison' => 'equals',
|
||||
'field' => 'uid',
|
||||
'join' => [
|
||||
'table' => "public.tbl_benutzer",
|
||||
'using' => "person_id"
|
||||
],
|
||||
'1-n' => true
|
||||
],
|
||||
'vorname' => [
|
||||
'alias' => ['firstname'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'vorname'
|
||||
],
|
||||
'nachname' => [
|
||||
'alias' => ['lastname', 'surename'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'nachname'
|
||||
],
|
||||
'name' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => "(vorname || ' ' || nachname)"
|
||||
],
|
||||
'email' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => 'kontakt',
|
||||
'join' => [
|
||||
'table' => "public.tbl_kontakt",
|
||||
'on' => "kontakttyp = 'email' AND tbl_kontakt.person_id = tbl_person.person_id"
|
||||
],
|
||||
"1-n" => true
|
||||
],
|
||||
'tel' => [
|
||||
'alias' => ['phone', 'telefon'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'kontakt',
|
||||
'join' => [
|
||||
'table' => "public.tbl_kontakt",
|
||||
'on' => "kontakttyp IN ('telefon', 'so.tel', 'mobil') AND tbl_kontakt.person_id = tbl_person.person_id"
|
||||
],
|
||||
"1-n" => true
|
||||
],
|
||||
'preid' => [
|
||||
'alias' => ['prestudent_id'],
|
||||
'comparison' => 'equal-int',
|
||||
'field' => 'prestudent_id',
|
||||
'join' => [
|
||||
'table' => "public.tbl_prestudent",
|
||||
'using' => "person_id"
|
||||
],
|
||||
'1-n' => true
|
||||
],
|
||||
'pid' => [
|
||||
'alias' => ['person_id'],
|
||||
'comparison' => 'equal-int',
|
||||
'field' => 'person_id'
|
||||
]
|
||||
],
|
||||
'resultfields' => [
|
||||
"b.uid",
|
||||
"p.person_id",
|
||||
"(p.vorname || ' ' || p.nachname) AS name",
|
||||
"ARRAY( SELECT kontakt FROM public.tbl_kontakt WHERE kontakttyp = 'email' AND person_id=p.person_id ) AS email",
|
||||
"p.foto"
|
||||
],
|
||||
'resultjoin' => "
|
||||
JOIN public.tbl_person p USING (person_id)
|
||||
LEFT JOIN public.tbl_benutzer b USING (person_id)"
|
||||
];
|
||||
|
||||
$config['student'] = [
|
||||
'primarykey' => 'student_uid',
|
||||
'table' => 'public.tbl_student',
|
||||
'searchfields' => [
|
||||
'uid' => [
|
||||
'comparison' => 'equals',
|
||||
'field' => 'student_uid'
|
||||
],
|
||||
'vorname' => [
|
||||
'alias' => ['firstname'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'vorname',
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_prestudent",
|
||||
'using' => "prestudent_id"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
]
|
||||
],
|
||||
'nachname' => [
|
||||
'alias' => ['lastname', 'surename'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'nachname',
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_prestudent",
|
||||
'using' => "prestudent_id"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
]
|
||||
],
|
||||
'name' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => "(vorname || ' ' || nachname)",
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_prestudent",
|
||||
'using' => "prestudent_id"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
]
|
||||
],
|
||||
'email' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => 'kontakt',
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_prestudent",
|
||||
'using' => "prestudent_id"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_kontakt",
|
||||
'on' => "kontakttyp = 'email' AND tbl_kontakt.person_id = tbl_prestudent.person_id"
|
||||
]
|
||||
],
|
||||
"1-n" => true
|
||||
],
|
||||
'tel' => [
|
||||
'alias' => ['phone', 'telefon'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'kontakt',
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_prestudent",
|
||||
'using' => "prestudent_id"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_kontakt",
|
||||
'on' => "kontakttyp IN ('telefon', 'so.tel', 'mobil') AND tbl_kontakt.person_id = tbl_prestudent.person_id"
|
||||
]
|
||||
],
|
||||
"1-n" => true
|
||||
],
|
||||
'stg' => [
|
||||
'alias' => ['studiengang'],
|
||||
'comparison' => 'equals',
|
||||
'field' => "typ || kurzbz",
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_prestudent",
|
||||
'using' => "prestudent_id"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_studiengang",
|
||||
'on' => "tbl_studiengang.studiengang_kz = tbl_prestudent.studiengang_kz"
|
||||
]
|
||||
]
|
||||
],
|
||||
'preid' => [
|
||||
'alias' => ['prestudent_id'],
|
||||
'comparison' => 'equal-int',
|
||||
'field' => 'prestudent_id'
|
||||
],
|
||||
'pid' => [
|
||||
'alias' => ['person_id'],
|
||||
'comparison' => 'equal-int',
|
||||
'field' => 'person_id',
|
||||
'join' => [
|
||||
'table' => "public.tbl_prestudent",
|
||||
'using' => "prestudent_id"
|
||||
]
|
||||
]
|
||||
],
|
||||
'resultfields' => [
|
||||
"s.student_uid AS uid",
|
||||
"s.matrikelnr",
|
||||
"p.person_id",
|
||||
"(p.vorname || ' ' || p.nachname) AS name",
|
||||
"ARRAY( SELECT kontakt FROM public.tbl_kontakt WHERE kontakttyp = 'email' AND person_id=p.person_id ) AS email",
|
||||
"p.foto"
|
||||
],
|
||||
'resultjoin' => "
|
||||
JOIN public.tbl_student s USING (student_uid)
|
||||
JOIN public.tbl_benutzer b ON(b.uid = s.student_uid)
|
||||
JOIN public.tbl_person p USING(person_id)"
|
||||
];
|
||||
|
||||
// TODO(chris): "ref"
|
||||
$config['prestudent'] = [
|
||||
'primarykey' => 'prestudent_id',
|
||||
'table' => 'public.tbl_prestudent',
|
||||
'searchfields' => [
|
||||
'uid' => [
|
||||
'comparison' => 'equals',
|
||||
'field' => 'student_uid',
|
||||
'join' => [
|
||||
'table' => "public.tbl_student",
|
||||
'using' => "prestudent_id"
|
||||
]
|
||||
],
|
||||
'vorname' => [
|
||||
'alias' => ['firstname'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'vorname',
|
||||
'join' => [
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
],
|
||||
'nachname' => [
|
||||
'alias' => ['lastname', 'surename'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'nachname',
|
||||
'join' => [
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
],
|
||||
'name' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => "(vorname || ' ' || nachname)",
|
||||
'join' => [
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
],
|
||||
'email' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => 'kontakt',
|
||||
'join' => [
|
||||
'table' => "public.tbl_kontakt",
|
||||
'on' => "kontakttyp = 'email' AND tbl_kontakt.person_id = tbl_prestudent.person_id"
|
||||
],
|
||||
"1-n" => true
|
||||
],
|
||||
'tel' => [
|
||||
'alias' => ['phone', 'telefon'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'kontakt',
|
||||
'join' => [
|
||||
'table' => "public.tbl_kontakt",
|
||||
'on' => "kontakttyp IN ('telefon', 'so.tel', 'mobil') AND tbl_kontakt.person_id = tbl_prestudent.person_id"
|
||||
],
|
||||
"1-n" => true
|
||||
],
|
||||
'stg' => [
|
||||
'alias' => ['studiengang'],
|
||||
'comparison' => 'equals',
|
||||
'field' => "typ || kurzbz",
|
||||
'join' => [
|
||||
'table' => "public.tbl_studiengang",
|
||||
'using' => "studiengang_kz"
|
||||
]
|
||||
],
|
||||
'preid' => [
|
||||
'alias' => ['prestudent_id'],
|
||||
'comparison' => 'equal-int',
|
||||
'field' => 'prestudent_id'
|
||||
],
|
||||
'pid' => [
|
||||
'alias' => ['person_id'],
|
||||
'comparison' => 'equal-int',
|
||||
'field' => 'person_id',
|
||||
'join' => [
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
]
|
||||
],
|
||||
'resultfields' => [
|
||||
"ps.prestudent_id",
|
||||
"ps.studiengang_kz",
|
||||
"s.matrikelnr",
|
||||
"p.person_id",
|
||||
"b.uid",
|
||||
"(p.vorname || ' ' || p.nachname) AS name",
|
||||
"ARRAY( SELECT kontakt FROM public.tbl_kontakt WHERE kontakttyp = 'email' AND person_id=p.person_id ) AS email",
|
||||
"p.foto",
|
||||
"UPPER(sg.typ || sg.kurzbz) AS stg_kuerzel",
|
||||
"sg.bezeichnung",
|
||||
"(
|
||||
SELECT bezeichnung_mehrsprachig[(TABLE lang)]
|
||||
FROM public.tbl_status
|
||||
WHERE status_kurzbz = public.get_rolle_prestudent(ps.prestudent_id, NULL)
|
||||
LIMIT 1
|
||||
) as status"
|
||||
],
|
||||
'resultjoin' => "
|
||||
LEFT JOIN public.tbl_prestudent ps USING (prestudent_id)
|
||||
LEFT JOIN public.tbl_student s ON (ps.prestudent_id = s.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)"
|
||||
];
|
||||
|
||||
$config['employee'] = [
|
||||
'alias' => ['ma', 'mitarbeiter'],
|
||||
'primarykey' => 'mitarbeiter_uid',
|
||||
'table' => 'public.tbl_mitarbeiter',
|
||||
'searchfields' => [
|
||||
'uid' => [
|
||||
'alias' => ['mitarbeiter_uid'],
|
||||
'comparison' => 'equals',
|
||||
'field' => "mitarbeiter_uid"
|
||||
],
|
||||
'vorname' => [
|
||||
'alias' => ['firstname'],
|
||||
'comparison' => 'similar',
|
||||
'field' => "vorname",
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_benutzer",
|
||||
'on' => "uid = mitarbeiter_uid"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
]
|
||||
],
|
||||
'nachname' => [
|
||||
'alias' => ['lastname', 'surename'],
|
||||
'comparison' => 'similar',
|
||||
'field' => "nachname",
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_benutzer",
|
||||
'on' => "uid = mitarbeiter_uid"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
]
|
||||
],
|
||||
'name' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => "(vorname || ' ' || nachname)",
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_benutzer",
|
||||
'on' => "uid = mitarbeiter_uid"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_person",
|
||||
'using' => "person_id"
|
||||
]
|
||||
]
|
||||
],
|
||||
'email' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => "COALESCE(alias, uid) || '" . '@' . DOMAIN . "'",
|
||||
'join' => [
|
||||
'table' => "public.tbl_benutzer",
|
||||
'on' => "uid = mitarbeiter_uid"
|
||||
]
|
||||
],
|
||||
'tel' => [
|
||||
'alias' => ['phone', 'telefon'],
|
||||
'comparison' => 'similar',
|
||||
'field' => "TRIM(COALESCE(kontakt, '') || ' ' || COALESCE(telefonklappe, ''))",
|
||||
'join' => [
|
||||
'table' => "public.tbl_kontakt",
|
||||
'on' => "kontakttyp = 'telefon' AND tbl_kontakt.standort_id = tbl_mitarbeiter.standort_id"
|
||||
],
|
||||
"1-n" => true
|
||||
],
|
||||
'pid' => [
|
||||
'alias' => ['person_id'],
|
||||
'comparison' => 'equal-int',
|
||||
'field' => "person_id"
|
||||
],
|
||||
'oe' => [
|
||||
'alias' => ['ou', 'organisationseinheit', 'organisationunit'],
|
||||
'comparison' => 'vector',
|
||||
'field' => "fts_bezeichnung",
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_benutzerfunktion",
|
||||
'on' => "mitarbeiter_uid = uid
|
||||
AND funktion_kurzbz = 'oezuordnung'
|
||||
AND (datum_von IS NULL OR datum_von <= NOW())
|
||||
AND (datum_bis IS NULL OR datum_bis >= NOW())"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_organisationseinheit",
|
||||
'using' => "oe_kurzbz"
|
||||
]
|
||||
],
|
||||
'1-n' => true
|
||||
],
|
||||
'kst' => [
|
||||
'comparison' => 'vector',
|
||||
'field' => "fts_bezeichnung",
|
||||
'join' => [
|
||||
[
|
||||
'table' => "public.tbl_benutzerfunktion",
|
||||
'on' => "mitarbeiter_uid = uid
|
||||
AND funktion_kurzbz = 'kstzuordnung'
|
||||
AND (datum_von IS NULL OR datum_von <= NOW())
|
||||
AND (datum_bis IS NULL OR datum_bis >= NOW())"
|
||||
],
|
||||
[
|
||||
'table' => "public.tbl_organisationseinheit",
|
||||
'using' => "oe_kurzbz"
|
||||
]
|
||||
],
|
||||
'1-n' => true
|
||||
]
|
||||
],
|
||||
'resultfields' => [
|
||||
"b.uid",
|
||||
"p.person_id",
|
||||
"(p.vorname || ' ' || p.nachname) AS name",
|
||||
"ARRAY(
|
||||
SELECT
|
||||
'[' || ot.bezeichnung || '] ' || o.bezeichnung AS bezeichnung
|
||||
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())
|
||||
AND bf.uid = b.uid
|
||||
GROUP BY o.bezeichnung, ot.bezeichnung
|
||||
) AS organisationunit_name",
|
||||
"COALESCE(b.alias, b.uid) || '" . '@' . DOMAIN . "' AS email",
|
||||
"TRIM(COALESCE(k.kontakt, '') || ' ' || COALESCE(m.telefonklappe, '')) AS phone",
|
||||
"'" . base_url("/cis/public/bild.php?src=person&person_id=") . "' || p.person_id AS photo_url",
|
||||
"ARRAY(
|
||||
SELECT
|
||||
'[' || ot.bezeichnung || '] ' || o.bezeichnung AS bezeichnung
|
||||
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())
|
||||
AND bf.uid = b.uid
|
||||
GROUP BY o.bezeichnung, ot.bezeichnung
|
||||
) AS standardkostenstelle"
|
||||
],
|
||||
'resultjoin' => "
|
||||
JOIN public.tbl_mitarbeiter m USING (mitarbeiter_uid)
|
||||
JOIN public.tbl_benutzer b ON (b.uid = m.mitarbeiter_uid)
|
||||
JOIN public.tbl_person p USING(person_id)
|
||||
LEFT JOIN (
|
||||
SELECT kontakt, standort_id
|
||||
FROM public.tbl_kontakt
|
||||
WHERE kontakttyp = 'telefon'
|
||||
) k ON (k.standort_id = m.standort_id)"
|
||||
];
|
||||
|
||||
$config['unassigned_employee'] = $config['employee'];
|
||||
$config['unassigned_employee']['alias'] = ['mitarbeiter_ohne_zuordnung'];
|
||||
$config['unassigned_employee']['prepare'] = "unassigned_employee AS (
|
||||
SELECT tbl_mitarbeiter.*
|
||||
FROM public.tbl_mitarbeiter
|
||||
LEFT JOIN public.tbl_benutzerfunktion ON (
|
||||
uid = mitarbeiter_uid
|
||||
AND funktion_kurzbz = 'kstzuordnung'
|
||||
AND (datum_von IS NULL OR datum_von <= NOW())
|
||||
AND (datum_bis IS NULL OR datum_bis >= NOW())
|
||||
)
|
||||
WHERE tbl_benutzerfunktion.bezeichnung IS NULL
|
||||
UNION
|
||||
SELECT tbl_mitarbeiter.*
|
||||
FROM public.tbl_mitarbeiter
|
||||
LEFT JOIN public.tbl_benutzerfunktion ON (
|
||||
uid = mitarbeiter_uid
|
||||
AND funktion_kurzbz = 'oezuordnung'
|
||||
AND (datum_von IS NULL OR datum_von <= NOW())
|
||||
AND (datum_bis IS NULL OR datum_bis >= NOW())
|
||||
)
|
||||
WHERE tbl_benutzerfunktion.bezeichnung IS NULL
|
||||
)";
|
||||
$config['unassigned_employee']['table'] = "unassigned_employee";
|
||||
$config['unassigned_employee']['searchfields']['tel']['join']['on'] = "kontakttyp = 'telefon' AND tbl_kontakt.standort_id = unassigned_employee.standort_id";
|
||||
|
||||
$config['organisationunit'] = [
|
||||
'alias' => ['ou', 'organisationseinheit', 'oe'],
|
||||
'primarykey' => 'oe_kurzbz',
|
||||
'table' => 'public.tbl_organisationseinheit',
|
||||
'searchfields' => [
|
||||
'uid' => [
|
||||
'comparison' => 'equals',
|
||||
'field' => 'uid',
|
||||
'prepare' => "organisationunit_leader(oe_kurzbz, uid, vorname, nachname) AS (
|
||||
SELECT oe_kurzbz, vorname, nachname, uid
|
||||
FROM public.tbl_benutzerfunktion
|
||||
JOIN public.tbl_benutzer USING (uid)
|
||||
JOIN public.tbl_person 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 tbl_benutzer.aktiv = TRUE
|
||||
)",
|
||||
'join' => [
|
||||
'table' => "organisationunit_leader",
|
||||
'using' => "oe_kurzbz"
|
||||
],
|
||||
'1-n' => true
|
||||
],
|
||||
'vorname' => [
|
||||
'alias' => ['firstname'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'vorname',
|
||||
'prepare' => "organisationunit_leader(oe_kurzbz, uid, vorname, nachname) AS (
|
||||
SELECT oe_kurzbz, vorname, nachname, uid
|
||||
FROM public.tbl_benutzerfunktion
|
||||
JOIN public.tbl_benutzer USING (uid)
|
||||
JOIN public.tbl_person 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 tbl_benutzer.aktiv = TRUE
|
||||
)",
|
||||
'join' => [
|
||||
'table' => "organisationunit_leader",
|
||||
'using' => "oe_kurzbz"
|
||||
],
|
||||
'1-n' => true
|
||||
],
|
||||
'nachname' => [
|
||||
'alias' => ['lastname', 'surename'],
|
||||
'comparison' => 'similar',
|
||||
'field' => 'nachname',
|
||||
'prepare' => "organisationunit_leader(oe_kurzbz, uid, vorname, nachname) AS (
|
||||
SELECT oe_kurzbz, vorname, nachname, uid
|
||||
FROM public.tbl_benutzerfunktion
|
||||
JOIN public.tbl_benutzer USING (uid)
|
||||
JOIN public.tbl_person 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 tbl_benutzer.aktiv = TRUE
|
||||
)",
|
||||
'join' => [
|
||||
'table' => "organisationunit_leader",
|
||||
'using' => "oe_kurzbz"
|
||||
],
|
||||
'1-n' => true
|
||||
],
|
||||
'name' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => "(vorname || ' ' || nachname)",
|
||||
'prepare' => "organisationunit_leader(oe_kurzbz, uid, vorname, nachname) AS (
|
||||
SELECT oe_kurzbz, vorname, nachname, uid
|
||||
FROM public.tbl_benutzerfunktion
|
||||
JOIN public.tbl_benutzer USING (uid)
|
||||
JOIN public.tbl_person 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 tbl_benutzer.aktiv = TRUE
|
||||
)",
|
||||
'join' => [
|
||||
'table' => "organisationunit_leader",
|
||||
'using' => "oe_kurzbz"
|
||||
],
|
||||
'1-n' => true
|
||||
],
|
||||
'oe' => [
|
||||
'alias' => ['ou', 'organisationseinheit', 'organisationunit'],
|
||||
'comparison' => 'vector',
|
||||
'field' => "fts_bezeichnung"
|
||||
],
|
||||
'kurzbz' => [
|
||||
'alias' => ['oe_kurzbz'],
|
||||
'comparison' => 'equals',
|
||||
'field' => "oe_kurzbz"
|
||||
]
|
||||
],
|
||||
'resultfields' => [
|
||||
"oe.oe_kurzbz",
|
||||
"('[' || type.bezeichnung || '] ' || oe.bezeichnung) AS name",
|
||||
"oe_parent.oe_kurzbz AS parentoe_kurzbz",
|
||||
"(CASE WHEN oe_parent.bezeichnung IS NOT NULL THEN '[' || type_parent.bezeichnung || '] ' || oe_parent.bezeichnung END) AS parentoe_name",
|
||||
"ARRAY(
|
||||
SELECT JSON_BUILD_OBJECT('uid', b.uid, 'vorname', p.vorname, 'nachname', p.nachname, 'name', (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
|
||||
AND oe_kurzbz = oe.oe_kurzbz
|
||||
) AS leaders",
|
||||
"(
|
||||
SELECT COUNT(*)
|
||||
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())
|
||||
AND oe_kurzbz = oe.oe_kurzbz
|
||||
) AS number_of_people",
|
||||
"(CASE WHEN oe.mailverteiler THEN oe.oe_kurzbz || '" . '@' . DOMAIN . "' END) AS mailgroup"
|
||||
],
|
||||
'resultjoin' => "
|
||||
JOIN public.tbl_organisationseinheit oe
|
||||
USING (oe_kurzbz)
|
||||
JOIN public.tbl_organisationseinheittyp type
|
||||
USING (organisationseinheittyp_kurzbz)
|
||||
LEFT JOIN public.tbl_organisationseinheit oe_parent
|
||||
ON (oe_parent.oe_kurzbz = oe.oe_parent_kurzbz)
|
||||
LEFT JOIN public.tbl_organisationseinheittyp type_parent
|
||||
ON (oe_parent.organisationseinheittyp_kurzbz = type_parent.organisationseinheittyp_kurzbz)"
|
||||
];
|
||||
|
||||
$config['room'] = [
|
||||
'alias' => ['raum'],
|
||||
'primarykey' => 'ort_kurzbz',
|
||||
'table' => 'public.tbl_ort',
|
||||
'searchfields' => [
|
||||
'name' => [
|
||||
'comparison' => 'similar',
|
||||
'field' => 'ort_kurzbz'
|
||||
]
|
||||
],
|
||||
'resultfields' => [
|
||||
"ort.ort_kurzbz",
|
||||
"ort.gebteil AS building",
|
||||
"ort.ausstattung AS equipment",
|
||||
"ort.stockwerk AS floor",
|
||||
"ort.dislozierung AS room_number",
|
||||
"ort.content_id",
|
||||
"address.ort AS city",
|
||||
"address.plz AS zip",
|
||||
"address.strasse AS street",
|
||||
"ort.max_person",
|
||||
"ort.arbeitsplaetze AS workplaces"
|
||||
],
|
||||
'resultjoin' => "
|
||||
JOIN public.tbl_ort ort
|
||||
USING (ort_kurzbz)
|
||||
LEFT JOIN public.tbl_standort
|
||||
USING (standort_id)
|
||||
LEFT JOIN public.tbl_adresse address
|
||||
USING (adresse_id)"
|
||||
];
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
if (! defined('BASEPATH')) exit('No direct script access allowed');
|
||||
|
||||
|
||||
$config['equal-int'] = [
|
||||
'priority' => 4,
|
||||
'rank' => "0",
|
||||
'compare' => "{field} = {word}",
|
||||
'force_integer' => true
|
||||
];
|
||||
|
||||
$config['equals'] = [
|
||||
'priority' => 3,
|
||||
'rank' => "0",
|
||||
'compare' => "LOWER({field}) = {word}"
|
||||
];
|
||||
|
||||
$config['similar'] = [
|
||||
'priority' => 2,
|
||||
'rank' => "(COALESCE({field}, '') <->> {word})",
|
||||
'compare' => "COALESCE({field}, '') %> {word}",
|
||||
'compare_boolean' => "COALESCE({field}, '') ILIKE {like:word}"
|
||||
];
|
||||
|
||||
$config['vector'] = [
|
||||
'priority' => 1,
|
||||
'rank' => "ts_rank_cd({field}, to_tsquery('simple', {word}))",
|
||||
'compare' => "to_tsquery('simple', {word}) @@ {field}"
|
||||
];
|
||||
|
||||
@@ -50,6 +50,7 @@ class Searchbar extends FHCAPI_Controller
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
#$searchstrings = ['pid:71995', 'email:eder.iris', 'schnabl', 'schnabl thomas', 'schnabl thomas sarim', 'schnabl -thomas', 'nachname:schnabl -vorname:thomas', 'schnabl thomas -sarim', 'schnabl or hacker', '-ali', '-ali -baba', '-ali -baba -raub', '-ali -honig', '-ali -baba ali', 'hofer martin'];
|
||||
$this->load->library('form_validation');
|
||||
|
||||
// Checks if the searchstr and the types parameters are in the POSTed JSON
|
||||
@@ -57,13 +58,17 @@ class Searchbar extends FHCAPI_Controller
|
||||
$this->form_validation->set_rules(self::TYPES_PARAM . '[]', null, 'required');
|
||||
|
||||
if (!$this->form_validation->run())
|
||||
$this->terminateWithError(SearchBarLib::ERROR_WRONG_JSON, self::ERROR_TYPE_GENERAL);
|
||||
$this->terminateWithValidationErrors($this->form_validation->error_array());
|
||||
|
||||
// Convert to json the result from searchbarlib->search
|
||||
$result = $this->searchbarlib->search($this->input->post(self::SEARCHSTR_PARAM), $this->input->post(self::TYPES_PARAM));
|
||||
if (property_exists($result, 'error'))
|
||||
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
|
||||
$this->terminateWithSuccess($result);
|
||||
|
||||
$data = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
$this->addMeta('time', $result->meta['time']);
|
||||
$this->addMeta('searchstring', $result->meta['searchstring']);
|
||||
|
||||
$this->terminateWithSuccess($data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,10 +106,15 @@ class FHCAPI_Controller extends Auth_Controller
|
||||
$error = [];
|
||||
|
||||
if (is_array($data)) {
|
||||
if ($type == self::ERROR_TYPE_VALIDATION)
|
||||
if ($type == self::ERROR_TYPE_VALIDATION) {
|
||||
$error['messages'] = $data;
|
||||
else
|
||||
} elseif (array_is_list($data)) {
|
||||
foreach ($data as $d)
|
||||
$this->addError($d, $type);
|
||||
return;
|
||||
} else {
|
||||
$error = $data;
|
||||
}
|
||||
} elseif (is_object($data)) {
|
||||
$error = (array)$data;
|
||||
} else {
|
||||
|
||||
@@ -424,6 +424,23 @@ function isValidDate($dateString)
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// PHP functions that don't exist in older versions
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns true if the given array is sequential
|
||||
*/
|
||||
if (!function_exists('array_is_list')) {
|
||||
function array_is_list(array $arr)
|
||||
{
|
||||
if ($arr === []) {
|
||||
return true;
|
||||
}
|
||||
return array_keys($arr) === range(0, count($arr) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Collection of utility functions for form validation purposes
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -92,4 +92,42 @@
|
||||
|
||||
.searchbar_inline_ul li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/* new variant with template/frame */
|
||||
|
||||
.searchbar-result {
|
||||
border-bottom: 1px solid lightgrey;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.searchbar-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: .5rem;
|
||||
padding: 0;
|
||||
margin: 1rem 0 0;
|
||||
}
|
||||
|
||||
.searchbar-square-image {
|
||||
position: relative;
|
||||
display: block;
|
||||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
|
||||
.searchbar-square-image > * {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.searchbar-square-image img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.no-margin-paragraphs p {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -16,9 +16,9 @@
|
||||
*/
|
||||
|
||||
export default {
|
||||
search(searchsettings) {
|
||||
search(searchsettings, config) {
|
||||
const url = '/api/frontend/v1/searchbar/search';
|
||||
return this.$fhcApi.post(url, searchsettings);
|
||||
return this.$fhcApi.post(url, searchsettings, config);
|
||||
},
|
||||
searchdummy(searchsettings) {
|
||||
const url = 'public/js/apps/api/dummyapi.php/Search';
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import TemplateFrame from "./template/frame.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TemplateFrame
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Object
|
||||
},
|
||||
template: `
|
||||
<template-frame
|
||||
class="searchbar-result-student"
|
||||
:res="res"
|
||||
:actions="actions"
|
||||
:title="res.name"
|
||||
:image="res.photo_url"
|
||||
image-fallback="fas fa-user-circle fa-7x"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
<div class="searchbar_table">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Standard-Kostenstelle</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<ul class="searchbar_inline_ul" v-if="res.standardkostenstelle.length > 0">
|
||||
<li v-for="(stdkst, idx) in res.standardkostenstelle" :key="idx">{{ stdkst }}</li>
|
||||
</ul>
|
||||
<span v-else="">keine</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Organisations-Einheit</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<ul class="searchbar_inline_ul" v-if="res.organisationunit_name.length > 0">
|
||||
<li v-for="(oe, idx) in res.organisationunit_name" :key="idx">{{ oe }}</li>
|
||||
</ul>
|
||||
<span v-else="">keine</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">EMails</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="'mailto:' + res.email" class="d-block">
|
||||
{{ res.email }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Telefon</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="'tel:' + res.phone" class="d-block">
|
||||
{{ res.phone }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template-frame>`
|
||||
};
|
||||
@@ -0,0 +1,166 @@
|
||||
import TemplateFrame from "./template/frame.js";
|
||||
import TemplateAction from "./template/action.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TemplateFrame,
|
||||
TemplateAction
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Object
|
||||
},
|
||||
computed: {
|
||||
telurl() {
|
||||
return 'tel:' + this.employee?.phone;
|
||||
},
|
||||
person() {
|
||||
const person = this.res.list.filter(item => item.type == 'person');
|
||||
if (person.length)
|
||||
return person.pop();
|
||||
|
||||
const { person_id, name, foto, photo_url, email } = this.res.list[0];
|
||||
return { person_id, name, foto, photo_url, email };
|
||||
},
|
||||
employee() {
|
||||
const ma = this.res.list.filter(item => [
|
||||
'employee',
|
||||
'unassigned_employee'
|
||||
].includes(item.type));
|
||||
return ma.length ? ma.pop() : null;
|
||||
},
|
||||
students() {
|
||||
const students = this.res.list.filter(item => item.type == 'prestudent');
|
||||
return students.length ? students : null;
|
||||
},
|
||||
foto() {
|
||||
if (this.person.foto)
|
||||
return 'data:image/jpeg;base64,' + this.person.foto;
|
||||
return this.person.photo_url;
|
||||
},
|
||||
emails() {
|
||||
if (Array.isArray(this.person.email))
|
||||
return this.person.email;
|
||||
return [this.person.email];
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<template-frame
|
||||
class="searchbar-result-mergedperson"
|
||||
:res="person"
|
||||
:actions="actions"
|
||||
:title="person.name"
|
||||
:image="foto"
|
||||
image-fallback="fas fa-user-circle fa-7x"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
<div class="searchbar_table">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Person ID</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ person.person_id }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">EMails</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a v-for="email in emails" :key="email" :href="'mailto:' + email" class="d-block">
|
||||
{{ email }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="employee">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">
|
||||
<template-action
|
||||
:res="employee"
|
||||
:action="actions.defaultactionemployee || actions.defaultaction"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
Mitarbeiter
|
||||
</template-action>
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell ps-3">Standard-Kostenstelle</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<ul class="searchbar_inline_ul" v-if="employee.standardkostenstelle.length > 0">
|
||||
<li
|
||||
v-for="(stdkst, idx) in employee.standardkostenstelle"
|
||||
:key="idx"
|
||||
>
|
||||
{{ stdkst }}
|
||||
</li>
|
||||
</ul>
|
||||
<span v-else="">keine</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell ps-3">Organisations-Einheit</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<ul class="searchbar_inline_ul" v-if="employee.organisationunit_name.length > 0">
|
||||
<li
|
||||
v-for="(oe, idx) in employee.organisationunit_name"
|
||||
:key="idx"
|
||||
>
|
||||
{{ oe }}
|
||||
</li>
|
||||
</ul>
|
||||
<span v-else="">keine</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell ps-3">Telefon</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="telurl">
|
||||
{{ employee.phone }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="students">
|
||||
<template v-for="student in students">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">
|
||||
<template-action
|
||||
v-if="actions.defaultaction"
|
||||
:res="student"
|
||||
:action="actions.defaultactionstudent || actions.defaultaction"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
{{ student.status }} ({{ student.stg_kuerzel }})
|
||||
</template-action>
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell ps-3">Studiengang</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ student.bezeichnung }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell ps-3">Prestudent ID</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ student.prestudent_id }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="student.uid" class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell ps-3">Student UID</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ student.uid }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="student.matrikelnr" class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell ps-3">Matrikelnummer</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ student.matrikelnr }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template-frame>`
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import ResultPrestudent from "./prestudent.js";
|
||||
import ResultStudent from "./student.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ResultPrestudent,
|
||||
ResultStudent
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Object
|
||||
},
|
||||
computed: {
|
||||
prestudent() {
|
||||
const prestudent = this.res.list.filter(item => item.type == 'prestudent');
|
||||
return prestudent.pop();
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<result-prestudent
|
||||
v-if="prestudent"
|
||||
:res="prestudent"
|
||||
:actions="actions"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
class="searchbar-result-mergedstudent"
|
||||
></result-prestudent>
|
||||
<result-student
|
||||
v-else
|
||||
:res="res.list[0]"
|
||||
:actions="actions"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
class="searchbar-result-mergedstudent"
|
||||
></result-student>`
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import TemplateFrame from "./template/frame.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TemplateFrame
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Object
|
||||
},
|
||||
computed: {
|
||||
foto() {
|
||||
if (this.res.foto)
|
||||
return 'data:image/jpeg;base64,' + this.res.foto;
|
||||
return null;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<template-frame
|
||||
class="searchbar-result-organisationunit"
|
||||
:res="res"
|
||||
:actions="actions"
|
||||
:title="res.name"
|
||||
image-fallback="fas fa-sitemap fa-4x p-4 text-white bg-primary"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
<div class="searchbar_table">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">übergeordnete OrgEinheit</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.parentoe_name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Gruppen-EMail</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a :href="'mailto:' + res.mailgroup">
|
||||
{{ res.mailgroup }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Leiter</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<ul class="searchbar_inline_ul" v-if="res.leaders.length > 0">
|
||||
<li v-for="(leader, idx) in res.leaders" :key="idx">{{ leader.name }}</li>
|
||||
</ul>
|
||||
<span v-else="">N.N.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Mitarbeiter-Anzahl</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.number_of_people }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template-frame>`
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import TemplateFrame from "./template/frame.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TemplateFrame
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Object
|
||||
},
|
||||
computed: {
|
||||
foto() {
|
||||
if (this.res.foto)
|
||||
return 'data:image/jpeg;base64,' + this.res.foto;
|
||||
return null;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<template-frame
|
||||
class="searchbar-result-student"
|
||||
:res="res"
|
||||
:actions="actions"
|
||||
:title="res.name"
|
||||
:image="foto"
|
||||
image-fallback="fas fa-user-circle fa-7x"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
<div class="searchbar_table">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Person ID</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.person_id }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">EMails</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a v-for="email in res.email" :key="email" :href="'mailto:' + email" class="d-block">
|
||||
{{ email }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template-frame>`
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
import TemplateFrame from "./template/frame.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TemplateFrame
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Object
|
||||
},
|
||||
computed: {
|
||||
foto() {
|
||||
if (this.res.foto)
|
||||
return 'data:image/jpeg;base64,' + this.res.foto;
|
||||
return null;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<template-frame
|
||||
class="searchbar-result-prestudent"
|
||||
:res="res"
|
||||
:actions="actions"
|
||||
:title="res.name + ' (' + res.status + ' ' + res.stg_kuerzel + ')'"
|
||||
:image="foto"
|
||||
image-fallback="fas fa-user-circle fa-7x"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
<div class="searchbar_table">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Person ID</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.person_id }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">EMails</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a v-for="email in res.email" :key="email" :href="'mailto:' + email" class="d-block">
|
||||
{{ email }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="res.uid" class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Student UID</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.uid }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="res.matrikelnr" class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Matrikelnummer</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.matrikelnr }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Prestudent ID</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.prestudent_id }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Studiengang</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.bezeichnung }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template-frame>`
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
import TemplateFrame from "./template/frame.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TemplateFrame
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Object
|
||||
},
|
||||
computed: {
|
||||
equipment() {
|
||||
if (!this.res.equipment)
|
||||
return "";
|
||||
return this.res.equipment.replace(new RegExp('<br />', 'ig'), '');
|
||||
},
|
||||
address() {
|
||||
let address = this.res.zip || '';
|
||||
if (this.res.city)
|
||||
address += (address ? ' ' : '') + this.res.city;
|
||||
if (this.res.street)
|
||||
address += (address ? ', ' : '') + this.res.street;
|
||||
if (this.res.floor)
|
||||
address += (address ? ' / ' : '') + this.res.floor + ' Stockwerk';
|
||||
|
||||
return address || 'N/A';
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<template-frame
|
||||
class="searchbar-result-room"
|
||||
:res="res"
|
||||
:actions="actions"
|
||||
:title="res.ort_kurzbz"
|
||||
image-fallback="fas fa-door-open fa-4x p-4 text-white bg-primary"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
<div class="searchbar_table">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Standort</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ address }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Sitzplätze</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<template v-if="res.max_person !== null && res.workplaces !== null">
|
||||
{{ res.max_person }}, davon {{ res.workplaces }} PC-Plätze
|
||||
</template>
|
||||
<template v-else>
|
||||
N/A
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Gebäude</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.building }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Zusatz Informationen</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<div class="no-margin-paragraphs" v-html="equipment"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template-frame>`
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
import TemplateFrame from "./template/frame.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TemplateFrame
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Object
|
||||
},
|
||||
computed: {
|
||||
foto() {
|
||||
if (this.res.foto)
|
||||
return 'data:image/jpeg;base64,' + this.res.foto;
|
||||
return null;
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<template-frame
|
||||
class="searchbar-result-student"
|
||||
:res="res"
|
||||
:actions="actions"
|
||||
:title="res.name"
|
||||
:image="foto"
|
||||
image-fallback="fas fa-user-circle fa-7x"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
<div class="searchbar_table">
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Student UID</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.uid }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Person ID</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.person_id }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">Matrikelnummer</div>
|
||||
<div class="searchbar_tablecell">
|
||||
{{ res.matrikelnr }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="searchbar_tablerow">
|
||||
<div class="searchbar_tablecell">EMails</div>
|
||||
<div class="searchbar_tablecell">
|
||||
<a v-for="email in res.email" :key="email" :href="'mailto:' + email" class="d-block">
|
||||
{{ email }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template-frame>`
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
export default {
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
action: Object
|
||||
},
|
||||
computed: {
|
||||
actionHref() {
|
||||
if (this.action.type !== 'link')
|
||||
return 'javascript:void(0);';
|
||||
return typeof this.action.action === 'function'
|
||||
? this.action.action(this.res)
|
||||
: this.action.action;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
actionFunc() {
|
||||
if (this.action.type !== 'function')
|
||||
return;
|
||||
this.action.action(this.res);
|
||||
this.$emit('actionexecuted');
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<a :href="actionHref" @click="actionFunc">
|
||||
<slot>Action</slot>
|
||||
</a>`
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import ResultAction from "./action.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ResultAction
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Array
|
||||
},
|
||||
template: `
|
||||
<div v-if="actions.length" class="searchbar-actions">
|
||||
<result-action
|
||||
v-for="(action, index) in actions"
|
||||
:key="action.label"
|
||||
:res="res"
|
||||
:action="action"
|
||||
class="btn btn-primary btn-sm"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
<i v-if="action.icon" :class="action.icon"></i>
|
||||
<span class="p-2">{{ action.label }}</span>
|
||||
</result-action>
|
||||
</div>`
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import ResultAction from "./action.js";
|
||||
import ResultActions from "./actions.js";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ResultAction,
|
||||
ResultActions
|
||||
},
|
||||
emits: [ 'actionexecuted' ],
|
||||
props: {
|
||||
res: Object,
|
||||
actions: Object,
|
||||
title: String,
|
||||
image: String,
|
||||
imageFallback: String
|
||||
},
|
||||
template: `
|
||||
<div class="searchbar-result">
|
||||
<div class="searchbar_grid">
|
||||
<div class="searchbar_icon">
|
||||
<result-action
|
||||
:res="res"
|
||||
:action="actions.defaultaction"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
class="searchbar-square-image"
|
||||
>
|
||||
<img
|
||||
v-if="image"
|
||||
:src="image"
|
||||
class="rounded-circle"
|
||||
/>
|
||||
<div v-else class="d-flex justify-content-center align-items-center rounded-circle overflow-hidden">
|
||||
<i :class="imageFallback"></i>
|
||||
</div>
|
||||
</result-action>
|
||||
</div>
|
||||
|
||||
<div class="searchbar_data">
|
||||
<result-action
|
||||
:res="res"
|
||||
:action="actions.defaultaction"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
class="mb-3"
|
||||
>
|
||||
<span class="fw-bold">{{ title }}</span>
|
||||
</result-action>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<result-actions
|
||||
:res="res"
|
||||
:actions="actions.childactions"
|
||||
@actionexecuted="$emit('actionexecuted')"
|
||||
>
|
||||
</result-actions>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
};
|
||||
@@ -1,168 +1,282 @@
|
||||
import person from "./person.js";
|
||||
import raum from "./raum.js";
|
||||
import employee from "./employee.js";
|
||||
import organisationunit from "./organisationunit.js";
|
||||
import student from "./student.js";
|
||||
import prestudent from "./prestudent.js";
|
||||
import ResultPerson from "./result/person.js";
|
||||
import ResultStudent from "./result/student.js";
|
||||
import ResultPrestudent from "./result/prestudent.js";
|
||||
import ResultEmployee from "./result/employee.js";
|
||||
import ResultOrganisationunit from "./result/organisationunit.js";
|
||||
import ResultRoom from "./result/room.js";
|
||||
import ResultMergedperson from "./result/mergedperson.js";
|
||||
import ResultMergedstudent from "./result/mergedstudent.js";
|
||||
|
||||
// TODO(chris): arrays in results
|
||||
|
||||
export default {
|
||||
props: [ "searchoptions", "searchfunction" ],
|
||||
data: function() {
|
||||
return {
|
||||
searchtimer: null,
|
||||
hidetimer: null,
|
||||
showsettings: false,
|
||||
searchsettings: {
|
||||
searchstr: '',
|
||||
types: []
|
||||
},
|
||||
showresult: false,
|
||||
searchresult: [],
|
||||
searching: false,
|
||||
error: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
person: person,
|
||||
raum: raum,
|
||||
employee: employee,
|
||||
organisationunit: organisationunit,
|
||||
student: student,
|
||||
prestudent: prestudent
|
||||
},
|
||||
template: `
|
||||
<form ref="searchform" class="d-flex me-3 position-relative" action="javascript:void(0);"
|
||||
@focusin="this.searchfocusin" @focusout="this.searchfocusout">
|
||||
<div class="input-group me-2 bg-white">
|
||||
<input ref="searchbox" @keyup="this.search" @focus="this.showsearchresult"
|
||||
v-model="this.searchsettings.searchstr" class="form-control"
|
||||
type="search" placeholder="Suche..." aria-label="Search">
|
||||
<button ref="settingsbutton" @click="this.togglesettings" class="btn btn-light border-start" type="button" id="search-filter"><i class="fas fa-cog"></i></button>
|
||||
</div>
|
||||
|
||||
<div v-show="this.showresult" ref="result"
|
||||
class="searchbar_results" tabindex="-1">
|
||||
<div v-if="this.searching">
|
||||
<i class="fas fa-spinner fa-spin fa-2x"></i>
|
||||
</div>
|
||||
<div v-else-if="this.error !== null">{{ this.error }}</div>
|
||||
<div v-else-if="this.searchresult.length < 1">Es wurden keine Ergebnisse gefunden.</div>
|
||||
<template v-else="" v-for="res in this.searchresult">
|
||||
<person v-if="res.type === 'person'" :res="res" :actions="this.searchoptions.actions.person" @actionexecuted="this.hideresult"></person>
|
||||
<student v-else-if="res.type === 'student'" :res="res" :actions="this.searchoptions.actions.student" @actionexecuted="this.hideresult"></student>
|
||||
<prestudent v-else-if="res.type === 'prestudent'" :res="res" :actions="this.searchoptions.actions.prestudent" @actionexecuted="this.hideresult"></prestudent>
|
||||
<employee v-else-if="res.type === 'mitarbeiter'" :res="res" :actions="this.searchoptions.actions.employee" @actionexecuted="this.hideresult"></employee>
|
||||
<employee v-else-if="res.type === 'mitarbeiter_ohne_zuordnung'" :res="res" :actions="this.searchoptions.actions.employee" @actionexecuted="this.hideresult"></employee>
|
||||
<organisationunit v-else-if="res.type === 'organisationunit'" :res="res" :actions="this.searchoptions.actions.organisationunit" @actionexecuted="this.hideresult"></organisationunit>
|
||||
<raum v-else-if="res.type === 'raum'" :res="res" :actions="this.searchoptions.actions.raum" @actionexecuted="this.hideresult"></raum>
|
||||
<div v-else="">Unbekannter Ergebnistyp: '{{ res.type }}'.</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-show="this.showsettings" ref="settings"
|
||||
class="searchbar_settings" tabindex="-1">
|
||||
<div class="btn-group" v-if="this.searchoptions.types.length > 0">
|
||||
<template v-for="(type, index) in this.searchoptions.types" :key="type">
|
||||
<input type="checkbox" class="btn-check" :id="this.$.uid + 'search_type_' + index" :value="type" v-model="this.searchsettings.types"/>
|
||||
<label class="btn btn-outline-secondary" :for="this.$.uid + 'search_type_' + index">{{ type }}</label>
|
||||
</template>
|
||||
</div>
|
||||
<div class="mb-2"></div>
|
||||
<button ref="settingsrefreshsearch" @click="this.refreshsearch" class="btn btn-primary" type="button">Übernehmen</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
`,
|
||||
beforeMount: function() {
|
||||
this.updateSearchOptions();
|
||||
},
|
||||
methods: {
|
||||
updateSearchOptions: function() {
|
||||
this.searchsettings.types = [];
|
||||
for( const idx in this.searchoptions.types ) {
|
||||
this.searchsettings.types.push(this.searchoptions.types[idx]);
|
||||
}
|
||||
},
|
||||
calcSearchResultExtent: function() {
|
||||
var rect = this.$refs.searchbox.getBoundingClientRect();
|
||||
components: {
|
||||
ResultPerson,
|
||||
ResultStudent,
|
||||
ResultPrestudent,
|
||||
ResultEmployee,
|
||||
ResultOrganisationunit,
|
||||
ResultRoom,
|
||||
ResultMergedperson,
|
||||
ResultMergedstudent
|
||||
},
|
||||
props: [ "searchoptions", "searchfunction" ],
|
||||
data() {
|
||||
return {
|
||||
searchtimer: null,
|
||||
hidetimer: null,
|
||||
showsettings: false,
|
||||
searchsettings: {
|
||||
searchstr: '',
|
||||
types: []
|
||||
},
|
||||
showresult: false,
|
||||
searchresult: [],
|
||||
searching: false,
|
||||
error: null,
|
||||
abortController: null,
|
||||
retry: 5
|
||||
};
|
||||
},
|
||||
beforeMount() {
|
||||
this.updateSearchOptions();
|
||||
},
|
||||
methods: {
|
||||
updateSearchOptions() {
|
||||
this.searchsettings.types = [];
|
||||
for (const idx in this.searchoptions.types) {
|
||||
this.searchsettings.types.push(this.searchoptions.types[idx]);
|
||||
}
|
||||
},
|
||||
calcSearchResultExtent() {
|
||||
var rect = this.$refs.searchbox.getBoundingClientRect();
|
||||
//console.log(window.innerWidth + ' ' + window.innerHeight + ' ' + JSON.stringify(rect));
|
||||
this.$refs.result.style.height = Math.floor(window.innerHeight * 0.80) + 'px';
|
||||
},
|
||||
search: function() {
|
||||
if( this.searchtimer !== null ) {
|
||||
clearTimeout(this.searchtimer);
|
||||
}
|
||||
if( this.searchsettings.searchstr.length >= 2 ) {
|
||||
this.calcSearchResultExtent();
|
||||
this.searchtimer = setTimeout(
|
||||
this.callsearchapi,
|
||||
500
|
||||
);
|
||||
} else {
|
||||
this.showresult = false;
|
||||
}
|
||||
},
|
||||
callsearchapi: function() {
|
||||
var that = this;
|
||||
this.error = null;
|
||||
this.searchresult = [];
|
||||
this.searching = true;
|
||||
this.showsearchresult();
|
||||
this.searchfunction(this.searchsettings)
|
||||
.then(function(response) {
|
||||
if( response.data?.error === 1 ) {
|
||||
that.error = 'Bei der Suche ist ein Fehler aufgetreten.';
|
||||
} else {
|
||||
that.searchresult = response.data.data;
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
that.error = 'Bei der Suche ist ein Fehler aufgetreten.'
|
||||
+ ' ' + error.message;
|
||||
})
|
||||
.finally(function() {
|
||||
that.searching = false;
|
||||
});
|
||||
},
|
||||
refreshsearch: function() {
|
||||
this.search();
|
||||
this.togglesettings();
|
||||
},
|
||||
calcSearchSettingsExtent: function() {
|
||||
var rect = this.$refs.settingsbutton.getBoundingClientRect();
|
||||
this.$refs.result.style.height = Math.floor(window.innerHeight * 0.80) + 'px';
|
||||
},
|
||||
search() {
|
||||
if (this.searchtimer !== null) {
|
||||
clearTimeout(this.searchtimer);
|
||||
}
|
||||
if (this.abortController) {
|
||||
this.abortController.abort();
|
||||
this.abortController = null;
|
||||
}
|
||||
if (this.searchsettings.searchstr.length >= 2) {
|
||||
this.calcSearchResultExtent();
|
||||
this.searchtimer = setTimeout(
|
||||
this.callsearchapi,
|
||||
500
|
||||
);
|
||||
} else {
|
||||
this.showresult = false;
|
||||
}
|
||||
},
|
||||
callsearchapi() {
|
||||
this.error = null;
|
||||
this.searchresult = [];
|
||||
this.searching = true;
|
||||
this.showsearchresult();
|
||||
|
||||
if (this.abortController)
|
||||
this.abortController.abort();
|
||||
this.abortController = new AbortController();
|
||||
|
||||
this
|
||||
.searchfunction(this.searchsettings, { signal: this.abortController.signal })
|
||||
.then(response => {
|
||||
if (!response.data) {
|
||||
this.error = 'Bei der Suche ist ein Fehler aufgetreten.';
|
||||
} else {
|
||||
let res = response.data.map(el => ({...el, ...JSON.parse(el.data)}));
|
||||
if (this.searchoptions.mergeResults) {
|
||||
let counter = 0;
|
||||
let mergeTypes = [];
|
||||
let mergedType = 'merged';
|
||||
let mergeKey = '';
|
||||
|
||||
switch (this.searchoptions.mergeResults) {
|
||||
case 'student':
|
||||
mergeTypes = ['student', 'prestudent'];
|
||||
mergedType += this.searchoptions.mergeResults;
|
||||
mergeKey = 'uid';
|
||||
break;
|
||||
case 'person':
|
||||
mergeTypes = ['person', 'employee', 'unassigned_employee', 'mitarbeiter', 'mitarbeiter_ohne_zuordnung', 'student', 'prestudent'];
|
||||
mergedType += this.searchoptions.mergeResults;
|
||||
mergeKey = 'person_id';
|
||||
break;
|
||||
}
|
||||
|
||||
if (mergeTypes.length) {
|
||||
res = Object.values(res.reduce((a, c) => {
|
||||
if (!mergeTypes.includes(c.type)) {
|
||||
a['nomerge' + counter++] = c;
|
||||
} else if (c[mergeKey] === null) {
|
||||
a['nomerge' + counter++] = c;
|
||||
} else if (a[c[mergeKey]] === undefined) {
|
||||
a[c[mergeKey]] = {
|
||||
rank: c.rank,
|
||||
type: mergedType,
|
||||
list: [c]
|
||||
};
|
||||
} else {
|
||||
a[c[mergeKey]].list.push(c);
|
||||
if (c.rank > a[c[mergeKey]].rank)
|
||||
a[c[mergeKey]].rank = c.rank;
|
||||
}
|
||||
return a;
|
||||
}, {})).sort((a, b) => b.rank - a.rank);
|
||||
}
|
||||
}
|
||||
this.searchresult = res;
|
||||
}
|
||||
this.searching = false;
|
||||
this.retry = 5;
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.code == "ERR_CANCELED") {
|
||||
return this.retry = 5;
|
||||
}
|
||||
if (error.code == "ECONNABORTED" && this.retry) {
|
||||
this.retry--;
|
||||
return this.callsearchapi();
|
||||
}
|
||||
|
||||
this.error = 'Bei der Suche ist ein Fehler aufgetreten.' + ' ' + error.message;
|
||||
this.searching = false;
|
||||
this.retry = 5;
|
||||
});
|
||||
},
|
||||
refreshsearch() {
|
||||
this.search();
|
||||
this.togglesettings();
|
||||
},
|
||||
calcSearchSettingsExtent() {
|
||||
var rect = this.$refs.settingsbutton.getBoundingClientRect();
|
||||
//console.log(window.innerWidth + ' ' + window.innerHeight + ' ' + JSON.stringify(rect));
|
||||
this.$refs.settings.style.top = Math.floor(rect.bottom + 3) + 'px';
|
||||
this.$refs.settings.style.right = Math.floor(window.innerWidth - rect.right) + 'px';
|
||||
this.$refs.settings.style.width = Math.floor(window.innerWidth * 0.5) + 'px';
|
||||
this.$refs.settings.style.top = Math.floor(rect.bottom + 3) + 'px';
|
||||
this.$refs.settings.style.right = Math.floor(window.innerWidth - rect.right) + 'px';
|
||||
this.$refs.settings.style.width = Math.floor(window.innerWidth * 0.5) + 'px';
|
||||
//this.$refs.settings.style.height = Math.floor(window.innerHeight * 0.5) + 'px';
|
||||
},
|
||||
togglesettings: function() {
|
||||
this.showsettings = !this.showsettings;
|
||||
this.calcSearchSettingsExtent();
|
||||
},
|
||||
hideresult: function() {
|
||||
this.showresult = false;
|
||||
window.removeEventListener('resize', this.calcSearchResultExtent);
|
||||
},
|
||||
showsearchresult: function() {
|
||||
if( this.searchsettings.searchstr.length >= 3 ) {
|
||||
this.showresult = true;
|
||||
window.addEventListener('resize', this.calcSearchResultExtent);
|
||||
}
|
||||
},
|
||||
searchfocusin: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if( this.hidetimer !== null ) {
|
||||
clearTimeout(this.hidetimer);
|
||||
}
|
||||
},
|
||||
searchfocusout: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.hidetimer = setTimeout(
|
||||
this.hideresult,
|
||||
100
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
togglesettings() {
|
||||
this.showsettings = !this.showsettings;
|
||||
this.calcSearchSettingsExtent();
|
||||
},
|
||||
hideresult() {
|
||||
this.showresult = false;
|
||||
window.removeEventListener('resize', this.calcSearchResultExtent);
|
||||
},
|
||||
showsearchresult() {
|
||||
if (this.searchsettings.searchstr.length >= 3) {
|
||||
this.showresult = true;
|
||||
window.addEventListener('resize', this.calcSearchResultExtent);
|
||||
}
|
||||
},
|
||||
searchfocusin(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (this.hidetimer !== null) {
|
||||
clearTimeout(this.hidetimer);
|
||||
}
|
||||
},
|
||||
searchfocusout(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.hidetimer = setTimeout(
|
||||
this.hideresult,
|
||||
100
|
||||
);
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<form
|
||||
ref="searchform"
|
||||
class="d-flex me-3 position-relative"
|
||||
action="javascript:void(0);"
|
||||
@focusin="searchfocusin"
|
||||
@focusout="searchfocusout"
|
||||
>
|
||||
<div class="input-group me-2 bg-white">
|
||||
<input
|
||||
ref="searchbox"
|
||||
@input="search"
|
||||
@focus="showsearchresult"
|
||||
v-model="searchsettings.searchstr"
|
||||
class="form-control"
|
||||
type="search"
|
||||
placeholder="Suche..."
|
||||
aria-label="Search"
|
||||
>
|
||||
<button
|
||||
ref="settingsbutton"
|
||||
@click="togglesettings"
|
||||
class="btn btn-light border-start"
|
||||
type="button"
|
||||
id="search-filter"
|
||||
>
|
||||
<i class="fas fa-cog"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-show="showresult"
|
||||
ref="result"
|
||||
class="searchbar_results"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div v-if="searching">
|
||||
<i class="fas fa-spinner fa-spin fa-2x"></i>
|
||||
</div>
|
||||
<div v-else-if="error !== null">{{ error }}</div>
|
||||
<div v-else-if="searchresult.length < 1">Es wurden keine Ergebnisse gefunden.</div>
|
||||
<template v-else>
|
||||
<template v-for="res in searchresult">
|
||||
<result-person v-if="res.type === 'person'" :res="res" :actions="searchoptions.actions.person" @actionexecuted="hideresult"></result-person>
|
||||
<result-student v-else-if="res.type === 'student'" :res="res" :actions="searchoptions.actions.student" @actionexecuted="hideresult"></result-student>
|
||||
<result-prestudent v-else-if="res.type === 'prestudent'" :res="res" :actions="searchoptions.actions.prestudent" @actionexecuted="hideresult"></result-prestudent>
|
||||
<result-employee v-else-if="res.type === 'employee'" :res="res" :actions="searchoptions.actions.employee" @actionexecuted="hideresult"></result-employee>
|
||||
<result-employee v-else-if="res.type === 'unassigned_employee'" :res="res" :actions="searchoptions.actions.employee" @actionexecuted="hideresult"></result-employee>
|
||||
<result-organisationunit v-else-if="res.type === 'organisationunit'" :res="res" :actions="searchoptions.actions.organisationunit" @actionexecuted="hideresult"></result-organisationunit>
|
||||
<result-room v-else-if="res.type === 'room'" :res="res" :actions="searchoptions.actions.room" @actionexecuted="hideresult"></result-room>
|
||||
<result-mergedperson v-else-if="res.type === 'mergedperson'" :res="res" :actions="searchoptions.actions.mergedperson" @actionexecuted="hideresult"></result-mergedperson>
|
||||
<result-mergedstudent v-else-if="res.type === 'mergedstudent'" :res="res" :actions="searchoptions.actions.mergedstudent" @actionexecuted="hideresult"></result-mergedstudent>
|
||||
<div v-else>Unbekannter Ergebnistyp: '{{ res.type }}'.</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
v-show="showsettings"
|
||||
ref="settings"
|
||||
class="searchbar_settings"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="btn-group" v-if="searchoptions.types.length > 0">
|
||||
<template v-for="(type, index) in searchoptions.types" :key="type">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="btn-check"
|
||||
:id="$.uid + 'search_type_' + index"
|
||||
:value="type"
|
||||
v-model="searchsettings.types"
|
||||
/>
|
||||
<label
|
||||
class="btn btn-outline-secondary"
|
||||
:for="$.uid + 'search_type_' + index"
|
||||
>
|
||||
{{ type }}
|
||||
</label>
|
||||
</template>
|
||||
</div>
|
||||
<div class="mb-2"></div>
|
||||
<button
|
||||
ref="settingsrefreshsearch"
|
||||
@click="refreshsearch"
|
||||
class="btn btn-primary"
|
||||
type="button"
|
||||
>
|
||||
Übernehmen
|
||||
</button>
|
||||
</div>
|
||||
</form>`
|
||||
};
|
||||
|
||||
@@ -40,6 +40,8 @@ export default {
|
||||
function _clean_return_value(response) {
|
||||
const result = response.data;
|
||||
delete response.data;
|
||||
if (!result)
|
||||
return {meta: {response}, data: null};
|
||||
if (!result.meta)
|
||||
result.meta = {response};
|
||||
else
|
||||
|
||||
@@ -58,6 +58,7 @@ require_once('dbupdate_3.4/17513_Entwicklungsteam.php');
|
||||
require_once('dbupdate_3.4/28575_softwarebereitstellung.php');
|
||||
require_once('dbupdate_3.4/41150_oe-pfad_db_view.php');
|
||||
require_once('dbupdate_3.4/44031_stv_favorites.php');
|
||||
require_once('dbupdate_3.4/40128_search.php');
|
||||
|
||||
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
|
||||
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
if (!defined('DB_NAME')) exit('No direct script access allowed');
|
||||
|
||||
// Activate module pg_trgm
|
||||
if (!$db->db_num_rows(@$db->db_query("SELECT 1
|
||||
FROM pg_extension WHERE extname = 'pg_trgm' LIMIT 1;")))
|
||||
{
|
||||
$qry = "CREATE extension pg_trgm;";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong>Module pg_trgm ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'Module pg_trgm: activated<br>';
|
||||
}
|
||||
|
||||
|
||||
// Add additional computed columns
|
||||
// Add column fts_bezeichnung to public.tbl_organisationseinheit
|
||||
if (!@$db->db_query("SELECT fts_bezeichnung FROM public.tbl_organisationseinheit LIMIT 1"))
|
||||
{
|
||||
$qry = "ALTER TABLE public.tbl_organisationseinheit ADD COLUMN fts_bezeichnung tsvector;";
|
||||
$qry .= "COMMENT ON COLUMN public.tbl_organisationseinheit.fts_bezeichnung IS 'used for search - auto generated w triggers';";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong> public.tbl_organisationseinheit ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'public.tbl_organisationseinheit: new column "fts_bezeichnung" added<br>';
|
||||
}
|
||||
|
||||
// Add function tr_update_tbl_organisationseinheit_fts_bezeichnung to public
|
||||
if (!$db->db_num_rows(@$db->db_query("SELECT 1 FROM pg_proc WHERE proname = 'tr_update_tbl_organisationseinheit_fts_bezeichnung' LIMIT 1;")))
|
||||
{
|
||||
$qry = "CREATE FUNCTION tr_update_tbl_organisationseinheit_fts_bezeichnung()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE PLPGSQL
|
||||
AS
|
||||
$$
|
||||
BEGIN
|
||||
IF TG_TABLE_NAME = 'tbl_organisationseinheit' THEN
|
||||
NEW.fts_bezeichnung := to_tsvector('simple', COALESCE((SELECT bezeichnung FROM public.tbl_organisationseinheittyp WHERE organisationseinheittyp_kurzbz = NEW.organisationseinheittyp_kurzbz), '') || ' ' || COALESCE(NEW.bezeichnung, ''));
|
||||
ELSIF TG_TABLE_NAME = 'tbl_organisationseinheittyp' THEN
|
||||
UPDATE public.tbl_organisationseinheit SET fts_bezeichnung = to_tsvector('simple', COALESCE(NEW.bezeichnung, '') || ' ' || COALESCE(bezeichnung, '')) WHERE organisationseinheittyp_kurzbz = NEW.organisationseinheittyp_kurzbz;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong> public.tr_update_tbl_organisationseinheit_fts_bezeichnung ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'public.tr_update_tbl_organisationseinheit_fts_bezeichnung: function created<br>';
|
||||
}
|
||||
|
||||
$update_column = false;
|
||||
// Add trigger tr_organisationseinheit_update_organisationseinheittyp_kurzbz to public.tbl_organisationseinheit
|
||||
if (!$db->db_num_rows(@$db->db_query("SELECT 1 FROM information_schema.triggers WHERE event_object_table ='tbl_organisationseinheit' AND trigger_name = 'tr_organisationseinheit_update_organisationseinheittyp_kurzbz' LIMIT 1;")))
|
||||
{
|
||||
$qry = "CREATE TRIGGER tr_organisationseinheit_update_organisationseinheittyp_kurzbz
|
||||
BEFORE UPDATE OF organisationseinheittyp_kurzbz OR INSERT
|
||||
ON public.tbl_organisationseinheit
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION tr_update_tbl_organisationseinheit_fts_bezeichnung();";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong> public.tbl_organisationseinheit ' . $db->db_last_error() . '</strong><br>';
|
||||
else {
|
||||
echo 'public.tbl_organisationseinheit: trigger "tr_organisationseinheit_update_organisationseinheittyp_kurzbz" created<br>';
|
||||
$update_column = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Add trigger tr_organisationseinheittyp_update_bezeichnung to public.tbl_organisationseinheittyp
|
||||
if (!$db->db_num_rows(@$db->db_query("SELECT 1 FROM information_schema.triggers WHERE event_object_table ='tbl_organisationseinheittyp' AND trigger_name = 'tr_organisationseinheittyp_update_bezeichnung' LIMIT 1;")))
|
||||
{
|
||||
$qry = "CREATE TRIGGER tr_organisationseinheittyp_update_bezeichnung
|
||||
BEFORE UPDATE OF bezeichnung
|
||||
ON public.tbl_organisationseinheittyp
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION tr_update_tbl_organisationseinheit_fts_bezeichnung();";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong> public.tbl_organisationseinheittyp ' . $db->db_last_error() . '</strong><br>';
|
||||
else {
|
||||
echo 'public.tbl_organisationseinheittyp: trigger "tr_organisationseinheittyp_update_bezeichnung" created<br>';
|
||||
$update_column = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update fts_bezeichnung on tbl_organisationseinheit with new triggers
|
||||
if ($update_column)
|
||||
{
|
||||
$qry = "UPDATE public.tbl_organisationseinheittyp SET bezeichnung = bezeichnung;";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong> public.tbl_organisationseinheit ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'public.tbl_organisationseinheit: column "fts_bezeichnung" updated<br>';
|
||||
}
|
||||
|
||||
|
||||
// Add Trigram Indexes
|
||||
// Add index for kontakt to public.tbl_kontakt
|
||||
if ($result = @$db->db_query("SELECT 1
|
||||
FROM pg_indexes WHERE indexname = 'idx_tbl_kontakt_kontakt_trgm';"))
|
||||
{
|
||||
if ($db->db_num_rows($result) == 0)
|
||||
{
|
||||
$qry = "CREATE INDEX idx_tbl_kontakt_kontakt_trgm ON public.tbl_kontakt USING GIN (COALESCE(kontakt, '') gin_trgm_ops);";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong>public.tbl_kontakt ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'public.tbl_kontakt: added index "idx_tbl_kontakt_kontakt_trgm"<br>';
|
||||
}
|
||||
}
|
||||
// Add index for vorname to public.tbl_person
|
||||
if ($result = @$db->db_query("SELECT 1
|
||||
FROM pg_indexes WHERE indexname = 'idx_tbl_person_vorname_trgm';"))
|
||||
{
|
||||
if ($db->db_num_rows($result) == 0)
|
||||
{
|
||||
$qry = "CREATE INDEX idx_tbl_person_vorname_trgm ON public.tbl_person USING GIN (COALESCE(vorname, '') gin_trgm_ops);";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong>public.tbl_person ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'public.tbl_person: added index "idx_tbl_person_vorname_trgm"<br>';
|
||||
}
|
||||
}
|
||||
// Add index for nachname to public.tbl_person
|
||||
if ($result = @$db->db_query("SELECT 1
|
||||
FROM pg_indexes WHERE indexname = 'idx_tbl_person_nachname_trgm';"))
|
||||
{
|
||||
if ($db->db_num_rows($result) == 0)
|
||||
{
|
||||
$qry = "CREATE INDEX idx_tbl_person_nachname_trgm ON public.tbl_person USING GIN (COALESCE(nachname, '') gin_trgm_ops);";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong>public.tbl_person ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'public.tbl_person: added index "idx_tbl_person_nachname_trgm"<br>';
|
||||
}
|
||||
}
|
||||
// Add index for vorname || ' ' || nachname to public.tbl_person
|
||||
if ($result = @$db->db_query("SELECT 1
|
||||
FROM pg_indexes WHERE indexname = 'idx_tbl_person_name_trgm';"))
|
||||
{
|
||||
if ($db->db_num_rows($result) == 0)
|
||||
{
|
||||
$qry = "CREATE INDEX idx_tbl_person_name_trgm ON public.tbl_person USING GIN (COALESCE((vorname || ' ' || nachname), '') gin_trgm_ops);";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong>public.tbl_person ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'public.tbl_person: added index "idx_tbl_person_name_trgm"<br>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add Vector Indexes
|
||||
// Add index for fts_bezeichnung to public.tbl_organisationseinheit
|
||||
if (!$db->db_num_rows(@$db->db_query("SELECT 1
|
||||
FROM pg_indexes WHERE indexname = 'idx_tbl_organisationseinheit_fts_bezeichnung_vector' LIMIT 1;")))
|
||||
{
|
||||
$qry = "CREATE INDEX idx_tbl_organisationseinheit_fts_bezeichnung_vector ON public.tbl_organisationseinheit USING GIN (fts_bezeichnung);";
|
||||
|
||||
if (!$db->db_query($qry))
|
||||
echo '<strong>public.tbl_organisationseinheit ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'public.tbl_organisationseinheit: added index "idx_tbl_organisationseinheit_fts_bezeichnung_vector"<br>';
|
||||
}
|
||||
Reference in New Issue
Block a user