From 5b699573e4bfcb70291e82ccc1d70ba7c1577741 Mon Sep 17 00:00:00 2001 From: Christian Binder Date: Wed, 30 Jan 2008 18:58:00 +0000 Subject: [PATCH] enabled class autoloading for notifications, let admins enable or disable notification backends, security fixes for email and egwpopup backend, unified link arrays used for notifications --- calendar/inc/class.bocalupdate.inc.php | 15 +- etemplate/inc/class.bo_tracking.inc.php | 59 ++-- ...on.inc.php => class.notifications.inc.php} | 282 ++++++++++++++---- ....php => class.notifications_email.inc.php} | 34 ++- ....php => class.notifications_iface.inc.php} | 4 +- ....php => class.notifications_popup.inc.php} | 64 ++-- ...p => class.notifications_winpopup.inc.php} | 10 +- notifications/inc/hook_admin.inc.php | 9 +- notifications/inc/hook_preferences.inc.php | 10 +- notifications/inc/hook_settings.inc.php | 31 +- notifications/setup/setup.inc.php | 2 +- notifications/templates/default/config.tpl | 49 ++- phpgwapi/templates/idots/css/idots.css | 4 +- .../templates/jerryr/css/brushed-metal.css | 4 +- phpgwapi/templates/jerryr/css/jerryr.css | 4 +- 15 files changed, 406 insertions(+), 175 deletions(-) rename notifications/inc/{class.notification.inc.php => class.notifications.inc.php} (51%) rename notifications/inc/{class.notification_email.inc.php => class.notifications_email.inc.php} (75%) rename notifications/inc/{class.iface_notification.inc.php => class.notifications_iface.inc.php} (92%) rename notifications/inc/{class.notification_popup.inc.php => class.notifications_popup.inc.php} (68%) rename notifications/inc/{class.notification_winpopup.inc.php => class.notifications_winpopup.inc.php} (95%) diff --git a/calendar/inc/class.bocalupdate.inc.php b/calendar/inc/class.bocalupdate.inc.php index cf8d5e1739..a9540afc48 100644 --- a/calendar/inc/class.bocalupdate.inc.php +++ b/calendar/inc/class.bocalupdate.inc.php @@ -605,9 +605,8 @@ class bocalupdate extends bocal } // send via notification_app if($GLOBALS['egw_info']['apps']['notifications']['enabled']) { - require_once(EGW_INCLUDE_ROOT. '/notifications/inc/class.notification.inc.php'); try { - $notification = new notification(); + $notification = new notifications(); $notification->set_receivers(array($userid)); $notification->set_message($body); $notification->set_sender($senderid); @@ -621,7 +620,7 @@ class bocalupdate extends bocal continue; } } else { - error_log('calendar: cannot send any notifications because notification-app is not installed'); + error_log('calendar: cannot send any notifications because notifications is not installed'); } } } @@ -869,13 +868,15 @@ class bocalupdate extends bocal /* this is needed for notification-app * notification-app creates the link individual for * every user, so we must provide a neutral link-style + * if calendar implements tracking in near future, this part can be deleted */ $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']; + $link_arr['view'] = array( 'menuaction' => 'calendar.uiforms.edit', + 'cal_id' => $event['id'], + 'date' => $eventStart_arr['full'], + ); + $link_arr['popup'] = '750x400'; $details['link_arr'] = $link_arr; $dis = array(); diff --git a/etemplate/inc/class.bo_tracking.inc.php b/etemplate/inc/class.bo_tracking.inc.php index d2bdffb02f..122adf7e11 100644 --- a/etemplate/inc/class.bo_tracking.inc.php +++ b/etemplate/inc/class.bo_tracking.inc.php @@ -22,7 +22,7 @@ require_once(EGW_API_INC.'/class.html.inc.php'); * 1. set the required class-vars: app, id_field * 2. optional set class-vars: creator_field, assigned_field, check2prefs * 3. implement the required methods: get_config, get_details - * 4. optionally re-implement: get_subject, get_body, get_attachments, get_link, get_message + * 4. optionally re-implement: get_title, get_subject, get_body, get_attachments, get_link, get_notification_link, get_message * They are all documented in this file via phpDocumentor comments. */ class bo_tracking @@ -370,16 +370,14 @@ class bo_tracking if ($GLOBALS['egw_info']['apps']['notifications']['enabled']) { // send via notification_app $receiver = is_numeric($user_or_lang) ? $user_or_lang : $email; - require_once(EGW_INCLUDE_ROOT. '/notifications/inc/class.notification.inc.php'); try { - $notification = new notification(); + $notification = new notifications(); $notification->set_receivers(array($receiver)); - $notification->set_message($this->get_body(false,$data,$old)); // set message as plaintext - $notification->set_message($this->get_body(true,$data,$old)); // and html + $notification->set_message($this->get_body(false,$data,$old,false)); // set message as plaintext + $notification->set_message($this->get_body(true,$data,$old,false)); // and html $notification->set_sender($this->get_sender($data,$old,true)); $notification->set_subject($this->get_subject($data,$old)); - // does not work atm - //$notification->set_links(array($this->get_notification_link($data,$old))); + $notification->set_links(array($this->get_notification_link($data,$old))); $attachments = $this->get_attachments($data,$old); if(is_array($attachments)) { $notification->set_attachments($attachments); } $notification->send(); @@ -389,7 +387,7 @@ class bo_tracking return false; } } else { - error_log('tracking: cannot send any notifications because notification-app is not installed'); + error_log('tracking: cannot send any notifications because notifications is not installed'); } return true; @@ -450,6 +448,23 @@ class bo_tracking //echo "

bo_tracking::get_sender()='".htmlspecialchars($sender)."'

\n"; return $sender; } + + /** + * Get the title for a given entry, can be reimplemented + * + * @param array $data + * @param array $old + * @return string + */ + function get_title($data,$old) + { + if (!is_object($GLOBALS['egw']->link)) + { + require_once(EGW_API_INC.'/class.bolink.inc.php'); + $GLOBALS['egw']->link =& new bolink(); + } + return $GLOBALS['egw']->link->title($this->app,$data[$this->id_field]); + } /** * Get the subject for a given entry, can be reimplemented @@ -532,7 +547,7 @@ class bo_tracking } /** - * Get a link for notifications to view the entry + * Get a link for notifications to view the entry, can be reimplemented * * @param array $data * @param array $old @@ -542,21 +557,16 @@ class bo_tracking { if (!is_object($GLOBALS['egw']->link)) { - require_once(EGW_API_INC.'/class.bolink.inc.php'); - $GLOBALS['egw']->link =& new bolink(); + $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'], + return array( 'text' => $this->get_title($data,$old), + 'view' => $view, + 'popup' => $GLOBALS['egw']->link->is_popup($this->app,'view'), ); - } else { - return false; } - } - + return false; + } /** * Get the body of the notification message, can be reimplemented @@ -564,9 +574,10 @@ class bo_tracking * @param boolean $html_email * @param array $data * @param array $old + * @param boolean $integrate_link to have links embedded inside the body * @return string */ - function get_body($html_email,$data,$old) + function get_body($html_email,$data,$old,$integrate_link = true) { $body = ''; if ($html_email) @@ -578,7 +589,7 @@ class bo_tracking { $body .= $this->format_line($html_email,'message',false,$message); } - if (($link = $this->get_link($data,$old))) + if ($integrate_link && ($link = $this->get_link($data,$old))) { $body .= $this->format_line($html_email,'link',false,lang('You can respond by visiting:'),$link); } @@ -621,7 +632,7 @@ class bo_tracking if (!$this->html_content_allow) $line = $this->html->htmlspecialchars($line); // XSS $color = $modified ? 'red' : false; - $size = '120%'; + $size = '110%'; $bold = false; $background = '#FFFFF1'; switch($type) @@ -686,7 +697,7 @@ class bo_tracking } /** - * Get the attachments for a notification mail + * Get the attachments for a notification * * @param array $data * @param array $old diff --git a/notifications/inc/class.notification.inc.php b/notifications/inc/class.notifications.inc.php similarity index 51% rename from notifications/inc/class.notification.inc.php rename to notifications/inc/class.notifications.inc.php index de7889f1a8..9900190d0e 100644 --- a/notifications/inc/class.notification.inc.php +++ b/notifications/inc/class.notifications.inc.php @@ -20,11 +20,11 @@ require_once(EGW_INCLUDE_ROOT.'/phpgwapi/inc/class.config.inc.php'); * This class takes care about the notification-routing. It chooses one or more backends for each * given recipient depending on its prefs or falls back to self::_fallback * - * The classes doing the notifications are called notification_ and should only be + * The classes doing the notifications are called notifications_ and should only be * called from this class. The backend's job is to deliver ONE message to ONE recipient. * */ -final class notification { +final class notifications { /** * Appname @@ -36,29 +36,58 @@ final class notification { */ const _fallback = 'email_only'; + /** + * registered backends + * @var array + */ + private $backends = array('popup', 'winpopup', 'email'); + /** - * 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 - */ + * 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'), + 'disable' => false, // will be created by $this->get_available_chains + 'email_only' => false, // will be created by $this->get_available_chains + 'all' => false, // will be created by $this->get_available_chains + 'popup_only' => array('popup' => 'stop'), + 'popup_or_email' => array('popup' => 'fail', 'email' => 'stop'), + //'popup_or_sms' => array('popup' => 'fail', 'sms' => 'stop'), + 'popup_and_email' => array('popup' => 'continue', 'email' => 'stop'), + 'popup_and_winpopup' => array('popup' => 'continue', 'winpopup' => 'stop'), + 'winpopup_only' => array('winpopup' => 'stop'), + 'winpopup_or_email' => array('winpopup' => 'fail', 'email' => 'stop'), + //'winpopup_or_sms' => array('winpopup' => 'fail', 'sms' => 'stop'), + 'winpopup_and_email' => array('winpopup' => 'continue', 'email' => 'stop'), + //'sms_only' => array('sms' => 'stop'), + ); + + /** + * human readable descriptions for the notification chains + * @var array + */ + private $chains_descriptions = array( + 'disable' => 'do not notify me at all', + 'email_only' => 'E-Mail only', + 'all' => 'all possible notification backends', + 'popup_only' => 'eGroupWare-Popup only', + 'popup_or_email' => 'eGroupWare-Popup first, if that fails notify me by E-Mail', + //'popup_or_sms' => 'eGroupware-Popup first, if that fails notify me by SMS', + 'popup_and_email' => 'eGroupWare-Popup and E-Mail', + 'popup_and_winpopup' => 'eGroupWare-Popup and Windows-Popup', + 'winpopup_only' => 'Windows-Popup only', + 'winpopup_or_email' => 'Windows-Popup first, if that fails notify me by E-Mail', + //'winpopup_or_sms' => 'Windows-Popup first, if that fails notify me by SMS', + 'winpopup_and_email' => 'Windows-Popup and E-Mail', + //'sms_only' => 'SMS only', ); /** @@ -111,7 +140,7 @@ final class notification { private $config; /** - * constructor of notification + * constructor of notifications * */ public function __construct() { @@ -221,13 +250,13 @@ final class notification { /** * sets the notification links * - * @param array $links link array (like defined in $this->add_link) + * @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']); + $this->add_link($link['text'], $link['view'], $link['popup']); } } return true; @@ -235,16 +264,16 @@ final class notification { /** * 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 + * + * @param string $_text a descriptive text for the link + * @param array $_view all params needed to view the link (name => value pairs) + * @param string $_popup if link can be viewed in a popup something like '300x200' otherwise false */ - public function add_link($_menuaction, $_params, $_text) { - if(!$_menuaction || !$_params || !$_text) { return false; } - $this->links[] = (object)array( 'menuaction' => $_menuaction, - 'params' => $_params, - 'text' => $_text, + public function add_link($_text, $_view, $_popup = false) { + if(!$_view || !$_text) { return false; } + $this->links[] = (object)array( 'text' => $_text, + 'view' => $_view, + 'popup' => $_popup, ); return true; } @@ -252,7 +281,7 @@ final class notification { /** * sets the notification attachments * - * @param array $attachments attachment array (like defined in $this->add_attachment + * @param array $_attachments attachment array (like defined in $this->add_attachment) */ public function set_attachments(array $_attachments) { $this->attachments = array(); // clear array if set @@ -273,10 +302,10 @@ final class notification { * 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. + * @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; } @@ -289,20 +318,24 @@ final class notification { } /** - * sends notification + * sends notifications */ public function send() { if (!is_object($this->sender)) { - throw new Exception('Error: cannot send notification. No sender supplied'); + throw new Exception('Error: cannot send notifications. No sender supplied'); } if (!is_array($this->receivers) || count($this->receivers) == 0) { - throw new Exception('Error: cannot send notification. No receivers supplied'); + throw new Exception('Error: cannot send notifications. No receivers supplied'); } if(!$messages = $this->create_messages($this->message_plain, $this->message_html)) { - throw new Exception('Error: cannot send notification. No valid messages supplied'); + throw new Exception('Error: cannot send notifications. No valid messages supplied'); } + + $available_chains = $this->get_available_chains('routing'); + foreach ($this->receivers as $receiver) { $user_notified = false; + $prepend_message = ''; $backend_errors = array(); try { // system or non-system user @@ -318,37 +351,42 @@ final class notification { $preferences = $prefs->read(); $preferences = (object)$preferences[self::_appname]; if($preferences->notification_chain) { - $notification_chain = $this->notification_chains[$preferences->notification_chain]; + // fallback: admin disabled user-chosen chain + if(!$notification_chain = $available_chains[$preferences->notification_chain]) { + $prepend_message .= lang( 'This eGroupWare notification has been sent to you by mail because your' + .' chosen notification-chain has been disabled by the administrator.' + .' Please choose another notification-chain in your preferences!'); + $notification_chain = $available_chains[self::_fallback]; + } } else { - $notification_chain = $this->notification_chains[self::_fallback]; // fallback: no prefs + $notification_chain = $available_chains[self::_fallback]; // fallback: no prefs } } else { - $notification_chain = $this->notification_chains[self::_fallback]; // fallback: no rights to app + $notification_chain = $available_chains[self::_fallback]; // fallback: no rights to app } } else { // non-system user $receiver->handle = $receiver->account_email; - $notification_chain = $this->notification_chains[self::_fallback]; // fallback: non-system user + $notification_chain = $available_chains[self::_fallback]; // fallback: non-system user } - if($notification_chain === false) { + if($notification_chain == 'disable') { continue; //user disabled notifications } foreach($notification_chain as $notification_backend => $action) { try { - $notification_backend = 'notification_'.$notification_backend; + $notification_backend = self::_appname.'_'.$notification_backend; if(!file_exists(EGW_INCLUDE_ROOT. SEP. self::_appname. SEP. 'inc'. SEP. 'class.'. $notification_backend. '.inc.php')) { throw new Exception('file for '.$notification_backend. ' does not exist'); } - require_once(EGW_INCLUDE_ROOT. SEP. self::_appname. SEP. 'inc'. SEP. 'class.'. $notification_backend. '.inc.php'); - $obj = @new $notification_backend( $this->sender, $receiver, $this->config, $preferences ); - if ( !is_a( $obj, iface_notification )) { + $obj = new $notification_backend( $this->sender, $receiver, $this->config, $preferences ); + if ( !($obj instanceof notifications_iface) ) { unset ( $obj ); - throw new Exception($notification_backend. ' is no implementation of iface_notification'); + throw new Exception($notification_backend. ' is no implementation of notifications_iface'); } - $obj->send($messages, $this->subject, $this->links, $this->attachments); + $obj->send($this->prepend_message($messages, $prepend_message), $this->subject, $this->links, $this->attachments); } catch (Exception $exception) { $backend_errors[] = $notification_backend.' failed: '.$exception->getMessage(); @@ -378,18 +416,15 @@ final class notification { } /** - * this function creates an array with the message as plaintext and html + * creates an array with the message as plaintext and html * - * @param string $message_plain - * @param string $message_html - * @param array $links - * @return array $messages + * @param string $_message_plain + * @param string $_message_html + * @return plain and html message in one array, $messages['plain'] and $messages['html'] */ private function create_messages($_message_plain = '', $_message_html = '') { if(empty($_message_plain) && empty($_message_html)) { return false; } // no message set $messages = array(); - $messages['plain'] = array(); - $messages['html'] = array(); // create the messages if(!empty($_message_plain)) { @@ -407,6 +442,33 @@ final class notification { return $messages; } + /** + * prepends another message to the messages array + * + * @param array $_messages the messages array from create_messages() + * @param string $_prepend just a plain message to prepend, no html! + * @return plain and html message in one array including the prepended message, $messages['plain'] and $messages['html'] + */ + private function prepend_message(array $_messages, $_prepend = null) { + if(strlen($_prepend) > 0) { + foreach($_messages as $key => $value) { + switch($key) { + case 'plain': + $_messages[$key] = $_prepend."\n\n".$value; + break; + case 'html': + // ToDo: move stylesheet to a nicer place + $_messages[$key] = '
'.$_prepend.'
'.$value; + break; + default: + break; + } + } + } + + return $_messages; + } + /** * returns specified part from a given mailaddress * @@ -434,5 +496,99 @@ final class notification { } return false; } - + + /** + * returns notification chains based on admin prefs + * @abstract the available chains can be retrieved in two different output formats: + * routing: array with common and enabled chains, chain-name as key and the chain-array as value (used for message-routing) + * human: array with common, enabled and disabled chains, chain-name as key and a human-readable description as value (used for config) + * + * @param string $_output one of: 'routing' or 'human', defaults to 'routing' + * @return array containing notification chains, output like given in $_output + */ + public function get_available_chains($_output = 'routing') { + // determine enabled backends from config + $enabled_backends = array(); + foreach($this->backends as $id => $backend) { + switch($backend) { + case 'email': + $enabled_backends[$backend] = true; // fallback must always be enabled + break; + default: + $param = $backend.'_enable'; + $enabled_backends[$backend] = $this->config->{$param} == true ? true : false; + break; + } + } + + $enabled_chains = array(); + $disabled_chains = array(); + foreach($this->notification_chains as $key => $chain) { + $allow_chain = true; + if(is_array($chain)) { + foreach($chain as $name => $action) { + if(!$enabled_backends[$name]) { + $allow_chain = false; // disable whole chain if one backend is disabled + } + } + if($allow_chain) { + $enabled_chains[$key] = $chain; + } else { + $disabled_chains[$key] = $chain; + } + } + } + + // common chain + $common_chains = array(); + $common_chains['disable'] = 'disable'; + $common_chains['email_only'] = array('email' => 'stop'); + // create the 'all' chain from the enabled backends + $chain_all = array(); + $backend_count = 1; + foreach($enabled_backends as $backend => $enabled) { + if($enabled) { + $chain_all[$backend] = count($enabled_backends) == $backend_count ? 'stop' : 'continue'; + } + $backend_count++; + } + $common_chains['all'] = $chain_all; + + switch($_output) { + case 'human': + $chain_groups = array( + lang('Common chains') => 'common_chains', + lang('Enabled chains') => 'enabled_chains', + lang('Disabled chains') => 'disabled_chains', + ); + $suffix = '_human'; + // create descriptions for each chain key in each group + foreach($chain_groups as $name => $arr_name) { + ${$arr_name.$suffix} = array(); + foreach(${$arr_name} as $key => $value) { + if($arr_name == 'disabled_chains') { + ${$arr_name.$suffix}[$key] = '('.lang('Disabled').') '.lang($this->chains_descriptions[$key]); + } else { + ${$arr_name.$suffix}[$key] = lang($this->chains_descriptions[$key]); + } + } + } + // summarize all groups with minimum one chain to the final array + $chains_final = array(); + foreach($chain_groups as $name => $arr_name) { + if(is_array(${$arr_name.$suffix}) && count(${$arr_name.$suffix}) > 0) { + $chains_final[$name] = ${$arr_name.$suffix}; + } + } + return $chains_final; + break; + + case 'routing': + default: + return array_merge($common_chains, $enabled_chains); + break; + } + + return false; + } } \ No newline at end of file diff --git a/notifications/inc/class.notification_email.inc.php b/notifications/inc/class.notifications_email.inc.php similarity index 75% rename from notifications/inc/class.notification_email.inc.php rename to notifications/inc/class.notifications_email.inc.php index f7456f49ab..1b7ccadb1a 100644 --- a/notifications/inc/class.notification_email.inc.php +++ b/notifications/inc/class.notifications_email.inc.php @@ -9,14 +9,10 @@ * @author Christian Binder */ -require_once('class.iface_notification.inc.php'); -require_once(EGW_INCLUDE_ROOT.'/phpgwapi/inc/class.html.inc.php'); -require_once(EGW_INCLUDE_ROOT. '/phpgwapi/inc/class.send.inc.php'); - /** * User notification via email. */ -class notification_email implements iface_notification { +class notifications_email implements notifications_iface { /** * Appname @@ -66,7 +62,7 @@ class notification_email implements iface_notification { private $html; /** - * constructor of notification_email + * constructor of notifications_email * * @param object $_sender * @param object $_recipient @@ -84,7 +80,7 @@ class notification_email implements iface_notification { { $this->mail = new send(); } - $this->html = & html::singleton(); + $this->html = html::singleton(); } /** @@ -97,7 +93,7 @@ class notification_email implements iface_notification { */ public function send(array $_messages, $_subject = false, $_links = false, $_attachments = false) { $body_plain = $_messages['plain'].$this->render_links($_links, false, $this->preferences->external_mailclient); - $body_html = "\n\n".$_messages['html'].$this->render_links($_links, true, $this->preferences->external_mailclient)."\n\n"; + $body_html = "\n".$_messages['html'].$this->render_links($_links, true, $this->preferences->external_mailclient)."\n\n"; $this->mail->ClearAddresses(); $this->mail->ClearAttachments(); @@ -118,7 +114,7 @@ class notification_email implements iface_notification { throw new Exception("Failed sending notification message via email.$error"); } } - + /** * renders plaintext/html links from given link array * @@ -134,18 +130,24 @@ class notification_email implements iface_notification { // php distinguishes between missing and present(null) arguments if(is_null($_render_html)) { $_render_html = false; } if(is_null($_render_external)) { $_render_external = true; } + $newline = $_render_html ? "
" : "\n"; + $hruler = $_render_html ? $this->html->hr() : ''; - $newline = $_render_html ? "
" : "\n"; - $link_array = array(); + $rendered_links = array(); foreach($_links as $link) { - if($_render_external) { - $link->params['no_popup'] = 1; + if($_render_external || ! $link->popup) { $link->view['no_popup'] = 1; } + $url = $this->html->link('/index.php', $link->view); + // do not expose sensitive data + $url = preg_replace('/(sessionid|kp3|domain)=[^&]+&?/','',$url); + // complete missing protocol and domain part if needed + if ($url{0} == '/' && $_render_external) { + $url = ($_SERVER['HTTPS'] || $GLOBALS['egw_info']['server']['enforce_ssl'] ? 'https://' : 'http://'). + ($GLOBALS['egw_info']['server']['hostname'] ? $GLOBALS['egw_info']['server']['hostname'] : $_SERVER['HTTP_HOST']).$url; } - $url = $this->html->link('/index.php?menuaction='.$link->menuaction, $link->params); - $link_array[] = $_render_html ? $this->html->a_href($link->text, $url) : $url; + $rendered_links[] = $_render_html ? $this->html->a_href($link->text, $url, false, 'target="_blank"') : $url; } - return lang('Linked entries:').$newline.implode($newline,$link_array); + return $hruler.$newline.lang('Linked entries:').$newline.implode($newline,$rendered_links); } } diff --git a/notifications/inc/class.iface_notification.inc.php b/notifications/inc/class.notifications_iface.inc.php similarity index 92% rename from notifications/inc/class.iface_notification.inc.php rename to notifications/inc/class.notifications_iface.inc.php index cc552ab595..b65b5005ea 100644 --- a/notifications/inc/class.iface_notification.inc.php +++ b/notifications/inc/class.notifications_iface.inc.php @@ -12,7 +12,7 @@ /** * Instant user notification */ -interface iface_notification { +interface notifications_iface { /** * constructor @@ -25,7 +25,7 @@ interface iface_notification { public function __construct($_sender, $_recipient, $_config = null, $_preferences = null); /** - * sends notification + * sends one notification to one recipient * * @abstract NOTE, $_messages is an array that contains * the notification message in plain and html diff --git a/notifications/inc/class.notification_popup.inc.php b/notifications/inc/class.notifications_popup.inc.php similarity index 68% rename from notifications/inc/class.notification_popup.inc.php rename to notifications/inc/class.notifications_popup.inc.php index cc20b11406..016b77cbf2 100644 --- a/notifications/inc/class.notification_popup.inc.php +++ b/notifications/inc/class.notifications_popup.inc.php @@ -6,25 +6,31 @@ * @package notifications * @subpackage backends * @link http://www.egroupware.org - * @author Cornelius Weiss + * @author Cornelius Weiss , Christian Binder * @version $Id$ */ -require_once('class.iface_notification.inc.php'); -require_once(EGW_INCLUDE_ROOT.'/phpgwapi/inc/class.html.inc.php'); - /** * Instant user notification with egroupware popup. * * @abstract egwpopup is a two stage notification. In the first stage - * notification is written into self::_notification_egwpopup - * table. In the second stage a request from the client reads + * notification is written into self::_notification_table. + * In the second stage a request from the client reads * out the table to look if there is a notificaton for this * client. The second stage is done in class.ajaxnotifications.inc.php + * + * Todo: + * - save the messages by uid instead of sessionid into the notification table, this + * has several advantages (users poll the messages via ajax from multiple logins, and + * do not have to read one message twice, poll after re-login with different sessionid) + * - delete message from the table only if the user has really seen it + * - if the above things are done we should get rid of rendering the links here, + * instead it should be done by the ajax class, so sessionids in links could be possible then + * * (multidisplay is supported) * */ -class notification_popup implements iface_notification { +class notifications_popup implements notifications_iface { /** * Appname @@ -79,7 +85,7 @@ class notification_popup implements iface_notification { private $html; /** - * constructor of notification_egwpopup + * constructor of notifications_egwpopup * * @param object $_sender * @param object $_recipient @@ -95,7 +101,7 @@ class notification_popup implements iface_notification { $this->preferences = $_preferences; $this->db = &$GLOBALS['egw']->db; $this->db->set_app( self::_appname ); - $this->html = & html::singleton(); + $this->html = html::singleton(); } /** @@ -126,8 +132,7 @@ class notification_popup implements iface_notification { } /** - * saves notification into database so that the client can fetch it from - * there via notification->get + * saves notification into database so that the client can fetch it from there * * @param string $_message * @param array $_user_sessions @@ -145,24 +150,41 @@ class notification_popup implements iface_notification { /** * renders plaintext/html links from given link array + * should be moved to the ajax class later - like mentioned in the Todo * * @param array $_links - * @return html rendered link(s) as complete string (jspopup) + * @return html rendered link(s) as complete string with jspopup or a new window */ private function render_links($_links = false) { if(!is_array($_links) || count($_links) == 0) { return false; } - $newline = "
"; + $newline = "
"; - $link_array = array(); + $rendered_links = array(); foreach($_links as $link) { - $url = $this->html->link('/index.php?menuaction='.$link->menuaction, $link->params); - $menuaction_arr = explode('.',$link->menuaction); - $application = $menuaction_arr[0]; - $image = $application ? $this->html->image($application,'navbar',$link->text,'align="middle" style="width: 24px; margin-right: 0.5em;"') : ''; - $link_array[] = $this->html->div($image.$link->text,'onclick="'.$this->jspopup($url).'"','jspopup'); + if(!$link->popup) { $link->view['no_popup'] = 1; } + + $url = $this->html->link('/index.php', $link->view); + // do not expose sensitive data + $url = preg_replace('/(sessionid|kp3|domain)=[^&]+&?/','',$url); + // extract application-icon from menuaction + if($link->view['menuaction']) { + $menuaction_arr = explode('.',$link->view['menuaction']); + $application = $menuaction_arr[0]; + $image = $application ? $this->html->image($application,'navbar',$link->text,'align="middle" style="width: 24px; margin-right: 0.5em;"') : ''; + } else { + $image = ''; + } + if($link->popup) { + $dimensions = explode('x', $link->popup); + $rendered_links[] = $this->html->div($image.$link->text,'onclick="'.$this->jspopup($url, '_blank', $dimensions[0], $dimensions[1]).'"','link'); + } else { + $rendered_links[] = $this->html->div($this->html->a_href($image.$link->text, $url, false, 'target="_blank"'),'','link'); + } + + } + if(count($rendered_links) > 0) { + return $this->html->bold(lang('Linked entries:')).$newline.implode($newline,$rendered_links); } - - return $this->html->bold(lang('Linked entries:')).$newline.implode($newline,$link_array); } /** diff --git a/notifications/inc/class.notification_winpopup.inc.php b/notifications/inc/class.notifications_winpopup.inc.php similarity index 95% rename from notifications/inc/class.notification_winpopup.inc.php rename to notifications/inc/class.notifications_winpopup.inc.php index 36deee0106..a3fe78efc5 100644 --- a/notifications/inc/class.notification_winpopup.inc.php +++ b/notifications/inc/class.notifications_winpopup.inc.php @@ -9,12 +9,10 @@ * @author Christian Binder */ -require_once('class.iface_notification.inc.php'); - /** * User notification via winpopup. */ -class notification_winpopup implements iface_notification { +class notifications_winpopup implements notifications_iface { /** * Appname @@ -69,10 +67,10 @@ class notification_winpopup implements iface_notification { * * @var string */ - private $netbios_command; + private $netbios_command = "/bin/echo [MESSAGE] >> /Users/jaytraxx/winpopup.out"; /** - * constructor of notification_winpopup + * constructor of notifications_winpopup * * @param object $_sender * @param object $_recipient @@ -85,7 +83,7 @@ class notification_winpopup implements iface_notification { if(!$this->netbios_command) { throw new Exception( 'Winpopup plugin not configured yet. Skipped sending notification message. '. 'Please check var "netbios_command" in winpopup backend '. - '('.EGW_INCLUDE_ROOT. SEP. self::_appname. SEP. 'inc'. SEP. 'class.notification_winpopup.inc.php).'); + '('.EGW_INCLUDE_ROOT. SEP. self::_appname. SEP. 'inc'. SEP. 'class.notifications_winpopup.inc.php).'); } $this->sender = $_sender; $this->recipient = $_recipient; diff --git a/notifications/inc/hook_admin.inc.php b/notifications/inc/hook_admin.inc.php index 19afe631f4..fde6c0ffda 100644 --- a/notifications/inc/hook_admin.inc.php +++ b/notifications/inc/hook_admin.inc.php @@ -8,9 +8,10 @@ * @author Christian Binder * @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); + $file = Array( 'Site Configuration' => $GLOBALS['egw']->link('/index.php', array( + 'menuaction' => 'admin.uiconfig.index', + 'appname' => $appname, + ))); + display_section($appname,$file); ?> diff --git a/notifications/inc/hook_preferences.inc.php b/notifications/inc/hook_preferences.inc.php index 637674e30f..f3ed29bbee 100644 --- a/notifications/inc/hook_preferences.inc.php +++ b/notifications/inc/hook_preferences.inc.php @@ -8,10 +8,10 @@ * @author Cornelius Weiss * @version $Id$ */ - - $title = $appname; - $file = Array( 'Preferences' => $GLOBALS['egw']->link('/index.php',array('menuaction'=>'preferences.uisettings.index','appname'=>'notifications')), - ); - display_section($appname,$title,$file); + $file = Array( 'Preferences' => $GLOBALS['egw']->link('/index.php',array( + 'menuaction' => 'preferences.uisettings.index', + 'appname' => $appname, + ))); + display_section($appname,$file); ?> diff --git a/notifications/inc/hook_settings.inc.php b/notifications/inc/hook_settings.inc.php index 400e3f601b..faed19e58d 100644 --- a/notifications/inc/hook_settings.inc.php +++ b/notifications/inc/hook_settings.inc.php @@ -1,6 +1,6 @@ */ -$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'), - ); +$notifications = new notifications(); +$available_chains = $notifications->get_available_chains('human'); + +$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.', + 'values' => $available_chains, + 'help' => 'Choose a notification-chain. You will be notified over the backends included in the chain.
' + .'Note: If a notification-chain is marked as "disabled", your Administrator does not allow one or' + .' more of the backends included in the chain and notifications falls back to "E-Mail" while notifying you.', 'xmlrpc' => True, 'admin' => False ), diff --git a/notifications/setup/setup.inc.php b/notifications/setup/setup.inc.php index 6047b95ab2..39d0f21b89 100644 --- a/notifications/setup/setup.inc.php +++ b/notifications/setup/setup.inc.php @@ -27,7 +27,7 @@ $setup_info[NOTIFICATION_APP]['maintainer'] = array( ); $setup_info[NOTIFICATION_APP]['license'] = 'GPL'; $setup_info[NOTIFICATION_APP]['description'] = -'Instant norification of users via various channels.'; +'Instant notification of users via various channels.'; /* The hooks this app includes, needed for hooks registration */ $setup_info[NOTIFICATION_APP]['hooks'][] = 'after_navbar'; diff --git a/notifications/templates/default/config.tpl b/notifications/templates/default/config.tpl index 701a03cdcf..fae52fd0d5 100644 --- a/notifications/templates/default/config.tpl +++ b/notifications/templates/default/config.tpl @@ -11,9 +11,54 @@ -  {lang_Notification_backends_(Preferences_for_enabling/disabling_will_follow_soon)} +  {lang_eGroupware-Popup_backend} - + + {lang_Enable_eGroupWare-Popup_backend} + + + + + +  {lang_Windows-Popup_backend} + + + {lang_Enable_Windows-Popup_backend} + + + + + + diff --git a/phpgwapi/templates/idots/css/idots.css b/phpgwapi/templates/idots/css/idots.css index 3ea04b16a6..7cc642cd4e 100755 --- a/phpgwapi/templates/idots/css/idots.css +++ b/phpgwapi/templates/idots/css/idots.css @@ -463,7 +463,7 @@ Preferences tabs */ #notificationwindow_message > table { - font-size: 90%; + font-size: 95%; } #notificationwindow_message hr @@ -473,7 +473,7 @@ Preferences tabs height: 1px; } -#notificationwindow_message .jspopup +#notificationwindow_message .link { cursor: pointer; } diff --git a/phpgwapi/templates/jerryr/css/brushed-metal.css b/phpgwapi/templates/jerryr/css/brushed-metal.css index 0b9f7ccee4..67d4631d6f 100644 --- a/phpgwapi/templates/jerryr/css/brushed-metal.css +++ b/phpgwapi/templates/jerryr/css/brushed-metal.css @@ -722,7 +722,7 @@ body { */ #notificationwindow_message > table { - font-size: 90%; + font-size: 95%; } #notificationwindow_message hr @@ -732,7 +732,7 @@ body { height: 1px; } -#notificationwindow_message .jspopup +#notificationwindow_message .link { cursor: pointer; } diff --git a/phpgwapi/templates/jerryr/css/jerryr.css b/phpgwapi/templates/jerryr/css/jerryr.css index d91add25ed..b615952470 100644 --- a/phpgwapi/templates/jerryr/css/jerryr.css +++ b/phpgwapi/templates/jerryr/css/jerryr.css @@ -646,7 +646,7 @@ body { */ #notificationwindow_message > table { - font-size: 90%; + font-size: 95%; } #notificationwindow_message hr @@ -656,7 +656,7 @@ body { height: 1px; } -#notificationwindow_message .jspopup +#notificationwindow_message .link { cursor: pointer; }