From e72ec7185e03b001ae0d1c4ff972b7f93c788234 Mon Sep 17 00:00:00 2001 From: Christian Binder Date: Thu, 22 Nov 2007 08:29:16 +0000 Subject: [PATCH] Extended notification-app and egwpopup. Added winpopup-backend and email-backend. PHP5.1+ is now mandatory in trunk. For more infos look at the egw-developers list. --- calendar/inc/class.bocalupdate.inc.php | 94 +++--- etemplate/inc/class.bo_tracking.inc.php | 253 +++++++------- .../inc/class.iface_notification.inc.php | 7 +- notifications/inc/class.notification.inc.php | 313 +++++++++++++++--- .../inc/class.notification_email.inc.php | 147 ++++++++ .../inc/class.notification_popup.inc.php | 96 ++++-- .../inc/class.notification_winpopup.inc.php | 145 ++++++++ .../inc/class.uinotificationprefs.inc.php | 83 ----- notifications/inc/hook_admin.inc.php | 16 + notifications/inc/hook_after_navbar.inc.php | 4 +- notifications/inc/hook_preferences.inc.php | 10 +- notifications/inc/hook_settings.inc.php | 58 ++++ notifications/js/notificationajaxpopup.js | 35 +- notifications/setup/setup.inc.php | 2 + notifications/templates/default/config.tpl | 43 +++ .../default/images/notificationbell.gif | Bin 0 -> 1023 bytes phpgwapi/inc/class.egw_framework.inc.php | 15 + .../idots/class.idots_framework.inc.php | 40 ++- .../jerryr/class.jerryr_framework.inc.php | 49 +-- 19 files changed, 1017 insertions(+), 393 deletions(-) create mode 100644 notifications/inc/class.notification_email.inc.php create mode 100644 notifications/inc/class.notification_winpopup.inc.php delete mode 100644 notifications/inc/class.uinotificationprefs.inc.php create mode 100644 notifications/inc/hook_admin.inc.php create mode 100644 notifications/inc/hook_settings.inc.php create mode 100644 notifications/templates/default/config.tpl create mode 100644 notifications/templates/default/images/notificationbell.gif diff --git a/calendar/inc/class.bocalupdate.inc.php b/calendar/inc/class.bocalupdate.inc.php index da486d9747..9f9d1156c3 100644 --- a/calendar/inc/class.bocalupdate.inc.php +++ b/calendar/inc/class.bocalupdate.inc.php @@ -410,7 +410,7 @@ class bocalupdate extends bocal * @param array $old_event Event before the change * @param array $new_event=null Event after the change * @param int $user=0 User who started the notify, default current user - * @return mixed returncode from send-class or false on error + * @return bool true/false */ function send_update($msg_type,$to_notify,$old_event,$new_event=null,$user=0) { @@ -443,9 +443,7 @@ class bocalupdate extends bocal $GLOBALS['egw']->preferences->preferences($user); $GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(); } - $sender = $GLOBALS['egw_info']['user']['email']; - $sender_fullname = $GLOBALS['egw_info']['user']['fullname']; - + $senderid = $GLOBALS['egw_info']['user']['account_id']; $event = $msg_type == MSG_ADDED || $msg_type == MSG_MODIFIED ? $new_event : $old_event; switch($msg_type) @@ -507,13 +505,7 @@ class bocalupdate extends bocal $notify_msg = $this->cal_prefs['notifyAdded']; // use a default } $details = $this->_get_event_details($event,$action,$event_arr,$disinvited); - - if(!is_object($GLOBALS['egw']->send)) - { - $GLOBALS['egw']->send =& CreateObject('phpgwapi.send'); - } - $send = &$GLOBALS['egw']->send; - + // add all group-members to the notification, unless they are already participants foreach($to_notify as $userid => $statusid) { @@ -554,14 +546,7 @@ class bocalupdate extends bocal } $GLOBALS['egw']->accounts->get_account_name($userid,$lid,$details['to-firstname'],$details['to-lastname']); $details['to-fullname'] = $GLOBALS['egw']->common->display_fullname('',$details['to-firstname'],$details['to-lastname']); - - $to = $GLOBALS['egw']->accounts->id2name($userid,'account_email'); - if (!$to || strpos($to,'@') === false) - { - // ToDo: give an error-message - echo '

'.lang('Invalid email-address "%1" for user %2',$to,$GLOBALS['egw']->common->grab_owner_name($userid))."

\n"; - continue; - } + $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset'] = $part_prefs['common']['tz_offset']; $GLOBALS['egw_info']['user']['preferences']['common']['timeformat'] = $part_prefs['common']['timeformat']; $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'] = $part_prefs['common']['dateformat']; @@ -576,21 +561,22 @@ class bocalupdate extends bocal list($subject,$body) = explode("\n",$GLOBALS['egw']->preferences->parse_notify($notify_msg,$details),2); - $send->ClearAddresses(); - $send->ClearAttachments(); - $send->IsHTML(False); - $send->AddAddress($to); - $send->AddCustomHeader('X-eGroupWare-type: calendarupdate'); - switch($part_prefs['calendar']['update_format']) { case 'extended': $body .= "\n\n".lang('Event Details follow').":\n"; foreach($event_arr as $key => $val) { - if ($key != 'access' && $key != 'priority' && strlen($details[$key])) - { - $body .= sprintf("%-20s %s\n",$val['field'].':',$details[$key]); + if(strlen($details[$key])) { + switch($key){ + case 'access': + case 'priority': + case 'link': + break; + default: + $body .= sprintf("%-20s %s\n",$val['field'].':',$details[$key]); + break; + } } } break; @@ -599,35 +585,37 @@ class bocalupdate extends bocal $ics = ExecMethod2('calendar.boical.exportVCal',$event['id'],'2.0',$method); if ($method == 'REQUEST') { - $send->AddStringAttachment($ics, "cal.ics", "8bit", "text/calendar; method=$method"); + $attachment = array( 'string' => $ics, + 'filename' => 'cal.ics', + 'encoding' => '8bit', + 'type' => 'text/calendar; method='.$method, + ); } break; } - $send->From = $sender; - $send->FromName = $sender_fullname; - $send->Subject = $send->encode_subject($subject); - $send->Body = $body; - if ($this->debug) - { - echo "

to: $to
from: $sender_fullname <$sender>
Subject: $subject
".nl2br($body)."


\n"; + // send via notification_app + require_once(EGW_INCLUDE_ROOT. '/notifications/inc/class.notification.inc.php'); + try { + $notification = new notification(); + $notification->set_receivers(array($userid)); + $notification->set_message($body); + $notification->set_sender($senderid); + $notification->set_subject($subject); + $notification->set_links(array($details['link_arr'])); + if(is_array($attachment)) { $notification->set_attachments(array($attachment)); } + $notification->send(); } - $returncode = $send->Send(); - - // ToDo: give error-messages for all all failed sendings - - // notification via notification app. - if (version_compare("5.1.0", phpversion()) && array_key_exists('notifications',$GLOBALS['egw_info']['apps'])) { - require_once(EGW_INCLUDE_ROOT. '/notifications/inc/class.notification.inc.php'); - include(EGW_INCLUDE_ROOT. '/calendar/inc/class.php5_notification.inc.php'); + catch (Exception $exception) { + error_log('error while notifying user '.$userid.':'.$exception->getMessage()); + continue; } - } } // restore the enviroment $GLOBALS['egw_info']['user'] = $temp_user; $GLOBALS['egw']->datetime->tz_offset = 3600 * $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset']; - return $returncode; + return true; } function get_update_message($event,$added) @@ -859,13 +847,25 @@ class bocalupdate extends bocal $link; } $event_arr['link']['data'] = $details['link'] = $link; + + /* this is needed for notification-app + * notification-app creates the link individual for + * every user, so we must provide a neutral link-style + */ + $link_arr = array(); + $link_arr['menuaction'] = 'calendar.uiforms.edit'; + $link_arr['params'] = array( 'cal_id' => $event['id'], + 'date' => $eventStart_arr['full'], + ); + $link_arr['text'] = $event['title']; + $details['link_arr'] = $link_arr; + $dis = array(); foreach($disinvited as $uid) { $dis[] = $this->participant_name($uid); } $details['disinvited'] = implode(', ',$dis); - return $details; } diff --git a/etemplate/inc/class.bo_tracking.inc.php b/etemplate/inc/class.bo_tracking.inc.php index d5f2a5328e..fcb8e23d43 100644 --- a/etemplate/inc/class.bo_tracking.inc.php +++ b/etemplate/inc/class.bo_tracking.inc.php @@ -308,30 +308,6 @@ class bo_tracking } return !count($this->errors); } - - /** - * Send a popup notification via the notification app - * - * @param int $user - * @param string $message - * @return boolean true on success, false on error - */ - function popup_notification($user,$message) - { - static $is_php51; - if (is_null($is_php51)) $is_php51 = version_compare(phpversion(),'5.1.0','>='); - - if (!$is_php51) return false; - - // check if the to notifying user has rights to run the notifcation app - $ids = $GLOBALS['egw']->accounts->memberships($user,true); - $ids[] = $user; - if (!$GLOBALS['egw']->acl->get_specific_rights_for_account($ids,'run','notifications')) return false; - - if (!include_once(EGW_INCLUDE_ROOT. '/notifications/inc/class.notification.inc.php')) return false; - - return is_null(notification::notify(array($user),$message)); // return the exeception on error - } /** * Sending a notification to the given email-address @@ -350,7 +326,7 @@ class bo_tracking { //error_log("bo_trackering::send_notification(,,'$email',$user_or_lang,$check)"); if (!$email) return false; - + if (!$this->save_prefs) $this->save_prefs = $GLOBALS['egw_info']['user']; if (is_numeric($user_or_lang)) // user --> read everything from his prefs @@ -369,6 +345,10 @@ class bo_tracking { return false; // only notification about changed assignment requested } + if($this->user == $user_or_lang) + { + return false; // no popup for own actions + } } else { @@ -380,70 +360,91 @@ class bo_tracking { $GLOBALS['egw']->translation->init(); } - // send popup notification - if (is_numeric($user_or_lang) && $this->user != $user_or_lang) // no popup for own actions - { - //die('sending popup notification'.$this->html->htmlspecialchars($this->get_popup_message($data,$old))); - $this->popup_notification($user_or_lang,$this->get_popup_message($data,$old)); - } - // PHPMailer aka send-class, seems not to be able to send more then one mail, IF we need to authenticate to the SMTP server - // There for the object is newly created for ever mail, 'til this get fixed in PHPMailer. - //if(!is_object($GLOBALS['egw']->send)) - { - require_once(EGW_API_INC.'/class.send.inc.php'); - $GLOBALS['egw']->send = $send =& new send(); - } - //$send = &$GLOBALS['egw']->send; - $send->ClearAddresses(); - $send->ClearAttachments(); - - // does the user wants html-emails - $html_email = !!$GLOBALS['egw_info']['user']['preferences']['tracker'][$this->check2pref ? $this->check2pref['notify_html'] : 'notify_html']; - $send->IsHTML($html_email); - - if (preg_match('/^(.+) *<(.+)>/',$email,$matches)) // allow to use eg. "Ralf Becker " as address - { - $send->AddAddress($matches[2],$matches[1]); - } - else - { - $send->AddAddress($email,is_numeric($user_or_lang) ? $GLOBALS['egw']->accounts->id2name($user_or_lang,'account_fullname') : ''); - } - $send->AddCustomHeader("X-eGroupWare-type: {$this->app}update"); - + $sender = $this->get_sender($data,$old); - if (preg_match('/^(.+) *<(.+)>/',$sender,$matches)) // allow to use eg. "Ralf Becker " as sender - { - $send->From = $matches[2]; - $send->FromName = $matches[1]; - } - else - { - $send->From = $sender; - $send->FromName = ''; - } - $send->Subject = $this->get_subject($data,$old); - - $send->Body = $this->get_body($html_email,$data,$old); - - foreach($this->get_attachments($data,$old) as $attachment) - { - if (isset($attachment['content'])) - { - $send->AddStringAttachment($attachment['content'],$attachment['filename'],$attachment['encoding'],$attachment['mimetype']); + $subject = $this->get_subject($data,$old); + $attachments = $this->get_attachments($data,$old); + /* send over notification_app or alternative old-style mail class + * in future, we can make the notification app able to send mails + * for non-system users, so the else part below could be dropped + */ + if (is_numeric($user_or_lang)) { + // send via notification_app + require_once(EGW_INCLUDE_ROOT. '/notifications/inc/class.notification.inc.php'); + try { + $notification = new notification(); + $notification->set_receivers(array($user_or_lang)); + $notification->set_message($this->get_notification_body($data,$old)); + $notification->set_sender($this->user); + $notification->set_subject($subject); + $notification->set_links(array($this->get_notification_link($data,$old))); + if(is_array($attachments)) { $notification->set_attachments($attachments); } + $notification->send(); } - elseif (isset($attachment['path'])) + catch (Exception $exception) { + $this->errors[] = $exception->getMessage(); + return false; + } + } else { + // PHPMailer aka send-class, seems not to be able to send more then one mail, IF we need to authenticate to the SMTP server + // There for the object is newly created for ever mail, 'til this get fixed in PHPMailer. + $notification_sent = false; + if(!is_object($GLOBALS['egw']->send)) { - $send->AddAttachment($attachment['path'],$attachment['filename'],$attachment['encoding'],$attachment['$mimetype']); + require_once(EGW_API_INC.'/class.send.inc.php'); + $GLOBALS['egw']->send = $send =& new send(); + } + + $send->ClearAddresses(); + $send->ClearAttachments(); + + // does the user want html-emails + $html_email = !!$GLOBALS['egw_info']['user']['preferences']['tracker'][$this->check2pref ? $this->check2pref['notify_html'] : 'notify_html']; + $send->IsHTML($html_email); + + if (preg_match('/^(.+) *<(.+)>/',$email,$matches)) // allow to use eg. "Ralf Becker " as address + { + $send->AddAddress($matches[2],$matches[1]); + } + else + { + $send->AddAddress($email,is_numeric($user_or_lang) ? $GLOBALS['egw']->accounts->id2name($user_or_lang,'account_fullname') : ''); + } + $send->AddCustomHeader("X-eGroupWare-type: {$this->app}update"); + + if (preg_match('/^(.+) *<(.+)>/',$sender,$matches)) // allow to use eg. "Ralf Becker " as sender + { + $send->From = $matches[2]; + $send->FromName = $matches[1]; + } + else + { + $send->From = $sender; + $send->FromName = ''; + } + $send->Subject = $subject; + $send->Body = $this->get_body($html_email,$data,$old); + + foreach($attachments as $attachment) + { + if (isset($attachment['content'])) + { + $send->AddStringAttachment($attachment['content'],$attachment['filename'],$attachment['encoding'],$attachment['mimetype']); + } + elseif (isset($attachment['path'])) + { + $send->AddAttachment($attachment['path'],$attachment['filename'],$attachment['encoding'],$attachment['$mimetype']); + } + } + + //echo "

bo_trackering::send_notification(): sending

".print_r($send,true)."
\n"; + $notification_sent = $send->Send(); + if(!$notification_sent) { + $this->errors[] = lang('Error while notifying %1: %2',$email,$send->ErrorInfo); + return false; } } - - //echo "

bo_trackering::send_notification(): sending

