EGroupware /
egroupware
| 1 | <?php |
||||
| 2 | /** |
||||
| 3 | * InfoLog - history and notifications |
||||
| 4 | * |
||||
| 5 | * @link http://www.egroupware.org |
||||
| 6 | * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> |
||||
| 7 | * @package tracker |
||||
| 8 | * @copyright (c) 2007-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de> |
||||
| 9 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||
| 10 | * @version $Id$ |
||||
| 11 | */ |
||||
| 12 | |||||
| 13 | use EGroupware\Api; |
||||
| 14 | use EGroupware\Api\Link; |
||||
| 15 | |||||
| 16 | /** |
||||
| 17 | * Tracker - tracking object for the tracker |
||||
| 18 | */ |
||||
| 19 | class infolog_tracking extends Api\Storage\Tracking |
||||
| 20 | { |
||||
| 21 | /** |
||||
| 22 | * Application we are tracking (required!) |
||||
| 23 | * |
||||
| 24 | * @var string |
||||
| 25 | */ |
||||
| 26 | var $app = 'infolog'; |
||||
| 27 | /** |
||||
| 28 | * Name of the id-field, used as id in the history log (required!) |
||||
| 29 | * |
||||
| 30 | * @var string |
||||
| 31 | */ |
||||
| 32 | var $id_field = 'info_id'; |
||||
| 33 | /** |
||||
| 34 | * Name of the field with the creator id, if the creator of an entry should be notified |
||||
| 35 | * |
||||
| 36 | * @var string |
||||
| 37 | */ |
||||
| 38 | var $creator_field = 'info_owner'; |
||||
| 39 | /** |
||||
| 40 | * Name of the field with the id(s) of assinged users, if they should be notified |
||||
| 41 | * |
||||
| 42 | * @var string |
||||
| 43 | */ |
||||
| 44 | var $assigned_field = 'info_responsible'; |
||||
| 45 | /** |
||||
| 46 | * Translate field-names to 2-char history status |
||||
| 47 | * |
||||
| 48 | * @var array |
||||
| 49 | */ |
||||
| 50 | var $field2history = array( |
||||
| 51 | 'info_type' => 'Ty', |
||||
| 52 | 'info_from' => 'Fr', |
||||
| 53 | 'info_link_id' => 'Li', |
||||
| 54 | 'info_id_parent' => 'parent', |
||||
| 55 | 'info_cat' => 'Ca', |
||||
| 56 | 'info_priority' => 'Pr', |
||||
| 57 | 'info_owner' => 'Ow', |
||||
| 58 | 'info_access' => 'Ac', |
||||
| 59 | 'info_status' => 'St', |
||||
| 60 | 'info_percent' => 'Pe', |
||||
| 61 | 'info_datecompleted' => 'Co', |
||||
| 62 | 'info_location' => 'Lo', |
||||
| 63 | 'info_startdate' => 'st', |
||||
| 64 | 'info_enddate' => 'En', |
||||
| 65 | 'info_responsible' => 'Re', |
||||
| 66 | 'info_cc' => 'cc', |
||||
| 67 | 'info_subject' => 'Su', |
||||
| 68 | 'info_des' => 'De', |
||||
| 69 | 'info_location' => 'Lo', |
||||
| 70 | // PM fields |
||||
| 71 | 'info_planned_time' => 'pT', |
||||
| 72 | 'info_replanned_time' => 'replanned', |
||||
| 73 | 'info_used_time' => 'uT', |
||||
| 74 | 'pl_id' => 'pL', |
||||
| 75 | 'info_price' => 'pr', |
||||
| 76 | // all custom fields together |
||||
| 77 | 'custom' => '#c', |
||||
| 78 | ); |
||||
| 79 | /** |
||||
| 80 | * Translate field-names to labels |
||||
| 81 | * |
||||
| 82 | * @note The order of these fields is used to determine the order for CSV export |
||||
| 83 | * @var array |
||||
| 84 | */ |
||||
| 85 | var $field2label = array( |
||||
| 86 | 'info_type' => 'Type', |
||||
| 87 | 'info_from' => 'Contact', |
||||
| 88 | 'info_subject' => 'Subject', |
||||
| 89 | 'info_des' => 'Description', |
||||
| 90 | 'info_link_id' => 'primary link', |
||||
| 91 | 'info_id_parent' => 'Parent', |
||||
| 92 | 'info_cat' => 'Category', |
||||
| 93 | 'info_priority' => 'Priority', |
||||
| 94 | 'info_owner' => 'Owner', |
||||
| 95 | 'info_modifier' => 'Modifier', |
||||
| 96 | 'info_access' => 'Access', |
||||
| 97 | 'info_status' => 'Status', |
||||
| 98 | 'info_percent' => 'Completed', |
||||
| 99 | 'info_datecompleted' => 'Date completed', |
||||
| 100 | 'info_datemodified' => 'Last changed', |
||||
| 101 | 'info_location' => 'Location', |
||||
| 102 | 'info_startdate' => 'Start date', |
||||
| 103 | 'info_enddate' => 'Enddate', |
||||
| 104 | 'info_responsible' => 'Responsible', |
||||
| 105 | 'info_cc' => 'Cc', |
||||
| 106 | // PM fields |
||||
| 107 | 'info_planned_time' => 'planned time', |
||||
| 108 | 'info_replanned_time' => 're-planned time', |
||||
| 109 | 'info_used_time' => 'used time', |
||||
| 110 | 'pl_id' => 'pricelist', |
||||
| 111 | 'info_price' => 'price', |
||||
| 112 | // custom fields |
||||
| 113 | 'custom' => 'custom fields' |
||||
| 114 | ); |
||||
| 115 | |||||
| 116 | /** |
||||
| 117 | * Instance of the infolog_bo class calling us |
||||
| 118 | * |
||||
| 119 | * @var infolog_bo |
||||
| 120 | */ |
||||
| 121 | private $infolog; |
||||
| 122 | |||||
| 123 | /** |
||||
| 124 | * Constructor |
||||
| 125 | * |
||||
| 126 | * @param botracker $botracker |
||||
|
0 ignored issues
–
show
|
|||||
| 127 | * @return tracker_tracking |
||||
|
0 ignored issues
–
show
The type
tracker_tracking was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths Loading history...
|
|||||
| 128 | */ |
||||
| 129 | function __construct(&$infolog_bo) |
||||
| 130 | { |
||||
| 131 | parent::__construct('infolog'); // add custom fields from infolog |
||||
| 132 | |||||
| 133 | $this->infolog =& $infolog_bo; |
||||
| 134 | } |
||||
| 135 | |||||
| 136 | /** |
||||
| 137 | * Get the subject for a given entry |
||||
| 138 | * |
||||
| 139 | * Reimpleneted to use a New|deleted|modified prefix. |
||||
| 140 | * |
||||
| 141 | * @param array $data |
||||
| 142 | * @param array $old |
||||
| 143 | * @return string |
||||
| 144 | */ |
||||
| 145 | function get_subject($data,$old) |
||||
| 146 | { |
||||
| 147 | if ($data['prefix']) |
||||
| 148 | { |
||||
| 149 | $prefix = $data['prefix']; // async notification |
||||
| 150 | } |
||||
| 151 | elseif (!$old || $old['info_status'] == 'deleted') |
||||
|
0 ignored issues
–
show
The expression
$old of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||||
| 152 | { |
||||
| 153 | $prefix = lang('New %1',lang($this->infolog->enums['type'][$data['info_type']])); |
||||
|
0 ignored issues
–
show
The call to
lang() has too many arguments starting with lang($this->infolog->enu...'][$data['info_type']]).
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. Loading history...
|
|||||
| 154 | } |
||||
| 155 | elseif($data['info_status'] == 'deleted') |
||||
| 156 | { |
||||
| 157 | $prefix = lang('%1 deleted',lang($this->infolog->enums['type'][$data['info_type']])); |
||||
| 158 | } |
||||
| 159 | else |
||||
| 160 | { |
||||
| 161 | $prefix = lang('%1 modified',lang($this->infolog->enums['type'][$data['info_type']])); |
||||
| 162 | } |
||||
| 163 | return $prefix.': '.$data['info_subject']; |
||||
| 164 | } |
||||
| 165 | |||||
| 166 | /** |
||||
| 167 | * Get the modified / new message (1. line of mail body) for a given entry, can be reimplemented |
||||
| 168 | * |
||||
| 169 | * @param array $data |
||||
| 170 | * @param array $old |
||||
| 171 | * @return string |
||||
| 172 | */ |
||||
| 173 | function get_message($data,$old) |
||||
| 174 | { |
||||
| 175 | if ($data['message']) return $data['message']; // async notification |
||||
| 176 | |||||
| 177 | if (!$old || $old['info_status'] == 'deleted') |
||||
|
0 ignored issues
–
show
The expression
$old of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||||
| 178 | { |
||||
| 179 | return lang('New %1 created by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]), |
||||
|
0 ignored issues
–
show
The call to
lang() has too many arguments starting with lang($this->infolog->enu...'][$data['info_type']]).
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. Loading history...
|
|||||
| 180 | Api\Accounts::username($this->infolog->user),$this->datetime('now')); |
||||
| 181 | } |
||||
| 182 | elseif($data['info_status'] == 'deleted') |
||||
| 183 | { |
||||
| 184 | return lang('%1 deleted by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]), |
||||
| 185 | Api\Accounts::username($data['info_modifier']), |
||||
| 186 | $this->datetime($data['info_datemodified'])); |
||||
| 187 | } |
||||
| 188 | return lang('%1 modified by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]), |
||||
| 189 | Api\Accounts::username($data['info_modifier']), |
||||
| 190 | $this->datetime($data['info_datemodified'])); |
||||
| 191 | } |
||||
| 192 | |||||
| 193 | /** |
||||
| 194 | * Get the details of an entry |
||||
| 195 | * |
||||
| 196 | * @param array|object $data |
||||
| 197 | * @param int|string $receiver nummeric account_id or email address |
||||
| 198 | * @return array of details as array with values for keys 'label','value','type' |
||||
| 199 | */ |
||||
| 200 | function get_details($data,$receiver=null) |
||||
| 201 | { |
||||
| 202 | //error_log(__METHOD__.__LINE__.' Data:'.array2string($data)); |
||||
| 203 | $responsible = array(); |
||||
| 204 | if ($data['info_responsible']) |
||||
| 205 | { |
||||
| 206 | foreach($data['info_responsible'] as $uid) |
||||
| 207 | { |
||||
| 208 | $responsible[] = Api\Accounts::username($uid); |
||||
| 209 | } |
||||
| 210 | } |
||||
| 211 | if ($GLOBALS['egw_info']['user']['preferences']['infolog']['show_id']) |
||||
| 212 | { |
||||
| 213 | $id = ' #'.$data['info_id']; |
||||
| 214 | } |
||||
| 215 | foreach(array( |
||||
| 216 | 'info_type' => lang($this->infolog->enums['type'][$data['info_type']]).$id, |
||||
| 217 | 'info_from' => $data['info_from'], |
||||
| 218 | 'info_cat' => $data['info_cat'] ? $GLOBALS['egw']->categories->id2name($data['info_cat']) : '', |
||||
| 219 | 'info_priority' => lang($this->infolog->enums['priority'][$data['info_priority']]), |
||||
| 220 | 'info_owner' => Api\Accounts::username($data['info_owner']), |
||||
| 221 | 'info_status' => lang($data['info_status']=='deleted'?'deleted':$this->infolog->status[$data['info_type']][$data['info_status']]), |
||||
| 222 | 'info_percent' => (int)$data['info_percent'].'%', |
||||
| 223 | 'info_datecompleted' => $data['info_datecompleted'] ? $this->datetime($data['info_datecompleted']) : '', |
||||
| 224 | 'info_location' => $data['info_location'], |
||||
| 225 | 'info_startdate' => $data['info_startdate'] ? $this->datetime($data['info_startdate'],null) : '', |
||||
| 226 | 'info_enddate' => $data['info_enddate'] ? $this->datetime($data['info_enddate'],null) : '', |
||||
| 227 | 'info_responsible' => implode(', ',$responsible), |
||||
| 228 | 'info_subject' => $data['info_subject'], |
||||
| 229 | ) as $name => $value) |
||||
| 230 | { |
||||
| 231 | //error_log(__METHOD__.__LINE__.' Key:'.$name.' val:'.array2string($value)); |
||||
| 232 | if ($name=='info_from' && empty($value)) |
||||
| 233 | { |
||||
| 234 | if(!empty($data['info_contact']) && is_array($data['link_to']['to_id'])) |
||||
| 235 | { |
||||
| 236 | $lkeys = array_keys($data['link_to']['to_id']); |
||||
| 237 | if (in_array($data['info_contact'],$lkeys)) |
||||
| 238 | { |
||||
| 239 | list($app,$id) = explode(':',$data['info_contact']); |
||||
| 240 | if (!empty($app)&&!empty($id)) $value = Link::title($app,$id); |
||||
| 241 | } |
||||
| 242 | } |
||||
| 243 | else if ($data['info_link_id']) |
||||
| 244 | { |
||||
| 245 | $this->infolog->link_id2from($data); |
||||
| 246 | if (is_array($data['info_contact'])) $value = $data['info_contact']['title']; |
||||
| 247 | } |
||||
| 248 | } |
||||
| 249 | $details[$name] = array( |
||||
| 250 | 'label' => lang($this->field2label[$name]), |
||||
| 251 | 'value' => $value, |
||||
| 252 | ); |
||||
| 253 | if ($name == 'info_subject') $details[$name]['type'] = 'summary'; |
||||
| 254 | } |
||||
| 255 | $details['info_des'] = array( |
||||
| 256 | 'value' => $data['info_des'], |
||||
| 257 | 'type' => 'multiline', |
||||
| 258 | ); |
||||
| 259 | // add custom fields for given type |
||||
| 260 | $details += $this->get_customfields($data, $data['info_type'], $receiver); |
||||
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
| 261 | //error_log(__METHOD__."(".array2string($data).", $receiver) returning ".array2string($details)); |
||||
| 262 | return $details; |
||||
| 263 | } |
||||
| 264 | |||||
| 265 | /** |
||||
| 266 | * Track changes |
||||
| 267 | * |
||||
| 268 | * Overrides parent to log the modified date in the history, but not to send a notification |
||||
| 269 | * |
||||
| 270 | * @param array $data current entry |
||||
| 271 | * @param array $old = null old/last state of the entry or null for a new entry |
||||
| 272 | * @param int $user = null user who made the changes, default to current user |
||||
| 273 | * @param boolean $deleted = null can be set to true to let the tracking know the item got deleted or undeleted |
||||
| 274 | * @param array $changed_fields = null changed fields from ealier call to $this->changed_fields($data,$old), to not compute it again |
||||
| 275 | * @param boolean $skip_notification = false do NOT send any notification |
||||
| 276 | * @return int|boolean false on error, integer number of changes logged or true for new entries ($old == null) |
||||
| 277 | */ |
||||
| 278 | public function track(array $data,array $old=null,$user=null,$deleted=null,array $changed_fields=null,$skip_notification=false) |
||||
| 279 | { |
||||
| 280 | //error_log(__METHOD__.__LINE__.' notify?'.($skip_notification?'no':'yes').function_backtrace()); |
||||
| 281 | $this->user = !is_null($user) ? $user : $GLOBALS['egw_info']['user']['account_id']; |
||||
| 282 | |||||
| 283 | $changes = true; |
||||
| 284 | |||||
| 285 | if ($old && $this->field2history) |
||||
|
0 ignored issues
–
show
The expression
$this->field2history of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||||
| 286 | { |
||||
| 287 | $changes = $this->save_history($data,$old,$deleted,$changed_fields); |
||||
| 288 | } |
||||
| 289 | |||||
| 290 | // Don't notify if the only change was to the modified date |
||||
| 291 | if(is_null($changed_fields)) |
||||
| 292 | { |
||||
| 293 | $changed_fields = $this->changed_fields($data, $old); |
||||
| 294 | $changes = count($changed_fields); // we need that since TRUE evaluates to 1 |
||||
| 295 | } |
||||
| 296 | //error_log(__METHOD__.__LINE__.array2string($changed_fields)); |
||||
| 297 | if(is_array($changed_fields) && $changes == 1 && in_array('info_datemodified', $changed_fields)) |
||||
| 298 | { |
||||
| 299 | return count($changes); |
||||
| 300 | } |
||||
| 301 | |||||
| 302 | // do not run do_notifications if we have no changes |
||||
| 303 | if ($changes && !$skip_notification && !$this->do_notifications($data,$old,$deleted)) |
||||
| 304 | { |
||||
| 305 | $changes = false; |
||||
| 306 | } |
||||
| 307 | return $changes; |
||||
| 308 | } |
||||
| 309 | |||||
| 310 | /** |
||||
| 311 | * Get a notification-config value |
||||
| 312 | * |
||||
| 313 | * @param string $name |
||||
| 314 | * - 'copy' array of email addresses notifications should be copied too, can depend on $data |
||||
| 315 | * - 'lang' string lang code for copy mail |
||||
| 316 | * - 'sender' string send email address |
||||
| 317 | * @param array $data current entry |
||||
| 318 | * @param array $old = null old/last state of the entry or null for a new entry |
||||
| 319 | * @return mixed |
||||
| 320 | */ |
||||
| 321 | function get_config($name,$data,$old=null) |
||||
| 322 | { |
||||
| 323 | unset($old); // not used, but required function signature |
||||
| 324 | switch($name) |
||||
| 325 | { |
||||
| 326 | case 'copy': // include the info_cc addresses |
||||
| 327 | if ($data['info_access'] == 'private') return array(); // no copies for private entries |
||||
| 328 | if ($data['info_cc']) |
||||
| 329 | { |
||||
| 330 | $config = preg_split('/, ?/',$data['info_cc']); |
||||
| 331 | } |
||||
| 332 | else |
||||
| 333 | { |
||||
| 334 | $config = array(); |
||||
| 335 | } |
||||
| 336 | break; |
||||
| 337 | case self::CUSTOM_NOTIFICATION: |
||||
| 338 | $info_config = Api\Config::read('infolog'); |
||||
| 339 | if(!$info_config[self::CUSTOM_NOTIFICATION]) |
||||
| 340 | { |
||||
| 341 | return ''; |
||||
| 342 | } |
||||
| 343 | // Per-type notification |
||||
| 344 | $type_config = $info_config[self::CUSTOM_NOTIFICATION][$data['info_type']]; |
||||
| 345 | $global = $info_config[self::CUSTOM_NOTIFICATION]['~global~']; |
||||
| 346 | |||||
| 347 | // Disabled |
||||
| 348 | if(!$type_config['use_custom'] && !$global['use_custom']) return ''; |
||||
| 349 | |||||
| 350 | // Type or globabl |
||||
| 351 | $config = trim(strip_tags($type_config['message'])) != '' && $type_config['use_custom'] ? $type_config['message'] : $global['message']; |
||||
| 352 | break; |
||||
| 353 | } |
||||
| 354 | return $config; |
||||
| 355 | } |
||||
| 356 | } |
||||
| 357 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths