| Total Complexity | 192 |
| Total Lines | 1014 |
| Duplicated Lines | 0 % |
| Changes | 2 | ||
| Bugs | 0 | Features | 0 |
Complex classes like calendar_uilist 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 calendar_uilist, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 31 | class calendar_uilist extends calendar_ui |
||
| 32 | { |
||
| 33 | var $public_functions = array( |
||
| 34 | 'listview' => True, |
||
| 35 | ); |
||
| 36 | /** |
||
| 37 | * integer level or string function- or widget-name |
||
| 38 | * |
||
| 39 | * @var mixed |
||
| 40 | */ |
||
| 41 | var $debug=false; |
||
| 42 | /** |
||
| 43 | * Filternames |
||
| 44 | * |
||
| 45 | * @var array |
||
| 46 | */ |
||
| 47 | var $date_filters = array( |
||
| 48 | 'after' => 'After current date', |
||
| 49 | 'before' => 'Before current date', |
||
| 50 | 'today' => 'Today', |
||
| 51 | 'week' => 'Week', |
||
| 52 | 'month' => 'Month', |
||
| 53 | 'all' => 'All events', |
||
| 54 | 'custom' => 'Selected range', |
||
| 55 | ); |
||
| 56 | |||
| 57 | /** |
||
| 58 | * Constructor |
||
| 59 | * |
||
| 60 | * @param array $set_states =null to manualy set / change one of the states, default NULL = use $_REQUEST |
||
| 61 | */ |
||
| 62 | function __construct($set_states=null) |
||
| 72 | } |
||
| 73 | |||
| 74 | /** |
||
| 75 | * Show the listview |
||
| 76 | */ |
||
| 77 | function listview($_content=null,$msg='',$home=false) |
||
| 78 | { |
||
| 79 | if ($_GET['msg']) $msg .= $_GET['msg']; |
||
| 80 | if ($this->group_warning) $msg .= $this->group_warning; |
||
|
|
|||
| 81 | |||
| 82 | $etpl = new Etemplate('calendar.list'); |
||
| 83 | |||
| 84 | // Handle merge from sidebox |
||
| 85 | if($_GET['merge']) |
||
| 86 | { |
||
| 87 | $_content['nm']['action'] = 'document_'.$_GET['merge']; |
||
| 88 | $_content['nm']['select_all'] = true; |
||
| 89 | } |
||
| 90 | |||
| 91 | if (is_array($_content)) |
||
| 92 | { |
||
| 93 | // handle a single button like actions |
||
| 94 | foreach(array('delete','timesheet','document') as $button) |
||
| 95 | { |
||
| 96 | if ($_content['nm']['rows'][$button]) |
||
| 97 | { |
||
| 98 | $id = key($_content['nm']['rows'][$button]); |
||
| 99 | $_content['nm']['action'] = $button; |
||
| 100 | $_content['nm']['selected'] = array($id); |
||
| 101 | } |
||
| 102 | } |
||
| 103 | // Handle actions |
||
| 104 | if ($_content['nm']['action']) |
||
| 105 | { |
||
| 106 | // Allow merge using the date range filter |
||
| 107 | if(strpos($_content['nm']['action'],'document') !== false && |
||
| 108 | !count($_content['nm']['selected']) && !$_content['nm']['select_all']) { |
||
| 109 | $_content['nm']['selected'][] = $this->get_merge_range($_content['nm']); |
||
| 110 | } |
||
| 111 | if (!count($_content['nm']['selected']) && !$_content['nm']['select_all']) |
||
| 112 | { |
||
| 113 | $msg = lang('You need to select some events first'); |
||
| 114 | } |
||
| 115 | else |
||
| 116 | { |
||
| 117 | $success = $failed = $action_msg = null; |
||
| 118 | if ($this->action($_content['nm']['action'],$_content['nm']['selected'],$_content['nm']['select_all'], |
||
| 119 | $success,$failed,$action_msg,'calendar_list',$msg, $_content['nm']['checkboxes']['no_notifications'])) |
||
| 120 | { |
||
| 121 | $msg .= lang('%1 event(s) %2',$success,$action_msg); |
||
| 122 | } |
||
| 123 | elseif(is_null($msg)) |
||
| 124 | { |
||
| 125 | $msg .= lang('%1 event(s) %2, %3 failed because of insufficient rights !!!',$success,$action_msg,$failed); |
||
| 126 | } |
||
| 127 | } |
||
| 128 | } |
||
| 129 | } |
||
| 130 | $content = array( |
||
| 131 | 'nm' => Api\Cache::getSession('calendar', 'calendar_list'), |
||
| 132 | ); |
||
| 133 | if (!is_array($content['nm'])) |
||
| 134 | { |
||
| 135 | $content['nm'] = array( |
||
| 136 | 'get_rows' => 'calendar.calendar_uilist.get_rows', |
||
| 137 | 'filter_no_lang' => True, // I set no_lang for filter (=dont translate the options) |
||
| 138 | 'no_filter2' => True, // I disable the 2. filter (params are the same as for filter) |
||
| 139 | 'no_cat' => True, // I disable the cat-selectbox |
||
| 140 | 'filter' => 'month', |
||
| 141 | 'order' => 'cal_start',// IO name of the column to sort after (optional for the sortheaders) |
||
| 142 | 'sort' => 'ASC',// IO direction of the sort: 'ASC' or 'DESC' |
||
| 143 | 'default_cols' => '!week,weekday,cal_title,cal_description,recure,cal_location,cal_owner,cat_id,pm_id', |
||
| 144 | 'filter_onchange' => "app.calendar.filter_change", |
||
| 145 | 'row_id' => 'row_id', // set in get rows "$event[id]:$event[recur_date]" |
||
| 146 | 'row_modified' => 'modified', |
||
| 147 | 'favorites' => true, |
||
| 148 | 'placeholder_actions' => array('add') |
||
| 149 | ); |
||
| 150 | } |
||
| 151 | $content['nm']['actions'] = $this->get_actions(); |
||
| 152 | |||
| 153 | // Skip first load if view is not listview |
||
| 154 | if($this->view && $this->view !== 'listview') |
||
| 155 | { |
||
| 156 | $content['nm']['num_rows'] = 0; |
||
| 157 | } |
||
| 158 | |||
| 159 | if (isset($_GET['filter']) && in_array($_GET['filter'],array_keys($this->date_filters))) |
||
| 160 | { |
||
| 161 | $content['nm']['filter'] = $_GET['filter']; |
||
| 162 | } |
||
| 163 | if ($_GET['search']) |
||
| 164 | { |
||
| 165 | $content['nm']['search'] = $_GET['search']; |
||
| 166 | } |
||
| 167 | if($this->owner) |
||
| 168 | { |
||
| 169 | $content['nm']['col_filter']['participant'] = is_array($this->owner) ? $this->owner : explode(',',$this->owner); |
||
| 170 | } |
||
| 171 | // search via jdots ajax_exec uses $_REQUEST['json_data'] instead of regular GET parameters |
||
| 172 | if (isset($_REQUEST['json_data']) && ($json_data = json_decode($_REQUEST['json_data'], true)) && |
||
| 173 | !empty($json_data['request']['parameters'][0])) |
||
| 174 | { |
||
| 175 | $params = null; |
||
| 176 | parse_str(substr($json_data['request']['parameters'][0], 10), $params); // cut off "/index.php?" |
||
| 177 | if (isset($params['keywords'])) // new search => set filters so every match is shown |
||
| 178 | { |
||
| 179 | $this->adjust_for_search($params['keywords'], $content['nm']); |
||
| 180 | } |
||
| 181 | unset($params['keywords']); |
||
| 182 | } |
||
| 183 | if (isset($_REQUEST['keywords'])) // new search => set filters so every match is shown |
||
| 184 | { |
||
| 185 | $this->adjust_for_search($_REQUEST['keywords'],$content['nm']); |
||
| 186 | unset($_REQUEST['keywords']); |
||
| 187 | } |
||
| 188 | $sel_options['filter'] = &$this->date_filters; |
||
| 189 | |||
| 190 | // Send categories for row styling - calendar uses no_cat, so they don't go automatically |
||
| 191 | $sel_options['category'] = array('' => lang('all')) + Etemplate\Widget\Select::typeOptions('select-cat', ',,calendar'); |
||
| 192 | // Prevent double encoding - widget does this on its own, but we're just grabbing the options |
||
| 193 | foreach($sel_options['category'] as &$label) |
||
| 194 | { |
||
| 195 | if(!is_array($label)) |
||
| 196 | { |
||
| 197 | $label = html_entity_decode($label, ENT_NOQUOTES,'utf-8'); |
||
| 198 | } |
||
| 199 | elseif($label['label']) |
||
| 200 | { |
||
| 201 | $label['label'] = html_entity_decode($label['label'], ENT_NOQUOTES,'utf-8'); |
||
| 202 | } |
||
| 203 | } |
||
| 204 | |||
| 205 | // add scrollbar to long describtion, if user choose so in his prefs |
||
| 206 | if ($this->prefs['limit_des_lines'] > 0 || (string)$this->prefs['limit_des_lines'] == '') |
||
| 207 | { |
||
| 208 | $content['css'] .= '<style type="text/css">@media screen { .listDescription { max-height: '. |
||
| 209 | (($this->prefs['limit_des_lines'] ? $this->prefs['limit_des_lines'] : 5) * 1.35). // dono why em is not real lines |
||
| 210 | 'em; overflow: auto; }}</style>'; |
||
| 211 | } |
||
| 212 | |||
| 213 | if($msg) |
||
| 214 | { |
||
| 215 | Framework::message($msg); |
||
| 216 | } |
||
| 217 | $html = $etpl->exec('calendar.calendar_uilist.listview',$content,$sel_options,array(),array(),$home ? -1 : 0); |
||
| 218 | |||
| 219 | // Not sure why this has to be echoed instead of appended, but that's what works. |
||
| 220 | //echo calendar_uiviews::edit_series(); |
||
| 221 | |||
| 222 | return $html; |
||
| 223 | } |
||
| 224 | |||
| 225 | /** |
||
| 226 | * set filter for search, so that everything is shown |
||
| 227 | */ |
||
| 228 | function adjust_for_search($keywords,&$params) |
||
| 229 | { |
||
| 230 | $params['search'] = $keywords; |
||
| 231 | $params['start'] = 0; |
||
| 232 | $params['order'] = 'cal_start'; |
||
| 233 | if ($keywords) |
||
| 234 | { |
||
| 235 | $params['sort'] = 'DESC'; |
||
| 236 | unset($params['col_filter']['participant']); |
||
| 237 | } |
||
| 238 | else |
||
| 239 | { |
||
| 240 | $params['sort'] = 'ASC'; |
||
| 241 | } |
||
| 242 | } |
||
| 243 | |||
| 244 | /** |
||
| 245 | * query calendar for nextmatch in the listview |
||
| 246 | * |
||
| 247 | * @internal |
||
| 248 | * @param array &$params parameters |
||
| 249 | * @param array &$rows returned rows/events |
||
| 250 | * @param array &$readonlys eg. to disable buttons based on Acl |
||
| 251 | */ |
||
| 252 | function get_rows(&$params,&$rows,&$readonlys) |
||
| 253 | { |
||
| 254 | unset($readonlys); // not used; |
||
| 255 | //echo "uilist::get_rows() params="; _debug_array($params); |
||
| 256 | $this->filter = $params['filter']; |
||
| 257 | if ($params['filter'] == 'custom') |
||
| 258 | { |
||
| 259 | if (!$params['startdate'] && !$params['enddate']) |
||
| 260 | { |
||
| 261 | $this->filter = 'all'; |
||
| 262 | } |
||
| 263 | elseif (!$params['startdate']) |
||
| 264 | { |
||
| 265 | $this->filter = 'before'; |
||
| 266 | $this->manage_states(array('date' => $this->bo->date2string($params['enddate']))); |
||
| 267 | } |
||
| 268 | elseif (!$params['enddate']) |
||
| 269 | { |
||
| 270 | $this->filter = 'after'; |
||
| 271 | $this->manage_states(array('date' => $this->bo->date2string($params['startdate']))); |
||
| 272 | } |
||
| 273 | } |
||
| 274 | $old_params = Api\Cache::getSession('calendar', 'calendar_list'); |
||
| 275 | if (is_array($old_params)) |
||
| 276 | { |
||
| 277 | if ($old_params['filter'] && $old_params['filter'] != $params['filter']) // filter changed => order accordingly |
||
| 278 | { |
||
| 279 | $params['order'] = 'cal_start'; |
||
| 280 | $params['sort'] = $params['filter'] == 'before' ? 'DESC' : 'ASC'; |
||
| 281 | } |
||
| 282 | if ($old_params['search'] != $params['search']) |
||
| 283 | { |
||
| 284 | $this->adjust_for_search($params['search'],$params); |
||
| 285 | $this->filter = $params['filter']; |
||
| 286 | } |
||
| 287 | } |
||
| 288 | |||
| 289 | if (!$params['csv_export']) |
||
| 290 | { |
||
| 291 | Api\Cache::setSession('calendar', 'calendar_list', |
||
| 292 | array_diff_key ($params, array_flip(array('rows', 'actions', 'action_links', 'placeholder_actions')))); |
||
| 293 | } |
||
| 294 | |||
| 295 | // release session to allow parallel requests to run |
||
| 296 | $GLOBALS['egw']->session->commit_session(); |
||
| 297 | |||
| 298 | // do we need to query custom fields and which |
||
| 299 | // Check stored preference if selectcols isn't available (ie: first call) |
||
| 300 | $select_cols = $params['selectcols'] ? $params['selectcols'] : $GLOBALS['egw_info']['user']['preferences']['calendar']['nextmatch-calendar.list.rows']; |
||
| 301 | if(!is_array($params['selectcols'])) |
||
| 302 | { |
||
| 303 | $select_cols = explode(',',$select_cols); |
||
| 304 | } |
||
| 305 | if (in_array('cfs',$select_cols)) |
||
| 306 | { |
||
| 307 | $cfs = array(); |
||
| 308 | foreach($select_cols as $col) |
||
| 309 | { |
||
| 310 | if ($col[0] == '#') $cfs[] = substr($col,1); |
||
| 311 | } |
||
| 312 | } |
||
| 313 | $search_params = array( |
||
| 314 | 'cat_id' => $params['cat_id'] ? $params['cat_id'] : 0, |
||
| 315 | 'filter' => $this->filter, |
||
| 316 | 'query' => $params['search'], |
||
| 317 | 'offset' => (int) $params['start'], |
||
| 318 | 'num_rows'=> $params['num_rows'], |
||
| 319 | 'order' => $params['order'] ? $params['order'].' '.$params['sort'] : 'cal_start ASC', |
||
| 320 | 'cfs' => $params['csv_export'] ? array() : $cfs, |
||
| 321 | ); |
||
| 322 | // Non-blocking events above blocking |
||
| 323 | $search_params['order'] .= ', cal_non_blocking DESC'; |
||
| 324 | |||
| 325 | switch($this->filter) |
||
| 326 | { |
||
| 327 | case 'all': |
||
| 328 | break; |
||
| 329 | case 'before': |
||
| 330 | $search_params['end'] = $params['date'] ? Api\DateTime::to($params['date'],'ts') : $this->date; |
||
| 331 | $label = lang('Before %1',$this->bo->long_date($search_params['end'])); |
||
| 332 | break; |
||
| 333 | case 'custom': |
||
| 334 | $this->first = $search_params['start'] = Api\DateTime::to($params['startdate'],'ts'); |
||
| 335 | $this->last = $search_params['end'] = strtotime('+1 day', $this->bo->date2ts($params['enddate']))-1; |
||
| 336 | $label = $this->bo->long_date($this->first,$this->last); |
||
| 337 | break; |
||
| 338 | case 'today': |
||
| 339 | $today = new Api\DateTime(); |
||
| 340 | $today->setTime(0, 0, 0); |
||
| 341 | $this->first = $search_params['start'] = $today->format('ts'); |
||
| 342 | $today->setTime(23,59,59); |
||
| 343 | $this->last = $search_params['end'] = $today->format('ts'); |
||
| 344 | break; |
||
| 345 | case 'week': |
||
| 346 | $start = new Api\DateTime($params['date'] ? $params['date'] : $this->date); |
||
| 347 | $start->setWeekstart(); |
||
| 348 | $this->first = $start->format('ts'); |
||
| 349 | $this->last = $this->bo->date2array($this->first); |
||
| 350 | $this->last['day'] += ($params['weekend'] == 'true' ? 7 : 5) - 1; |
||
| 351 | $this->last['hour'] = 23; $this->last['minute'] = $this->last['sec'] = 59; |
||
| 352 | unset($this->last['raw']); |
||
| 353 | $this->last = $this->bo->date2ts($this->last); |
||
| 354 | $this->date_filters['week'] = $label = lang('Week').' '.adodb_date('W',$this->first).': '.$this->bo->long_date($this->first,$this->last); |
||
| 355 | $search_params['start'] = $this->first; |
||
| 356 | $search_params['end'] = $this->last; |
||
| 357 | $params['startdate'] = Api\DateTime::to($this->first, Api\DateTime::ET2); |
||
| 358 | $params['enddate'] = Api\DateTime::to($this->last, Api\DateTime::ET2); |
||
| 359 | break; |
||
| 360 | |||
| 361 | case 'month': |
||
| 362 | default: |
||
| 363 | $this->first = $this->bo->date2array($params['date'] ? $params['date'] : $this->date); |
||
| 364 | $this->first['day'] = 1; |
||
| 365 | unset($this->first['raw']); |
||
| 366 | $this->last = $this->first; |
||
| 367 | $this->last['month'] += 1; |
||
| 368 | $this->date_filters['month'] = $label = lang(adodb_date('F',$this->bo->date2ts($params['date']))).' '.$this->first['year']; |
||
| 369 | $this->first = $this->bo->date2ts($this->first); |
||
| 370 | $this->last = $this->bo->date2ts($this->last); |
||
| 371 | $this->last--; |
||
| 372 | $search_params['start'] = $this->first; |
||
| 373 | $search_params['end'] = $this->last; |
||
| 374 | $params['startdate'] = Api\DateTime::to($this->first, Api\DateTime::ET2); |
||
| 375 | $params['enddate'] = Api\DateTime::to($this->last, Api\DateTime::ET2); |
||
| 376 | break; |
||
| 377 | |||
| 378 | case 'after': |
||
| 379 | $this->date = $params['startdate'] ? Api\DateTime::to($params['startdate'],'ts') : $this->date; |
||
| 380 | $label = lang('After %1',$this->bo->long_date($this->date)); |
||
| 381 | $search_params['start'] = $this->date; |
||
| 382 | break; |
||
| 383 | } |
||
| 384 | if($params['status_filter']) |
||
| 385 | { |
||
| 386 | $search_params['filter'] = $params['status_filter']; |
||
| 387 | } |
||
| 388 | if ($params['col_filter']['participant']) |
||
| 389 | { |
||
| 390 | $search_params['users'] = is_array($params['col_filter']['participant']) ? $params['col_filter']['participant'] : array( $params['col_filter']['participant']); |
||
| 391 | } |
||
| 392 | elseif (!$params['col_filter'] || !$params['col_filter']['participant']) |
||
| 393 | { |
||
| 394 | $search_params['users'] = $params['owner'] ? $params['owner'] : explode(',',$this->owner); |
||
| 395 | } |
||
| 396 | // Allow private to stay for all viewed owners, even if in separate calendars |
||
| 397 | $search_params['private_allowed'] = (array)$params['selected_owners'] + (array)$search_params['users']; |
||
| 398 | |||
| 399 | if ($params['col_filter']) |
||
| 400 | { |
||
| 401 | $col_filter = array(); |
||
| 402 | foreach($params['col_filter'] as $name => $val) |
||
| 403 | { |
||
| 404 | if (!in_array($name, array('participant','row_id')) && (string)$val !== '') |
||
| 405 | { |
||
| 406 | $col_filter[$name] = $val; |
||
| 407 | } |
||
| 408 | } |
||
| 409 | } |
||
| 410 | $rows = $js_integration_data = array(); |
||
| 411 | |||
| 412 | // App header is mostly taken care of on the client side, but here we update |
||
| 413 | // it to match changing list filters |
||
| 414 | if($params['view'] && $params['view'] == 'listview' && Api\Json\Response::isJSONResponse()) |
||
| 415 | { |
||
| 416 | Api\Json\Response::get()->call('app.calendar.set_app_header', |
||
| 417 | (count($search_params['users']) == 1 ? $this->bo->participant_name($search_params['users'][0]).': ' : '') . |
||
| 418 | $label); |
||
| 419 | } |
||
| 420 | foreach((array) $this->bo->search($search_params, !empty($col_filter) ? $col_filter : null) as $event) |
||
| 421 | { |
||
| 422 | |||
| 423 | if ($params['csv_export']) |
||
| 424 | { |
||
| 425 | $event['participants'] = implode(",\n",$this->bo->participants($event,true)); |
||
| 426 | } |
||
| 427 | else |
||
| 428 | { |
||
| 429 | $this->to_client($event); |
||
| 430 | } |
||
| 431 | |||
| 432 | $matches = null; |
||
| 433 | if(!(int)$event['id'] && preg_match('/^([a-z_-]+)([0-9]+)$/i',$event['id'],$matches)) |
||
| 434 | { |
||
| 435 | $app = $matches[1]; |
||
| 436 | $app_id = $matches[2]; |
||
| 437 | $icons = array(); |
||
| 438 | if (($is_private = calendar_bo::integration_get_private($app,$app_id,$event))) |
||
| 439 | { |
||
| 440 | $icons[] = Api\Html::image('calendar','private'); |
||
| 441 | } |
||
| 442 | else |
||
| 443 | { |
||
| 444 | $icons = calendar_uiviews::integration_get_icons($app,$app_id,$event); |
||
| 445 | } |
||
| 446 | } |
||
| 447 | else |
||
| 448 | { |
||
| 449 | $is_private = !$this->bo->check_perms(Acl::READ,$event); |
||
| 450 | } |
||
| 451 | if ($is_private) |
||
| 452 | { |
||
| 453 | $event['class'] .= 'rowNoView '; |
||
| 454 | } |
||
| 455 | |||
| 456 | $event['app'] = 'calendar'; |
||
| 457 | $event['app_id'] = $event['id']; |
||
| 458 | |||
| 459 | // Edit link |
||
| 460 | if($app && $app_id) |
||
| 461 | { |
||
| 462 | $popup = calendar_uiviews::integration_get_popup($app,$app_id); |
||
| 463 | |||
| 464 | // Need to strip off 'onclick' |
||
| 465 | $event['edit_link'] = preg_replace('/ ?onclick="(.+)"/i', '$1', $popup); |
||
| 466 | |||
| 467 | $event['app'] = $app; |
||
| 468 | $event['app_id'] = $app_id; |
||
| 469 | |||
| 470 | // populate js_integration_data, if not already set |
||
| 471 | if (!isset($js_integration_data[$app])) |
||
| 472 | { |
||
| 473 | $js_integration_data[$app] = calendar_bo::integration_get_data($app,'edit_link'); |
||
| 474 | } |
||
| 475 | } |
||
| 476 | elseif ($event['recur_type'] != MCAL_RECUR_NONE) |
||
| 477 | { |
||
| 478 | $event['app_id'] .= ':'.Api\DateTime::to($event['recur_date'] ? $event['recur_date'] : $event['start'],'ts'); |
||
| 479 | } |
||
| 480 | |||
| 481 | // Format start and end with timezone |
||
| 482 | foreach(array('start','end') as $time) |
||
| 483 | { |
||
| 484 | $event[$time] = Api\DateTime::to($event[$time],'Y-m-d\TH:i:s\Z'); |
||
| 485 | } |
||
| 486 | |||
| 487 | $rows[] = $event; |
||
| 488 | unset($app); |
||
| 489 | unset($app_id); |
||
| 490 | } |
||
| 491 | // set js_calendar_integration object, to use it in app.js cal_open() function |
||
| 492 | $params['js_integration_data'] = json_encode($js_integration_data); |
||
| 493 | |||
| 494 | $wv=0; |
||
| 495 | $dv=0; |
||
| 496 | |||
| 497 | // Add in some select options |
||
| 498 | $users = is_array($search_params['users']) ? $search_params['users'] : explode(',',$search_params['users']); |
||
| 499 | |||
| 500 | $this->bo->warnings['groupmembers'] = ''; |
||
| 501 | if(($message = $this->check_owners_access($users))) |
||
| 502 | { |
||
| 503 | Api\Json\Response::get()->error($message); |
||
| 504 | } |
||
| 505 | else if($this->bo->warnings['groupmembers']) |
||
| 506 | { |
||
| 507 | Api\Json\Response::get()->error($this->bo->warnings['groupmembers']); |
||
| 508 | } |
||
| 509 | $rows['sel_options']['filter'] = $this->date_filters; |
||
| 510 | if($label) |
||
| 511 | { |
||
| 512 | $rows['sel_options']['filter'][$params['filter']] = $label; |
||
| 513 | } |
||
| 514 | foreach($users as $owner) |
||
| 515 | { |
||
| 516 | if(!is_int($owner) && $this->bo->resources[$owner[0]]) |
||
| 517 | { |
||
| 518 | $app = $this->bo->resources[$owner[0]]['app']; |
||
| 519 | $_owner = substr($owner,1); |
||
| 520 | // Try link first |
||
| 521 | $title = Link::title($app, $_owner ); |
||
| 522 | if($title) |
||
| 523 | { |
||
| 524 | $rows['sel_options']['owner'][$owner] = $title; |
||
| 525 | } |
||
| 526 | } |
||
| 527 | } |
||
| 528 | $params['options-selectcols']['week'] = lang('Week'); |
||
| 529 | $params['options-selectcols']['weekday'] = lang('Weekday'); |
||
| 530 | if ((substr($this->cal_prefs['nextmatch-calendar.list.rows'],0,4) == 'week' && strlen($this->cal_prefs['nextmatch-calendar.list.rows'])==4) || substr($this->cal_prefs['nextmatch-calendar.list.rows'],0,5) == 'week,') |
||
| 531 | { |
||
| 532 | $rows['format'] = '32'; // prefix date with week-number |
||
| 533 | $wv=1; |
||
| 534 | } |
||
| 535 | if (!(strpos($this->cal_prefs['nextmatch-calendar.list.rows'],'weekday')===FALSE)) |
||
| 536 | { |
||
| 537 | $rows['format'] = '16'; |
||
| 538 | $dv=1; |
||
| 539 | } |
||
| 540 | if ($wv && $dv) |
||
| 541 | { |
||
| 542 | $rows['format'] = '64'; |
||
| 543 | } |
||
| 544 | if ($this->cat_id) $rows['no_cat_id'] = true; |
||
| 545 | if (!$GLOBALS['egw_info']['user']['apps']['projectmanager']) |
||
| 546 | { |
||
| 547 | $params['options-selectcols']['pm_id'] = false; |
||
| 548 | } |
||
| 549 | //_debug_array($rows); |
||
| 550 | return $this->bo->total; |
||
| 551 | } |
||
| 552 | |||
| 553 | /** |
||
| 554 | * apply an action to multiple events |
||
| 555 | * |
||
| 556 | * @param string/int $action 'delete', 'ical', 'print', 'email' |
||
| 557 | * @param array $checked event id's to use if !$use_all |
||
| 558 | * @param boolean $use_all if true use all events of the current selection (in the session) |
||
| 559 | * @param int &$success number of succeded actions |
||
| 560 | * @param int &$failed number of failed actions (not enought permissions) |
||
| 561 | * @param string &$action_msg translated verb for the actions, to be used in a message like %1 events 'deleted' |
||
| 562 | * @param string/array $session_name 'calendar_list' |
||
| 563 | * @return boolean true if all actions succeded, false otherwise |
||
| 564 | */ |
||
| 565 | function action($action,$checked,$use_all,&$success,&$failed,&$action_msg,$session_name,&$msg,$skip_notification=false) |
||
| 566 | { |
||
| 567 | //error_log(__METHOD__."('$action', ".array2string($checked).', all='.(int)$use_all.", ...)"); |
||
| 568 | $success = $failed = 0; |
||
| 569 | $msg = null; |
||
| 570 | |||
| 571 | // Split out combined values |
||
| 572 | if(strpos($action, 'status') !== false) |
||
| 573 | { |
||
| 574 | list($action, $status) = explode('-', $action); |
||
| 575 | } |
||
| 576 | elseif (strpos($action, '_') !== false) |
||
| 577 | { |
||
| 578 | list($action, $settings) = explode('_', $action,2); |
||
| 579 | } |
||
| 580 | |||
| 581 | if ($use_all) |
||
| 582 | { |
||
| 583 | // get the whole selection |
||
| 584 | $query = is_array($session_name) ? $session_name : Api\Cache::getSession('calendar', $session_name); |
||
| 585 | @set_time_limit(0); // switch off the execution time limit, as for big selections it's too small |
||
| 586 | $query['num_rows'] = -1; // all |
||
| 587 | $readonlys = null; |
||
| 588 | $this->get_rows($query,$checked,$readonlys,!in_array($action,array('ical','document'))); // true = only return the id's |
||
| 589 | // Get rid of any extras (rows that aren't events) |
||
| 590 | if(in_array($action,array('ical','document'))) |
||
| 591 | { |
||
| 592 | foreach($checked as $key => $event) |
||
| 593 | { |
||
| 594 | if(!is_numeric($key)) |
||
| 595 | { |
||
| 596 | unset($checked[$key]); |
||
| 597 | } |
||
| 598 | } |
||
| 599 | } |
||
| 600 | } |
||
| 601 | // for calendar integration we have to fetch all rows and unset the not selected ones, as we can not filter by id |
||
| 602 | elseif($action == 'document') |
||
| 603 | { |
||
| 604 | $query = is_array($session_name) ? $session_name : Api\Cache::getSession('calendar', $session_name); |
||
| 605 | @set_time_limit(0); // switch off the execution time limit, as for big selections it's too small |
||
| 606 | $events = null; |
||
| 607 | $this->get_rows($query,$events,$readonlys); |
||
| 608 | foreach($events as $key => $event) |
||
| 609 | { |
||
| 610 | $recur_date = Api\DateTime::to($event['recur_date'],'ts'); |
||
| 611 | if (!in_array($event['id'],$checked) && !in_array($event['id'].':'.$recur_date, $checked)) unset($events[$key]); |
||
| 612 | } |
||
| 613 | $checked = array_values($events); // Clear keys |
||
| 614 | } |
||
| 615 | |||
| 616 | // Actions where one action is done to the group |
||
| 617 | switch($action) |
||
| 618 | { |
||
| 619 | case 'ical': |
||
| 620 | // compile list of unique cal_id's, as iCal should contain whole series, not recurrences |
||
| 621 | // calendar_ical->exportVCal needs to read events again, to get them in server-time |
||
| 622 | $ids = array(); |
||
| 623 | foreach($checked as $id) |
||
| 624 | { |
||
| 625 | if (is_array($id)) $id = $id['id']; |
||
| 626 | // get rid of recurrences, doublicate series and calendar-integration events |
||
| 627 | if (($id = (int)$id)) |
||
| 628 | { |
||
| 629 | $ids[$id] = $id; |
||
| 630 | } |
||
| 631 | } |
||
| 632 | $boical = new calendar_ical(); |
||
| 633 | $ical =& $boical->exportVCal($ids, '2.0', 'PUBLISH'); |
||
| 634 | Api\Header\Content::type('event.ics', 'text/calendar', bytes($ical)); |
||
| 635 | echo $ical; |
||
| 636 | exit(); |
||
| 637 | |||
| 638 | case 'document': |
||
| 639 | if (!$settings) $settings = $GLOBALS['egw_info']['user']['preferences']['calendar']['default_document']; |
||
| 640 | $document_merge = new calendar_merge(); |
||
| 641 | $msg = $document_merge->download($settings, $checked, '', $GLOBALS['egw_info']['user']['preferences']['calendar']['document_dir']); |
||
| 642 | $failed = count($checked); |
||
| 643 | error_log($msg); |
||
| 644 | return false; |
||
| 645 | } |
||
| 646 | |||
| 647 | // Actions where the action is applied to each entry |
||
| 648 | if(strpos($action, 'timesheet') !== false) |
||
| 649 | { |
||
| 650 | $timesheet_bo = new timesheet_bo(); |
||
| 651 | } |
||
| 652 | foreach($checked as &$id) |
||
| 653 | { |
||
| 654 | $recur_date = $app = $app_id = null; |
||
| 655 | if(is_array($id) && $id['id']) |
||
| 656 | { |
||
| 657 | $id = $id['id']; |
||
| 658 | } |
||
| 659 | $matches = null; |
||
| 660 | if(!(int)$id && preg_match('/^([a-z_-]+)([0-9]+)$/i',$id,$matches)) |
||
| 661 | { |
||
| 662 | $app = $matches[1]; |
||
| 663 | $app_id = $matches[2]; |
||
| 664 | $id = null; |
||
| 665 | } |
||
| 666 | else |
||
| 667 | { |
||
| 668 | list($id,$recur_date) = explode(':',$id); |
||
| 669 | } |
||
| 670 | switch($action) |
||
| 671 | { |
||
| 672 | case 'delete': |
||
| 673 | $action_msg = lang('deleted'); |
||
| 674 | if($settings == 'series') |
||
| 675 | { |
||
| 676 | // Delete the whole thing |
||
| 677 | $recur_date = 0; |
||
| 678 | } |
||
| 679 | if ($id && $this->bo->delete($id, $recur_date,false,$skip_notification)) |
||
| 680 | { |
||
| 681 | $success++; |
||
| 682 | if(!$recur_date && $settings == 'series') |
||
| 683 | { |
||
| 684 | // If there are multiple events in a series selected, the next one could purge |
||
| 685 | foreach($checked as $key => $c_id) |
||
| 686 | { |
||
| 687 | list($c_id,$recur_date) = explode(':',$c_id); |
||
| 688 | if($c_id == $id) |
||
| 689 | { |
||
| 690 | unset($checked[$key]); |
||
| 691 | } |
||
| 692 | } |
||
| 693 | } |
||
| 694 | |||
| 695 | if(Api\Json\Response::isJSONResponse()) |
||
| 696 | { |
||
| 697 | Api\Json\Response::get()->call('egw.refresh','','calendar',$id,'delete'); |
||
| 698 | } |
||
| 699 | } |
||
| 700 | else |
||
| 701 | { |
||
| 702 | $failed++; |
||
| 703 | } |
||
| 704 | break; |
||
| 705 | case 'undelete': |
||
| 706 | $action_msg = lang('recovered'); |
||
| 707 | if($settings == 'series') |
||
| 708 | { |
||
| 709 | // unDelete the whole thing |
||
| 710 | $recur_date = 0; |
||
| 711 | } |
||
| 712 | if ($id && ($event = $this->bo->read($id, $recur_date)) && $this->bo->check_perms(Acl::EDIT,$id) && |
||
| 713 | is_array($event) && $event['deleted']) |
||
| 714 | { |
||
| 715 | $event['deleted'] = null; |
||
| 716 | if($this->bo->save($event)) |
||
| 717 | { |
||
| 718 | $success++; |
||
| 719 | |||
| 720 | if(Api\Json\Response::isJSONResponse()) |
||
| 721 | { |
||
| 722 | Api\Json\Response::get()->call('egw.dataStoreUID','calendar::'.$id,$this->to_client($this->bo->read($id,$recur_date))); |
||
| 723 | Api\Json\Response::get()->call('egw.refresh','','calendar',$id,'edit'); |
||
| 724 | } |
||
| 725 | break; |
||
| 726 | } |
||
| 727 | } |
||
| 728 | $failed++; |
||
| 729 | break; |
||
| 730 | case 'status': |
||
| 731 | $action_msg = lang('Status changed'); |
||
| 732 | if($id && ($event = $this->bo->read($id, $recur_date))) |
||
| 733 | { |
||
| 734 | $old_status = $event['participants'][$GLOBALS['egw_info']['user']['account_id']]; |
||
| 735 | $quantity = $role = null; |
||
| 736 | calendar_so::split_status($old_status, $quantity, $role); |
||
| 737 | if ($old_status != $status) |
||
| 738 | { |
||
| 739 | //echo "<p>$uid: status changed '$data[old_status]' --> '$status<'/p>\n"; |
||
| 740 | $new_status = calendar_so::combine_status($status, $quantity, $role); |
||
| 741 | if ($this->bo->set_status($event,$GLOBALS['egw_info']['user']['account_id'],$new_status,$recur_date, |
||
| 742 | false,true,$skip_notification)) |
||
| 743 | { |
||
| 744 | if(Api\Json\Response::isJSONResponse()) |
||
| 745 | { |
||
| 746 | Api\Json\Response::get()->call('egw.dataStoreUID','calendar::'.$id,$this->to_client($this->bo->read($id,$recur_date))); |
||
| 747 | } |
||
| 748 | $success++; |
||
| 749 | //$msg = lang('Status changed'); |
||
| 750 | } |
||
| 751 | else |
||
| 752 | { |
||
| 753 | $failed++; |
||
| 754 | } |
||
| 755 | } |
||
| 756 | } |
||
| 757 | else |
||
| 758 | { |
||
| 759 | $failed++; |
||
| 760 | } |
||
| 761 | break; |
||
| 762 | case 'timesheet-add': |
||
| 763 | if($id && !$app) |
||
| 764 | { |
||
| 765 | $event = $this->bo->read($id, $recur_date); |
||
| 766 | } |
||
| 767 | elseif ($app) |
||
| 768 | { |
||
| 769 | $query = Api\Cache::getSession('calendar', 'calendar_list'); |
||
| 770 | $query['query'] = $app_id; |
||
| 771 | $query['search'] = $app_id; |
||
| 772 | $result = $this->bo->search($query); |
||
| 773 | $event = $result[$app.$app_id]; |
||
| 774 | } |
||
| 775 | if(!$event) |
||
| 776 | { |
||
| 777 | $failed++; |
||
| 778 | continue 2; // +1 for switch |
||
| 779 | } |
||
| 780 | $timesheet = array( |
||
| 781 | 'ts_title' => $event['title'], |
||
| 782 | 'ts_description' => $event['description'], |
||
| 783 | 'ts_start' => $event['start'], |
||
| 784 | 'ts_duration' => ($event['end'] - $event['start']) / 60, |
||
| 785 | 'ts_quantity' => ($event['end'] - $event['start']) / 3600, |
||
| 786 | 'ts_owner' => $GLOBALS['egw_info']['user']['account_id'], |
||
| 787 | 'cat_id' => null, |
||
| 788 | 'pl_id' => null |
||
| 789 | ); |
||
| 790 | |||
| 791 | // Add global categories |
||
| 792 | $categories = explode(',',$event['category']); |
||
| 793 | $global_categories = array(); |
||
| 794 | foreach($categories as $cat_id) |
||
| 795 | { |
||
| 796 | if($GLOBALS['egw']->categories->is_global($cat_id)) |
||
| 797 | { |
||
| 798 | $global_categories[] = $cat_id; |
||
| 799 | } |
||
| 800 | } |
||
| 801 | if(count($global_categories)) |
||
| 802 | { |
||
| 803 | $timesheet['cat_id'] = implode(',', $global_categories); |
||
| 804 | } |
||
| 805 | $timesheet_bo->data = array(); |
||
| 806 | $err = $timesheet_bo->save($timesheet); |
||
| 807 | |||
| 808 | //get the project manager linked to the calnedar entry |
||
| 809 | $calApp_links = Link::get_links('calendar', $event['id']); |
||
| 810 | foreach ($calApp_links as $l_app) |
||
| 811 | { |
||
| 812 | if ($l_app['app'] == 'projectmanager') |
||
| 813 | { |
||
| 814 | $prj_links = $l_app; |
||
| 815 | //Links timesheet to projectmanager |
||
| 816 | Link::link('timesheet', $timesheet_bo->data['ts_id'], 'projectmanager', $prj_links['id']); |
||
| 817 | |||
| 818 | } |
||
| 819 | } |
||
| 820 | |||
| 821 | if(!$err) |
||
| 822 | { |
||
| 823 | $success++; |
||
| 824 | |||
| 825 | // Can't link to just one of a recurring series of events |
||
| 826 | if(!$recur_date || $app) { |
||
| 827 | // Create link |
||
| 828 | $link_id = $app ? $app_id : $id; |
||
| 829 | Link::link($app ? $app : 'calendar', $link_id, 'timesheet', $timesheet_bo->data['ts_id']); |
||
| 830 | } |
||
| 831 | } |
||
| 832 | else |
||
| 833 | { |
||
| 834 | $failed++; |
||
| 835 | } |
||
| 836 | $msg = lang('Timesheet entries created for '); |
||
| 837 | break; |
||
| 838 | } |
||
| 839 | } |
||
| 840 | //error_log(__METHOD__."('$action', ".array2string($checked).', '.array2string($use_all).") sucess=$success, failed=$failed, action_msg='$action_msg', msg=".array2string($msg).' returning '.array2string(!$failed)); |
||
| 841 | return !$failed; |
||
| 842 | } |
||
| 843 | |||
| 844 | /** |
||
| 845 | * Get date ranges to select for merging instead of individual events |
||
| 846 | * |
||
| 847 | * @param $nm nextmatch array from submit |
||
| 848 | * |
||
| 849 | * @return array of ranges |
||
| 850 | */ |
||
| 851 | protected function get_merge_range($nm) |
||
| 881 | } |
||
| 882 | |||
| 883 | /** |
||
| 884 | * Get actions / context menu items |
||
| 885 | * |
||
| 886 | * @return array see nextmatch_widget::get_actions() |
||
| 887 | */ |
||
| 888 | public function get_actions() |
||
| 1045 | } |
||
| 1046 | } |
||
| 1047 |