* */ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); define('ADRESSBOOK_SQL',false); # for development purposes class syntax_plugin_addressbook extends DokuWiki_Syntax_Plugin { public $editor = false; # Does user have editing rights for contacts? public $loggedin = false; # User logged in? function getSort(){ return 158; } public function getType() { return 'substition'; } /** * Paragraph Type */ public function getPType() { return 'block'; } function __construct(){ global $INFO; if ($INFO['ismanager'] === true) $this->editor = true; if (isset($INFO['userinfo'])) $this->loggedin = true; } /** * @param string $mode */ public function connectTo($mode) {$this->Lexer->addEntryPattern('\[ADDRESSBOOK.*?', $mode, 'plugin_addressbook');} public function postConnect() { $this->Lexer->addExitPattern('\]','plugin_addressbook'); } /** * Handler to prepare matched data for the rendering process */ public function handle($match, $state, $pos, Doku_Handler $handler) { if ($state == DOKU_LEXER_UNMATCHED) { if ($match==':') return 'LOOKUP'; if ($match[0]==':') $match = substr($match,1); return trim($match); } return false; } /** * @param $mode string output format being rendered * @param $renderer Doku_Renderer the current renderer object * @param $data array data created by handler() * @return boolean rendered correctly? */ public function render($mode, Doku_Renderer $renderer, $data) { global $ID; if($mode == 'xhtml') { $renderer->info['cache'] = false; # addressbook_debug_show(); if ($_REQUEST['Submit'] == $this->getLang('exec cancel')){ unset($_REQUEST); unset ($cinfo); $action = $data; } # Main action given by tag data if (!isset($_REQUEST['Submit'])) $action = $data; /* Save a contact * * On fail: show edit form again */ if ($_REQUEST['Submit']==$this->getLang('exec save')) $action = "savedata"; # Certain actions could cause double saving, which is avoided by counting if ($action=='savedata' && $this->saveOnce == 0 && $this->editor){ $this->saveOnce++; $contact_id = $_REQUEST['editcontact']; $cinfo = $this->loadFormData(); # Loads form data concerning the contact $res = $this->saveData($cinfo); if (!$res) { $action = 'edit'; $contact_id = $_REQUEST['contactid']; } else { # Clear all unset($_REQUEST); unset ($cinfo); $action = $data; } } /* Directly show contact card by tag * * Cards are only display, when there is NO edit or save action */ if (substr($data,0,7) == 'contact' && !isset($_REQUEST['editcontact']) && $action != 'edit' ) { $renderer->doc .= $this->showcontact(intval(substr($data,8)),$ID); return; #no following actions } if (substr($data,0,5) == 'index' && !isset($_REQUEST['editcontact'])) { # showcontact once before if necessary if (isset($_REQUEST['showcontact']) && $this->showCount==0) { $this->showCount++; $out = $this->showcontact($_REQUEST['showcontact'],$ID); if ($out !== false) $renderer->doc .= $out.'
'; } # now show index # keyword 'departments' if (strpos($data,'departments') > 0) { $list = $this->getList(false,'department,surname,firstname,cfunction'); $renderer->doc .= $this->buildIndex($list,'department',$ID); } else $renderer->doc .= $this->buildIndex(false,false,$ID); return; # no following actions } # --------------------------------------------------------# # only one instance beyond this point $this->instance++; if ($this->instance > 1) return; # --------------------------------------------------------# # Generate printable list if (substr($data,0,5) == 'print') { $pList = false; $sep = false; $params = $this->getValue($data,''); if (isset($params['department'])) $sep = 'department'; # Select one department if (isset($params['select'])) { $pList = $this->getList(Array('department' => $params['select'])); $sep = false; } $renderer->doc .= $this->buildPrintList($pList,$sep); return; } /* Edit contact or add a new contact * * No futher actions are performed after an edit */ if ($action == 'addcontact' && $this->editor) { # Add a new contact. Can be overwritten by edit $contact_id = 'new'; $action = 'edit'; # redefine action } if (isset($_REQUEST['editcontact']) && $this->editor) { # Override new contact if the action is to edit an existing one $contact_id = $_REQUEST['editcontact']; $cinfo = $this->getContactData($contact_id); $action = 'edit'; } if ($action == 'edit' && $this->editor) { $out = $this->buildForm($contact_id,$cinfo); $renderer->doc .= $out; return; # no following actions } # Show search box if ($action == 'search' || $_REQUEST['Submit'] == $this->getLang('exec search')) { $out = $this->searchDialog(); $renderer->doc .= $out; } if ($_REQUEST['Submit'] == $this->getLang('exec search')) { $list = $this->searchDB($_REQUEST['searchtext']); if ($list != false){ if (count($list)<5) { foreach ($list as $l) $renderer->doc .= $this->showcontact($l['id'],$ID); } else $renderer->doc .= $this->buildIndex($list,false,$ID); } } /* Show contact per request * can only be shown once due to the instance count above * * placed below the searchbox */ if (isset($_REQUEST['showcontact']) && $this->showCount==0) { $this->showCount++; $out = $this->showcontact($_REQUEST['showcontact'],$ID); if ($out !== false) $renderer->doc .= $out.'
'; } # Delete a contact if (isset($_REQUEST['erasecontact']) && $this->editor){ $this->deleteContact($_REQUEST['erasecontact']); } return true; } return false; } function searchDialog(){ global $ID; $out = ''; $out .= ''; return $out; } function buildForm($contact_id,$cinfo){ global $ID; $out = '
'; if ($contact_id == 'new') {$out .= '

