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 $sn= "";
21 var $cn= "";
22 var $givenName= "";
23 var $mail= "";
24 var $title= "";
25 var $personalTitle= "";
26 var $initials= "";
27 var $homePostalAddress= "";
28 var $homePhone= "";
29 var $mobile= "";
30 var $o= "";
31 var $postalAddress= "";
32 var $l= "";
33 var $postalCode= "";
34 var $st= "";
35 var $ou= "";
36 var $telephoneNumber= "";
37 var $facsimileTelephoneNumber= "";
38 var $pager= "";
40 /* attribute list for save action */
41 var $attributes= array("sn", "givenName", "mail", "title",
42 "initials", "homePostalAddress", "displayName",
43 "homePhone", "mobile", "o", "postalAddress", "l",
44 "postalCode", "st", "ou", "telephoneNumber",
45 "facsimileTelephoneNumber", "pager");
46 var $objectclasses= array("top", "person", "organizationalPerson", "inetOrgPerson");
48 function addressbook ($config, $dn= NULL)
49 {
50 /* Include config object */
51 $this->config= $config;
53 #FIXME: ACL is set to default for testing
54 $this->acl= "#all#";
56 /* Get global filter config */
57 if (!is_global("phonefilter")){
58 $ui= get_userinfo();
59 $base= get_base_from_people($ui->dn);
60 $phonefilter= array("search_base" => $base,
61 "organizational" => "checked",
62 "global" => "checked",
63 "search_for" => "*",
64 "object_type" => "*");
65 register_global("phonefilter", $phonefilter);
66 }
67 }
69 function execute()
70 {
71 $smarty= get_smarty();
73 /* Save formular information */
74 $phonefilter= get_global("phonefilter");
75 foreach( array("search_for", "search_base", "object_type") as $type){
76 if (isset($_POST[$type])){
77 $phonefilter[$type]= $_POST[$type];
78 }
79 $this->$type= $phonefilter[$type];
80 }
81 if (isset($_POST['search_base'])){
82 foreach( array("organizational", "global") as $type){
83 if (isset($_POST[$type])){
84 $phonefilter[$type]= "checked";
85 } else {
86 $phonefilter[$type]= "";
87 }
88 }
89 }
91 /* Search string */
92 $s= $phonefilter['search_for'];
93 if ($s == "") {
94 $s= "*";
95 }
96 if (isset($_GET['search'])){
97 $s= validate(mb_substr($_GET['search'], 0, 1, "UTF8"))."*";
98 if ($s == "**"){
99 $s= "*";
100 }
101 $this->search_for= $s;
102 $phonefilter['search_for']= $s;
103 }
104 register_global("phonefilter", $phonefilter);
106 /* Perform actions with CTI hook */
107 if (isset($_GET['target'])
108 && isset($_GET['dial'])
109 && isset($this->config->current['CTIHOOK'])){
111 $dialmode= $_GET['dial'];
112 if ($dialmode == "telephoneNumber" ||
113 $dialmode == "mobile" ||
114 $dialmode == "homePhone"){
116 /* Get target */
117 $ldap= $this->config->get_ldap_link();
118 $ldap->cat(base64_decode($_GET['target']));
119 $attrs= $ldap->fetch();
120 if (isset($attrs["$dialmode"])){
121 $target= $attrs[$dialmode][0];
122 } else {
123 $target= "";
124 }
126 /* Get source */
127 $ui= get_userinfo();
128 $ldap->cat($ui->dn);
129 $attrs= $ldap->fetch();
130 if (isset($attrs["telephoneNumber"])){
131 $source= $attrs['telephoneNumber'][0];
132 } else {
133 $source= "";
134 }
136 /* Save to session */
137 $_SESSION['source']= $source;
138 $_SESSION['target']= $target;
140 /* Perform call */
141 if ($target != "" && $source != ""){
142 $smarty->assign("phone_image", get_template_path('images/phone.png'));
143 $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>"));
144 return($smarty->fetch(get_template_path('dial.tpl', TRUE)));
145 return;
146 } else {
147 print_red (_("You have no personal phone number set. Please change that in order to perform direct dials."));
148 }
149 }
151 }
153 /* Finally dial */
154 if (isset($_POST['dial']) && isset($_SESSION['source']) && isset($_SESSION['target'])){
155 exec ($this->config->current['CTIHOOK']." '".$_SESSION['source']."' '".$_SESSION['target']."'", $dummy, $retval);
156 unset($_SESSION['source']);
157 unset($_SESSION['target']);
158 }
160 /* Delete entry? */
161 if (isset($_POST['delete_entry_confirm'])){
163 /* Some nice guy may send this as POST, so we've to check
164 for the permissions again. */
165 if (chkacl($this->acl, "delete") == ""){
167 /* Delete request is permitted, perform LDAP action */
168 $ldap= $this->config->get_ldap_link();
169 $ldap->rmdir ($this->dn);
170 gosa_log ("Address book object'".$this->dn."' has been removed");
172 } else {
174 /* Normally this shouldn't be reached, send some extra
175 logs to notify the administrator */
176 print_red (_("You are not allowed to delete this entry!"));
177 gosa_log ("Warning: '".$this->ui->uid."' tried to trick address book deletion.");
178 }
180 /* Remove lock file after successfull deletion */
181 del_lock ($this->dn);
183 /* Clean up */
184 if (isset($_SESSION['saved_start'])){
185 $_GET['start']= $_SESSION['saved_start'];
186 }
187 unset($_SESSION['show_info']);
188 unset($_SESSION['saved_start']);
189 }
191 /* Delete entry? */
192 if (isset($_POST['delete_cancel'])){
193 del_lock ($this->dn);
194 }
196 /* Save address entry? */
197 if (isset($_POST['save'])){
198 $this->save_object();
199 $this->storage_base= $_POST['storage_base'];
201 /* Perform checks */
202 $message= $this->check ();
204 /* No errors, save object */
205 if (count ($message) == 0){
206 $this->save();
207 gosa_log ("Addressbook object '".$this->dn."' has been saved");
209 /* Clean up */
210 if (isset($_SESSION['saved_start'])){
211 $_GET['start']= $_SESSION['saved_start'];
212 }
213 $_SESSION['show_info']= $this->dn;
214 unset($_SESSION['saved_start']);
215 } else {
216 /* Errors found, show message */
217 show_errors ($message);
218 }
219 }
221 /* Close info window */
222 if (isset($_GET['close']) || isset($_POST['cancel'])){
223 if (isset($_SESSION['saved_start'])){
224 $_GET['start']= $_SESSION['saved_start'];
225 }
226 unset($_SESSION['show_info']);
227 unset($_SESSION['saved_start']);
228 }
230 /* Start address book edit mode? */
231 if (isset($_GET['global'])){
232 if (!isset($_SESSION['saved_start']) && isset($_GET['start'])){
233 $_SESSION['saved_start']= $_GET['start'];
234 }
235 switch ($_GET['global']){
236 case "add":
237 $this->dn= "new";
238 $this->orig_cn= "";
240 /* Clean values */
241 foreach ($this->attributes as $name){
242 $this->$name= "";
243 }
244 $this->storage_base= $this->config->current["BASE"];
245 break;
246 case "edit":
247 /* Clean values */
248 foreach ($this->attributes as $name){
249 $this->$name= "";
250 }
251 $this->dn= $_SESSION['show_info'];
252 $this->load();
253 $this->orig_cn= $this->cn;
254 break;
255 case "remove":
256 $this->dn= $_SESSION['show_info'];
258 /* Load permissions for selected 'dn' and check if
259 we're allowed to remove this 'dn' */
260 $ui= get_userinfo();
261 $dn= preg_replace("/,dc=addressbook,/", "", $this->dn);
262 $acl= get_permissions ($dn, $ui->subtreeACL);
263 $this->acl= get_module_permission($acl, "global-addressbook", $dn);
264 if (chkacl($this->acl, "delete") == ""){
266 /* Check locking, save current plugin in 'back_plugin', so
267 the dialog knows where to return. */
268 if (($user= get_lock($this->dn)) != ""){
269 return(gen_locked_message ($user, $this->dn));
270 }
272 /* Lock the current entry, so nobody will edit it during deletion */
273 add_lock ($this->dn, $ui->dn);
274 $smarty->assign("info", sprintf(_("You're about to delete the entry %s."), $this->dn));
275 return($smarty->fetch(get_template_path('remove.tpl', TRUE)));
276 } else {
278 /* Obviously the user isn't allowed to delete. Show message and
279 clean session. */
280 print_red (_("You are not allowed to delete this entry!"));
281 }
282 }
283 $_SESSION['show_info']= "ADD";
284 }
286 /* Open info window */
287 if (isset($_GET['show'])){
288 if (!isset($_SESSION['saved_start'])){
289 $_SESSION['saved_start']= $_GET['start'];
290 }
291 $_SESSION['show_info']= base64_decode($_GET['show']);
292 }
294 /* Get ldap link / build filter */
295 $ldap= $this->config->get_ldap_link();
296 $this->telephone_list= array ();
298 /* Assemble bases */
299 $bases= array();
300 $filter= "";
301 if ($phonefilter['global'] == "checked"){
302 $bases[]= preg_replace("/".$this->config->current['BASE']."/", "dc=addressbook,".$this->config->current['BASE'], $this->search_base);
303 } else {
304 $filter= '(objectClass=gosaAccount)';
305 }
306 if ($phonefilter['organizational'] == "checked"){
307 $bases[]= $this->search_base;
308 }
309 foreach ($bases as $base){
310 $ldap->cd ($base);
311 if ($phonefilter['object_type'] == '*'){
312 $ldap->search ("(&(objectClass=person)$filter(!(objectClass=gosaUserTemplate))".
313 "(|(uid=$s)(homePhone=$s)(telephoneNumber=$s)".
314 "(facsimileTelephoneNumber=$s)(mobile=$s)(givenName=$s)(sn=$s)))", array("sn", "givenName", "telephoneNumber", "facsimileTelephoneNumber", "mobile", "homePhone", "uid", "mail", "cn"));
315 } else {
316 $ldap->search ("(&$filter(!(objectClass=gosaUserTemplate))".
317 "(".$phonefilter['object_type']."=$s))", array("sn", "givenName", "telephoneNumber", "facsimileTelephoneNumber", "mobile", "homePhone", "uid", "mail", "cn"));
318 }
320 /* Build current list, error reporting is off, because many of the
321 objects may not be defined after LDAP queries. Asking for presence
322 first is too much overhead. */
323 error_reporting(0);
325 /* Walk through LDAP results */
326 while ($attrs= $ldap->fetch()){
328 /* Only show lines that have set any mail or phone informations */
329 if (isset($attrs['telephoneNumber'][0]) ||
330 isset($attrs['facsimileTelephoneNumber'][0]) ||
331 isset($attrs['mobile'][0]) ||
332 isset($attrs['homePhone'][0]) ||
333 isset($attrs['mail'][0])){
335 $this->telephone_list[$attrs['sn'][0].$attrs['dn']]=
336 "<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 href=main.php?plug=".validate($_GET['plug'])."&start=".validate($_GET['start'])."&show=".base64_encode($attrs['dn']).">".$attrs['sn'][0].", ".$attrs['givenName'][0]."</a></td>".
337 "<td title=\""._("Dial")." ".$attrs['telephoneNumber'][0]."\"><a href=main.php?plug=".validate($_GET['plug'])."&dial=telephoneNumber&start=".validate($_GET['start'])."&target=".base64_encode($attrs['dn']).">".$attrs['telephoneNumber'][0]."</a></td>".
338 "<td title=\"".$attrs['facsimileTelephoneNumber'][0]."\">".$attrs['facsimileTelephoneNumber'][0]."</td>".
339 "<td title=\""._("Dial")." ".$attrs['mobile'][0]."\"><a href=main.php?plug=".validate($_GET['plug'])."&dial=mobile&start=".validate($_GET['start'])."&target=".base64_encode($attrs['dn']).">".$attrs['mobile'][0]."</a></td>".
340 "<td title=\""._("Dial")." ".$attrs['homePhone'][0]."\"><a href=main.php?plug=".validate($_GET['plug'])."&dial=homePhone&start=".validate($_GET['start'])."&target=".base64_encode($attrs['dn']).">".$attrs['homePhone'][0]."</a></td><td>".
341 "<a href=\"getvcard.php?dn=".base64_encode($attrs['dn'])."\">".
342 "<img align=\"top\" border=0 src=\"images/save.png\"".
343 "alt=\"vcf\" title=\"".sprintf(_("Save contact for %s as vcard"), $attrs['givenName'][0]." ".$attrs['sn'][0])."\"></a>";
345 if (isset($attrs['mail'])){
346 $dest= sprintf(_("Send mail to %s"), $attrs['mail'][0]);
347 $this->telephone_list[$attrs['sn'][0].$attrs['dn']].=
348 "<a href=\"mailto:".$attrs['givenName'][0]." ".$attrs['sn'][0]." <".$attrs['mail'][0].">\">".
349 "<img align=\"top\" border=0 src=\"images/mailto.png\"".
350 "alt=\"vcf\" title=\"$dest\"></a>";
351 }
352 $this->telephone_list[$attrs['sn'][0].$attrs['dn']].= "</td>";
353 }
354 }
355 error_reporting(E_ALL);
356 }
358 /* Sort up list */
359 ksort ($this->telephone_list);
360 reset ($this->telephone_list);
362 /* Fill template variables */
363 $smarty->assign("search_for", $this->search_for);
364 $smarty->assign("object_type", $this->object_type);
365 $smarty->assign("deplist", $this->config->idepartments);
366 $smarty->assign("depselect", $this->search_base);
367 $smarty->assign("global", $phonefilter['global']);
368 $smarty->assign("organizational", $phonefilter['organizational']);
369 $smarty->assign("search_image", get_template_path('images/search.png'));
370 $smarty->assign("tree_image", get_template_path('images/tree.png'));
371 $smarty->assign("infoimage", get_template_path('images/info.png'));
372 $smarty->assign("actionimage", get_template_path('images/action.png'));
373 $smarty->assign("launchimage", get_template_path('images/launch.png'));
375 /* Generate alphabet */
376 $alphabet= generate_alphabet();
378 /* Build list output */
379 $output= "";
380 $mod= 0;
381 if (isset($_SESSION['show_info'])){
382 $range= 4;
383 $smarty->assign("show_info", "1");
384 $smarty->assign("url", "main.php?plug=".validate($_GET['plug'])."&close=1");
386 switch ($_SESSION['show_info']){
388 case "ADD":
389 $smarty->assign ('storage_base', $this->storage_base);
390 $smarty->assign ('address_info',
391 get_template_path('address_edit.tpl', TRUE));
392 break;
394 default:
395 $smarty->assign ('address_info',
396 get_template_path('address_info.tpl', TRUE));
397 break;
398 }
400 /* Fill variables from LDAP */
401 # FIXME: Missing ACL support for addressbook yet
402 if ($_SESSION['show_info'] != "ADD"){
403 $ldap->cat($_SESSION['show_info']);
404 $info= $ldap->fetch();
405 }
406 foreach ($this->attributes as $name){
407 if ($_SESSION['show_info'] != "ADD" && isset($info["$name"][0])){
408 error_reporting(0);
409 /* Special treatment for phone attributes */
410 if ($name == "mobile" ||
411 $name == "homePhone" ||
412 $name == "telephoneNumber"){
413 $smarty->assign("info_$name",
414 "<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>");
415 } else {
416 $smarty->assign("info_$name", preg_replace("/\n/", "<br>", $info["$name"][0]));
417 }
418 error_reporting(E_ALL);
419 } elseif ($_SESSION['show_info'] == "ADD") {
420 $smarty->assign("info_$name", $this->$name);
421 } else {
422 $smarty->assign("info_$name", "-");
423 }
424 }
425 if (preg_match("/,dc=addressbook,/", $_SESSION['show_info'])){
426 $storage= _("global addressbook");
427 } else {
428 $storage= _("organizations user database");
429 $smarty->assign("internal", 1);
430 }
431 if ($_SESSION['show_info'] != "ADD"){
432 $smarty->assign("storage_info", sprintf(_("Contact stored in %s"), $storage));
433 } else {
434 $smarty->assign("storage_info", _("Creating new entry in"));
435 }
436 } else {
437 $range= 20;
438 $smarty->assign("internal", 1);
439 }
440 if (isset($_GET['start'])){
441 $this->start= validate($_GET['start']);
442 }
443 foreach ($this->telephone_list as $val){
444 if ($mod < $this->start) {
445 $mod++;
446 continue;
447 }
448 if ($mod >= ($this->start + $range)){
449 $mod++;
450 break;
451 }
452 if ( ($mod++) & 1){
453 $col= "style=\"background-color: #ECECEC;\"";
454 } else {
455 $col= "style=\"background-color: #F5F5F5;\"";
456 }
457 $output.= "<tr $col>$val</tr>";
458 }
459 $smarty->assign("search_result", $output);
460 $smarty->assign("apply", apply_filter());
461 $smarty->assign("alphabet", $alphabet);
462 $smarty->assign("range_selector", range_selector(count($this->telephone_list), $this->start, $range));
463 $tmp= array("*" => _("All"), "sn" => _("Name"), "givenName" => _("Given name"),
464 "telephoneNumber" => _("Work phone"), "mobile" => _("Cell phone"),
465 "homePhone" => _("Home phone"), "uid" => _("User ID"));
466 natsort($tmp);
467 $smarty->assign("objlist", $tmp);
469 /* Show main page */
470 $smarty->assign ('personal_image', get_template_path('images/addr_personal.png'));
471 $smarty->assign ('home_image', get_template_path('images/addr_home.png'));
472 $smarty->assign ('company_image', get_template_path('images/addr_company.png'));
473 $smarty->assign ('add_image', get_template_path('images/editpaste.png'));
474 $smarty->assign ('edit_image', get_template_path('images/edit.png'));
475 $smarty->assign ('delete_image', get_template_path('images/editdelete.png'));
476 return($smarty->fetch(get_template_path('contents.tpl', TRUE)));
477 }
480 function check()
481 {
482 $message= array();
484 /* must: sn, givenName */
485 if ($this->sn == ""){
486 $message[]= _("The required field 'Name' is not set.");
487 return ($message);
488 }
489 if ($this->givenName == ""){
490 $message[]= _("The required field 'Given name' is not set.");
491 return ($message);
492 }
494 /* Check for valid name definition */
495 if (preg_match ("/[\\\\]/", $this->sn)){
496 $message[]= _("The field 'Name' contains invalid characters.");
497 }
498 if (preg_match ("/[\\\\]/", $this->givenName)){
499 $message[]= _("The field 'Given name' contains invalid characters.");
500 }
502 /* Check phone numbers */
503 if (!is_phone_nr($this->homePhone)){
504 $message[]= _("The field 'Phone' contains an invalid phone number.");
505 }
506 if (!is_phone_nr($this->telephoneNumber)){
507 $message[]= _("The field 'Phone' contains an invalid phone number.");
508 }
509 if (!is_phone_nr($this->facsimileTelephoneNumber)){
510 $message[]= _("The field 'Fax' contains an invalid phone number.");
511 }
512 if (!is_phone_nr($this->mobile)){
513 $message[]= _("The field 'Mobile' contains an invalid phone number.");
514 }
515 if (!is_phone_nr($this->pager)){
516 $message[]= _("The field 'Pager' contains an invalid phone number.");
517 }
519 /* Check for reserved characers */
520 if (preg_match ('/[,+"<>;]/', $this->givenName)){
521 $message[]= _("The field 'Given name' contains invalid characters.");
522 }
523 if (preg_match ('/[,+"<>;]/', $this->sn)){
524 $message[]= _("The field 'Name' contains invalid characters.");
525 }
527 /* Check mail */
528 if (!is_email($this->mail)){
529 $message[]= _("Please enter a valid email address in 'Primary address' field.");
530 }
532 /* Assemble cn/dn */
533 $this->cn= $this->givenName." ".$this->sn;
534 if ($this->orig_cn != $this->cn || $this->storage_base != $this->orig_storage_base){
535 $this->new_dn= $this->create_unique_dn("cn", preg_replace("/,*".$this->config->current['BASE']."$/", "", $this->storage_base).",dc=addressbook,".$this->config->current['BASE']);
536 if ($this->new_dn == "none"){
537 $message[]= _("Cannot create a unique DN for your entry. Please fill more formular fields.");
538 return ($message);
539 }
540 } else {
541 $this->new_dn= $this->dn;
542 }
544 $ui= get_userinfo();
545 $dn= preg_replace("/,dc=addressbook,/", "", $this->new_dn);
546 $acl= get_permissions ($dn, $ui->subtreeACL);
547 $acl= get_module_permission($acl, "global-addressbook", $this->dn);
548 if ($_SESSION['show_info'] == "ADD" && chkacl($acl, "create") != ""){
549 $message[]= _("You have no permissions to create or modify a global address book entry.");
550 }
552 return ($message);
553 }
556 function load()
557 {
558 /* Load base attributes */
559 plugin::plugin ($this->config, $this->dn);
560 $this->storage_base= preg_replace('/^[^,]+,/', '', preg_replace('/dc=addressbook,/', '', $this->dn));
561 }
564 function save()
565 {
566 /* First use parents methods to do some basic fillup in $this->attrs */
567 plugin::save ();
569 $this->attrs['cn']= $this->cn;
570 $this->attrs['displayName']= $this->givenName." ".$this->sn;
572 /* Move entry if it got another name... */
573 if ($this->dn != "new" && $this->dn != $this->new_dn){
574 $this->move($this->dn, $this->new_dn);
575 }
576 $this->dn= $this->new_dn;
578 /* Save data. Using 'modify' implies that the entry is already present, use 'add' for
579 new entries. So do a check first... */
580 $ldap= $this->config->get_ldap_link();
581 $ldap->cat ($this->dn);
582 if ($ldap->fetch()){
583 $mode= "modify";
584 } else {
585 $mode= "add";
586 $ldap->cd($this->config->current['BASE']);
587 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $this->dn));
588 }
590 /* Finally write data with selected 'mode' */
591 $ldap->cd ($this->dn);
592 $ldap->$mode ($this->attrs);
593 if (show_ldap_error($ldap->get_error())){
594 return (1);
595 }
596 }
598 }
600 ?>