* @package tracker * @copyright (c) 2007-16 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ use EGroupware\Api; use EGroupware\Api\Link; /** * Tracker - tracking object for the tracker */ class infolog_tracking extends Api\Storage\Tracking { /** * Application we are tracking (required!) * * @var string */ var $app = 'infolog'; /** * Name of the id-field, used as id in the history log (required!) * * @var string */ var $id_field = 'info_id'; /** * Name of the field with the creator id, if the creator of an entry should be notified * * @var string */ var $creator_field = 'info_owner'; /** * Name of the field with the id(s) of assinged users, if they should be notified * * @var string */ var $assigned_field = 'info_responsible'; /** * Translate field-names to 2-char history status * * @var array */ var $field2history = array( 'info_type' => 'Ty', 'info_from' => 'Fr', 'info_link_id' => 'Li', 'info_id_parent' => 'parent', 'info_cat' => 'Ca', 'info_priority' => 'Pr', 'info_owner' => 'Ow', 'info_access' => 'Ac', 'info_status' => 'St', 'info_percent' => 'Pe', 'info_datecompleted' => 'Co', 'info_location' => 'Lo', 'info_startdate' => 'st', 'info_enddate' => 'En', 'info_responsible' => 'Re', 'info_cc' => 'cc', 'info_subject' => 'Su', 'info_des' => 'De', 'info_location' => 'Lo', // PM fields 'info_planned_time' => 'pT', 'info_replanned_time' => 'replanned', 'info_used_time' => 'uT', 'pl_id' => 'pL', 'info_price' => 'pr', // all custom fields together 'custom' => '#c', ); /** * Translate field-names to labels * * @note The order of these fields is used to determine the order for CSV export * @var array */ var $field2label = array( 'info_type' => 'Type', 'info_from' => 'Contact', 'info_subject' => 'Subject', 'info_des' => 'Description', 'info_link_id' => 'primary link', 'info_id_parent' => 'Parent', 'info_cat' => 'Category', 'info_priority' => 'Priority', 'info_owner' => 'Owner', 'info_modifier' => 'Modifier', 'info_access' => 'Access', 'info_status' => 'Status', 'info_percent' => 'Completed', 'info_datecompleted' => 'Date completed', 'info_datemodified' => 'Last changed', 'info_location' => 'Location', 'info_startdate' => 'Start date', 'info_enddate' => 'Enddate', 'info_responsible' => 'Responsible', 'info_cc' => 'Cc', // PM fields 'info_planned_time' => 'planned time', 'info_replanned_time' => 're-planned time', 'info_used_time' => 'used time', 'pl_id' => 'pricelist', 'info_price' => 'price', // custom fields 'custom' => 'custom fields' ); /** * Instance of the infolog_bo class calling us * * @var infolog_bo */ private $infolog; /** * Constructor * * @param botracker $botracker * @return tracker_tracking */ function __construct(&$infolog_bo) { parent::__construct('infolog'); // add custom fields from infolog $this->infolog =& $infolog_bo; } /** * Get the subject for a given entry * * Reimpleneted to use a New|deleted|modified prefix. * * @param array $data * @param array $old * @return string */ function get_subject($data, $old, $deleted = null, $receiver = null) { if (!empty($data['prefix'])) { $prefix = $data['prefix']; // async notification } elseif (!$old || $old['info_status'] == 'deleted') { $prefix = lang('New %1',lang($this->infolog->enums['type'][$data['info_type']])); } elseif($data['info_status'] == 'deleted') { $prefix = lang('%1 deleted',lang($this->infolog->enums['type'][$data['info_type']])); } else { $prefix = lang('%1 modified',lang($this->infolog->enums['type'][$data['info_type']])); } return $prefix.': '.$data['info_subject']; } /** * Get the modified / new message (1. line of mail body) for a given entry, can be reimplemented * * @param array $data * @param array $old * @return string */ function get_message($data, $old, $receiver = null) { if (!empty($data['message'])) return $data['message']; // async notification if (!$old || $old['info_status'] == 'deleted') { return lang('New %1 created by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]), Api\Accounts::username($this->infolog->user),$this->datetime('now')); } elseif($data['info_status'] == 'deleted') { return lang('%1 deleted by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]), Api\Accounts::username($data['info_modifier']), $this->datetime($data['info_datemodified'])); } return lang('%1 modified by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]), Api\Accounts::username($data['info_modifier']), $this->datetime($data['info_datemodified'])); } /** * Get the details of an entry * * @param array|object $data * @param int|string $receiver nummeric account_id or email address * @return array of details as array with values for keys 'label','value','type' */ function get_details($data,$receiver=null) { //error_log(__METHOD__.__LINE__.' Data:'.array2string($data)); $responsible = array(); if ($data['info_responsible']) { foreach($data['info_responsible'] as $uid) { $responsible[] = Api\Accounts::username($uid); } } if ($GLOBALS['egw_info']['user']['preferences']['infolog']['show_id']) { $id = ' #'.$data['info_id']; } if (is_array($data['info_cat'])) { $data['info_cat'] = array_shift($data['info_cat']); } foreach(array( 'info_type' => lang($this->infolog->enums['type'][$data['info_type']]).$id, 'info_from' => $data['info_from'], 'info_cat' => $data['info_cat'] ? $GLOBALS['egw']->categories->id2name($data['info_cat']) : '', 'info_priority' => lang($this->infolog->enums['priority'][$data['info_priority']]), 'info_owner' => Api\Accounts::username($data['info_owner']), 'info_status' => lang($data['info_status']=='deleted'?'deleted':$this->infolog->status[$data['info_type']][$data['info_status']]), 'info_percent' => (int)$data['info_percent'].'%', 'info_datecompleted' => $data['info_datecompleted'] ? $this->datetime($data['info_datecompleted']) : '', 'info_location' => $data['info_location'], 'info_startdate' => $data['info_startdate'] ? $this->datetime($data['info_startdate'],null) : '', 'info_enddate' => $data['info_enddate'] ? $this->datetime($data['info_enddate'],null) : '', 'info_responsible' => implode(', ',$responsible), 'info_subject' => $data['info_subject'], ) as $name => $value) { //error_log(__METHOD__.__LINE__.' Key:'.$name.' val:'.array2string($value)); if ($name=='info_from' && empty($value)) { if(!empty($data['info_contact']) && is_array($data['link_to']['to_id'])) { $lkeys = array_keys($data['link_to']['to_id']); if (in_array($data['info_contact'],$lkeys)) { list($app,$id) = explode(':',$data['info_contact']); if (!empty($app)&&!empty($id)) $value = Link::title($app,$id); } } else if ($data['info_link_id']) { $this->infolog->link_id2from($data); if (is_array($data['info_contact'])) $value = $data['info_contact']['title']; } } $details[$name] = array( 'label' => lang($this->field2label[$name]), 'value' => $value, ); if ($name == 'info_subject') $details[$name]['type'] = 'summary'; } $details['info_des'] = array( 'value' => $data['info_des'], 'type' => 'multiline', ); // add custom fields for given type $details += $this->get_customfields($data, $data['info_type'], $receiver); //error_log(__METHOD__."(".array2string($data).", $receiver) returning ".array2string($details)); return $details; } /** * Track changes * * Overrides parent to log the modified date in the history, but not to send a notification * * @param array $data current entry * @param array $old = null old/last state of the entry or null for a new entry * @param int $user = null user who made the changes, default to current user * @param boolean $deleted = null can be set to true to let the tracking know the item got deleted or undeleted * @param array $changed_fields = null changed fields from ealier call to $this->changed_fields($data,$old), to not compute it again * @param boolean $skip_notification = false do NOT send any notification * @return int|boolean false on error, integer number of changes logged or true for new entries ($old == null) */ public function track(array $data,array $old=null,$user=null,$deleted=null,array $changed_fields=null,$skip_notification=false) { //error_log(__METHOD__.__LINE__.' notify?'.($skip_notification?'no':'yes').function_backtrace()); $this->user = !is_null($user) ? $user : $GLOBALS['egw_info']['user']['account_id']; $changes = true; if ($old && $this->field2history) { $changes = $this->save_history($data,$old,$deleted,$changed_fields); } // Don't notify if the only change was to the modified date if(is_null($changed_fields)) { $changed_fields = $this->changed_fields($data, $old); $changes = count($changed_fields); // we need that since TRUE evaluates to 1 } //error_log(__METHOD__.__LINE__.array2string($changed_fields)); if(is_array($changed_fields) && $changes == 1 && in_array('info_datemodified', $changed_fields)) { return count($changes); } // do not run do_notifications if we have no changes if ($changes && !$skip_notification && !$this->do_notifications($data,$old,$deleted)) { $changes = false; } return $changes; } /** * Get a notification-config value * * @param string $name * - 'copy' array of email addresses notifications should be copied too, can depend on $data * - 'lang' string lang code for copy mail * - 'sender' string send email address * @param array $data current entry * @param array $old = null old/last state of the entry or null for a new entry * @return mixed */ function get_config($name,$data,$old=null) { unset($old); // not used, but required function signature switch($name) { case 'reply_to': // for async notification use owner of infolog, otherwise the user causing the change if(($reply_to = Api\Accounts::getInstance() ->id2name(!empty($GLOBALS['egw_info']['flags']['async-service']) ? $data['info_owner'] : $this->user, 'account_email' ))) { $config = $reply_to; } break; case 'sender': // Use infolog owner as the notification sender $config = $data['info_owner']; break; case 'copy': // include the info_cc addresses if ($data['info_access'] == 'private') return array(); // no copies for private entries if ($data['info_cc']) { $config = preg_split('/, ?/',$data['info_cc']); } else { $config = array(); } break; case self::CUSTOM_NOTIFICATION: $info_config = Api\Config::read('infolog'); if(!$info_config[self::CUSTOM_NOTIFICATION]) { return ''; } // Per-type notification $type_config = $info_config[self::CUSTOM_NOTIFICATION][$data['info_type']] ?? null; $global = $info_config[self::CUSTOM_NOTIFICATION]['~global~']; // Disabled if(empty($type_config['use_custom']) && empty($global['use_custom'])) return ''; // Type or global $config = trim(strip_tags($type_config['message'])) != '' && $type_config['use_custom'] ? $type_config['message'] : $global['message']; break; } return $config ?? null; } }