1<?php
2
3/**
4 * Addressbook Plugin
5 *
6 * @license  GPL2
7 * @author   Gero Gothe <gero.gothe@medizindoku.de>
8 *
9 */
10
11// must be run within Dokuwiki
12if(!defined('DOKU_INC')) die();
13
14define('ADRESSBOOK_SQL',false); # for development purposes
15
16class syntax_plugin_addressbook extends DokuWiki_Syntax_Plugin {
17
18    public $editor = false;   # Does user have editing rights for contacts?
19    public $loggedin = false; # User logged in?
20
21    function getSort(){
22        return 158;
23    }
24
25    public function getType() {
26        return 'substition';
27    }
28
29    /**
30     * Paragraph Type
31     */
32    public function getPType() {
33        return 'block';
34    }
35
36
37    function __construct(){
38        global $INFO;
39        if ($INFO['ismanager'] === true) $this->editor = true;
40        if (isset($INFO['userinfo'])) $this->loggedin = true;
41    }
42
43    /**
44     * @param string $mode
45     */
46    public function connectTo($mode) {$this->Lexer->addEntryPattern('\[ADDRESSBOOK.*?', $mode, 'plugin_addressbook');}
47
48    public function postConnect() { $this->Lexer->addExitPattern('\]','plugin_addressbook'); }
49
50    /**
51     * Handler to prepare matched data for the rendering process
52    */
53    public function handle($match, $state, $pos, Doku_Handler $handler) {
54
55        if ($state == DOKU_LEXER_UNMATCHED) {
56            if ($match==':') return 'LOOKUP';
57            if ($match[0]==':') $match = substr($match,1);
58
59            return trim($match);
60        }
61        return false;
62    }
63
64
65    /**
66     * @param   $mode     string        output format being rendered
67     * @param   $renderer Doku_Renderer the current renderer object
68     * @param   $data     array         data created by handler()
69     * @return  boolean                 rendered correctly?
70     */
71    public function render($mode, Doku_Renderer $renderer, $data) {
72        global $ID;
73
74        if($mode == 'xhtml') {
75            $renderer->info['cache'] = false;
76
77            # addressbook_debug_show();
78
79            if ($_REQUEST['Submit'] == $this->getLang('exec cancel')){
80                unset($_REQUEST);
81                unset ($cinfo);
82                $action = $data;
83            }
84
85            # Main action given by tag data
86            if (!isset($_REQUEST['Submit'])) $action = $data;
87
88
89            /* Save a contact
90             *
91             * On fail: show edit form again
92             */
93            if ($_REQUEST['Submit']==$this->getLang('exec save')) $action = "savedata";
94
95            # Certain actions could cause double saving, which is avoided by counting
96            if ($action=='savedata' && $this->saveOnce == 0 && $this->editor){
97                $this->saveOnce++;
98                $contact_id = $_REQUEST['editcontact'];
99                $cinfo = $this->loadFormData(); # Loads form data concerning the contact
100                $res = $this->saveData($cinfo);
101                if (!$res) {
102                    $action = 'edit';
103                    $contact_id = $_REQUEST['contactid'];
104                } else { # Clear all
105                    unset($_REQUEST);
106                    unset ($cinfo);
107                    $action = $data;
108                }
109            }
110
111
112            /* Directly show contact card by tag
113             *
114             * Cards are only display, when there is NO edit or save action
115             */
116            if (substr($data,0,7) == 'contact' &&
117                !isset($_REQUEST['editcontact']) &&
118                $action != 'edit'
119                ) {
120                $renderer->doc .= $this->showcontact(intval(substr($data,8)),$ID);
121                return; #no following actions
122            }
123
124
125            if (substr($data,0,5) == 'index' &&
126                !isset($_REQUEST['editcontact'])) {
127                # showcontact once before if necessary
128                if (isset($_REQUEST['showcontact']) && $this->showCount==0) {
129                    $this->showCount++;
130                    $out = $this->showcontact($_REQUEST['showcontact'],$ID);
131                    if ($out !== false) $renderer->doc .= $out.'<br>';
132                }
133                # now show index
134
135                # keyword 'departments'
136                if (strpos($data,'departments') > 0) {
137                    $list = $this->getList(false,'department,surname,firstname,cfunction');
138                    $renderer->doc .= $this->buildIndex($list,'department',$ID);
139                } else $renderer->doc .= $this->buildIndex(false,false,$ID);
140
141                return; # no following actions
142            }
143
144
145            # --------------------------------------------------------#
146            # only one instance beyond this point
147            $this->instance++;
148            if ($this->instance > 1) return;
149            # --------------------------------------------------------#
150
151
152            # Generate printable list
153            if (substr($data,0,5) == 'print') {
154
155                $pList = false;
156                $sep = false;
157
158                $params = $this->getValue($data,'');
159
160                if (isset($params['department'])) $sep = 'department';
161
162                # Select one department
163                if (isset($params['select'])) {
164                    $pList = $this->getList(Array('department' => $params['select']));
165                    $sep = false;
166                }
167
168                $renderer->doc .= $this->buildPrintList($pList,$sep);
169
170                return;
171            }
172
173
174            /* Edit contact or add a new contact
175             *
176             * No futher actions are performed after an edit
177             */
178            if ($action == 'addcontact' && $this->editor) { # Add a new contact. Can be overwritten by edit
179                $contact_id = 'new';
180                $action = 'edit'; # redefine action
181            }
182
183            if (isset($_REQUEST['editcontact'])  && $this->editor) { # Override new contact if the action is to edit an existing one
184                $contact_id = $_REQUEST['editcontact'];
185                $cinfo = $this->getContactData($contact_id);
186                $action = 'edit';
187            }
188
189            if ($action == 'edit'  && $this->editor) {
190                $out = $this->buildForm($contact_id,$cinfo);
191                $renderer->doc .= $out;
192                return; # no following actions
193            }
194
195
196            # Show search box
197            if ($action == 'search' || $_REQUEST['Submit'] == $this->getLang('exec search')) {
198                $out = $this->searchDialog();
199                $renderer->doc .= $out;
200            }
201
202
203            if ($_REQUEST['Submit'] == $this->getLang('exec search')) {
204                $list = $this->searchDB($_REQUEST['searchtext']);
205                if ($list != false){
206                    if (count($list)<5) {
207                        foreach ($list as $l) $renderer->doc .= $this->showcontact($l['id'],$ID);
208                    } else $renderer->doc .= $this->buildIndex($list,false,$ID);
209                }
210            }
211
212
213            /* Show contact per request
214             * can only be shown once due to the instance count above
215             *
216             * placed below the searchbox
217             */
218            if (isset($_REQUEST['showcontact']) && $this->showCount==0) {
219                $this->showCount++;
220                $out = $this->showcontact($_REQUEST['showcontact'],$ID);
221                if ($out !== false) $renderer->doc .= $out.'<br>';
222            }
223
224
225            # Delete a contact
226            if (isset($_REQUEST['erasecontact'])  && $this->editor){
227                $this->deleteContact($_REQUEST['erasecontact']);
228            }
229
230            return true;
231        }
232
233        return false;
234    }
235
236
237    function searchDialog(){
238        global $ID;
239        $out = '';
240
241        $out .= '<div class="plugin_addressbook_searchbox">';
242
243        $out .= '<form enctype="multipart/form-data" action="'.wl($ID).'" method="POST">';
244        $out .= '<span>'.$this->getLang('addressbook').'</span> <input type="text" name="searchtext" placeholder="'.$this->getLang('form search').'" value="'.$_REQUEST['searchtext'].'">';
245        $out .= '<input type="submit" name="Submit" value="'.$this->getLang('exec search').'" />';
246
247        $out .= '</form>';
248        $out .= '</div>';
249
250        return $out;
251    }
252
253
254    function buildForm($contact_id,$cinfo){
255        global $ID;
256        $out = '<div class="plugin_addressbook_editform">';
257
258        if ($contact_id == 'new') {$out .= '<h2>'.$this->getLang('header add').'</h2>';} else {$out .= '<h2>'.$this->getLang('header edit').'</h2>';}
259
260        $out .= '<br><form enctype="multipart/form-data" action="'.wl($ID).'" method="POST">';
261        $out .= '<input type="hidden" name="MAX_FILE_SIZE" value="2200000" />';
262        $out .= '<input type="hidden" name="contactid" value="'.$contact_id.'" />';
263        $out .= '<input type="text" name="firstname" placeholder="'.$this->getLang('form firstname').'" value="'.$cinfo['firstname'].'">';
264        $out .= '<input type="text" name="surname" placeholder="'.$this->getLang('form surname').'" value="'.$cinfo['surname'].'"><br>';
265        $out .= '<input type="text" name="cfunction" placeholder="'.$this->getLang('form function').'" value="'.$cinfo['cfunction'].'">';
266        $out .= '<input type="text" name="department" placeholder="'.$this->getLang('form department').'" value="'.$cinfo['department'].'"><br>';
267        $out .= '<input type="text" name="tel1" placeholder="'.$this->getLang('form tel1').'" value="'.$cinfo['tel1'].'">';
268        $out .= '<input type="text" name="tel2" placeholder="'.$this->getLang('form tel2').'" value="'.$cinfo['tel2'].'"><br>';
269        $out .= '<input type="text" name="fax" placeholder="'.$this->getLang('form fax').'" value="'.$cinfo['fax'].'"><br>';
270        $out .= '<input type="text" name="email" placeholder="'.$this->getLang('form email').'" value="'.$cinfo['email'].'"><br>';
271        $out .= '<br>'.$this->getLang('form description').':<br><textarea name="description">'.$cinfo['description'].'</textarea><br><br>';
272
273        $out .= '<div class="photoupload">';
274        if (isset($_REQUEST['blob'])) $cinfo['photo'] = $_REQUEST['blob'];
275        if ($cinfo['photo'] != false) {
276            $out .= "<img style='float:left;max-width:120px' src='data:image/jpg;base64,".($cinfo['photo'])."'>";
277            $out .= '<br><input type="checkbox" id="removephoto" name="removephoto" value="Remove photo"> ';
278            $out .= '<label for="removephoto">'.$this->getLang('form remove').'</label><br><br>';
279            $out .= $this->getLang('form upload info2').'.<br>';
280            $out .= '<input type="hidden" name="blob" value="'.($cinfo['photo']).'">';
281        }
282
283        $out .= '</div>';
284        $out .= $this->getLang('form upload').': <input name="photo" type="file" /> '.$this->getLang('form upload info').'.<br>';
285
286
287
288        $out .= '<br><input type="submit" name="Submit" value="'.$this->getLang('exec save').'" />';
289        $out .= '<input type="submit" name="Submit" value="'.$this->getLang('exec cancel').'" />';
290        $out .= '</form>';
291        $out .= "<div class='id'>ID: $contact_id</div>";
292        $out .= '</div>';
293
294        return $out;
295    }
296
297    function showcontact($cid,$target = false){
298
299        $r = $this->getContactData($cid);
300
301        if ($res === false) return false;
302
303        $out ='';
304
305        $out .= '<div class="plugin_addressbook_singlecontact">';
306
307        $out .= '<div class="content">';
308
309        $out .= '<div class="data">';
310
311        # Name if existant
312        $out .= '<b>'.$r['surname'] .($r['firstname'] <> ''? ', '.$r['firstname']:'').'</b>';
313        if (strlen($r['surname'] .$r['firstname'])>0) $out .= '<br>';
314
315        # Function/department if existant
316        if ($r['surname'].$r['firstname'] == '') $out .= '<b>';
317
318        if ($r['department'].$r['cfunction'] != '') $out .= $this->names(array($r['cfunction'],$r['department']));
319
320        if (strlen($r['cfuntion'] .$r['department'])>0) $out .= '<br>';
321        if ($r['surname'].$r['firstname'] == '') $out .= '</b>';
322
323        # Telephone
324        if ($r['tel1'].$r['tel2'] <> '') $out .= '<br>Tel.: '.$this->names(array($r['tel1'],$r['tel2']),' / ');
325
326        # Fax
327        if ($r['fax']<>'') $out .= '<br>Fax: '.$r['fax'];
328
329        # Mail
330        if ($r['email']<>'') $out .= '<br>Mail: <a href="mailto:'.$r['email'].'">'.$r['email'].'</a>';
331
332        $out .= '</div>';
333
334        if ($r['photo'] != false) $out .= "<img class='photo' src='data:image/jpg;base64,".($r['photo'])."'>";
335
336        $out .= '</div>';
337
338        if ($r['description']<>'') $out .= $r['description'];
339
340        if ($this->loggedin) {
341            $out .= '<div class="footer">';
342
343            $out .= 'Nr. '.$r['id'];
344
345            if ($this->editor && $target != false) {
346                $out .= '<span class="buttons">';
347                $out .= '<a href="'.wl($target,'editcontact='.$r['id']).'">'.$this->getLang('exec edit').'</a>';
348                $out .= '<a href="'.wl($target,'erasecontact='.$r['id']).'" onclick="return confirm(\'Sure?\');">'.$this->getLang('exec delete').'</a>';
349                $out .= '</span>';
350            }
351
352            $out .= '</div>';
353        }
354
355        $out .= '</div>';
356
357        return $out;
358    }
359
360
361    /* Get single contact data per id
362     *
363     * @param id: id in the database (unique)
364     *
365     * @return:
366     * - false if id is not found
367     * - array containing all data of the contact
368     */
369    function getContactData($cid){
370        try {
371            $db_helper = plugin_load('helper', 'addressbook_db');
372            $sqlite = $db_helper->getDB();
373        } catch (Exception $e) {
374            msg($e->getMessage(), -1);
375            return false;
376        }
377
378        $sql = "SELECT * FROM addresslist WHERE id = $cid";
379        $query = $sqlite->query($sql);
380
381        if (ADRESSBOOK_SQL) msg("<code>$sql</code>",2);
382
383        if ($sqlite->res2count($query) ==0){
384            msg ("Contact not found (id: $cid)",-1);
385            return false;
386        }
387
388        $res = $sqlite->res2arr($query)[0];
389
390        return $res;
391    }
392
393
394    /*
395     * @return: Array - Contact data in the sequence they should be displayed
396     * ! result does not contain the photo and the id
397     */
398    function getKeys(){
399        return Array('firstname','surname','cfunction','department','tel1','tel2','fax','email','description');
400    }
401
402
403    /* Loads the data from the $_REQUEST-Variables into an array
404     * the image is loaded as a blob
405     *
406     * @return array
407     */
408    function loadFormData(){
409        $res = Array();
410
411        $keys = $this->getKeys();#Array('firstname','surname','cfunction','description');
412
413        foreach ($keys as $k) $res[$k] = $_REQUEST[$k];
414
415        # Validate and load photo data
416        if (isset($_FILES) && $_FILES['photo']['error'] == UPLOAD_ERR_OK && $_FILES['photo']['tmp_name']!='') {
417            if (filesize($_FILES['photo']['tmp_name']) > (2*1024*1024)) {
418                msg('Uploaded photo exceeds 2 MB. Not processed',-1);
419                $res['photo'] = false;
420            } elseif (exif_imagetype($_FILES['photo']['tmp_name']) != IMAGETYPE_JPEG){
421                msg('Image ist not *.jpg file. Not processed.',-1);
422                $res['photo'] = false;
423            } else {
424                $pic = $this->scaleJPG($_FILES['photo']['tmp_name']);
425                $res['photo'] = base64_encode($pic);
426                unset($pic);
427            }
428        } else {
429            $res['photo'] = false;
430            if ($_FILES['photo']['error'] != UPLOAD_ERR_OK && $_FILES['photo']['tmp_name']!='' ) msg('Image could not be uploaded. Error code: '.$_FILES['photo']['error'],-1);
431        }
432
433        return $res;
434    }
435
436    /* SaveData-Function: Saves Data to an existing contact or adds a new contact
437     *
438     * @param $info: Array containing the form data
439     *
440     * additions params used:
441     * $_REQUEST['blob']      - contains the blob of an existing id
442     * $_REQUEST['contactid'] - the id of the existing contact or "new" for a new one
443     */
444    function saveData($info){
445        #msg(print_r($info,true));
446
447        if ($info['surname'] =='' && $info['cfunction']=='') {
448            msg('Please enter either a last name or a function.',-1);
449            return false;
450        } else {
451            try {
452                $db_helper = plugin_load('helper', 'addressbook_db');
453                $sqlite = $db_helper->getDB();
454
455            } catch (Exception $e) {
456                msg($e->getMessage(), -1);
457                return false;
458            }
459
460            $keys = array_keys($info);
461
462            # Use existing photo in existing id
463            if ($_REQUEST['contactid'] != 'new' && isset($_REQUEST['blob']) && $_REQUEST['removephoto'] != 'Remove photo' && $info['photo']== false) {
464                $info['photo'] = $_REQUEST['blob'];
465                # msg("Keep existing photo",2);
466            }
467
468            if ($info['photo']!== false) $blob = $info['photo'];
469
470            # Add new contact
471            if ($_REQUEST['contactid'] == 'new') {
472                #if ($info['photo']!== false) $blob = $info['photo'];
473                $sql = "INSERT INTO addresslist
474                        (".implode(',',$keys).") VALUES
475                        (";
476
477                foreach ($keys as $k) {
478                    if ($k != 'photo') $sql .= "'".$info[$k]."',";
479                    if ($k == 'photo') $sql .= "'$blob'";
480                }
481
482                $sql .= ')';
483
484                unset ($blob);
485
486                $res = $sqlite->query($sql);
487
488                if (ADRESSBOOK_SQL) msg("<code>$sql</code>",2);
489
490                msg("Added new contact",1);
491                return true;
492            } else {
493
494            # Update existing contact
495
496                $sql = "UPDATE addresslist SET ";
497
498                foreach ($keys as $k) {
499                    if ($k != 'photo') $sql .= " $k = '".$info[$k]."', ";
500                    if ($k == 'photo')  $sql .= " $k = '$blob'";
501                }
502
503                $sql .= " WHERE id = ".$_REQUEST['contactid'];
504
505                if (ADRESSBOOK_SQL) msg("<code>$sql</code>",2);
506
507                $res = $sqlite->query($sql);
508                msg("Contact data updated",1);
509                return true;
510            }
511        }
512    }
513
514
515    function deleteContact($cid){
516        try {
517            $db_helper = plugin_load('helper', 'addressbook_db');
518            $sqlite = $db_helper->getDB();
519        } catch (Exception $e) {
520            msg($e->getMessage(), -1);
521            return false;
522        }
523
524        $sql = "DELETE FROM addresslist WHERE id = $cid";
525        $sqlite->query($sql);
526
527        if (ADRESSBOOK_SQL) msg("<code>$sql</code>",2);
528
529        msg('Contact deleted',1);
530
531    }
532
533    /* Scale image to a maximum size (either width or height maximum)
534     * from jpg to jpg
535     *
536     * @param $filename: name of source file
537     * @param $max: maximum width or heigth (depending on which is longer)
538     * @param $quality: compression rate
539     *
540     * return: blob with jpg
541     */
542    function scaleJPG($filename,$max=120,$quality=70){
543
544        # calculate new dimensions
545        list($width, $height) = getimagesize($filename);
546
547        if ($width > $height) {$percent = $max/$width;} else {$percent = $max/$height;}
548
549        $newwidth = $width * $percent;
550        $newheight = $height * $percent;
551
552        # load image
553        $thumb = imagecreatetruecolor($newwidth, $newheight);
554        $source = imagecreatefromjpeg($filename);
555
556        # scale
557        imagecopyresized($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
558
559        # output
560        ob_start();
561        imagejpeg($thumb,NULL,$quality);
562        $result = ob_get_contents();
563        ob_end_clean();
564
565        return $result;
566    }
567
568
569    /* Searches the database for the occurence of search terms.
570     * The search uses AND-operator for multiple terms
571     *
572     * @param $text: search terms separated with a blank space
573     *
574     * return:
575     * - boolean false: no matches
576     * - array with matches
577     */
578    function searchDB($text=false,$order='surname,firstname'){
579        if ($text == false || strlen($text) < 2) {msg("Invalid search.");return;}
580
581         try {
582            $db_helper = plugin_load('helper', 'addressbook_db');
583            $sqlite = $db_helper->getDB();
584        } catch (Exception $e) {
585            msg($e->getMessage(), -1);
586            return false;
587        }
588
589        $text = explode(" ",$text);
590
591        $sql = "select * from addresslist WHERE instr(lower(surname || firstname || tel1 || tel2 || fax || description || cfunction || department) ,'".$text[0]."') > 0";
592        for ($c=1;$c<count($text);$c++) $sql .= " AND instr(lower(' ' || surname || firstname || tel1 || tel2 || fax || description || cfunction || department) ,'".$text[$c]."') > 0";
593        $sql .= " ORDER BY $order";
594
595        $query = $sqlite->query($sql);
596        $res = $sqlite->res2arr($query);
597
598        if (ADRESSBOOK_SQL) msg("<code>$sql</code>",2);
599
600        if ($sqlite->res2count($sqlite->query($sql)) == 0) {
601            msg("No matches found",2);
602            return false;
603        }
604
605        return $res;
606    }
607
608
609    function getList($filters=false,$order="surname,firstname,cfunction,department"){
610        try {
611            $db_helper = plugin_load('helper', 'addressbook_db');
612            $sqlite = $db_helper->getDB();
613        } catch (Exception $e) {
614            msg($e->getMessage(), -1);
615            return false;
616        }
617
618        # Get all elements if no filters are set
619        if ($filters === false){
620            $sql = "select * from addresslist ORDER BY $order";
621        }
622
623        # Get one department
624        if (isset($filters['department'])){
625            $sql = "select * from addresslist WHERE UPPER(department) = UPPER('".$filters['department']."') ORDER BY $order";
626        }
627
628        $query = $sqlite->query($sql);
629        $res = $sqlite->res2arr($query);
630
631        if (ADRESSBOOK_SQL) msg("<code>$sql</code>",2);
632
633        # return false if no matches are found
634        if ($sqlite->res2count($sqlite->query($sql)) == 0) {
635            msg("No matches found for list",2);
636            return false;
637        }
638
639        # Adjust content for sorting purposes when information is missing / not stated
640        foreach ($res as &$r) {
641            if ($r['surname'] == '' && $r['department'] == '') $r['department'] = $this->getLang('departments');
642            if ($r['surname'] != '' && $r['department'] == '') $r['department'] = $this->getLang('general');
643        }
644
645        return $res;
646    }
647
648
649    /* Creates a list of values from a column. Each element is listed
650     * once.
651     *
652     * @param string $column: choose column from which to get data
653     * @param boolean $htmlselect: creates output string containing html for a dropdown box
654     *
655     * @return:
656     * - array: elements founds
657     * - string: with html for checkbox
658     */
659    function getCriteria($column,$htmlselect = true){
660        $allowed = array('cfunction','department');
661        if (!in_array($column,$allowed)) return false;
662
663        try {
664            $db_helper = plugin_load('helper', 'addressbook_db');
665            $sqlite = $db_helper->getDB();
666        } catch (Exception $e) {
667            msg($e->getMessage(), -1);
668            return false;
669        }
670
671        $sql = "SELECT DISTINCT $column FROM addresslist WHERE $column NOT LIKE '' ORDER by $column";
672
673        $query = $sqlite->query($sql);
674        $res = $sqlite->res2arr($query);
675
676        if (ADRESSBOOK_SQL) msg("<code>$sql</code>",2);
677
678        # return false if no matches are found
679        if ($sqlite->res2count($sqlite->query($sql)) == 0) {
680            #msg("No matches found for criteria",2);
681            return false;
682        }
683
684        foreach ($res as $r) $n[] =$r[$column];
685        if ($htmlselect){
686            $res = '';
687            $res .= ucfirst($column);
688            $res .= '<select name="column" id="column">';
689            $res .= "<option value='all'>All</option>";
690            foreach ($n as $s) $res .= "<option value='$s'>$s</option>";
691            $res .= '</select>';
692            return $res;
693        }
694
695        return $n;
696    }
697
698    /* Helper function, similar to implode
699     * Adds string elements to an array only if it is not empty and then implodes them
700     */
701    function names($list,$symbol=', '){
702        $res = Array();
703        foreach ($list as $l) if ($l != '')  $res[]=$l;
704        return implode($symbol,$res);
705    }
706
707
708    /* Helper function
709     * Return array with parameter=>value pairs
710     *
711     * @param string $params = parameters in form "<somet_text>?param1=value1&param2=value2...paramN=valueN"
712
713     *
714     * @return array(parameter1 => value1, ...)
715     */
716    function getValue($params){
717        $params = substr($params,strpos($params,'?')+1);
718        $opts = explode('&',$params);
719
720        foreach ($opts as $o) {
721            if (strpos($o,'=') == 0) {$res[$o] = false;} else {
722                $t = explode('=',$o);
723                $res[$t[0]] = $t[1];
724            }
725        }
726
727        return $res;
728    }
729
730
731    /* Build an index showing contacts
732     *
733     *  @param $list: list of contacts
734     *  @param $separator = db_field for which headers are created in the list.
735     */
736    function buildIndex($list=false,$separator=false,$target=false){
737
738        # if no list ist stated, get all. If no entry in DB, return
739        if ($list===false){
740            $list =$this->getList();
741            if ($list === false) return;
742        }
743
744        # Sort by Surname or Function->if no surname ist stated
745        if (!$separator) usort($list,'contact_custom_double_sort');
746
747        $out .= $this->getLang('elements').': '.count($list);
748        $out .= '<div class="addressbook_list" style="column-width:20em;margin-top:3px;">';
749
750
751        $sep = '';
752        foreach ($list as $r){
753
754            if ($separator !== false){
755                if ($sep != $r[$separator]) {
756                    $sep = $r[$separator];
757                    $out .= '<h3>'.$r[$separator].'</h3>';
758                }
759            }
760
761            $out .= '<span>';
762
763            if ($r['surname'].$r['firstname'] <> '') {$names = true;} else {$names = false;}
764
765            if ($target != false) $out .= '<a href="'.wl($target,'showcontact='.$r['id']).'">';
766
767            $out .= $r['surname'] .($r['firstname'] <> ''? ', '.$r['firstname']:'');
768            if (!$names) $out .= $this->names(array($r['cfunction'],$r['department']));
769
770            if ($target != false) $out .= '</a>';
771
772            if ($names && $r['cfunction'].$r['department'] <>'') $out .= ' ('.$this->names(array($r['cfunction'],$r['department'])).')';
773
774            if ($r['tel1'].$r['tel2'] <> '') $out .= ' Tel: '.$this->names(array($r['tel1'],$r['tel2']));
775
776            $out .= '</span>';
777        }
778        $out .= '</div>';
779
780        return $out;
781
782    }
783
784
785    /* Builds a printable contact list
786     *
787     * @param array $list = list of contact entries in format array[1..n](db_field => value)
788     *
789     * @param string $separator = name of database field. This name is added as a
790     *                            header between the contact entries. Important: The
791     *                            entries should be sorted in first place according
792     *                            to this speparator, otherweise there will be as many
793     *                            headers as contacts!.
794     *                            Allowed separators: 'department'
795     *
796     * @param integer $entriesperpage = amount of list items per page, must be even!
797     * @param
798     */
799    function buildPrintList($list=false,$separator = false,$entriesperpage = 80){
800
801        # validation: separator type correct
802        $allowed_separators = Array('department');
803        if (!in_array($separator,$allowed_separators)) $separator = false;
804
805        # validation entries per page must be even
806        if ($entriesperpage % 2 == 1) $entriesperpage++;
807
808        # if no list ist stated, get all. If no entry in DB, return
809        if ($list===false){
810            $list =$this->getList(false,($separator == false? '': "$separator,").'surname,firstname,cfunction');
811            if ($list === false) return;
812        }
813
814        # Sort by Surname or Function->if no surname ist stated
815        if (!$separator) usort($list,'contact_custom_double_sort');
816
817        $amount = count($list);
818        $pages = ceil($amount/$entriesperpage);
819
820        # $separator = 'department';
821        # process list before generating the table
822        if ($separator == 'department'){
823            $dep = $list[$c+1]['department'];
824            $insert = Array();
825            $i_count = 0;
826            for ($c=0;$c<$amount;$c++) {
827                if ($list[$c+1]['department'] != $dep) {
828
829
830                    $i_count++;
831
832                    if ($c+$i_count % $entriesperpage > 0) {
833
834                        $insert[] = array('position' => $c+$i_count, 'title' =>true, 'cfunction' => '');
835                        $i_count++;
836                    }
837
838                    $insert[] = array('position' => $c+$i_count, 'title' =>true, 'cfunction' => $list[$c+1]['department']);
839
840
841                    $dep = $list[$c+1]['department'];
842
843                }
844
845            }
846
847            foreach ($insert as $i){
848
849                unset($temp);
850                for ($c=0;$c<count($list);$c++){
851
852                    if ($c==$i['position']){
853                        $temp[] = array('cfunction'=>$i['cfunction'],'title' => true);
854                    }
855
856                    $temp[] = $list[$c];
857
858                }
859                $list = $temp;
860
861            }
862
863            $amount = count($list);
864            $pages = ceil($amount/$entriesperpage);
865
866        }
867
868
869        for ($p=0;$p<$pages;$p++) {
870
871            $out .= '<table class="plugin_addressbook_print">';
872
873            for ($row=0;$row<$entriesperpage/2;$row++) {
874
875                unset($i);
876                $i[] = ($p * $entriesperpage) + $row;
877                $i[] = ($p * $entriesperpage) + $row + ($entriesperpage/2);
878                $col = 0;
879
880                #if ($i[0] < $amount)
881                $out .= '<tr'.($row % 2 == 1? ' style="background:lightgray"':'').'>';
882
883                foreach ($i as $d) {
884                    # Output title
885                    if ($separator != false && $list[$d]['title'] == true) {
886                        $out .= '<td style="font-weight:bold;text-decoration:underline;font-size:12px;text-align:left;background:white" colspan=4>'.$list[$d]['cfunction'].'</td>';
887                        $col++;
888                        if ($col < count($i)) $out .= '<td style="background:white;width:10px;"></td>';
889                        }
890
891                    # Output contact data
892                    if ($d < $amount && !isset($list[$d]['title'])) {
893
894                        $out .= '<td style="text-align:left">'.$this->names(array($list[$d]['cfunction'],$list[$d]['surname']),' ').'</td>';
895                        $out .= '<td>'.$list[$d]['tel1'].'</td>';
896                        $out .= '<td>'.$list[$d]['tel2'].'</td>';
897                        $out .= '<td>'.$list[$d]['fax'].'</td>';
898
899                        $col++;
900                        if ($col < count($i)) $out .= '<td style="background:white;width:10px;"></td>';
901                    }
902
903                    # Fill with empty cells if there are no entries, so that the table is continued
904                    if ($d> $amount) {
905                        $out.= '<td colspan=4 style="background:white;">'.str_repeat('&nbsp;',15).'</td>';
906                        $col++;
907                        if ($col < count($i)) $out .= '<td style="background:white;width:10px;"></td>';
908                    }
909
910                }
911
912                $out .= '</tr>';
913
914            }
915
916            $out .= '</table>';
917
918        }
919
920        return $out;
921
922    }
923
924
925    /* Copies the first entry of the database multiple time for testing purposes
926     * Beware: This can take minutes!
927     *
928     * @param $n: amount of copie to be made
929     */
930    function fillDB($n=0){
931        try {
932            $db_helper = plugin_load('helper', 'addressbook_db');
933            $sqlite = $db_helper->getDB();
934        } catch (Exception $e) {
935            msg($e->getMessage(), -1);
936            return false;
937        }
938
939        $sql = "select * from addresslist  WHERE id = 1";
940
941        $query = $sqlite->query($sql);
942        $res = $sqlite->res2arr($query)[0];
943        unset($res['id']);
944
945        for ($c=0;$c<$n;$c++){
946            $sql = "INSERT INTO addresslist
947                    (firstname,surname,cfunction,tel1,tel2,fax,email,department,description,photo) VALUES
948                    (";
949            foreach ($res as $k=>$r) $sql.= "'$r'".($k=='photo'? ')':',');
950            $query = $sqlite->query($sql);
951        }
952    }
953
954}
955
956
957/* Inspired by https://www.php.net/manual/de/function.asort.php
958 *
959 * Callback function to sort the contact list by surname OR
960 * cfunction if nor surname ist stated
961 *
962 * */
963function contact_custom_double_sort($a,$b) {
964    if ($a['surname'] == '') $a['surname'] = $a['cfunction'];
965    if ($b['surname'] == '') $b['surname'] = $b['cfunction'];
966    return $a['surname'] > $b['surname'];
967}
968
969
970function addressbook_debug_show($direct=true){
971    $out = '<pre>'.print_r($_REQUEST,true).'</pre>';
972    if ($direct) echo $out;
973    return $out;
974}
975