".print_r($send,true)."
\n"; - if (!$send->Send()) - { - $this->errors[] = lang('Error while notifying %1: %2',$email,$send->ErrorInfo); - return false; - } + return true; } @@ -579,28 +580,45 @@ class bo_tracking } /** - * Get the body of the notification message, can be reimplemented + * Get a link for notifications to view the entry + * + * @param array $data + * @param array $old + * @return array with link + */ + function get_notification_link($data,$old) + { + if (!is_object($GLOBALS['egw']->link)) + { + require_once(EGW_API_INC.'/class.bolink.inc.php'); + $GLOBALS['egw']->link =& new bolink(); + } + if($view = $GLOBALS['egw']->link->view($this->app,$data[$this->id_field])) { + return array( 'menuaction' => $view['menuaction'], + 'params' => array ( 'action' => $view['action'], + 'action_id' => $view['action_id'], + ), + 'text' => $data['info_subject'], + ); + } else { + return false; + } + } + + /** + * Get the body of the notification message for notification-app * - * @param boolean $html_email * @param array $data * @param array $old * @return string */ - function get_body($html_email,$data,$old) + function get_notification_body($data,$old) { $body = ''; - if ($html_email) - { - $body = "\n\n".''."\n"; - } // new or modified message if (($message = $this->get_message($data,$old))) { - $body .= $this->format_line($html_email,'message',false,$message); - } - if (($link = $this->get_link($data,$old))) - { - $body .= $this->format_line($html_email,'link',false,lang('You can respond by visiting:'),$link); + $body .= $this->format_line(false,'message',false,$message); } foreach($this->get_details($data) as $name => $detail) { @@ -610,13 +628,9 @@ class bo_tracking //if ($modified) error_log("data[$name]=".print_r($data[$name],true).", old[$name]=".print_r($old[$name],true)." --> modified=".(int)$modified); if (empty($detail['value']) && !$modified) continue; // skip unchanged, empty values - $body .= $this->format_line($html_email,$detail['type'],$modified, + $body .= $this->format_line(false,$detail['type'],$modified, ($detail['label'] ? $detail['label'].': ':'').$detail['value']); } - if ($html_email) - { - $body .= "
\n\n\n"; - } return $body; } @@ -698,7 +712,7 @@ class bo_tracking } /** - * Get the attachments for a notificaton mail + * Get the attachments for a notification mail * * @param array $data * @param array $old @@ -706,39 +720,6 @@ class bo_tracking */ function get_attachments($data,$old) { - return array(); - } - - /** - * Get the message for the popup - * - * Default implementation uses get_subject() with get_link() and get_message() as an extra line - * - * @param array $data - * @param array $old - * @return string - */ - function get_popup_message($data,$old) - { - $message = $this->html->htmlspecialchars($this->get_subject($data,$old)); - - if ((list($link,$popup) = $this->get_link($data,$old,true))) - { - if ($popup) - { - list($width,$height) = explode('x',$popup); - $options = 'onclick="egw_openWindowCentered2(this.href,\'_blank\', \''.$width.'\', \''.$height.'\', \'yes\'); return false;"'; - } - else - { - $options = 'target="_blank"'; - } - $message = $this->html->a_href($this->html->htmlspecialchars($message),$link,'',$options); - } - if (($extra = $this->get_message($data,$old))) - { - $message .= '
'.$this->html->htmlspecialchars($extra); - } - return $message; + return array(); } } \ No newline at end of file diff --git a/notifications/inc/class.iface_notification.inc.php b/notifications/inc/class.iface_notification.inc.php index 528e2acfe1..f61d95952c 100644 --- a/notifications/inc/class.iface_notification.inc.php +++ b/notifications/inc/class.iface_notification.inc.php @@ -17,10 +17,11 @@ interface iface_notification { /** * constructor * - * @param object $_account + * @param object $_sender + * @param object $_recipient * @param object $_preferences */ - public function __construct( $_account=false, $_preferences=false ); + public function __construct( $_sender=false, $_recipient=false, $_config=false, $_preferences=false ); /** * sends notification @@ -29,5 +30,5 @@ interface iface_notification { * implementing class needs to handle them somehow. * @param string $_message */ - public function send( $_message ); + public function send( $_subject = false, $_messages, $_attachments = false); } diff --git a/notifications/inc/class.notification.inc.php b/notifications/inc/class.notification.inc.php index bc93c11746..cf4da55d5c 100644 --- a/notifications/inc/class.notification.inc.php +++ b/notifications/inc/class.notification.inc.php @@ -9,6 +9,9 @@ * @version $Id$ */ +require_once(EGW_INCLUDE_ROOT.'/phpgwapi/inc/class.html.inc.php'); +require_once(EGW_INCLUDE_ROOT.'/phpgwapi/inc/class.config.inc.php'); + /** * Notifies users according to their preferences. * @@ -29,12 +32,49 @@ final class notification { */ const _appname = 'notifications'; + /** + * pre-defined notificaton chains + * @abstract + * arrays with name => chain pairs + * the chain itself consists of an array with framework => action pairs + * where action defines what to do after the framework has been executed: + * stop: stop executing notifications + * fail: do not stop if framework fails, otherwise stop + * continue: execute next framework + * + * @var array + */ + private $notification_chains = array( + 'disable' => false, + 'popup_only' => array('popup' => 'stop'), + 'winpopup_only' => array('winpopup' => 'stop'), + 'email_only' => array('email' => 'stop'), + 'popup_or_email' => array('popup' => 'fail', 'email' => 'stop'), + 'winpopup_or_email' => array('winpopup' => 'fail', 'email' => 'stop'), + 'popup_and_email' => array('popup' => 'continue', 'email' => 'stop'), + 'winpopup_and_email' => array('winpopup' => 'continue', 'email' => 'stop'), + 'egwpopup_and_winpopup' => array('popup' => 'continue', 'winpopup' => 'stop'), + 'all' => array('popup' => 'continue', 'winpopup' => 'continue', 'email' => 'stop'), + ); + /** * array with objects of receivers * @var array */ private $receivers = array(); + /** + * objects of sender + * @var object + */ + private $sender; + + /** + * holds notification subject + * @var string + */ + private $subject = ''; + /** * holds notification message * @var string @@ -42,15 +82,49 @@ final class notification { private $message = ''; /** - * sets notification message - * @abstract $message accepts html tags:


. - * NOTE: There is no XSS prevention in notifications framework! - * You have to filter userinputs yourseve (e.g. htmlspechialchars() ) - * - * @param string &$message + * array with objects of links + * @var array */ - public function set_message($_message) { - $this->message = $_message; + private $links = array(); + + /** + * array with objects of attachments + * @var array + */ + private $attachments = array(); + + /** + * holds html object to render elements + * + * @var object + */ + private $html; + + /** + * holds config object (sitewide configuration of app) + * + * @var object + */ + private $config; + + /** + * constructor of notification + * + */ + public function __construct() { + $this->html = & html::singleton(); + $config = new config(self::_appname); + $this->config = (object) $config->read_repository(); + } + + /** + * Set sender for the current notification + * + * @param $_sender object of account + * as long as the accounts class isn't a nice object, it's an int with the account id :-( + */ + public function set_sender($_sender) { + $this->sender = is_object($_sender) ? $_sender : (object) $GLOBALS['egw']->accounts->get_account_data($_sender); return true; } @@ -66,6 +140,100 @@ final class notification { $receiver[$receiver_id]['id'] = $receiver_id; $this->receivers[$receiver_id] = (object)$receiver[$receiver_id]; } + return true; + } + + /** + * sets notification subject + * + * @param string $_subject + */ + public function set_subject($_subject) { + $this->subject = $_subject; + return true; + } + + /** + * sets notification message + * @abstract $message accepts html tags:


. + * NOTE: There is no XSS prevention in notifications framework! + * You have to filter userinputs yourseve (e.g. htmlspechialchars() ) + * + * @param string $_message + */ + public function set_message($_message) { + $this->message = $_message; + return true; + } + + /** + * sets the notification links + * + * @param array $links link array (like defined in $this->add_link) + */ + public function set_links(array $_links) { + $this->links = array(); // clear array if set + foreach($_links as $link) { + if(is_array($link)) { + $this->add_link($link['menuaction'], $link['params'], $link['text']); + } + } + return true; + } + + /** + * adds a notification link + * + * @param string $menuaction egw menuaction (appname.classname.functionname) + * @param array $params params to append (name => value pairs) + * @param string $text a descriptive text for the link + */ + public function add_link($_menuaction, $_params, $_text) { + if(!$_menuaction || !$_params || !$_text) { return false; } + $this->links[] = (object)array( 'menuaction' => $_menuaction, + 'params' => $_params, + 'text' => $_text, + ); + return true; + } + + /** + * sets the notification attachments + * + * @param array $attachments attachment array (like defined in $this->add_attachment + */ + public function set_attachments(array $_attachments) { + $this->attachments = array(); // clear array if set + foreach($_attachments as $attachment) { + if(is_array($attachment)) { + $this->add_attachment( $attachment['string'], + $attachment['filename'], + $attachment['encoding'], + $attachment['type'] + ); + } + } + return true; + } + + /** + * adds a notification attachment + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * + * @param string $string Attachment data. + * @param string $filename Name of the attachment. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + */ + public function add_attachment($_string, $_filename, $_encoding = "base64", $_type = "application/octet-stream") { + if(!$_string || !$_filename) { return false; } + $this->attachments[] = (object)array( 'string' => $_string, + 'filename' => $_filename, + 'encoding' => $_encoding, + 'type' => $_type, + ); + return true; } /** @@ -73,42 +241,66 @@ final class notification { */ public function send() { if (empty($this->receivers) || !$this->message) { - throw new Exception('Error: Coud not send notification. No receiver or no message where supplied'); + throw new Exception('Error: Could not send notification. No receiver or no message where supplied'); } - + // create all message styles + $messages = $this->create_messages($this->message, $this->links); foreach ($this->receivers as $receiver) { - $prefs = new preferences($receiver->id); - $preferences = $prefs->read(); - $preferences = (object)$preferences[self::_appname ]; - - if (!$preferences->disable_ajaxpopup) { - $notification_backends[] = 'notification_popup'; + try { + // check if the receiver has rights to run the notifcation app + $ids = $GLOBALS['egw']->accounts->memberships($receiver->id,true); + $ids[] = $receiver->id; + if (!$GLOBALS['egw']->acl->get_specific_rights_for_account($ids,'run','notifications')) { + throw new Exception('Error: Could not send notification to user '.$receiver->lid.' because of missing execute rights on notification-app.'); + } + + $prefs = new preferences($receiver->id); + $preferences = $prefs->read(); + $preferences = (object)$preferences[self::_appname]; + $notification_chain = $this->notification_chains[$preferences->notification_chain]; + if(!is_array($notification_chain)) { + throw new Exception('Error: Could not send notification to user '.$receiver->lid.' because of missing notification settings.'); + } } - - $send_succseed = 0; - foreach ((array)$notification_backends as $notification_backend) { + catch (Exception $exception) { + error_log('notification of receiver'.$receiver->lid.' failed: '.$exception->getMessage()); + } + + $user_notified = false; + $backend_errors = array(); + foreach($notification_chain as $notification_backend => $action) { try { + $notification_backend = 'notification_'.$notification_backend; require_once(EGW_INCLUDE_ROOT. SEP. self::_appname. SEP. 'inc'. SEP. 'class.'. $notification_backend. '.inc.php'); - - $obj = @new $notification_backend( $receiver, $preferences ); + $obj = @new $notification_backend( $this->sender, $receiver, $this->config, $preferences ); if ( !is_a( $obj, iface_notification )) { unset ( $obj ); - throw new Exception('Error: '.$notification_backend. ' is no implementation of iface_notification'); + throw new Exception('Error: '.$notification_backend. ' is no implementation of iface_notification'); } - - $obj->send( $this->message ); - $send_succseed++; + + $obj->send($this->subject, $messages, $this->attachments); } catch (Exception $exception) { - $send_succseed--; - //echo $exception->getMessage(), "\n"; + $backend_errors[] = $notification_backend.' failed: '.$exception->getMessage(); + // try next backend + if($action == 'fail' || $action == 'continue') { + continue; + } + // all backends failed - give error message + if(!$user_notified) { + error_log('Error: notification of receiver '.$receiver->lid.' failed'); + foreach($backend_errors as $id=>$backend_error) { + error_log($backend_error); + } + } + break; // stop running through chain } - } - - if ($send_succseed == 0) { - throw new Exception('Error: Was not able to send Notification to user!'); + // backend sucseeded + $user_notified = true; + if($action == 'stop' || $action == 'fail') { break; } // stop running through chain } } + return true; } /** @@ -128,27 +320,54 @@ final class notification { public function get_receivers() { return $this->receivers; } - + /** - * Small helper function to just send a message + * this function creates an array with the message as plaintext and html * - * @abstract To stay php4 compatible for the 1.4 release we don't - * throw exeptions here. This behaviour will change after 1.4! - * @param array $receivers * @param string $message - * @return string + * @param array $links + * @return array $messages */ - public static function notify( array $_receivers, $_message ) { - $notification = new notification(); - $notification->set_receivers( $_receivers ); - $notification->set_message( $_message ); - try{ - $notification->send(); + private function create_messages($_message, $_links = false) { + $messages = array(); + $messages['plain'] = array(); + $messages['html'] = array(); + + $messages['plain']['text'] = $_message; + $messages['html']['text'] = nl2br($_message); + if(is_array($_links)) { + foreach($_links as $link) { + $params = ''; + foreach($link->params as $param => $value) { + $params.='&'.$param.'='.$value; + } + $url = $GLOBALS['egw_info']['server']['webserver_url'].'/index.php?menuaction='.$link->menuaction.$params; + $menuaction_arr = explode('.',$link->menuaction); + $application = $menuaction_arr[0]; + $image = $application ? $this->html->image($application,'navbar',$link->text,'align="middle"').' ' : ''; + $messages['plain']['link_internal'] .= "\n".$url; + $messages['plain']['link_external'] .= "\n".$url.'&no_popup=1'; + $messages['html']['link_internal'] .= '
'.$image.$link->text.''; + $messages['html']['link_external'] .= '
'.$link->text.''; + $messages['html']['link_jspopup'] .= '

'.$image.$link->text.'
'; + } } - catch(Exception $exception) { - return $exception->getMessage(); - } - return null; + return $messages; + } + + /** + * returns javascript to open a popup window: window.open(...) + * + * @param string $link link or this.href + * @param string $target='_blank' name of target or this.target + * @param int $width=750 width of the window + * @param int $height=400 height of the window + * @return string javascript (using single quotes) + */ + private function popup($link,$target='_blank',$width=750,$height=410) + { + return 'egw_openWindowCentered2('.($link == 'this.href' ? $link : "'".$link."'").','. + ($target == 'this.target' ? $target : "'".$target."'").",$width,$height,'yes')"; } } \ No newline at end of file diff --git a/notifications/inc/class.notification_email.inc.php b/notifications/inc/class.notification_email.inc.php new file mode 100644 index 0000000000..989b09bee3 --- /dev/null +++ b/notifications/inc/class.notification_email.inc.php @@ -0,0 +1,147 @@ + + */ + +require_once('class.iface_notification.inc.php'); +require_once(EGW_INCLUDE_ROOT. '/phpgwapi/inc/class.send.inc.php'); +require_once(EGW_INCLUDE_ROOT.'/phpgwapi/inc/class.config.inc.php'); + +/** + * User notification via email. + */ +class notification_email implements iface_notification { + + /** + * Appname + */ + const _appname = 'notifications'; + + /** + * holds account object for user who sends the message + * + * @var object + */ + private $sender; + + /** + * holds account object for user to notify + * + * @var object + */ + private $recipient; + + /** + * holds config object (sitewide application config) + * + * @var object + */ + private $config; + + /** + * holds preferences object of user to notify + * + * @var object + */ + private $preferences; + + /** + * holds mail object + * + * @var object + */ + private $mail; + + /** + * constructor of notification_email + * + * @param object $_recipient + * @param object $_preferences + */ + public function __construct( $_sender=false, $_recipient=false, $_config=false, $_preferences=false) { + // If we are called from class notification sender, recipient, config and prefs are objects. + // otherwise we have to fetch this objects for current user. + if (!is_object($_sender)) { + $this->sender = (object) $GLOBALS['egw']->accounts->read($_sender); + $this->sender->id =& $this->sender->account_id; + } + else { + $this->sender = $_sender; + } + if (!is_object($_recipient)) { + $this->recipient = (object) $GLOBALS['egw']->accounts->read($_recipient); + $this->recipient->id =& $this->recipient->account_id; + } + else { + $this->recipient = $_recipient; + } + if(!is_object($_config)) { + $config = new config(self::_appname); + $this->config = (object) $config->read_repository(); + } else { + $this->config = $_config; + } + if(!is_object($_preferences)) { + $prefs = new preferences($this->recipient->id); + $preferences = $prefs->read(); + $this->preferences = (object)$preferences[self::_appname ]; + } else { + $this->preferences = $_preferences; + } + if(!is_object($this->mail)) + { + $this->mail = new send(); + } + } + + /** + * sends notification + * + * @param string $_subject + * @param array $_messages + * @param array $_attachments + */ + public function send( $_subject = false, $_messages, $_attachments = false) { + $sender_email = $GLOBALS['egw']->accounts->id2name($this->sender->id,'account_email'); + $sender_fullname = $GLOBALS['egw']->accounts->id2name($this->sender->id,'account_fullname'); + $recipient_email = $GLOBALS['egw']->accounts->id2name($this->recipient->id,'account_email'); + $recipient_fullname = $GLOBALS['egw']->accounts->id2name($this->recipient->id,'account_fullname'); + if (!$sender_email || strpos($sender_email,'@') === false) { + throw new Exception("Failed sending notification message via email. No valid sender given."); + } + if (!$recipient_email || strpos($recipient_email,'@') === false) { + throw new Exception("Failed sending notification message via email. No valid recipient given."); + } + if($this->preferences->external_mailclient) { + $body_plain = $_messages['plain']['text'].$_messages['plain']['link_external']; + $body_html = $_messages['html']['text'].$_messages['html']['link_external']; + } else { + $body_plain = $_messages['plain']['text'].$_messages['plain']['link_internal']; + $body_html = $_messages['html']['text'].$_messages['html']['link_internal']; + } + $this->mail->ClearAddresses(); + $this->mail->ClearAttachments(); + $this->mail->IsHTML(true); + $this->mail->AddAddress($recipient_email, $recipient_fullname); + $this->mail->AddCustomHeader('X-eGroupWare-type: notification-mail'); + $this->mail->From = $sender_email; + $this->mail->FromName = $sender_fullname; + $this->mail->Subject = $this->mail->encode_subject($_subject); + $this->mail->Body = $body_html; + $this->mail->AltBody = $body_plain; + if(is_array($_attachments)) { + foreach($_attachments as $attachment) { + $this->mail->AddStringAttachment($attachment->string, $attachment->filename, $attachment->encoding, $attachment->type); + } + } + if(!$error=$this->mail->Send()) { + throw new Exception("Failed sending notification message via email.$error"); + } + } + +} diff --git a/notifications/inc/class.notification_popup.inc.php b/notifications/inc/class.notification_popup.inc.php index 61eac8725b..4299d1b3f4 100644 --- a/notifications/inc/class.notification_popup.inc.php +++ b/notifications/inc/class.notification_popup.inc.php @@ -11,6 +11,7 @@ */ require_once('class.iface_notification.inc.php'); +require_once(EGW_INCLUDE_ROOT.'/phpgwapi/inc/class.config.inc.php'); /** * Instant user notification with egroupware popup. @@ -37,12 +38,26 @@ class notification_popup implements iface_notification { */ const _notification_table = 'egw_notificationpopup'; + /** + * holds account object for user who sends the message + * + * @var object + */ + private $sender; + /** * holds account object for user to notify * * @var object */ - private $account; + private $recipient; + + /** + * holds config object (sitewide application config) + * + * @var object + */ + private $config; /** * holds preferences object of user to notify @@ -61,20 +76,40 @@ class notification_popup implements iface_notification { /** * constructor of notification_egwpopup * - * @param object $_account + * @param object $_sender + * @param object $_recipient * @param object $_preferences */ - public function __construct( $_account=false, $_preferences=false) { - // If we are called from class notification account and prefs are objects. + public function __construct( $_sender=false, $_recipient=false, $_config=false, $_preferences=false) { + // If we are called from class notification sender, recipient, config and prefs are objects. // otherwise we have to fetch this objects for current user. - if (!is_object($_account)) { - $this->account = (object) $GLOBALS['egw']->accounts->read($_account); - $this->account->id =& $this->account->account_id; + if (!is_object($_sender)) { + $this->sender = (object) $GLOBALS['egw']->accounts->read($_sender); + $this->sender->id =& $this->sender->account_id; } else { - $this->account = $_account; + $this->sender = $_sender; + } + if (!is_object($_recipient)) { + $this->recipient = (object) $GLOBALS['egw']->accounts->read($_recipient); + $this->recipient->id =& $this->recipient->account_id; + } + else { + $this->recipient = $_recipient; + } + if(!is_object($_config)) { + $config = new config(self::_appname); + $this->config = (object) $config->read_repository(); + } else { + $this->config = $_config; + } + if(!is_object($_preferences)) { + $prefs = new preferences($this->recipient->id); + $preferences = $prefs->read(); + $this->preferences = (object)$preferences[self::_appname ]; + } else { + $this->preferences = $_preferences; } - $this->preferences = is_object($_preferences) ? $_preferences : $GLOBALS['egw']->preferences; $this->db = &$GLOBALS['egw']->db; $this->db->set_app( self::_appname ); } @@ -82,18 +117,20 @@ class notification_popup implements iface_notification { /** * sends notification if user is online * - * @param string $_message + * @param string $_subject + * @param array $_messages + * @param array $_attachments */ - public function send( $_message ) { + public function send( $_subject = false, $_messages, $_attachments = false) { $sessions = $GLOBALS['egw']->session->list_sessions(0, 'asc', 'session_dla', true); $user_sessions = array(); foreach ($sessions as $session) { - if ($session['session_lid'] == $this->account->lid. '@'. $GLOBALS['egw_info']['user']['domain']) { + if ($session['session_lid'] == $this->recipient->lid. '@'. $GLOBALS['egw_info']['user']['domain']) { $user_sessions[] = $session['session_id']; } } - if ( empty($user_sessions) ) throw new Exception("Notice: User #{$this->account->id} isn't online. Can't send notification via popup"); - $this->save( $_message, $user_sessions ); + if ( empty($user_sessions) ) throw new Exception("User {$this->recipient->lid} isn't online. Can't send notification via popup"); + $this->save( $_messages['html']['text'].$_messages['html']['link_jspopup'], $user_sessions ); } /** @@ -108,7 +145,7 @@ class notification_popup implements iface_notification { $message = ''; $this->db->select(self::_notification_table, '*', array( - 'account_id' => $this->account->id, + 'account_id' => $this->recipient->id, 'session_id' => $session_id, ), __LINE__,__FILE__); @@ -116,25 +153,36 @@ class notification_popup implements iface_notification { while ($notification = $this->db->row(true)) { switch (self::_window ) { case 'div' : - $message .= '

'. nl2br($notification['message']). '

'; + $response->addScriptCall('append_notification_message',$notification['message']); break; case 'alert' : - $message .= ".\n". $notification['message']. "\n"; + $response->addAlert($notification['message']); break; } } - $this->db->delete(self::_notification_table,array( - 'account_id' =>$this->account->id, + $myval=$this->db->delete(self::_notification_table,array( + 'account_id' => $this->recipient->id, 'session_id' => $session_id, ),__LINE__,__FILE__); switch (self::_window) { case 'div' : - $response->addAppend('notificationwindow_message','innerHTML',$message); - $response->addScript('notificationwindow_display();'); + switch($this->preferences->egwpopup_verbosity) { + case 'low': + $response->addScript('notificationbell_switch("active");'); + break; + case 'high': + $response->addAlert(lang('eGroupware has some notifications for you')); + $response->addScript('notificationwindow_display();'); + break; + case 'medium': + default: + $response->addScript('notificationwindow_display();'); + break; + } break; case 'alert' : - $response->addAlert($message); + // nothing to do for now break; } } @@ -151,11 +199,11 @@ class notification_popup implements iface_notification { private function save( $_message, array $_user_sessions ) { foreach ($_user_sessions as $user_session) { $result =& $this->db->insert( self::_notification_table, array( - 'account_id' => $this->account->id, + 'account_id' => $this->recipient->id, 'session_id' => $user_session, 'message' => $_message ), false,__LINE__,__FILE__); } - if ($result === false) throw new Exception("Error: Can't save notification into SQL table"); + if ($result === false) throw new Exception("Can't save notification into SQL table"); } } diff --git a/notifications/inc/class.notification_winpopup.inc.php b/notifications/inc/class.notification_winpopup.inc.php new file mode 100644 index 0000000000..1aab59f566 --- /dev/null +++ b/notifications/inc/class.notification_winpopup.inc.php @@ -0,0 +1,145 @@ + + */ + +require_once('class.iface_notification.inc.php'); +require_once(EGW_INCLUDE_ROOT.'/phpgwapi/inc/class.config.inc.php'); + +/** + * User notification via winpopup. + */ +class notification_winpopup implements iface_notification { + + /** + * Appname + */ + const _appname = 'notifications'; + + /** + * Notification table in SQL database + */ + const _login_table = 'egw_access_log'; + + /** + * holds account object for user who sends the message + * + * @var object + */ + private $sender; + + /** + * holds account object for user to notify + * + * @var object + */ + private $recipient; + + /** + * holds config object (sitewide application config) + * + * @var object + */ + private $config; + + /** + * holds preferences object of user to notify + * + * @var object + */ + private $preferences; + + /** + * constructor of notification_winpopup + * + * @param object $_recipient + * @param object $_preferences + */ + public function __construct($_sender=false, $_recipient=false, $_config=false, $_preferences=false) { + // If we are called from class notification sender, recipient, config and prefs are objects. + // otherwise we have to fetch this objects for current user. + if (!is_object($_sender)) { + $this->sender = (object) $GLOBALS['egw']->accounts->read($_sender); + $this->sender->id =& $this->sender->account_id; + } + else { + $this->sender = $_sender; + } + if (!is_object($_recipient)) { + $this->recipient = (object) $GLOBALS['egw']->accounts->read($_recipient); + $this->recipient->id =& $this->recipient->account_id; + } + else { + $this->recipient = $_recipient; + } + if(!is_object($_config)) { + $config = new config(self::_appname); + $this->config = (object) $config->read_repository(); + } else { + $this->config = $_config; + } + if(!is_object($_preferences)) { + $prefs = new preferences($this->recipient->id); + $preferences = $prefs->read(); + $this->preferences = (object)$preferences[self::_appname ]; + } else { + $this->preferences = $_preferences; + } + } + + /** + * sends notification + * + * @param string $_subject + * @param array $_messages + * @param array $_attachments + */ + public function send( $_subject = false, $_messages, $_attachments = false) { + if(!$this->config->winpopup_netbios_command) { + throw new Exception("Winpopup plugin not configured yet. Skipped sending notification message. Please check your settings."); + } + $sessions = $GLOBALS['egw']->session->list_sessions(0, 'asc', 'session_dla', true); + $user_sessions = array(); + foreach ($sessions as $session) { + if ($session['session_lid'] == $this->recipient->lid. '@'. $GLOBALS['egw_info']['user']['domain']) { + $user_sessions[] = $session['session_ip']; + } + } + if ( empty($user_sessions) ) throw new Exception("User #{$this->recipient->id} isn't online. Can't send notification via winpopup"); + $this->send_winpopup( $_messages['plain']['text'], $user_sessions ); + } + + /** + * sends the winpopup message via pre-defined smbclient tool in prefs + * + * @param string $_message + * @param array $_user_sessions + */ + private function send_winpopup( $_message, array $_user_sessions ) { + foreach($_user_sessions as $user_session) { + $ip_octets=explode(".",$user_session); + // format the ip_octets to 3 digits each + foreach($ip_octets as $id=>$ip_octet) { + if(strlen($ip_octet)==1) { $ip_octets[$id] = '00'.$ip_octet; } + if(strlen($ip_octet)==2) { $ip_octets[$id] = '0'.$ip_octet; } + } + $placeholders = array( '/\[MESSAGE\]/' => $_message, + '/\[1\]/' => $ip_octets[0], + '/\[2\]/' => $ip_octets[1], + '/\[3\]/' => $ip_octets[2], + '/\[4\]/' => $ip_octets[3], + '/\[IP\]/' => $user_session, + '/\[SENDER\]/' => $GLOBALS['egw']->accounts->id2name($this->sender->id,'account_fullname'), + ); + $command = preg_replace(array_keys($placeholders), $placeholders, $this->config->winpopup_netbios_command); + if(!exec($command)) { + throw new Exception("Failed sending notification message via winpopup. Please check your settings."); + } + } + } +} \ No newline at end of file diff --git a/notifications/inc/class.uinotificationprefs.inc.php b/notifications/inc/class.uinotificationprefs.inc.php deleted file mode 100644 index 66b37737d5..0000000000 --- a/notifications/inc/class.uinotificationprefs.inc.php +++ /dev/null @@ -1,83 +0,0 @@ - - * @version $Id$ - */ - -require_once(EGW_INCLUDE_ROOT.'/etemplate/inc/class.etemplate.inc.php'); - -class uinotificationprefs { - - const _appname = 'notifications'; - - public $public_functions = array( - 'index' => true, - ); - - /** - * This are the preferences for notifications - * - * @var array - */ - private $notification_preferences = array( - 'disable_ajaxpopup' => '', // bool: true / false - ); - - /** - * Holds preferences object for current user - * - * @var object - */ - private $preferences; - - public function __construct($_account_id = 0, $_referer = false) { - $account_id = $_account_id > 0 ? $_account_id : $GLOBALS['egw_info']['user']['account_id']; - $this->preferences = new preferences($account_id); - $GLOBALS['egw_info']['flags']['app_header'] = lang('Preferences for notification'); - } - - public function index($_content = false) { - $et = new etemplate(self::_appname. '.prefsindex'); - $content = array(); - $sel_options = array(); - $readonlys = array(); - $preserv = array(); - if (is_array($_content)) { - if (is_array($_content['button'])) { - $preferences = array_intersect_key($_content, $this->notification_preferences); - list($button) = each($_content['button']); - switch ($button) { - case 'save' : - $this->save($preferences); - $GLOBALS['egw']->redirect_link($_content['referer']); - case 'apply' : - $this->save($preferences); - break; - case 'cancel' : - default : - $GLOBALS['egw']->redirect_link($_content['referer']); - } - } - } - else { - $preferences = $this->preferences->read(); - $preferences = $preferences[self::_appname]; - $preserv['referer'] = $GLOBALS['egw']->common->get_referer(); - } - - $content = array_merge($content,(array)$preferences); - return $et->exec(self::_appname. '.uinotificationprefs.index',$content,$sel_options,$readonlys,$preserv); - } - - private function save($_preferences) { - $this->preferences->read(); - $this->preferences->user[self::_appname] = $_preferences; - $this->preferences->save_repository(); - return; - } -} \ No newline at end of file diff --git a/notifications/inc/hook_admin.inc.php b/notifications/inc/hook_admin.inc.php new file mode 100644 index 0000000000..19afe631f4 --- /dev/null +++ b/notifications/inc/hook_admin.inc.php @@ -0,0 +1,16 @@ + + * @version $Id: hook_preferences.inc.php 22498 2006-09-25 10:20:46Z jaytraxx $ + */ + + $title = $appname; + + $file = Array( 'Site Configuration' => $GLOBALS['egw']->link('/index.php','menuaction=admin.uiconfig.index&appname=notifications')); + display_section($appname,$title,$file); +?> diff --git a/notifications/inc/hook_after_navbar.inc.php b/notifications/inc/hook_after_navbar.inc.php index 3cea8cbdb0..875badd7bb 100644 --- a/notifications/inc/hook_after_navbar.inc.php +++ b/notifications/inc/hook_after_navbar.inc.php @@ -21,7 +21,9 @@ if (!$GLOBALS['egw_info']['user']['preferences']['notifications']['disable_ajaxp
'. lang('Notification'). '
-
+
+ +
'; diff --git a/notifications/inc/hook_preferences.inc.php b/notifications/inc/hook_preferences.inc.php index 968afb4be0..637674e30f 100644 --- a/notifications/inc/hook_preferences.inc.php +++ b/notifications/inc/hook_preferences.inc.php @@ -10,14 +10,8 @@ */ $title = $appname; - $file = array( - 'Preferences' => $GLOBALS['egw']->link( - '/index.php', - 'menuaction=notifications.uinotificationprefs.index', - '_account_id=0', - '_referer=' - ), - ); + $file = Array( 'Preferences' => $GLOBALS['egw']->link('/index.php',array('menuaction'=>'preferences.uisettings.index','appname'=>'notifications')), + ); display_section($appname,$title,$file); ?> diff --git a/notifications/inc/hook_settings.inc.php b/notifications/inc/hook_settings.inc.php new file mode 100644 index 0000000000..400e3f601b --- /dev/null +++ b/notifications/inc/hook_settings.inc.php @@ -0,0 +1,58 @@ + + */ + +$notification_chains = array( 'disable' => lang('do not notify me at all'), + 'popup_only' => lang('eGroupware-Popup only'), + 'winpopup_only' => lang('Windows-Popup only'), + 'email_only' => lang('E-Mail only'), + 'popup_or_email' => lang('eGroupware-Popup first, if that fails notify me by E-Mail'), + 'winpopup_or_email' => lang('Windows-Popup first, if that fails notify me by E-Mail'), + 'popup_and_email' => lang('eGroupware-Popup and E-Mail'), + 'winpopup_and_email' => lang('Windows-Popup and E-Mail'), + 'egwpopup_and_winpopup' => lang('eGroupware-Poupup and Windows-Popup'), + 'all' => lang('all possible notification extensions'), + ); +$verbosity_values = array( 'low' => lang('low'), + 'medium' => lang('medium'), + 'high' => lang('high'), + ); + +$GLOBALS['settings'] = array( + 'notification_chain' => array( + 'type' => 'select', + 'label' => 'Notify me by', + 'name' => 'notification_chain', + 'values' => $notification_chains, + 'help' => 'Choose a notification-chain. You will be notified over the chosen extensions.', + 'xmlrpc' => True, + 'admin' => False + ), + 'egwpopup_verbosity' => array( + 'type' => 'select', + 'label' => 'eGroupware-Popup verbosity', + 'name' => 'egwpopup_verbosity', + 'values' => $verbosity_values, + 'help' => 'How verbose should the eGroupware-Popup behave if a notification is sent to the user:
' + .'low: just display the notification bell in the topmenu - topmenu must be enabled !
' + .'medium: bring notification window to front
' + .'high: bring notification window to front and let the browser do something to announce itself', + 'xmlrpc' => True, + 'admin' => False + ), + 'external_mailclient' => array( + 'type' => 'check', + 'label' => 'Optimize E-Mails for external mail client', + 'name' => 'external_mailclient', + 'help' => 'If set, embedded links get rendered special for external clients', + 'xmlrpc' => True, + 'admin' => False + ), +); +?> \ No newline at end of file diff --git a/notifications/js/notificationajaxpopup.js b/notifications/js/notificationajaxpopup.js index aeb51dabf5..012850cd22 100644 --- a/notifications/js/notificationajaxpopup.js +++ b/notifications/js/notificationajaxpopup.js @@ -8,6 +8,8 @@ * @version $Id$ */ +var notifymessages = new Array(); + function notificationwindow_init() { window.setTimeout("notificationwindow_refresh();", 1000); } @@ -22,13 +24,32 @@ function notificationwindow_refresh() { function notificationwindow_display() { var notificationwindow; + var notificationwindow_message; notificationwindow = document.getElementById("notificationwindow"); + notificationwindow_message = document.getElementById("notificationwindow_message"); + notificationwindow_ok_button = document.getElementById("notificationwindow_ok_button"); notificationwindow.style.display = "inline"; notificationwindow.style.position = "absolute"; notificationwindow.style.width = "500px"; notificationwindow.style.left = screen.availWidth/2 - 250 + "px"; notificationwindow.style.top = screen.availHeight/4 + "px"; notificationwindow.style.height = "100%"; + notificationwindow_message.innerHTML = notifymessages[0]; + if(notifymessages.length-1 > 0 ) { + notificationwindow_ok_button.value = "OK (" + (notifymessages.length-1) + ")"; + } else { + notificationwindow_ok_button.value = "OK"; + } +} + +function notificationbell_switch(mode) { + var notificationbell; + notificationbell = document.getElementById("notificationbell"); + if(mode == "active") { + notificationbell.style.display = "inline"; + } else { + notificationbell.style.display = "none"; + } } function notificationwindow_button_ok() { @@ -36,6 +57,16 @@ function notificationwindow_button_ok() { var notificationwindow_message; notificationwindow = document.getElementById("notificationwindow"); notificationwindow_message = document.getElementById("notificationwindow_message"); - notificationwindow.style.display = "none"; - notificationwindow_message.innerHTML = ""; + notifymessages.shift(); + if(notifymessages.length > 0) { + notificationwindow_display(); + } else { + notificationwindow.style.display = "none"; + notificationwindow_message.innerHTML = ""; + notificationbell_switch("inactive"); + } +} + +function append_notification_message(_message) { + notifymessages.push(_message); } \ No newline at end of file diff --git a/notifications/setup/setup.inc.php b/notifications/setup/setup.inc.php index b1525dda97..6047b95ab2 100644 --- a/notifications/setup/setup.inc.php +++ b/notifications/setup/setup.inc.php @@ -32,6 +32,8 @@ $setup_info[NOTIFICATION_APP]['description'] = /* The hooks this app includes, needed for hooks registration */ $setup_info[NOTIFICATION_APP]['hooks'][] = 'after_navbar'; $setup_info[NOTIFICATION_APP]['hooks'][] = 'preferences'; +$setup_info[NOTIFICATION_APP]['hooks'][] = 'settings'; +$setup_info[NOTIFICATION_APP]['hooks'][] = 'admin'; //$setup_info[NOTIFICATION_APP]['hooks']['settings'] = NOTIFICATION_APP.'.ts_admin_prefs_sidebox_hooks.settings'; //$setup_info[NOTIFICATION_APP]['hooks']['admin'] = NOTIFICATION_APP.'.ts_admin_prefs_sidebox_hooks.all_hooks'; //$setup_info[NOTIFICATION_APP]['hooks']['sidebox_menu'] = NOTIFICATION_APP.'.ts_admin_prefs_sidebox_hooks.all_hooks'; diff --git a/notifications/templates/default/config.tpl b/notifications/templates/default/config.tpl new file mode 100644 index 0000000000..396d9cc3dc --- /dev/null +++ b/notifications/templates/default/config.tpl @@ -0,0 +1,43 @@ + +
+{hidden_vars} + + + + + + + + + + + + + + + + + + + + + + + + + +
 {title}
 {error}
 {lang_Windows_Popup_Configuration}
 {lang_Netbios_command}:
  + Example: /bin/echo '[message]' | /usr/bin/smbclient -M computer-[4] -I [IP] -U '[SENDER]'

+ placeholders:
+ [MESSAGE] is the notification message itself
+ [1] - [4] are the IP-Octets of the windows machine to notify
+ [IP] is the IP-Adress of the windows machine to notify
+ [SENDER] is the sender of the netbios message configured above
+ Note: the webserver-user needs execute rights for this command
+ Don't forget to enclose placeholders containig whitespaces with apostrophes +
+ + +
+
+ diff --git a/notifications/templates/default/images/notificationbell.gif b/notifications/templates/default/images/notificationbell.gif new file mode 100644 index 0000000000000000000000000000000000000000..b8cd941a397847613cfd7634c7357c4d7379cac7 GIT binary patch literal 1023 zcmc(eKTE_g7>5(mK#NP>t#00~5`;j-rD9I&B7qp_DtOT$JxUTrO|UDs`o0#HmJWx${Neb55yGB5;q2>@N%s>d>G?J*;qOkUinA5)uuv+{~dl zG-yyz}gToqfrR1|gRw1XPM}tZtr)q}=RcPnm4eshtml->select('quick_add','',$options,true,$options=' onchange="eval(this.value); this.value=0; return false;"'); } + function _get_notification_bell() { + if (!is_object($GLOBALS['egw']->html)) + { + require_once(EGW_API_INC.'/class.html.inc.php'); + $GLOBALS['egw']->html =& new html(); + } + return $GLOBALS['egw']->html->div( $GLOBALS['egw']->html->a_href( $GLOBALS['egw']->html->image('notifications','notificationbell',lang('notifications')), + 'javascript: notificationwindow_display();' + ), + 'id="notificationbell"', // options + '', // class + 'display: none' //style + ); + } + /** * Prepare an array with apps used to render the navbar diff --git a/phpgwapi/templates/idots/class.idots_framework.inc.php b/phpgwapi/templates/idots/class.idots_framework.inc.php index 3093593b0a..94a8ef1b1b 100644 --- a/phpgwapi/templates/idots/class.idots_framework.inc.php +++ b/phpgwapi/templates/idots/class.idots_framework.inc.php @@ -606,33 +606,37 @@ */ function topmenu() { - $this->tplsav2->menuitems = array(); - $this->tplsav2->menuinfoitems = array(); + $this->tplsav2->menuitems = array(); + $this->tplsav2->menuinfoitems = array(); - $this->apps = $this->_get_navbar_apps(); + $this->apps = $this->_get_navbar_apps(); - $this->_add_topmenu_item('home'); + $this->_add_topmenu_item('home'); - if($GLOBALS['egw_info']['user']['apps']['preferences']) - { + if($GLOBALS['egw_info']['user']['apps']['preferences']) + { $this->_add_topmenu_item('preferences'); - } + } - if($GLOBALS['egw_info']['user']['apps']['manual'] && $this->apps['manual']) - { - $this->_add_topmenu_item('manual'); - } + if($GLOBALS['egw_info']['user']['apps']['manual'] && $this->apps['manual']) + { + $this->_add_topmenu_item('manual'); + } - //$this->_add_topmenu_item('about',lang('About %1',$GLOBALS['egw_info']['apps'][$GLOBALS['egw_info']['flags']['currentapp']]['title'])); - $this->_add_topmenu_item('logout'); + //$this->_add_topmenu_item('about',lang('About %1',$GLOBALS['egw_info']['apps'][$GLOBALS['egw_info']['flags']['currentapp']]['title'])); + $this->_add_topmenu_item('logout'); - $this->tplsav2->assign('info_icons',$this->topmenu_icon_arr); + $this->tplsav2->assign('info_icons',$this->topmenu_icon_arr); - $this->_add_topmenu_info_item($this->_user_time_info()); - $this->_add_topmenu_info_item($this->_current_users()); - $this->_add_topmenu_info_item($this->_get_quick_add()); + if($GLOBALS['egw_info']['user']['apps']['notifications']) + { + $this->_add_topmenu_info_item($this->_get_notification_bell()); + } + $this->_add_topmenu_info_item($this->_user_time_info()); + $this->_add_topmenu_info_item($this->_current_users()); + $this->_add_topmenu_info_item($this->_get_quick_add()); - return $this->tplsav2->fetch('topmenu.tpl.php'); + return $this->tplsav2->fetch('topmenu.tpl.php'); } /** diff --git a/phpgwapi/templates/jerryr/class.jerryr_framework.inc.php b/phpgwapi/templates/jerryr/class.jerryr_framework.inc.php index 2605560831..65024cc7bd 100644 --- a/phpgwapi/templates/jerryr/class.jerryr_framework.inc.php +++ b/phpgwapi/templates/jerryr/class.jerryr_framework.inc.php @@ -31,39 +31,40 @@ class jerryr_framework extends idots_framework } function topmenu() - { - $this->tplsav2->menuitems = array(); - $this->tplsav2->menuinfoitems = array(); + $this->tplsav2->menuitems = array(); + $this->tplsav2->menuinfoitems = array(); - $this->apps = $this->_get_navbar_apps(); + $this->apps = $this->_get_navbar_apps(); - $this->_add_topmenu_item('home'); + $this->_add_topmenu_item('home'); - /*if($GLOBALS['egw_info']['user']['apps']['manual']) - { - $this->_add_topmenu_item('manual'); - } - */ - if($GLOBALS['egw_info']['user']['apps']['preferences']) - { - $this->_add_topmenu_item('preferences'); - } + /*if($GLOBALS['egw_info']['user']['apps']['manual']) + { + $this->_add_topmenu_item('manual'); + } + */ + if($GLOBALS['egw_info']['user']['apps']['preferences']) + { + $this->_add_topmenu_item('preferences'); + } if($GLOBALS['egw_info']['user']['apps']['manual'] && $this->apps['manual']) { $this->_add_topmenu_item('manual'); } - //$this->_add_topmenu_item('about',lang('About %1',$GLOBALS['egw_info']['apps'][$GLOBALS['egw_info']['flags']['currentapp']]['title'])); - $this->_add_topmenu_item('logout'); + //$this->_add_topmenu_item('about',lang('About %1',$GLOBALS['egw_info']['apps'][$GLOBALS['egw_info']['flags']['currentapp']]['title'])); + $this->_add_topmenu_item('logout'); - $this->tplsav2->assign('info_icons',$this->topmenu_icon_arr); + $this->tplsav2->assign('info_icons',$this->topmenu_icon_arr); - $this->_add_topmenu_info_item($this->_user_time_info()); - $this->_add_topmenu_info_item($this->_current_users()); - $this->_add_topmenu_info_item($this->_get_quick_add()); + if($GLOBALS['egw_info']['user']['apps']['notifications']) + { + $this->_add_topmenu_info_item($this->_get_notification_bell()); + } + $this->_add_topmenu_info_item($this->_user_time_info()); + $this->_add_topmenu_info_item($this->_current_users()); + $this->_add_topmenu_info_item($this->_get_quick_add()); - $this->tplsav2->display('topmenu.tpl.php'); - } - - + $this->tplsav2->display('topmenu.tpl.php'); + } }