'.$this->getLang('header add').'

';} else {$out .= '

'.$this->getLang('header edit').'

';} $out .= '
'; $out .= ''; $out .= ''; $out .= ''; $out .= '
'; $out .= ''; $out .= '
'; $out .= ''; $out .= '
'; $out .= '
'; $out .= '
'; $out .= '
'.$this->getLang('form description').':


'; $out .= '
'; if (isset($_REQUEST['blob'])) $cinfo['photo'] = $_REQUEST['blob']; if ($cinfo['photo'] != false) { $out .= ""; $out .= '
'; $out .= '

'; $out .= $this->getLang('form upload info2').'.
'; $out .= ''; } $out .= '
'; $out .= $this->getLang('form upload').': '.$this->getLang('form upload info').'.
'; $out .= '
'; $out .= ''; $out .= '
'; $out .= "
ID: $contact_id
"; $out .= '
'; return $out; } function showcontact($cid,$target = false){ $r = $this->getContactData($cid); if ($res === false) return false; $out =''; $out .= '
'; $out .= '
'; $out .= '
'; # Name if existant $out .= ''.$r['surname'] .($r['firstname'] <> ''? ', '.$r['firstname']:'').''; if (strlen($r['surname'] .$r['firstname'])>0) $out .= '
'; # Function/department if existant if ($r['surname'].$r['firstname'] == '') $out .= ''; if ($r['department'].$r['cfunction'] != '') $out .= $this->names(array($r['cfunction'],$r['department'])); if (strlen($r['cfuntion'] .$r['department'])>0) $out .= '
'; if ($r['surname'].$r['firstname'] == '') $out .= '
'; # Telephone if ($r['tel1'].$r['tel2'] <> '') $out .= '
Tel.: '.$this->names(array($r['tel1'],$r['tel2']),' / '); # Fax if ($r['fax']<>'') $out .= '
Fax: '.$r['fax']; # Mail if ($r['email']<>'') $out .= '
Mail: '.$r['email'].''; $out .= '
'; if ($r['photo'] != false) $out .= ""; $out .= '
'; if ($r['description']<>'') $out .= $r['description']; if ($this->loggedin) { $out .= ''; } $out .= '
'; return $out; } /* Get single contact data per id * * @param id: id in the database (unique) * * @return: * - false if id is not found * - array containing all data of the contact */ function getContactData($cid){ try { $db_helper = plugin_load('helper', 'addressbook_db'); $sqlite = $db_helper->getDB(); } catch (Exception $e) { msg($e->getMessage(), -1); return false; } $sql = "SELECT * FROM addresslist WHERE id = $cid"; $query = $sqlite->query($sql); if (ADRESSBOOK_SQL) msg("$sql",2); if ($sqlite->res2count($query) ==0){ msg ("Contact not found (id: $cid)",-1); return false; } $res = $sqlite->res2arr($query)[0]; return $res; } /* * @return: Array - Contact data in the sequence they should be displayed * ! result does not contain the photo and the id */ function getKeys(){ return Array('firstname','surname','cfunction','department','tel1','tel2','fax','email','description'); } /* Loads the data from the $_REQUEST-Variables into an array * the image is loaded as a blob * * @return array */ function loadFormData(){ $res = Array(); $keys = $this->getKeys();#Array('firstname','surname','cfunction','description'); foreach ($keys as $k) $res[$k] = $_REQUEST[$k]; # Validate and load photo data if (isset($_FILES) && $_FILES['photo']['error'] == UPLOAD_ERR_OK && $_FILES['photo']['tmp_name']!='') { if (filesize($_FILES['photo']['tmp_name']) > (2*1024*1024)) { msg('Uploaded photo exceeds 2 MB. Not processed',-1); $res['photo'] = false; } elseif (exif_imagetype($_FILES['photo']['tmp_name']) != IMAGETYPE_JPEG){ msg('Image ist not *.jpg file. Not processed.',-1); $res['photo'] = false; } else { $pic = $this->scaleJPG($_FILES['photo']['tmp_name']); $res['photo'] = base64_encode($pic); unset($pic); } } else { $res['photo'] = false; if ($_FILES['photo']['error'] != UPLOAD_ERR_OK && $_FILES['photo']['tmp_name']!='' ) msg('Image could not be uploaded. Error code: '.$_FILES['photo']['error'],-1); } return $res; } /* SaveData-Function: Saves Data to an existing contact or adds a new contact * * @param $info: Array containing the form data * * additions params used: * $_REQUEST['blob'] - contains the blob of an existing id * $_REQUEST['contactid'] - the id of the existing contact or "new" for a new one */ function saveData($info){ #msg(print_r($info,true)); if ($info['surname'] =='' && $info['cfunction']=='') { msg('Please enter either a last name or a function.',-1); return false; } else { try { $db_helper = plugin_load('helper', 'addressbook_db'); $sqlite = $db_helper->getDB(); } catch (Exception $e) { msg($e->getMessage(), -1); return false; } $keys = array_keys($info); # Use existing photo in existing id if ($_REQUEST['contactid'] != 'new' && isset($_REQUEST['blob']) && $_REQUEST['removephoto'] != 'Remove photo' && $info['photo']== false) { $info['photo'] = $_REQUEST['blob']; # msg("Keep existing photo",2); } if ($info['photo']!== false) $blob = $info['photo']; # Add new contact if ($_REQUEST['contactid'] == 'new') { #if ($info['photo']!== false) $blob = $info['photo']; $sql = "INSERT INTO addresslist (".implode(',',$keys).") VALUES ("; foreach ($keys as $k) { if ($k != 'photo') $sql .= "'".$info[$k]."',"; if ($k == 'photo') $sql .= "'$blob'"; } $sql .= ')'; unset ($blob); $res = $sqlite->query($sql); if (ADRESSBOOK_SQL) msg("$sql",2); msg("Added new contact",1); return true; } else { # Update existing contact $sql = "UPDATE addresslist SET "; foreach ($keys as $k) { if ($k != 'photo') $sql .= " $k = '".$info[$k]."', "; if ($k == 'photo') $sql .= " $k = '$blob'"; } $sql .= " WHERE id = ".$_REQUEST['contactid']; if (ADRESSBOOK_SQL) msg("$sql",2); $res = $sqlite->query($sql); msg("Contact data updated",1); return true; } } } function deleteContact($cid){ try { $db_helper = plugin_load('helper', 'addressbook_db'); $sqlite = $db_helper->getDB(); } catch (Exception $e) { msg($e->getMessage(), -1); return false; } $sql = "DELETE FROM addresslist WHERE id = $cid"; $sqlite->query($sql); if (ADRESSBOOK_SQL) msg("$sql",2); msg('Contact deleted',1); } /* Scale image to a maximum size (either width or height maximum) * from jpg to jpg * * @param $filename: name of source file * @param $max: maximum width or heigth (depending on which is longer) * @param $quality: compression rate * * return: blob with jpg */ function scaleJPG($filename,$max=120,$quality=70){ # calculate new dimensions list($width, $height) = getimagesize($filename); if ($width > $height) {$percent = $max/$width;} else {$percent = $max/$height;} $newwidth = $width * $percent; $newheight = $height * $percent; # load image $thumb = imagecreatetruecolor($newwidth, $newheight); $source = imagecreatefromjpeg($filename); # scale imagecopyresized($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height); # output ob_start(); imagejpeg($thumb,NULL,$quality); $result = ob_get_contents(); ob_end_clean(); return $result; } /* Searches the database for the occurence of search terms. * The search uses AND-operator for multiple terms * * @param $text: search terms separated with a blank space * * return: * - boolean false: no matches * - array with matches */ function searchDB($text=false,$order='surname,firstname'){ if ($text == false || strlen($text) < 2) {msg("Invalid search.");return;} try { $db_helper = plugin_load('helper', 'addressbook_db'); $sqlite = $db_helper->getDB(); } catch (Exception $e) { msg($e->getMessage(), -1); return false; } $text = explode(" ",$text); $sql = "select * from addresslist WHERE instr(lower(surname || firstname || tel1 || tel2 || fax || description || cfunction || department) ,'".$text[0]."') > 0"; for ($c=1;$c 0"; $sql .= " ORDER BY $order"; $query = $sqlite->query($sql); $res = $sqlite->res2arr($query); if (ADRESSBOOK_SQL) msg("$sql",2); if ($sqlite->res2count($sqlite->query($sql)) == 0) { msg("No matches found",2); return false; } return $res; } function getList($filters=false,$order="surname,firstname,cfunction,department"){ try { $db_helper = plugin_load('helper', 'addressbook_db'); $sqlite = $db_helper->getDB(); } catch (Exception $e) { msg($e->getMessage(), -1); return false; } # Get all elements if no filters are set if ($filters === false){ $sql = "select * from addresslist ORDER BY $order"; } # Get one department if (isset($filters['department'])){ $sql = "select * from addresslist WHERE UPPER(department) = UPPER('".$filters['department']."') ORDER BY $order"; } $query = $sqlite->query($sql); $res = $sqlite->res2arr($query); if (ADRESSBOOK_SQL) msg("$sql",2); # return false if no matches are found if ($sqlite->res2count($sqlite->query($sql)) == 0) { msg("No matches found for list",2); return false; } # Adjust content for sorting purposes when information is missing / not stated foreach ($res as &$r) { if ($r['surname'] == '' && $r['department'] == '') $r['department'] = $this->getLang('departments'); if ($r['surname'] != '' && $r['department'] == '') $r['department'] = $this->getLang('general'); } return $res; } /* Creates a list of values from a column. Each element is listed * once. * * @param string $column: choose column from which to get data * @param boolean $htmlselect: creates output string containing html for a dropdown box * * @return: * - array: elements founds * - string: with html for checkbox */ function getCriteria($column,$htmlselect = true){ $allowed = array('cfunction','department'); if (!in_array($column,$allowed)) return false; try { $db_helper = plugin_load('helper', 'addressbook_db'); $sqlite = $db_helper->getDB(); } catch (Exception $e) { msg($e->getMessage(), -1); return false; } $sql = "SELECT DISTINCT $column FROM addresslist WHERE $column NOT LIKE '' ORDER by $column"; $query = $sqlite->query($sql); $res = $sqlite->res2arr($query); if (ADRESSBOOK_SQL) msg("$sql",2); # return false if no matches are found if ($sqlite->res2count($sqlite->query($sql)) == 0) { #msg("No matches found for criteria",2); return false; } foreach ($res as $r) $n[] =$r[$column]; if ($htmlselect){ $res = ''; $res .= ucfirst($column); $res .= ''; return $res; } return $n; } /* Helper function, similar to implode * Adds string elements to an array only if it is not empty and then implodes them */ function names($list,$symbol=', '){ $res = Array(); foreach ($list as $l) if ($l != '') $res[]=$l; return implode($symbol,$res); } /* Helper function * Return array with parameter=>value pairs * * @param string $params = parameters in form "?param1=value1¶m2=value2...paramN=valueN" * * @return array(parameter1 => value1, ...) */ function getValue($params){ $params = substr($params,strpos($params,'?')+1); $opts = explode('&',$params); foreach ($opts as $o) { if (strpos($o,'=') == 0) {$res[$o] = false;} else { $t = explode('=',$o); $res[$t[0]] = $t[1]; } } return $res; } /* Build an index showing contacts * * @param $list: list of contacts * @param $separator = db_field for which headers are created in the list. */ function buildIndex($list=false,$separator=false,$target=false){ # if no list ist stated, get all. If no entry in DB, return if ($list===false){ $list =$this->getList(); if ($list === false) return; } # Sort by Surname or Function->if no surname ist stated if (!$separator) usort($list,'contact_custom_double_sort'); $out .= $this->getLang('elements').': '.count($list); $out .= '
'; $sep = ''; foreach ($list as $r){ if ($separator !== false){ if ($sep != $r[$separator]) { $sep = $r[$separator]; $out .= '

'.$r[$separator].'

'; } } $out .= ''; if ($r['surname'].$r['firstname'] <> '') {$names = true;} else {$names = false;} if ($target != false) $out .= ''; $out .= $r['surname'] .($r['firstname'] <> ''? ', '.$r['firstname']:''); if (!$names) $out .= $this->names(array($r['cfunction'],$r['department'])); if ($target != false) $out .= ''; if ($names && $r['cfunction'].$r['department'] <>'') $out .= ' ('.$this->names(array($r['cfunction'],$r['department'])).')'; if ($r['tel1'].$r['tel2'] <> '') $out .= ' Tel: '.$this->names(array($r['tel1'],$r['tel2'])); $out .= ''; } $out .= '
'; return $out; } /* Builds a printable contact list * * @param array $list = list of contact entries in format array[1..n](db_field => value) * * @param string $separator = name of database field. This name is added as a * header between the contact entries. Important: The * entries should be sorted in first place according * to this speparator, otherweise there will be as many * headers as contacts!. * Allowed separators: 'department' * * @param integer $entriesperpage = amount of list items per page, must be even! * @param */ function buildPrintList($list=false,$separator = false,$entriesperpage = 80){ # validation: separator type correct $allowed_separators = Array('department'); if (!in_array($separator,$allowed_separators)) $separator = false; # validation entries per page must be even if ($entriesperpage % 2 == 1) $entriesperpage++; # if no list ist stated, get all. If no entry in DB, return if ($list===false){ $list =$this->getList(false,($separator == false? '': "$separator,").'surname,firstname,cfunction'); if ($list === false) return; } # Sort by Surname or Function->if no surname ist stated if (!$separator) usort($list,'contact_custom_double_sort'); $amount = count($list); $pages = ceil($amount/$entriesperpage); # $separator = 'department'; # process list before generating the table if ($separator == 'department'){ $dep = $list[$c+1]['department']; $insert = Array(); $i_count = 0; for ($c=0;$c<$amount;$c++) { if ($list[$c+1]['department'] != $dep) { $i_count++; if ($c+$i_count % $entriesperpage > 0) { $insert[] = array('position' => $c+$i_count, 'title' =>true, 'cfunction' => ''); $i_count++; } $insert[] = array('position' => $c+$i_count, 'title' =>true, 'cfunction' => $list[$c+1]['department']); $dep = $list[$c+1]['department']; } } foreach ($insert as $i){ unset($temp); for ($c=0;$c$i['cfunction'],'title' => true); } $temp[] = $list[$c]; } $list = $temp; } $amount = count($list); $pages = ceil($amount/$entriesperpage); } for ($p=0;$p<$pages;$p++) { $out .= ''; for ($row=0;$row<$entriesperpage/2;$row++) { unset($i); $i[] = ($p * $entriesperpage) + $row; $i[] = ($p * $entriesperpage) + $row + ($entriesperpage/2); $col = 0; #if ($i[0] < $amount) $out .= ''; foreach ($i as $d) { # Output title if ($separator != false && $list[$d]['title'] == true) { $out .= ''; $col++; if ($col < count($i)) $out .= ''; } # Output contact data if ($d < $amount && !isset($list[$d]['title'])) { $out .= ''; $out .= ''; $out .= ''; $out .= ''; $col++; if ($col < count($i)) $out .= ''; } # Fill with empty cells if there are no entries, so that the table is continued if ($d> $amount) { $out.= ''; $col++; if ($col < count($i)) $out .= ''; } } $out .= ''; } $out .= '
'.$list[$d]['cfunction'].''.$this->names(array($list[$d]['cfunction'],$list[$d]['surname']),' ').''.$list[$d]['tel1'].''.$list[$d]['tel2'].''.$list[$d]['fax'].''.str_repeat(' ',15).'
'; } return $out; } /* Copies the first entry of the database multiple time for testing purposes * Beware: This can take minutes! * * @param $n: amount of copie to be made */ function fillDB($n=0){ try { $db_helper = plugin_load('helper', 'addressbook_db'); $sqlite = $db_helper->getDB(); } catch (Exception $e) { msg($e->getMessage(), -1); return false; } $sql = "select * from addresslist WHERE id = 1"; $query = $sqlite->query($sql); $res = $sqlite->res2arr($query)[0]; unset($res['id']); for ($c=0;$c<$n;$c++){ $sql = "INSERT INTO addresslist (firstname,surname,cfunction,tel1,tel2,fax,email,department,description,photo) VALUES ("; foreach ($res as $k=>$r) $sql.= "'$r'".($k=='photo'? ')':','); $query = $sqlite->query($sql); } } } /* Inspired by https://www.php.net/manual/de/function.asort.php * * Callback function to sort the contact list by surname OR * cfunction if nor surname ist stated * * */ function contact_custom_double_sort($a,$b) { if ($a['surname'] == '') $a['surname'] = $a['cfunction']; if ($b['surname'] == '') $b['surname'] = $b['cfunction']; return $a['surname'] > $b['surname']; } function addressbook_debug_show($direct=true){ $out = '
'.print_r($_REQUEST,true).'
'; if ($direct) echo $out; return $out; }