| Total Complexity | 207 |
| Total Lines | 1013 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like timesheet_bo often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use timesheet_bo, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 28 | class timesheet_bo extends Api\Storage |
||
| 29 | { |
||
| 30 | /** |
||
| 31 | * Flag for timesheets deleted, but preserved |
||
| 32 | */ |
||
| 33 | const DELETED_STATUS = -1; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * Timesheets Api\Config data |
||
| 37 | * |
||
| 38 | * @var array |
||
| 39 | */ |
||
| 40 | var $config_data = array(); |
||
| 41 | /** |
||
| 42 | * Should we show a quantity sum, makes only sense if we sum up identical units (can be used to sum up negative (over-)time) |
||
| 43 | * |
||
| 44 | * @var boolean |
||
| 45 | */ |
||
| 46 | var $quantity_sum=false; |
||
| 47 | /** |
||
| 48 | * current user |
||
| 49 | * |
||
| 50 | * @var int |
||
| 51 | */ |
||
| 52 | var $user; |
||
| 53 | /** |
||
| 54 | * Timestaps that need to be adjusted to user-time on reading or saving |
||
| 55 | * |
||
| 56 | * @var array |
||
| 57 | */ |
||
| 58 | var $timestamps = array( |
||
| 59 | 'ts_start','ts_created', 'ts_modified' |
||
| 60 | ); |
||
| 61 | /** |
||
| 62 | * Start of today in user-time |
||
| 63 | * |
||
| 64 | * @var int |
||
| 65 | */ |
||
| 66 | var $today; |
||
| 67 | /** |
||
| 68 | * Filter for search limiting the date-range |
||
| 69 | * |
||
| 70 | * @var array |
||
| 71 | */ |
||
| 72 | var $date_filters = array( // Start: year,month,day,week, End: year,month,day,week |
||
| 73 | 'Today' => array(0,0,0,0, 0,0,1,0), |
||
| 74 | 'Yesterday' => array(0,0,-1,0, 0,0,0,0), |
||
| 75 | 'This week' => array(0,0,0,0, 0,0,0,1), |
||
| 76 | 'Last week' => array(0,0,0,-1, 0,0,0,0), |
||
| 77 | 'This month' => array(0,0,0,0, 0,1,0,0), |
||
| 78 | 'Last month' => array(0,-1,0,0, 0,0,0,0), |
||
| 79 | '2 month ago' => array(0,-2,0,0, 0,-1,0,0), |
||
| 80 | 'This year' => array(0,0,0,0, 1,0,0,0), |
||
| 81 | 'Last year' => array(-1,0,0,0, 0,0,0,0), |
||
| 82 | '2 years ago' => array(-2,0,0,0, -1,0,0,0), |
||
| 83 | '3 years ago' => array(-3,0,0,0, -2,0,0,0), |
||
| 84 | ); |
||
| 85 | /** |
||
| 86 | * Grants: $GLOBALS['egw']->acl->get_grants(TIMESHEET_APP); |
||
| 87 | * |
||
| 88 | * @var array |
||
| 89 | */ |
||
| 90 | var $grants; |
||
| 91 | /** |
||
| 92 | * Sums of the last search in keys duration and price |
||
| 93 | * |
||
| 94 | * @var array |
||
| 95 | */ |
||
| 96 | var $summary; |
||
| 97 | /** |
||
| 98 | * Array with boolean values in keys 'day', 'week' or 'month', for the sums to return in the search |
||
| 99 | * |
||
| 100 | * @var array |
||
| 101 | */ |
||
| 102 | var $show_sums; |
||
| 103 | /** |
||
| 104 | * Array with custom fileds |
||
| 105 | * |
||
| 106 | * @var array |
||
| 107 | */ |
||
| 108 | var $customfields=array(); |
||
| 109 | /** |
||
| 110 | * Array with status label |
||
| 111 | * |
||
| 112 | * @var array |
||
| 113 | */ |
||
| 114 | var $status_labels = array(); |
||
| 115 | /** |
||
| 116 | * Array with status label configuration |
||
| 117 | * |
||
| 118 | * @var array |
||
| 119 | */ |
||
| 120 | var $status_labels_config = array(); |
||
| 121 | /** |
||
| 122 | * Instance of the timesheet_tracking object |
||
| 123 | * |
||
| 124 | * @var timesheet_tracking |
||
| 125 | */ |
||
| 126 | var $tracking; |
||
| 127 | /** |
||
| 128 | * Translates field / acl-names to labels |
||
| 129 | * |
||
| 130 | * @var array |
||
| 131 | */ |
||
| 132 | var $field2label = array( |
||
| 133 | 'ts_project' => 'Project', |
||
| 134 | 'ts_title' => 'Title', |
||
| 135 | 'cat_id' => 'Category', |
||
| 136 | 'ts_description' => 'Description', |
||
| 137 | 'ts_start' => 'Start', |
||
| 138 | 'ts_duration' => 'Duration', |
||
| 139 | 'ts_quantity' => 'Quantity', |
||
| 140 | 'ts_unitprice' => 'Unitprice', |
||
| 141 | 'ts_owner' => 'Owner', |
||
| 142 | 'ts_modifier' => 'Modifier', |
||
| 143 | 'ts_status' => 'Status', |
||
| 144 | 'pm_id' => 'Projectid', |
||
| 145 | // pseudo fields used in edit |
||
| 146 | //'link_to' => 'Attachments & Links', |
||
| 147 | 'customfields' => 'Custom fields', |
||
| 148 | ); |
||
| 149 | /** |
||
| 150 | * Name of the timesheet table storing custom fields |
||
| 151 | */ |
||
| 152 | const EXTRA_TABLE = 'egw_timesheet_extra'; |
||
| 153 | |||
| 154 | /** |
||
| 155 | * Columns to search when user does a text search |
||
| 156 | */ |
||
| 157 | var $columns_to_search = array('egw_timesheet.ts_id', 'ts_project', 'ts_title', 'ts_description', 'ts_duration', 'ts_quantity', 'ts_unitprice'); |
||
| 158 | |||
| 159 | /** |
||
| 160 | * all cols in data which are not (direct)in the db, for data_merge |
||
| 161 | * |
||
| 162 | * @var array |
||
| 163 | */ |
||
| 164 | var $non_db_cols = array('pm_id'); |
||
| 165 | |||
| 166 | function __construct() |
||
| 167 | { |
||
| 168 | parent::__construct(TIMESHEET_APP,'egw_timesheet',self::EXTRA_TABLE,'','ts_extra_name','ts_extra_value','ts_id'); |
||
| 169 | |||
| 170 | $this->config_data = Api\Config::read(TIMESHEET_APP); |
||
| 171 | $this->quantity_sum = $this->config_data['quantity_sum'] == 'true'; |
||
| 172 | |||
| 173 | // Load & process statuses |
||
| 174 | if($this->config_data['status_labels']) $this->load_statuses(); |
||
| 175 | |||
| 176 | $this->today = mktime(0,0,0,date('m',$this->now),date('d',$this->now),date('Y',$this->now)); |
||
| 177 | |||
| 178 | // save us in $GLOBALS['timesheet_bo'] for ExecMethod used in hooks |
||
| 179 | if (!is_object($GLOBALS['timesheet_bo'])) |
||
| 180 | { |
||
| 181 | $GLOBALS['timesheet_bo'] =& $this; |
||
| 182 | } |
||
| 183 | $this->grants = $GLOBALS['egw']->acl->get_grants(TIMESHEET_APP); |
||
| 184 | } |
||
| 185 | |||
| 186 | /** |
||
| 187 | * Load status labels |
||
| 188 | */ |
||
| 189 | protected function load_statuses() |
||
| 235 | } |
||
| 236 | |||
| 237 | /** |
||
| 238 | * Return evtl. existing sub-statuses of given status |
||
| 239 | * |
||
| 240 | * @param int $status |
||
| 241 | * @return array|int with sub-statuses incl. $status or just $status |
||
| 242 | */ |
||
| 243 | function get_sub_status($status) |
||
| 244 | { |
||
| 245 | if (!isset($this->status_labels_config)) $this->load_statuses(); |
||
| 246 | $stati = array($status); |
||
| 247 | foreach($this->status_labels_config as $stat) |
||
| 248 | { |
||
| 249 | if ($stat['parent'] && in_array($stat['parent'], $stati)) |
||
| 250 | { |
||
| 251 | $stati[] = $stat['id']; |
||
| 252 | } |
||
| 253 | } |
||
| 254 | //error_log(__METHOD__."($status) returning ".array2string(count($stati) == 1 ? $status : $stati)); |
||
| 255 | return count($stati) == 1 ? $status : $stati; |
||
| 256 | } |
||
| 257 | |||
| 258 | /** |
||
| 259 | * Make nice labels with leading spaces depending on depth |
||
| 260 | * |
||
| 261 | * @param statuses List of statuses to process, with sub-statuses in a 'substatus' array |
||
|
|
|||
| 262 | * @param labels Array of labels, pass array() and labels will be built in it |
||
| 263 | * @param depth Current depth |
||
| 264 | * |
||
| 265 | * @return None, labels are built in labels parameter |
||
| 266 | */ |
||
| 267 | protected function make_status_labels($statuses, &$labels, $depth=0) |
||
| 268 | { |
||
| 269 | foreach($statuses as $status) |
||
| 270 | { |
||
| 271 | $labels[$status['id']] = str_pad('',$depth*12, " ",STR_PAD_LEFT).trim(str_replace(' ','',$status['name'])); |
||
| 272 | if(count($status['substatus']) > 0) |
||
| 273 | { |
||
| 274 | $this->make_status_labels($status['substatus'], $labels, $depth+1); |
||
| 275 | } |
||
| 276 | } |
||
| 277 | } |
||
| 278 | |||
| 279 | /** |
||
| 280 | * Get status labels with admin statuses (optionally) filtered out |
||
| 281 | * |
||
| 282 | * @param boolean $admin |
||
| 283 | * |
||
| 284 | * @return Array |
||
| 285 | */ |
||
| 286 | protected function get_status_labels($admin = null) |
||
| 287 | { |
||
| 288 | if(is_null($admin)) |
||
| 289 | { |
||
| 290 | $admin = isset($GLOBALS['egw_info']['user']['apps']['admin']); |
||
| 291 | } |
||
| 292 | $labels = array(); |
||
| 293 | foreach($this->status_labels as $status_id => $label) |
||
| 294 | { |
||
| 295 | if($admin || !$admin && !$this->status_labels_config[$status_id]['admin']) |
||
| 296 | { |
||
| 297 | $labels[$status_id] = $label; |
||
| 298 | } |
||
| 299 | } |
||
| 300 | return $labels; |
||
| 301 | } |
||
| 302 | |||
| 303 | /** |
||
| 304 | * get list of specified grants as uid => Username pairs |
||
| 305 | * |
||
| 306 | * @param int $required =Acl::READ |
||
| 307 | * @param boolean $hide_deactive =null default only Acl::EDIT hides deactivates users |
||
| 308 | * @return array with uid => Username pairs |
||
| 309 | */ |
||
| 310 | function grant_list($required=Acl::READ, $hide_deactive=null) |
||
| 311 | { |
||
| 312 | if (!isset($hide_deactive)) $hide_deactive = $required == Acl::EDIT; |
||
| 313 | |||
| 314 | $result = array(); |
||
| 315 | foreach($this->grants as $uid => $grant) |
||
| 316 | { |
||
| 317 | if ($grant & $required && (!$hide_deactive || Api\Accounts::getInstance()->is_active($uid))) |
||
| 318 | { |
||
| 319 | $result[$uid] = Api\Accounts::username($uid); |
||
| 320 | } |
||
| 321 | } |
||
| 322 | natcasesort($result); |
||
| 323 | |||
| 324 | return $result; |
||
| 325 | } |
||
| 326 | |||
| 327 | /** |
||
| 328 | * checks if the user has enough rights for a certain operation |
||
| 329 | * |
||
| 330 | * Rights are given via status Api\Config admin/noadmin |
||
| 331 | * |
||
| 332 | * @param array|int $data =null use $this->data or $this->data['ts_id'] (to fetch the data) |
||
| 333 | * @param int $user =null for which user to check, default current user |
||
| 334 | * @return boolean true if the rights are ok, false if no rights |
||
| 335 | */ |
||
| 336 | function check_statusForEditRights($data=null,$user=null) |
||
| 359 | } |
||
| 360 | |||
| 361 | /** |
||
| 362 | * checks if the user has enough rights for a certain operation |
||
| 363 | * |
||
| 364 | * Rights are given via owner grants or role based Acl |
||
| 365 | * |
||
| 366 | * @param int $required Acl::READ, EGW_ACL_WRITE, Acl::ADD, Acl::DELETE, EGW_ACL_BUDGET, EGW_ACL_EDIT_BUDGET |
||
| 367 | * @param array|int $data =null project or project-id to use, default the project in $this->data |
||
| 368 | * @param int $user =null for which user to check, default current user |
||
| 369 | * @return boolean true if the rights are ok, null if not found, false if no rights |
||
| 370 | */ |
||
| 371 | function check_acl($required,$data=null,$user=null) |
||
| 372 | { |
||
| 373 | if (is_null($data) || (int)$data == $this->data['ts_id']) |
||
| 374 | { |
||
| 375 | $data =& $this->data; |
||
| 376 | } |
||
| 377 | if (!is_array($data)) |
||
| 378 | { |
||
| 379 | $save_data = $this->data; |
||
| 380 | $data = $this->read($data,true); |
||
| 381 | $this->data = $save_data; |
||
| 382 | |||
| 383 | if (!$data) return null; // entry not found |
||
| 384 | } |
||
| 385 | if (!$user) $user = $this->user; |
||
| 386 | if ($user == $this->user) |
||
| 387 | { |
||
| 388 | $grants = $this->grants; |
||
| 389 | } |
||
| 390 | else |
||
| 391 | { |
||
| 392 | $grants = $GLOBALS['egw']->acl->get_grants(TIMESHEET_APP,true,$user); |
||
| 393 | } |
||
| 394 | $ret = $data && !!($grants[$data['ts_owner']] & $required); |
||
| 395 | |||
| 396 | if(($required & Acl::DELETE) && $this->config_data['history'] == 'history' && |
||
| 397 | $data['ts_status'] == self::DELETED_STATUS) |
||
| 398 | { |
||
| 399 | $ret = !!($GLOBALS['egw_info']['user']['apps']['admin']); |
||
| 400 | } |
||
| 401 | //error_log(__METHOD__."($required,$data[ts_id],$user) returning ".array2string($ret)); |
||
| 402 | return $ret; |
||
| 403 | } |
||
| 404 | |||
| 405 | /** |
||
| 406 | * return SQL implementing filtering by date |
||
| 407 | * |
||
| 408 | * @param string $name |
||
| 409 | * @param int &$start |
||
| 410 | * @param int &$end |
||
| 411 | * @return string |
||
| 412 | */ |
||
| 413 | function date_filter($name,&$start,&$end) |
||
| 416 | } |
||
| 417 | |||
| 418 | /** |
||
| 419 | * search the timesheet |
||
| 420 | * |
||
| 421 | * reimplemented to limit result to users we have grants from |
||
| 422 | * Use $filter['ts_owner'] === false for no ACL check. |
||
| 423 | * |
||
| 424 | * @param array|string $criteria array of key and data cols, OR a SQL query (content for WHERE), fully quoted (!) |
||
| 425 | * @param boolean|string $only_keys =true True returns only keys, False returns all cols. comma seperated list of keys to return |
||
| 426 | * @param string $order_by ='' fieldnames + {ASC|DESC} separated by colons ',', can also contain a GROUP BY (if it contains ORDER BY) |
||
| 427 | * @param string|array $extra_cols ='' string or array of strings to be added to the SELECT, eg. "count(*) as num" |
||
| 428 | * @param string $wildcard ='' appended befor and after each criteria |
||
| 429 | * @param boolean $empty =false False=empty criteria are ignored in query, True=empty have to be empty in row |
||
| 430 | * @param string $op ='AND' defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together |
||
| 431 | * @param mixed $start =false if != false, return only maxmatch rows begining with start, or array($start,$num) |
||
| 432 | * @param array $filter =null if set (!=null) col-data pairs, to be and-ed (!) into the query without wildcards |
||
| 433 | * @param string $join ='' sql to do a join, added as is after the table-name, eg. ", table2 WHERE x=y" or |
||
| 434 | * "LEFT JOIN table2 ON (x=y)", Note: there's no quoting done on $join! |
||
| 435 | * @param boolean $need_full_no_count =false If true an unlimited query is run to determine the total number of rows, default false |
||
| 436 | * @param boolean $only_summary =false If true only return the sums as array with keys duration and price, default false |
||
| 437 | * @return array of matching rows (the row is an array of the cols) or False |
||
| 438 | */ |
||
| 439 | function &search($criteria,$only_keys=True,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null,$join='',$need_full_no_count=false,$only_summary=false) |
||
| 561 | } |
||
| 562 | |||
| 563 | /** |
||
| 564 | * read a timesheet entry |
||
| 565 | * |
||
| 566 | * @param int $ts_id |
||
| 567 | * @param boolean $ignore_acl =false should the Acl be checked |
||
| 568 | * @return array|boolean array with timesheet entry, null if timesheet not found or false if no rights |
||
| 569 | */ |
||
| 570 | function read($ts_id,$ignore_acl=false) |
||
| 582 | } |
||
| 583 | |||
| 584 | /** |
||
| 585 | * saves a timesheet entry |
||
| 586 | * |
||
| 587 | * reimplemented to notify the link-class |
||
| 588 | * |
||
| 589 | * @param array $keys if given $keys are copied to data before saveing => allows a save as |
||
| 590 | * @param boolean $touch_modified =true should modification date+user be set, default yes |
||
| 591 | * @param boolean $ignore_acl =false should the Acl be checked, returns true if no edit-rigts |
||
| 592 | * @return int 0 on success and errno != 0 else |
||
| 593 | */ |
||
| 594 | function save($keys=null,$touch_modified=true,$ignore_acl=false) |
||
| 595 | { |
||
| 596 | if ($keys) $this->data_merge($keys); |
||
| 597 | |||
| 598 | if (!$ignore_acl && $this->data['ts_id'] && !$this->check_acl(Acl::EDIT)) |
||
| 599 | { |
||
| 600 | return true; |
||
| 601 | } |
||
| 602 | if ($touch_modified) |
||
| 603 | { |
||
| 604 | $this->data['ts_modifier'] = $GLOBALS['egw_info']['user']['account_id']; |
||
| 605 | $this->data['ts_modified'] = $this->now; |
||
| 606 | $this->user = $this->data['ts_modifier']; |
||
| 607 | } |
||
| 608 | |||
| 609 | // check if we have a real modification of an existing record |
||
| 610 | if ($this->data['ts_id']) |
||
| 611 | { |
||
| 612 | $new =& $this->data; |
||
| 613 | unset($this->data); |
||
| 614 | $this->read($new['ts_id']); |
||
| 615 | $old =& $this->data; |
||
| 616 | $this->data =& $new; |
||
| 617 | $changed = array(); |
||
| 618 | if (isset($old)) foreach($old as $name => $value) |
||
| 619 | { |
||
| 620 | if (isset($new[$name]) && $new[$name] != $value) $changed[] = $name; |
||
| 621 | } |
||
| 622 | } |
||
| 623 | if (!$this->data['ts_created']) |
||
| 624 | { |
||
| 625 | $this->data['ts_created'] = time(); |
||
| 626 | } |
||
| 627 | if (isset($old) && !$changed) |
||
| 628 | { |
||
| 629 | return false; |
||
| 630 | } |
||
| 631 | // Update ts_project to match project |
||
| 632 | if ($this->pm_integration == 'full' && ( |
||
| 633 | !$old && $this->data['pm_id'] != $this->data['old_pm_id'] || $old && $old['pm_id'] != $new['pm_id'] |
||
| 634 | )) |
||
| 635 | { |
||
| 636 | $this->data['ts_project'] = $this->data['pm_id'] ? Link::title('projectmanager', $this->data['pm_id']) : ''; |
||
| 637 | if($this->data['ts_title'] == Link::title('projectmanager', $old['pm_id'])) |
||
| 638 | { |
||
| 639 | $this->data['ts_title'] = $this->data['ts_project']; |
||
| 640 | } |
||
| 641 | } |
||
| 642 | |||
| 643 | $type = !isset($old) ? 'add' : |
||
| 644 | ($new['ts_status'] == self::DELETED_STATUS ? 'delete' : 'update'); |
||
| 645 | |||
| 646 | // Check for restore of deleted contact, restore held links |
||
| 647 | if($old && $old['ts_status'] == self::DELETED_STATUS && $new['ts_status'] != self::DELETED_STATUS) |
||
| 648 | { |
||
| 649 | Link::restore(TIMESHEET_APP, $new['ts_id']); |
||
| 650 | $type = 'add'; |
||
| 651 | } |
||
| 652 | |||
| 653 | if (!($err = parent::save())) |
||
| 654 | { |
||
| 655 | if (!is_object($this->tracking)) |
||
| 656 | { |
||
| 657 | $this->tracking = new timesheet_tracking($this); |
||
| 658 | |||
| 659 | $this->tracking->html_content_allow = true; |
||
| 660 | } |
||
| 661 | if ($this->tracking->track($this->data,$old,$this->user) === false) |
||
| 662 | { |
||
| 663 | return implode(', ',$this->tracking->errors); |
||
| 664 | } |
||
| 665 | // notify the link-class about the update, as other apps may be subscribt to it |
||
| 666 | Link::notify_update(TIMESHEET_APP, $this->data['ts_id'], $this->data, $type); |
||
| 667 | } |
||
| 668 | |||
| 669 | return $err; |
||
| 670 | } |
||
| 671 | |||
| 672 | /** |
||
| 673 | * deletes a timesheet entry identified by $keys or the loaded one, reimplemented to notify the link class (unlink) |
||
| 674 | * |
||
| 675 | * @param array $keys if given array with col => value pairs to characterise the rows to delete |
||
| 676 | * @param boolean $ignore_acl =false should the Acl be checked, returns false if no delete-rigts |
||
| 677 | * @return int affected rows, should be 1 if ok, 0 if an error |
||
| 678 | */ |
||
| 679 | function delete($keys=null,$ignore_acl=false) |
||
| 680 | { |
||
| 681 | if (!is_array($keys) && (int) $keys) |
||
| 682 | { |
||
| 683 | $keys = array('ts_id' => (int) $keys); |
||
| 684 | } |
||
| 685 | $ts_id = is_null($keys) ? $this->data['ts_id'] : $keys['ts_id']; |
||
| 686 | |||
| 687 | if (!$ignore_acl && !$this->check_acl(Acl::DELETE,$ts_id) || !($old = $this->read($ts_id))) |
||
| 688 | { |
||
| 689 | return false; |
||
| 690 | } |
||
| 691 | |||
| 692 | // check if we only mark timesheets as deleted, or really delete them |
||
| 693 | if ($old['ts_owner'] && $this->config_data['history'] != '' && $old['ts_status'] != self::DELETED_STATUS) |
||
| 694 | { |
||
| 695 | $delete = $old; |
||
| 696 | $delete['ts_status'] = self::DELETED_STATUS; |
||
| 697 | $ret = !($this->save($delete)); |
||
| 698 | Link::unlink(0,TIMESHEET_APP,$ts_id,'','','',true); |
||
| 699 | } |
||
| 700 | elseif (($ret = parent::delete($keys)) && $ts_id) |
||
| 701 | { |
||
| 702 | // delete all links to timesheet entry $ts_id |
||
| 703 | Link::unlink(0,TIMESHEET_APP,$ts_id); |
||
| 704 | } |
||
| 705 | return $ret; |
||
| 706 | } |
||
| 707 | |||
| 708 | /** |
||
| 709 | * delete / move all timesheets of a given user |
||
| 710 | * |
||
| 711 | * @param array $data |
||
| 712 | * @param int $data['account_id'] owner to change |
||
| 713 | * @param int $data['new_owner'] new owner or 0 for delete |
||
| 714 | */ |
||
| 715 | function deleteaccount($data) |
||
| 716 | { |
||
| 717 | $account_id = $data['account_id']; |
||
| 718 | $new_owner = $data['new_owner']; |
||
| 719 | |||
| 720 | if (!$new_owner) |
||
| 721 | { |
||
| 722 | Link::unlink(0, TIMESHEET_APP, '', $account_id); |
||
| 723 | parent::delete(array('ts_owner' => $account_id)); |
||
| 724 | } |
||
| 725 | else |
||
| 726 | { |
||
| 727 | $this->db->update($this->table_name, array( |
||
| 728 | 'ts_owner' => $new_owner, |
||
| 729 | ), array( |
||
| 730 | 'ts_owner' => $account_id, |
||
| 731 | ), __LINE__, __FILE__, TIMESHEET_APP); |
||
| 732 | } |
||
| 733 | } |
||
| 734 | |||
| 735 | /** |
||
| 736 | * set a status for timesheet entry identified by $keys |
||
| 737 | * |
||
| 738 | * @param array $keys =null if given array with col => value pairs to characterise single timesheet or null for $this->data |
||
| 739 | * @param int $status =0 |
||
| 740 | * @return int affected rows, should be 1 if ok, 0 if an error |
||
| 741 | */ |
||
| 742 | function set_status($keys=null, $status=0) |
||
| 743 | { |
||
| 744 | $ret = true; |
||
| 745 | if (!is_array($keys) && (int) $keys) |
||
| 746 | { |
||
| 747 | $keys = array('ts_id' => (int) $keys); |
||
| 748 | } |
||
| 749 | $ts_id = is_null($keys) ? $this->data['ts_id'] : $keys['ts_id']; |
||
| 750 | |||
| 751 | if (!$this->check_acl(Acl::EDIT,$ts_id) || !$this->read($ts_id,true)) |
||
| 752 | { |
||
| 753 | return false; |
||
| 754 | } |
||
| 755 | |||
| 756 | $this->data['ts_status'] = $status; |
||
| 757 | if ($this->save($ts_id)!=0) $ret = false; |
||
| 758 | |||
| 759 | return $ret; |
||
| 760 | } |
||
| 761 | |||
| 762 | /** |
||
| 763 | * Get the time-, price-, quantity-sum and max. modification date for the given timesheet entries |
||
| 764 | * |
||
| 765 | * @param array $ids array of timesheet id's |
||
| 766 | * @return array with values for keys "duration", "price", "max_modified" and "quantity" |
||
| 767 | */ |
||
| 768 | function sum($ids) |
||
| 769 | { |
||
| 770 | if (!$ids) |
||
| 771 | { |
||
| 772 | return array('duration' => 0, 'quantity' => 0, 'price' => 0, 'max_modified' => null); |
||
| 773 | } |
||
| 774 | return $this->search(array('ts_id'=>$ids),true,'','','',false,'AND',false,null,'',false,true); |
||
| 775 | } |
||
| 776 | |||
| 777 | /** |
||
| 778 | * get title for a timesheet entry identified by $entry |
||
| 779 | * |
||
| 780 | * Is called as hook to participate in the linking |
||
| 781 | * |
||
| 782 | * @param int|array $entry int ts_id or array with timesheet entry |
||
| 783 | * @return string/boolean string with title, null if timesheet not found, false if no perms to view it |
||
| 784 | */ |
||
| 785 | function link_title( $entry ) |
||
| 786 | { |
||
| 787 | if (!is_array($entry)) |
||
| 788 | { |
||
| 789 | // need to preserve the $this->data |
||
| 790 | $backup =& $this->data; |
||
| 791 | unset($this->data); |
||
| 792 | $entry = $this->read( $entry,false,false); |
||
| 793 | // restore the data again |
||
| 794 | $this->data =& $backup; |
||
| 795 | } |
||
| 796 | if (!$entry) |
||
| 797 | { |
||
| 798 | return $entry; |
||
| 799 | } |
||
| 800 | $format = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat']; |
||
| 801 | if (date('H:i',$entry['ts_start']) != '00:00') // dont show 00:00 time, as it means date only |
||
| 802 | { |
||
| 803 | $format .= ' '.($GLOBALS['egw_info']['user']['preferences']['common']['timeformat'] == 12 ? 'h:i a' : 'H:i'); |
||
| 804 | } |
||
| 805 | return date($format,$entry['ts_start']).': '.$entry['ts_title']; |
||
| 806 | } |
||
| 807 | |||
| 808 | /** |
||
| 809 | * get title for multiple timesheet entries identified by $ids |
||
| 810 | * |
||
| 811 | * Is called as hook to participate in the linking |
||
| 812 | * |
||
| 813 | * @param array $ids array with ts_id's |
||
| 814 | * @return array with titles, see link_title |
||
| 815 | */ |
||
| 816 | function link_titles( array $ids ) |
||
| 817 | { |
||
| 818 | $titles = array(); |
||
| 819 | if (($entries = $this->search(array('ts_id' => $ids),'ts_id,ts_title,ts_start'))) |
||
| 820 | { |
||
| 821 | foreach($entries as $entry) |
||
| 822 | { |
||
| 823 | $titles[$entry['ts_id']] = $this->link_title($entry); |
||
| 824 | } |
||
| 825 | } |
||
| 826 | // we assume all not returned entries are not readable by the user, as we notify Link about all deletes |
||
| 827 | foreach($ids as $id) |
||
| 828 | { |
||
| 829 | if (!isset($titles[$id])) |
||
| 830 | { |
||
| 831 | $titles[$id] = false; |
||
| 832 | } |
||
| 833 | } |
||
| 834 | return $titles; |
||
| 835 | } |
||
| 836 | |||
| 837 | /** |
||
| 838 | * query timesheet for entries matching $pattern |
||
| 839 | * |
||
| 840 | * Is called as hook to participate in the linking |
||
| 841 | * |
||
| 842 | * @param string $pattern pattern to search |
||
| 843 | * @param array $options Array of options for the search |
||
| 844 | * @return array with ts_id - title pairs of the matching entries |
||
| 845 | */ |
||
| 846 | function link_query( $pattern, Array &$options = array() ) |
||
| 847 | { |
||
| 848 | $limit = false; |
||
| 849 | $need_count = false; |
||
| 850 | if($options['start'] || $options['num_rows']) { |
||
| 851 | $limit = array($options['start'], $options['num_rows']); |
||
| 852 | $need_count = true; |
||
| 853 | } |
||
| 854 | $result = array(); |
||
| 855 | foreach((array) $this->search($pattern,false,'','','%',false,'OR', $limit, null, '', $need_count) as $ts ) |
||
| 856 | { |
||
| 857 | if ($ts) $result[$ts['ts_id']] = $this->link_title($ts); |
||
| 858 | } |
||
| 859 | $options['total'] = $need_count ? $this->total : count($result); |
||
| 860 | return $result; |
||
| 861 | } |
||
| 862 | |||
| 863 | /** |
||
| 864 | * Check access to the file store |
||
| 865 | * |
||
| 866 | * @param int|array $id id of entry or entry array |
||
| 867 | * @param int $check Acl::READ for read and Acl::EDIT for write or delete access |
||
| 868 | * @param string $rel_path =null currently not used in InfoLog |
||
| 869 | * @param int $user =null for which user to check, default current user |
||
| 870 | * @return boolean true if access is granted or false otherwise |
||
| 871 | */ |
||
| 872 | function file_access($id,$check,$rel_path=null,$user=null) |
||
| 873 | { |
||
| 874 | unset($rel_path); // not used, but required by function signature |
||
| 875 | |||
| 876 | return $this->check_acl($check,$id,$user); |
||
| 877 | } |
||
| 878 | |||
| 879 | /** |
||
| 880 | * updates the project titles in the timesheet application (called whenever a project name is changed in the project manager) |
||
| 881 | * |
||
| 882 | * Todo: implement via notification |
||
| 883 | * |
||
| 884 | * @param string $oldtitle => the origin title of the project |
||
| 885 | * @param string $newtitle => the new title of the project |
||
| 886 | * @return boolean true for success, false for invalid parameters |
||
| 887 | */ |
||
| 888 | function update_ts_project($oldtitle='', $newtitle='') |
||
| 889 | { |
||
| 890 | if(strlen($oldtitle) > 0 && strlen($newtitle) > 0) |
||
| 891 | { |
||
| 892 | $this->db->update('egw_timesheet',array( |
||
| 893 | 'ts_project' => $newtitle, |
||
| 894 | 'ts_title' => $newtitle, |
||
| 895 | ),array( |
||
| 896 | 'ts_project' => $oldtitle, |
||
| 897 | ),__LINE__,__FILE__,TIMESHEET_APP); |
||
| 898 | |||
| 899 | return true; |
||
| 900 | } |
||
| 901 | return false; |
||
| 902 | } |
||
| 903 | |||
| 904 | /** |
||
| 905 | * returns array with relation link_id and ts_id (necessary for project-selection) |
||
| 906 | * |
||
| 907 | * @param int $pm_id ID of selected project |
||
| 908 | * @return array containing link_id and ts_id |
||
| 909 | */ |
||
| 910 | function get_ts_links($pm_id=0) |
||
| 911 | { |
||
| 912 | if($pm_id && isset($GLOBALS['egw_info']['user']['apps']['projectmanager'])) |
||
| 913 | { |
||
| 914 | $pm_ids = ExecMethod('projectmanager.projectmanager_bo.children',$pm_id); |
||
| 915 | $pm_ids[] = $pm_id; |
||
| 916 | $links = Link\Storage::get_links('projectmanager',$pm_ids,'timesheet'); // Link\Storage::get_links not egw_links::get_links! |
||
| 917 | if ($links) |
||
| 918 | { |
||
| 919 | $links = array_unique(call_user_func_array('array_merge',$links)); |
||
| 920 | } |
||
| 921 | return $links; |
||
| 922 | } |
||
| 923 | return array(); |
||
| 924 | } |
||
| 925 | |||
| 926 | /** |
||
| 927 | * receives notifications from the link-class: new, deleted links to timesheets, or updated content of linked entries |
||
| 928 | * |
||
| 929 | * Function makes sure timesheets linked or unlinked to projects via projectmanager behave like ones |
||
| 930 | * linked via timesheets project-selector, thought timesheet only stores project-title, not the id! |
||
| 931 | * |
||
| 932 | * @param array $data array with keys type, id, target_app, target_id, link_id, data |
||
| 933 | */ |
||
| 934 | function notify($data) |
||
| 977 | } |
||
| 978 | |||
| 979 | |||
| 980 | /** |
||
| 981 | * changes the data from the db-format to your work-format |
||
| 982 | * |
||
| 983 | * Reimplemented to store just ts_project in db, but have pm_id and ts_project in memory, |
||
| 984 | * with ts_project only set, if it contains a custom project name. |
||
| 985 | * |
||
| 986 | * @param array $data =null if given works on that array and returns result, else works on internal data-array |
||
| 987 | * @return array |
||
| 988 | */ |
||
| 989 | function db2data($data=null) |
||
| 1018 | } |
||
| 1019 | |||
| 1020 | /** |
||
| 1021 | * changes the data from your work-format to the db-format |
||
| 1022 | * |
||
| 1023 | * Reimplemented to store just ts_project in db, but have pm_id and ts_project in memory, |
||
| 1024 | * with ts_project only set, if it contains a custom project name. |
||
| 1025 | * |
||
| 1026 | * @param array $data =null if given works on that array and returns result, else works on internal data-array |
||
| 1027 | * @return array |
||
| 1028 | */ |
||
| 1029 | function data2db($data=null) |
||
| 1041 | } |
||
| 1042 | } |
||
| 1043 |
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