1 <?php
3 class addressbook extends plugin
4 {
5 /* Definitions */
6 var $plHeadline= "Addressbook";
7 var $plDescription= "This does something";
9 /* Phonelist attributes */
10 var $telephone_list = array();
11 var $start= 0;
12 var $search_for= "*";
13 var $search_base= "";
14 var $search_type= "";
15 var $new_dn= "";
16 var $orig_cn= "";
17 var $storage_base= "";
18 var $orig_storage_base= "";
20 var $range = 20;
22 var $sn= "";
23 var $cn= "";
24 var $givenName= "";
25 var $mail= "";
26 var $title= "";
27 var $personalTitle= "";
28 var $initials= "";
29 var $homePostalAddress= "";
30 var $homePhone= "";
31 var $mobile= "";
32 var $o= "";
33 var $postalAddress= "";
34 var $l= "";
35 var $postalCode= "";
36 var $st= "";
37 var $ou= "";
38 var $telephoneNumber= "";
39 var $facsimileTelephoneNumber= "";
40 var $pager= "";
42 /* attribute list for save action */
43 var $attributes= array("sn", "givenName", "mail", "title",
44 "initials", "homePostalAddress", "displayName",
45 "homePhone", "mobile", "o", "postalAddress", "l",
46 "postalCode", "st", "ou", "telephoneNumber",
47 "facsimileTelephoneNumber", "pager");
48 var $objectclasses= array("top", "person", "organizationalPerson", "inetOrgPerson");
50 function addressbook ($config, $dn= NULL)
51 {
52 /* Include config object */
53 $this->config= $config;
55 /* Get global filter config */
56 if (!is_global("phonefilter")){
57 $ui= get_userinfo();
58 $base= get_base_from_people($ui->dn);
59 $phonefilter= array("search_base" => $base,
60 "organizational" => "checked",
61 "global" => "checked",
62 "search_for" => "*",
63 "object_type" => "*");
64 register_global("phonefilter", $phonefilter);
65 }
66 }
68 function execute()
69 {
70 /* Call parent execute */
71 plugin::execute();
73 $smarty= get_smarty();
74 foreach($this->attributes as $attr){
75 $smarty->assign($attr."ACL",chkacl($this->acl,$attr));
76 }
79 #! Hickert
80 /*prevent empty variables for smarty*/
81 foreach($this->attributes as $atr) $smarty->assign($atr,"");
83 /* Save formular information */
84 $phonefilter= get_global("phonefilter");
85 foreach( array("search_for", "search_base", "object_type") as $type){
86 if (isset($_POST[$type])){
87 $phonefilter[$type]= $_POST[$type];
88 }
89 $this->$type= $phonefilter[$type];
90 }
91 if (isset($_POST['search_base'])){
92 foreach( array("organizational", "global") as $type){
93 if (isset($_POST[$type])){
94 $phonefilter[$type]= "checked";
95 } else {
96 $phonefilter[$type]= "";
97 }
98 }
99 }
101 /* Search string */
102 $s= $phonefilter['search_for'];
103 if ($s == "") {
104 $s= "*";
105 }
106 if (isset($_GET['search'])){
107 $s= validate(mb_substr($_GET['search'], 0, 1, "UTF8"))."*";
108 if ($s == "**"){
109 $s= "*";
110 }
111 $this->search_for= $s;
112 $phonefilter['search_for']= $s;
113 }
114 register_global("phonefilter", $phonefilter);
116 /* Perform actions with CTI hook */
117 if (isset($_GET['target'])
118 && isset($_GET['dial'])
119 && isset($this->config->current['CTIHOOK'])){
121 $dialmode= $_GET['dial'];
122 if ($dialmode == "telephoneNumber" ||
123 $dialmode == "mobile" ||
124 $dialmode == "homePhone"){
126 /* Get target */
127 $ldap= $this->config->get_ldap_link();
128 $ldap->cat(base64_decode($_GET['target']), array('telephoneNumber', 'mobile', 'homePhone'));
129 $attrs= $ldap->fetch();
130 if (isset($attrs["$dialmode"])){
131 $target= $attrs[$dialmode][0];
132 } else {
133 $target= "";
134 }
136 /* Get source */
137 $ui= get_userinfo();
138 $ldap->cat($ui->dn, array('telephoneNumber'));
139 $attrs= $ldap->fetch();
140 if (isset($attrs["telephoneNumber"])){
141 $source= $attrs['telephoneNumber'][0];
142 } else {
143 $source= "";
144 }
146 /* Save to session */
147 $_SESSION['source']= $source;
148 $_SESSION['target']= $target;
150 /* Perform call */
151 if ($target != "" && $source != ""){
152 $smarty->assign("phone_image", get_template_path('images/phone.png'));
153 $smarty->assign("dial_info", sprintf(_("Dial from %s to %s now?"), "<b style='font-size:22px; color:red'>".$source."</b>", "<b style='font-size:22px;color:red'>".$target."</b>"));
154 return($smarty->fetch(get_template_path('dial.tpl', TRUE)));
155 return;
156 } else {
157 print_red (_("You have no personal phone number set. Please change that in order to perform direct dials."));
158 }
159 }
161 }
163 /* Finally dial */
164 if (isset($_POST['dial']) && isset($_SESSION['source']) && isset($_SESSION['target'])){
165 exec ($this->config->current['CTIHOOK']." '".$_SESSION['source']."' '".$_SESSION['target']."'", $dummy, $retval);
166 unset($_SESSION['source']);
167 unset($_SESSION['target']);
168 }
170 /* Delete entry? */
171 if (isset($_POST['delete_entry_confirm'])){
173 /* Some nice guy may send this as POST, so we've to check
174 for the permissions again. */
175 if (chkacl($this->acl, "delete") == ""){
177 /* Delete request is permitted, perform LDAP action */
178 $ldap= $this->config->get_ldap_link();
179 $ldap->rmdir ($this->dn);
180 show_ldap_error($ldap->get_error(), _("Removing addressbook entry failed"));
181 gosa_log ("Address book object'".$this->dn."' has been removed");
183 } else {
185 /* Normally this shouldn't be reached, send some extra
186 logs to notify the administrator */
187 print_red (_("You are not allowed to delete this entry!"));
188 gosa_log ("Warning: '".$this->ui->uid."' tried to trick address book deletion.");
189 }
191 /* Remove lock file after successfull deletion */
192 del_lock ($this->dn);
194 /* Clean up */
195 if (isset($_SESSION['saved_start'])){
196 $_GET['start']= $_SESSION['saved_start'];
197 }
198 unset($_SESSION['show_info']);
199 unset($_SESSION['saved_start']);
200 }
202 /* Delete entry? */
203 if (isset($_POST['delete_cancel'])){
204 del_lock ($this->dn);
205 }
207 /* Save address entry? */
208 if (isset($_POST['save'])){
209 $this->save_object();
210 $this->storage_base= $_POST['storage_base'];
212 /* Perform checks */
213 $message= $this->check ();
215 /* No errors, save object */
216 if (count ($message) == 0){
217 $this->save();
218 gosa_log ("Addressbook object '".$this->dn."' has been saved");
220 /* Clean up */
221 if (isset($_SESSION['saved_start'])){
222 $_GET['start']= $_SESSION['saved_start'];
223 }
224 $_SESSION['show_info']= $this->dn;
225 unset($_SESSION['saved_start']);
226 } else {
227 /* Errors found, show message */
228 show_errors ($message);
229 }
230 }
232 /* Close info window */
233 if (isset($_GET['close']) || isset($_POST['cancel'])){
234 if (isset($_SESSION['saved_start'])){
235 $_GET['start']= $_SESSION['saved_start'];
236 }
237 unset($_SESSION['show_info']);
238 unset($_SESSION['saved_start']);
239 }
241 /* Start address book edit mode? */
242 if (isset($_GET['global'])){
243 if (!isset($_SESSION['saved_start']) && isset($_GET['start'])){
244 $_SESSION['saved_start']= $_GET['start'];
245 }
246 switch ($_GET['global']){
247 case "add":
248 $this->dn= "new";
249 $this->orig_cn= "";
251 /* Clean values */
252 foreach ($this->attributes as $name){
253 $this->$name= "";
254 }
255 $this->storage_base= $this->config->current["BASE"];
256 break;
257 case "edit":
258 /* Clean values */
259 foreach ($this->attributes as $name){
260 $this->$name= "";
261 }
262 $this->dn= $_SESSION['show_info'];
263 $this->load();
264 $this->orig_cn= $this->cn;
265 break;
266 case "remove":
267 $this->dn= $_SESSION['show_info'];
268 $this->load();
269 /* Load permissions for selected 'dn' and check if
270 we're allowed to remove this 'dn' */
271 if (chkacl($this->acl, "delete") == ""){
273 /* Check locking, save current plugin in 'back_plugin', so
274 the dialog knows where to return. */
275 if (($user= get_lock($this->dn)) != ""){
276 return(gen_locked_message ($user, $this->dn));
277 }
279 /* Lock the current entry, so nobody will edit it during deletion */
280 $ui= get_userinfo();
281 add_lock ($this->dn, $ui->dn);
282 $smarty->assign("info", sprintf(_("You're about to delete the entry %s."), $this->dn));
283 return($smarty->fetch(get_template_path('remove.tpl', TRUE)));
284 } else {
286 /* Obviously the user isn't allowed to delete. Show message and
287 clean session. */
288 print_red (_("You are not allowed to delete this entry!"));
289 }
290 }
291 $_SESSION['show_info']= "ADD";
292 }
294 /* Open info window */
295 if (isset($_GET['show'])){
296 if (!isset($_SESSION['saved_start'])){
297 $_SESSION['saved_start']= $_GET['start'];
298 }
299 $_SESSION['show_info']= base64_decode($_GET['show']);
300 }
302 /* Get ldap link / build filter */
303 $ldap= $this->config->get_ldap_link();
304 $this->telephone_list= array ();
306 /* Assemble bases */
307 $bases= array();
308 $filter= "";
309 if ($phonefilter['global'] == "checked"){
310 $bases[]= preg_replace("/".$this->config->current['BASE']."/", "dc=addressbook,".$this->config->current['BASE'], $this->search_base);
311 } else {
312 $filter= '(objectClass=gosaAccount)';
313 }
314 if ($phonefilter['organizational'] == "checked"){
315 $bases[]= $this->search_base;
316 }
317 foreach ($bases as $base){
318 $ldap->cd ($base);
319 if ($phonefilter['object_type'] == '*'){
320 $ldap->search ("(&(objectClass=person)$filter(!(objectClass=gosaUserTemplate))(!(uid=*$))". //array
321 "(|(uid=$s)(homePhone=$s)(telephoneNumber=$s)".
322 "(facsimileTelephoneNumber=$s)(mobile=$s)(givenName=$s)(sn=$s)))", array("sn", "givenName", "telephoneNumber", "facsimileTelephoneNumber", "mobile", "homePhone", "uid", "mail", "cn"));
323 } else {
324 $ldap->search ("(&$filter(!(uid=*$))(!(objectClass=gosaUserTemplate))". //array
325 "(".$phonefilter['object_type']."=$s))", array("sn", "givenName", "telephoneNumber", "facsimileTelephoneNumber", "mobile", "homePhone", "uid", "mail", "cn"));
326 }
328 /* Build current list, error reporting is off, because many of the
329 objects may not be defined after LDAP queries. Asking for presence
330 first is too much overhead. */
331 error_reporting(0);
336 /* Walk through LDAP results */
337 while ($attrs= $ldap->fetch()){
339 #! hickert
340 /* prevent empty vaiables */
341 foreach($this->attributes as $atr) {
342 if(!isset($attrs[$atr][0])) {
343 $attrs[$atr][0] = "";
344 }
345 }
346 if(!isset($_GET['start'])) $_GET['start']="";
349 /* Only show lines that have set any mail or phone informations */
350 if (isset($attrs['telephoneNumber'][0]) ||
351 isset($attrs['facsimileTelephoneNumber'][0]) ||
352 isset($attrs['mobile'][0]) ||
353 isset($attrs['homePhone'][0]) ||
354 isset($attrs['mail'][0])){
356 $this->telephone_list[$attrs['sn'][0].$attrs['dn']]=
358 "<td class=\"phonelist\" title=\"".$attrs['sn'][0].", ".$attrs['givenName'][0]."\" onClick='location.href=\"main.php?plug=".validate($_GET['plug'])."&start=".validate($_GET['start'])."&show=".base64_encode($attrs['dn'])."\"'><a style='vertical-align:middle;' href=\"main.php?plug=".validate($_GET['plug'])."&start=".validate($_GET['start'])."&show=".base64_encode($attrs['dn'])."\">".$attrs['sn'][0].", ".$attrs['givenName'][0].
359 "</a>
360 </td>
361 <td title=\""._("Dial")." ".$attrs['telephoneNumber'][0]."\">
362 <a style='vertical-align:middle;' href=\"main.php?plug=".validate($_GET['plug'])."&dial=telephoneNumber&start=".validate($_GET['start'])."&target=".base64_encode($attrs['dn'])."\">".$attrs['telephoneNumber'][0]."
363 </a>
364 </td>
365 <td title=\"".$attrs['facsimileTelephoneNumber'][0]."\">
366 ".$attrs['facsimileTelephoneNumber'][0]."
367 </td>
368 <td title=\""._("Dial")." ".$attrs['mobile'][0]."\">
369 <a style='vertical-align:middle;' href=\"main.php?plug=".validate($_GET['plug'])."&dial=mobile&start=".validate($_GET['start'])."&target=".base64_encode($attrs['dn'])."\">".$attrs['mobile'][0]."
370 </a>
371 </td>
372 <td title=\""._("Dial")." ".$attrs['homePhone'][0]."\">
373 <a style='vertical-align:middle;' href=\"main.php?plug=".validate($_GET['plug'])."&dial=homePhone&start=".validate($_GET['start'])."&target=".base64_encode($attrs['dn'])."\">".$attrs['homePhone'][0]."
374 </a>
375 </td>
376 <td>
377 <a href=\"getvcard.php?dn=".base64_encode($attrs['dn'])."\">
378 <img align=\"top\" border=0 src=\"images/save.png\" alt=\"vcf\" title=\"".sprintf(_("Save contact for %s as vcard"), $attrs['givenName'][0]." ".$attrs['sn'][0])."\">
379 </a>";
381 if (isset($attrs['mail'])){
382 $dest= sprintf(_("Send mail to %s"), $attrs['mail'][0]);
383 $this->telephone_list[$attrs['sn'][0].$attrs['dn']].=
385 "<a href=\"mailto:".htmlentities($attrs['mail'][0])."\">".
386 "<img align=\"top\" border=0 src=\"images/mailto.png\" alt=\"vcf\" title=\"$dest\"></a>";
387 }
388 $this->telephone_list[$attrs['sn'][0].$attrs['dn']].= "</td>";
389 }
390 }
391 error_reporting(E_ALL);
392 }
394 /* Sort up list */
395 ksort ($this->telephone_list);
396 reset ($this->telephone_list);
398 /* Fill template variables */
399 $smarty->assign("search_for", $this->search_for);
400 $smarty->assign("object_type", $this->object_type);
401 $smarty->assign("deplist", $this->config->idepartments);
402 $smarty->assign("depselect", $this->search_base);
403 $smarty->assign("global", $phonefilter['global']);
404 $smarty->assign("organizational", $phonefilter['organizational']);
405 $smarty->assign("search_image", get_template_path('images/search.png'));
406 $smarty->assign("obj_image", get_template_path('images/list_ogroup.png'));
407 $smarty->assign("tree_image", get_template_path('images/tree.png'));
408 $smarty->assign("infoimage", get_template_path('images/info.png'));
409 $smarty->assign("actionimage", get_template_path('images/action.png'));
410 $smarty->assign("launchimage", get_template_path('images/launch.png'));
412 /* Generate alphabet */
413 $alphabet= generate_alphabet();
415 /* Build list output */
416 $output= "";
417 $mod= 0;
419 #! hickert
420 if(!isset($_SESSION['show_info'])) $smarty->assign("show_info", "");;
422 if (isset($_SESSION['show_info'])){
423 $range= 4;
424 $smarty->assign("show_info", "1");
425 $smarty->assign("url", "main.php?plug=".validate($_GET['plug'])."&close=1");
427 switch ($_SESSION['show_info']){
429 case "ADD":
430 $smarty->assign ('storage_base', $this->storage_base);
431 $smarty->assign ('address_info',
432 get_template_path('address_edit.tpl', TRUE));
433 break;
435 default:
436 $smarty->assign ('address_info',
437 get_template_path('address_info.tpl', TRUE));
438 break;
439 }
441 /* Fill variables from LDAP */
442 if ($_SESSION['show_info'] != "ADD"){
443 $ldap->cat($_SESSION['show_info'], $this->attributes);
444 $info= $ldap->fetch();
445 }
446 foreach ($this->attributes as $name){
447 if ($_SESSION['show_info'] != "ADD" && isset($info["$name"][0])){
448 error_reporting(0);
449 /* Special treatment for phone attributes */
450 if ($name == "mobile" ||
451 $name == "homePhone" ||
452 $name == "telephoneNumber"){
453 $smarty->assign("info_$name",
454 "<a title=\""._("Dial")." ".$info["$name"][0]."\" href=\"main.php?plug=".validate($_GET['plug'])."&dial=$name&start=".validate($_GET['start'])."&target=".base64_encode($_SESSION['show_info'])."\">".$info["$name"][0]."</a>");
455 } else {
456 $smarty->assign("info_$name", preg_replace("/\n/", "<br>", $info["$name"][0]));
457 }
458 error_reporting(E_ALL);
459 } elseif ($_SESSION['show_info'] == "ADD" && isset($this->$name)) {
460 $smarty->assign("info_$name", $this->$name);
461 } else {
462 $smarty->assign("info_$name", "-");
463 }
464 }
465 if (preg_match("/,dc=addressbook,/", $_SESSION['show_info'])){
466 $storage= _("global addressbook");
467 $smarty->assign("internal", 0);
468 } else {
469 $storage= _("user database");
470 $smarty->assign("internal", 1);
471 }
472 if ($_SESSION['show_info'] != "ADD"){
473 $smarty->assign("storage_info", sprintf(_("Contact stored in '%s'"), $storage));
474 } else {
475 $smarty->assign("storage_info", _("Creating new entry in"));
476 }
477 } else {
480 if(isset($_POST['EntryPerPage'])){
481 $this->range = $_POST['EntryPerPage'];
482 }
483 $range = $this->range;
484 $smarty->assign("internal", 1);
485 }
486 if (isset($_GET['start'])){
487 $this->start= validate($_GET['start']);
488 }
489 foreach ($this->telephone_list as $val){
490 if ($mod < $this->start) {
491 $mod++;
492 continue;
493 }
494 if ($mod >= ($this->start + $range)){
495 $mod++;
496 break;
497 }
498 if ( ($mod++) & 1){
499 $col= "style=\"background-color: #ECECEC;\"";
500 } else {
501 $col= "style=\"background-color: #F5F5F5;\"";
502 }
503 $output.= "<tr $col>\n$val</tr>\n";
504 }
506 $smarty->assign("search_result", $output);
507 $smarty->assign("apply", apply_filter());
508 $smarty->assign("alphabet", $alphabet);
509 if($range < 20){
510 $smarty->assign("range_selector", range_selector(count($this->telephone_list), $this->start, $range));
511 }else{
512 $smarty->assign("range_selector", range_selector(count($this->telephone_list), $this->start, $range, "EntryPerPage"));
513 }
514 $tmp= array("*" => _("All"), "sn" => _("Name"), "givenName" => _("Given name"),
515 "telephoneNumber" => _("Work phone"), "mobile" => _("Cell phone"),
516 "homePhone" => _("Home phone"), "uid" => _("User ID"));
517 natsort($tmp);
518 $smarty->assign("objlist", $tmp);
520 /* Show main page */
521 $smarty->assign ('personal_image', get_template_path('images/addr_personal.png'));
522 $smarty->assign ('home_image', get_template_path('images/addr_home.png'));
523 $smarty->assign ('company_image', get_template_path('images/addr_company.png'));
524 $smarty->assign ('add_image', get_template_path('images/editpaste.png'));
525 $smarty->assign ('edit_image', get_template_path('images/edit.png'));
526 $smarty->assign ('delete_image', get_template_path('images/editdelete.png'));
527 return($smarty->fetch(get_template_path('contents.tpl', TRUE)));
528 }
530 function save_object()
531 {
532 plugin::save_object();
533 foreach($this->attributes as $attr){
534 if(isset($_POST[$attr])){
535 $this->$attr = $_POST[$attr];
536 }
537 }
538 }
540 function check()
541 {
542 /* Call common method to give check the hook */
543 $message= plugin::check();
545 /* must: sn, givenName */
546 if ($this->sn == ""){
547 $message[]= _("The required field 'Name' is not set.");
548 return ($message);
549 }
550 if ($this->givenName == ""){
551 $message[]= _("The required field 'Given name' is not set.");
552 return ($message);
553 }
555 /* Check for valid name definition */
556 if (preg_match ("/[\\\\]/", $this->sn)){
557 $message[]= _("The field 'Name' contains invalid characters.");
558 }
559 if (preg_match ("/[\\\\]/", $this->givenName)){
560 $message[]= _("The field 'Given name' contains invalid characters.");
561 }
563 /* Check phone numbers */
564 if (!is_phone_nr($this->homePhone)){
565 $message[]= _("The field 'Phone' contains an invalid phone number.");
566 }
567 if (!is_phone_nr($this->telephoneNumber)){
568 $message[]= _("The field 'Phone' contains an invalid phone number.");
569 }
570 if (!is_phone_nr($this->facsimileTelephoneNumber)){
571 $message[]= _("The field 'Fax' contains an invalid phone number.");
572 }
573 if (!is_phone_nr($this->mobile)){
574 $message[]= _("The field 'Mobile' contains an invalid phone number.");
575 }
576 if (!is_phone_nr($this->pager)){
577 $message[]= _("The field 'Pager' contains an invalid phone number.");
578 }
580 /* Check for reserved characers */
581 if (preg_match ('/[,+"<>;]/', $this->givenName)){
582 $message[]= _("The field 'Given name' contains invalid characters.");
583 }
584 if (preg_match ('/[,+"<>;]/', $this->sn)){
585 $message[]= _("The field 'Name' contains invalid characters.");
586 }
588 /* Check mail */
589 if (!is_email($this->mail)){
590 $message[]= _("Please enter a valid email address in 'Primary address' field.");
591 }
593 /* Assemble cn/dn */
594 $this->cn= $this->givenName." ".$this->sn;
595 if ($this->orig_cn != $this->cn || $this->storage_base != $this->orig_storage_base){
596 $this->new_dn= $this->create_unique_dn("cn", preg_replace("/,*".$this->config->current['BASE']."$/", "", $this->storage_base).",dc=addressbook,".$this->config->current['BASE']);
597 if ($this->new_dn == "none"){
598 $message[]= _("Cannot create a unique DN for your entry. Please fill more formular fields.");
599 return ($message);
600 }
601 } else {
602 $this->new_dn= $this->dn;
603 }
605 if ($_SESSION['show_info'] == "ADD" && chkacl($this->acl, "add") != ""){
606 $message[]= _("You have no permissions to create or modify a global address book entry.");
607 }
609 return ($message);
610 }
613 function load()
614 {
615 /* Load base attributes */
616 plugin::plugin ($this->config, $this->dn);
617 $this->storage_base= preg_replace('/^[^,]+,/', '', preg_replace('/dc=addressbook,/', '', $this->dn));
618 }
621 function save()
622 {
623 /* First use parents methods to do some basic fillup in $this->attrs */
624 plugin::save ();
626 $this->attrs['cn']= $this->cn;
627 $this->attrs['displayName']= $this->givenName." ".$this->sn;
629 /* Move entry if it got another name... */
630 if ($this->dn != "new" && $this->dn != $this->new_dn){
631 $this->move($this->dn, $this->new_dn);
632 }
633 $this->dn= $this->new_dn;
635 /* Save data. Using 'modify' implies that the entry is already present, use 'add' for
636 new entries. So do a check first... */
637 $ldap= $this->config->get_ldap_link();
638 $ldap->cat ($this->dn,array('dn'));
639 if ($ldap->fetch()){
640 $mode= "modify";
641 } else {
642 $mode= "add";
643 $ldap->cd($this->config->current['BASE']);
644 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $this->dn));
645 }
647 /* Finally write data with selected 'mode' */
648 $ldap->cd ($this->dn);
649 $this->cleanup();
650 $ldap->$mode ($this->attrs);
651 if (show_ldap_error($ldap->get_error(), _("Saving addressbook entry failed"))){
652 return (1);
653 }
654 }
656 }
657 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
658 ?>