| Total Complexity | 193 |
| Total Lines | 994 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like resources_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 resources_bo, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 24 | class resources_bo |
||
| 25 | { |
||
| 26 | /** |
||
| 27 | * Path where the icons are stored (relative to webserver_url) |
||
| 28 | */ |
||
| 29 | const ICON_PATH = '/api/images'; |
||
| 30 | |||
| 31 | const DELETED = 'deleted'; |
||
| 32 | const PICTURE_NAME = '.picture.jpg'; |
||
| 33 | var $resource_icons = '/resources/templates/default/images/resource_icons/'; |
||
| 34 | var $debug = 0; |
||
| 35 | /** |
||
| 36 | * Instance of resources so object |
||
| 37 | * |
||
| 38 | * @var resources_so |
||
| 39 | */ |
||
| 40 | var $so; |
||
| 41 | /** |
||
| 42 | * Instance of resources Acl class |
||
| 43 | * |
||
| 44 | * @var resources_acl_bo |
||
| 45 | */ |
||
| 46 | var $acl; |
||
| 47 | /** |
||
| 48 | * Instance of Api\Categories class for resources |
||
| 49 | */ |
||
| 50 | var $cats; |
||
| 51 | |||
| 52 | /** |
||
| 53 | * List of filter options |
||
| 54 | */ |
||
| 55 | public static $filter_options = array( |
||
| 56 | -1 => 'resources', |
||
| 57 | -2 => 'accessories', |
||
| 58 | -3 => 'resources and accessories' |
||
| 59 | // Accessories of a resource added when resource selected |
||
| 60 | ); |
||
| 61 | |||
| 62 | public static $field2label = array( |
||
| 63 | 'res_id' => 'Resource ID', |
||
| 64 | 'name' => 'name', |
||
| 65 | 'short_description' => 'short description', |
||
| 66 | 'cat_id' => 'Category', |
||
| 67 | 'quantity' => 'Quantity', |
||
| 68 | 'useable' => 'Useable', |
||
| 69 | 'location' => 'Location', |
||
| 70 | 'storage_info' => 'Storage', |
||
| 71 | 'bookable' => 'Bookable', |
||
| 72 | 'buyable' => 'Buyable', |
||
| 73 | 'prize' => 'Prize', |
||
| 74 | 'long_description' => 'Long description', |
||
| 75 | 'inventory_number' => 'inventory number', |
||
| 76 | 'accessory_of' => 'Accessory of' |
||
| 77 | ); |
||
| 78 | |||
| 79 | /** |
||
| 80 | * Constructor |
||
| 81 | * |
||
| 82 | * @param int $user=null account_id of user to use for Acl, default current user |
||
| 83 | */ |
||
| 84 | function __construct($user=null) |
||
| 94 | ); |
||
| 95 | } |
||
| 96 | |||
| 97 | /** |
||
| 98 | * get rows for resources list |
||
| 99 | * |
||
| 100 | * Cornelius Weiss <[email protected]> |
||
| 101 | */ |
||
| 102 | function get_rows($query,&$rows,&$readonlys) |
||
| 103 | { |
||
| 104 | if(!$query['csv_export']) |
||
| 105 | { |
||
| 106 | Api\Cache::setSession('resources', 'index_nm', $query); |
||
| 107 | } |
||
| 108 | if ($query['store_state']) // request to store state in session and filter in prefs? |
||
| 109 | { |
||
| 110 | Api\Cache::setSession('resources',$query['store_state'],$query); |
||
| 111 | //echo "<p>".__METHOD__."() query[filter]=$query[filter], prefs[resources][filter]={$GLOBALS['egw_info']['user']['preferences']['resources']['filter']}</p>\n"; |
||
| 112 | if ($query['filter'] != $GLOBALS['egw_info']['user']['preferences']['resources']['filter']) |
||
| 113 | { |
||
| 114 | $GLOBALS['egw']->preferences->add('resources','filter',$query['filter'],'user'); |
||
| 115 | $GLOBALS['egw']->preferences->save_repository(); |
||
| 116 | } |
||
| 117 | } |
||
| 118 | if ($this->debug) _debug_array($query); |
||
| 119 | $read_onlys = 'res_id,name,short_description,quantity,useable,bookable,buyable,cat_id,location,storage_info'; |
||
| 120 | |||
| 121 | $GLOBALS['egw']->session->commit_session(); |
||
| 122 | $filter = array(); |
||
| 123 | $join = ''; |
||
| 124 | $extra_cols = array(); |
||
| 125 | |||
| 126 | // Sub-query to get the count of accessories |
||
| 127 | $acc_join = "LEFT JOIN (SELECT accessory_of AS accessory_id, count(res_id) as acc_count FROM {$this->so->table_name} GROUP BY accessory_of) AS acc ON acc.accessory_id = {$this->so->table_name}.res_id "; |
||
| 128 | |||
| 129 | switch($query['filter2']) |
||
| 130 | { |
||
| 131 | case -1: |
||
| 132 | // Resources only |
||
| 133 | $filter['accessory_of'] = -1; |
||
| 134 | $join = $acc_join; |
||
| 135 | $extra_cols[] = 'acc_count'; |
||
| 136 | break; |
||
| 137 | case -2: |
||
| 138 | // Accessories only |
||
| 139 | $filter[] = 'accessory_of != -1'; |
||
| 140 | break; |
||
| 141 | case -3: |
||
| 142 | // All |
||
| 143 | $join = $acc_join; |
||
| 144 | $extra_cols[] = 'acc_count'; |
||
| 145 | break; |
||
| 146 | case self::DELETED: |
||
| 147 | $join = $acc_join; |
||
| 148 | $extra_cols[] = 'acc_count'; |
||
| 149 | $filter[] = 'deleted IS NOT NULL'; |
||
| 150 | break; |
||
| 151 | default: |
||
| 152 | $filter['accessory_of'] = $query['filter2']; |
||
| 153 | } |
||
| 154 | if($query['filter2'] != self::DELETED) |
||
| 155 | { |
||
| 156 | $filter['deleted'] = null; |
||
| 157 | } |
||
| 158 | |||
| 159 | if ($query['filter']) |
||
| 160 | { |
||
| 161 | if (($children = $this->acl->get_cats(Acl::READ,$query['filter']))) |
||
| 162 | { |
||
| 163 | $filter['cat_id'] = array_keys($children); |
||
| 164 | $filter['cat_id'][] = $query['filter']; |
||
| 165 | } |
||
| 166 | else |
||
| 167 | { |
||
| 168 | $filter['cat_id'] = $query['filter']; |
||
| 169 | } |
||
| 170 | } |
||
| 171 | elseif (($readcats = $this->acl->get_cats(Acl::READ))) |
||
| 172 | { |
||
| 173 | $filter['cat_id'] = array_keys($readcats); |
||
| 174 | } |
||
| 175 | // if there is no catfilter -> this means you have no rights, so set the cat filter to null |
||
| 176 | if (!isset($filter['cat_id']) || empty($filter['cat_id'])) { |
||
| 177 | $filter['cat_id'] = NUll; |
||
| 178 | } |
||
| 179 | |||
| 180 | if ($query['show_bookable']) |
||
| 181 | { |
||
| 182 | $filter['bookable'] = true; |
||
| 183 | } |
||
| 184 | $order_by = $query['order'] ? $query['order'].' '. $query['sort'] : ''; |
||
| 185 | $start = (int)$query['start']; |
||
| 186 | |||
| 187 | foreach ($filter as $k => $v) $query['col_filter'][$k] = $v; |
||
| 188 | $this->so->get_rows($query, $rows, $readonlys, $join, false, false, $extra_cols); |
||
| 189 | $nr = $this->so->total; |
||
| 190 | |||
| 191 | // we are called to serve bookable resources (e.g. calendar-dialog) |
||
| 192 | if($query['show_bookable']) |
||
| 193 | { |
||
| 194 | // This is somehow ugly, i know... |
||
| 195 | foreach((array)$rows as $num => $resource) |
||
| 196 | { |
||
| 197 | $rows[$num]['default_qty'] = 1; |
||
| 198 | } |
||
| 199 | // we don't need all the following testing |
||
| 200 | return $nr; |
||
| 201 | } |
||
| 202 | |||
| 203 | $config = Api\Config::read('resources'); |
||
| 204 | foreach($rows as $num => &$resource) |
||
| 205 | { |
||
| 206 | if (!$this->acl->is_permitted($resource['cat_id'],Acl::EDIT)) |
||
| 207 | { |
||
| 208 | $readonlys["edit[$resource[res_id]]"] = true; |
||
| 209 | } |
||
| 210 | elseif($resource['deleted']) |
||
| 211 | { |
||
| 212 | $resource['class'] .= 'deleted '; |
||
| 213 | } |
||
| 214 | if (!$this->acl->is_permitted($resource['cat_id'],Acl::DELETE) || |
||
| 215 | ($resource['deleted'] && !$GLOBALS['egw_info']['user']['apps']['admin'] && $config['history'] == 'history') |
||
| 216 | ) |
||
| 217 | { |
||
| 218 | $readonlys["delete[$resource[res_id]]"] = true; |
||
| 219 | $resource['class'] .= 'no_delete '; |
||
| 220 | } |
||
| 221 | if ((!$this->acl->is_permitted($resource['cat_id'],Acl::ADD)) || |
||
| 222 | // Allow new accessory action when viewing accessories of a certain resource |
||
| 223 | $query['filter2'] <= 0 && $resource['accessory_of'] != -1) |
||
| 224 | { |
||
| 225 | $readonlys["new_acc[$resource[res_id]]"] = true; |
||
| 226 | $resource['class'] .= 'no_new_accessory '; |
||
| 227 | } |
||
| 228 | if (!$resource['bookable']) |
||
| 229 | { |
||
| 230 | $readonlys["bookable[$resource[res_id]]"] = true; |
||
| 231 | $readonlys["calendar[$resource[res_id]]"] = true; |
||
| 232 | $resource['class'] .= 'no_book '; |
||
| 233 | $resource['class'] .= 'no_view_calendar '; |
||
| 234 | } |
||
| 235 | if(!$this->acl->is_permitted($resource['cat_id'],resources_acl_bo::CAL_READ)) |
||
| 236 | { |
||
| 237 | $readonlys["calendar[$resource[res_id]]"] = true; |
||
| 238 | $resource['class'] .= 'no_view_calendar '; |
||
| 239 | } |
||
| 240 | if (!$resource['buyable']) |
||
| 241 | { |
||
| 242 | $readonlys["buyable[$resource[res_id]]"] = true; |
||
| 243 | $resource['class'] .= 'no_buy '; |
||
| 244 | } |
||
| 245 | $readonlys["view_acc[{$resource['res_id']}]"] = ($resource['acc_count'] == 0); |
||
| 246 | $resource['class'] .= ($resource['accessory_of']==-1 ? 'resource ' : 'accessory '); |
||
| 247 | if($resource['acc_count']) |
||
| 248 | { |
||
| 249 | $resource['class'] .= 'hasAccessories '; |
||
| 250 | $accessories = $this->get_acc_list($resource['res_id'],$query['filter2']==self::DELETED); |
||
| 251 | foreach($accessories as $acc_id => $acc_name) |
||
| 252 | { |
||
| 253 | $resource['accessories'][] = array('acc_id' => $acc_id, 'name' => $this->link_title($acc_id)); |
||
| 254 | } |
||
| 255 | } elseif ($resource['accessory_of'] > 0) { |
||
| 256 | $resource['accessory_of_label'] = $this->link_title($resource['accessory_of']); |
||
| 257 | } |
||
| 258 | |||
| 259 | if($resource['deleted']) |
||
| 260 | { |
||
| 261 | $rows[$num]['picture_thumb'] = 'deleted'; |
||
| 262 | } |
||
| 263 | else |
||
| 264 | { |
||
| 265 | $rows[$num]['picture_thumb'] = $this->get_picture($resource, false); |
||
| 266 | if ($rows[$num]['picture_src'] == 'own_src') |
||
| 267 | { |
||
| 268 | // VFS picture fullsize |
||
| 269 | $rows[$num]['picture_original'] = 'webdav.php/apps/resources/'.$rows[$num]['res_id'].'/.picture.jpg'; |
||
| 270 | } |
||
| 271 | else |
||
| 272 | { |
||
| 273 | // cat or generic icon fullsize |
||
| 274 | $rows[$num]['picture_original'] = $this->get_picture($resource, true); |
||
| 275 | } |
||
| 276 | } |
||
| 277 | $rows[$num]['admin'] = $this->acl->get_cat_admin($resource['cat_id']); |
||
| 278 | } |
||
| 279 | |||
| 280 | if(!Api\Storage\Customfields::get('resources')) |
||
| 281 | { |
||
| 282 | $rows['no_customfields'] = true; |
||
| 283 | } |
||
| 284 | return $nr; |
||
| 285 | } |
||
| 286 | |||
| 287 | /** |
||
| 288 | * reads a resource exept binary datas |
||
| 289 | * |
||
| 290 | * Cornelius Weiss <[email protected]> |
||
| 291 | * @param int $res_id resource id |
||
| 292 | * @return array with key => value or false if not found or allowed |
||
| 293 | */ |
||
| 294 | function read($res_id) |
||
| 295 | { |
||
| 296 | if (!($data = $this->so->read(array('res_id' => $res_id)))) |
||
| 297 | { |
||
| 298 | return null; // not found |
||
| 299 | } |
||
| 300 | if (!$this->acl->is_permitted($data['cat_id'],Acl::READ)) |
||
| 301 | { |
||
| 302 | return false; // permission denied |
||
| 303 | } |
||
| 304 | return $data; |
||
| 305 | } |
||
| 306 | |||
| 307 | /** |
||
| 308 | * saves a resource. pictures are saved in vfs |
||
| 309 | * |
||
| 310 | * Cornelius Weiss <[email protected]> |
||
| 311 | * @param array $resource array with key => value of all needed datas |
||
| 312 | * @return string msg if somthing went wrong; nothing if all right |
||
| 313 | */ |
||
| 314 | function save($resource) |
||
| 315 | { |
||
| 316 | if(!$this->acl->is_permitted($resource['cat_id'],Acl::EDIT)) |
||
| 317 | { |
||
| 318 | return lang('You are not permitted to edit this resource!'); |
||
| 319 | } |
||
| 320 | $old = array(); |
||
| 321 | // we need an id to save pictures and make links... |
||
| 322 | if(!$resource['res_id']) |
||
| 323 | { |
||
| 324 | $resource['res_creator'] = $GLOBALS['egw_info']['user']['account_id']; |
||
| 325 | $resource['res_created'] = Api\DateTime::server2user(time(),'ts'); |
||
| 326 | $resource['res_id'] = $this->so->save($resource); |
||
| 327 | } |
||
| 328 | else |
||
| 329 | { |
||
| 330 | $resource['res_modifier'] = $GLOBALS['egw_info']['user']['account_id']; |
||
| 331 | $resource['res_modified'] = Api\DateTime::server2user(time(),'ts'); |
||
| 332 | $old = $this->read($resource['res_id']); |
||
| 333 | } |
||
| 334 | |||
| 335 | switch ($resource['picture_src']) |
||
| 336 | { |
||
| 337 | case 'own_src': |
||
| 338 | if($resource['own_file']['size'] > 0) |
||
| 339 | { |
||
| 340 | $msg = $this->save_picture($resource['own_file'],$resource['res_id']); |
||
| 341 | unset($resource['own_file']); |
||
| 342 | break; |
||
| 343 | } |
||
| 344 | elseif(@Vfs::stat('/apps/resources/'.$resource['res_id'].'/'.self::PICTURE_NAME)) |
||
| 345 | { |
||
| 346 | break; |
||
| 347 | } |
||
| 348 | $resource['picture_src'] = 'cat_src'; |
||
| 349 | case 'cat_src': |
||
| 350 | break; |
||
| 351 | case 'gen_src': |
||
| 352 | $resource['picture_src'] = 'gen_src'; |
||
| 353 | break; |
||
| 354 | default: |
||
| 355 | if($resource['own_file']['size'] > 0) |
||
| 356 | { |
||
| 357 | $resource['picture_src'] = 'own_src'; |
||
| 358 | $msg = $this->save_picture($resource['own_file'],$resource['res_id']); |
||
| 359 | } |
||
| 360 | else |
||
| 361 | { |
||
| 362 | $resource['picture_src'] = 'cat_src'; |
||
| 363 | } |
||
| 364 | } |
||
| 365 | // somthing went wrong on saving own picture |
||
| 366 | if($msg) |
||
| 367 | { |
||
| 368 | return $msg; |
||
| 369 | } |
||
| 370 | |||
| 371 | // Check for restore of deleted, restore held links |
||
| 372 | if($old && $old['deleted'] && !$resource['deleted']) |
||
| 373 | { |
||
| 374 | Link::restore('resources', $resource['res_id']); |
||
| 375 | } |
||
| 376 | |||
| 377 | // delete old pictures |
||
| 378 | if($resource['picture_src'] != 'own_src') |
||
| 379 | { |
||
| 380 | $this->remove_picture($resource['res_id']); |
||
| 381 | } |
||
| 382 | |||
| 383 | // Update link title |
||
| 384 | Link::notify_update('resources',$resource['res_id'], $resource); |
||
| 385 | // save links |
||
| 386 | if(is_array($resource['link_to']['to_id'])) |
||
| 387 | { |
||
| 388 | Link::link('resources',$resource['res_id'],$resource['link_to']['to_id']); |
||
| 389 | } |
||
| 390 | if($resource['accessory_of'] != $old['accessory_of']) |
||
| 391 | { |
||
| 392 | Link::unlink(0,'resources',$resource['res_id'],'','resources',$old['accessory_of']); |
||
| 393 | |||
| 394 | // Check for resource changing to accessory - move its accessories to resource |
||
| 395 | if($old['accessory_of'] == -1 && ($accessories = $this->get_acc_list($resource['res_id']))) |
||
| 396 | { |
||
| 397 | foreach($accessories as $accessory => $name) |
||
| 398 | { |
||
| 399 | Link::unlink(0,'resources',$accessory,'','resources',$resource['res_id']); |
||
| 400 | if (($acc = $this->read($accessory))) |
||
| 401 | { |
||
| 402 | $acc['accessory_of'] = -1; |
||
| 403 | $this->so->save($acc); |
||
| 404 | } |
||
| 405 | } |
||
| 406 | } |
||
| 407 | } |
||
| 408 | if($resource['accessory_of'] != -1) |
||
| 409 | { |
||
| 410 | Link::link('resources',$resource['res_id'],'resources',$resource['accessory_of']); |
||
| 411 | } |
||
| 412 | |||
| 413 | if(!empty($resource['res_id']) && $this->so->get_value("cat_id",$resource['res_id']) != $resource['cat_id'] && $resource['accessory_of'] == -1) |
||
| 414 | { |
||
| 415 | $accessories = $this->get_acc_list($resource['res_id']); |
||
| 416 | foreach($accessories as $accessory => $name) |
||
| 417 | { |
||
| 418 | if (($acc = $this->so->read($accessory))) |
||
| 419 | { |
||
| 420 | $acc['cat_id'] = $resource['cat_id']; |
||
| 421 | $this->so->save($acc); |
||
| 422 | } |
||
| 423 | } |
||
| 424 | } |
||
| 425 | |||
| 426 | $res_id = $this->so->save($resource); |
||
| 427 | |||
| 428 | // History & notifications |
||
| 429 | if (!is_object($this->tracking)) |
||
| 430 | { |
||
| 431 | $this->tracking = new resources_tracking(); |
||
| 432 | } |
||
| 433 | if ($this->tracking->track($resource,$old,$this->user) === false) |
||
| 434 | { |
||
| 435 | return implode(', ',$this->tracking->errors); |
||
| 436 | } |
||
| 437 | |||
| 438 | return $res_id ? $res_id : lang('Something went wrong by saving resource'); |
||
| 439 | } |
||
| 440 | |||
| 441 | /** |
||
| 442 | * deletes resource including pictures and links |
||
| 443 | * |
||
| 444 | * @author Lukas Weiss <[email protected]> |
||
| 445 | * @param int $res_id id of resource |
||
| 446 | * @return string|false string with error or false on success |
||
| 447 | */ |
||
| 448 | function delete($res_id) |
||
| 449 | { |
||
| 450 | if(!$this->acl->is_permitted($this->so->get_value('cat_id',$res_id),Acl::DELETE)) |
||
| 451 | { |
||
| 452 | return lang('You are not permitted to delete this resource!'); |
||
| 453 | } |
||
| 454 | |||
| 455 | // check if we only mark resources as deleted, or really delete them |
||
| 456 | $config = Api\Config::read('resources'); |
||
| 457 | if (!($old = $this->read($res_id))) |
||
| 458 | { |
||
| 459 | // error is returned at end of function |
||
| 460 | } |
||
| 461 | elseif ($config['history'] != '' && $old['deleted'] == null) |
||
| 462 | { |
||
| 463 | $old['deleted'] = time(); |
||
| 464 | $this->save($old); |
||
| 465 | Link::unlink(0,'resources',$res_id,'','','',true); |
||
| 466 | $accessories = $this->get_acc_list($res_id); |
||
| 467 | foreach($accessories as $acc_id => $name) |
||
| 468 | { |
||
| 469 | // Don't purge already deleted accessories |
||
| 470 | if (($acc = $this->read($acc_id)) && !$acc['deleted']) |
||
| 471 | { |
||
| 472 | $acc['deleted'] = time(); |
||
| 473 | $this->save($acc); |
||
| 474 | Link::unlink(0,'resources',$acc_id,'','','',true); |
||
| 475 | } |
||
| 476 | } |
||
| 477 | return false; |
||
| 478 | } |
||
| 479 | elseif ($this->so->delete(array('res_id'=>$res_id))) |
||
| 480 | { |
||
| 481 | $accessories = $this->get_acc_list($res_id, true); |
||
| 482 | foreach($accessories as $acc_id => $name) |
||
| 483 | { |
||
| 484 | if($this->delete($acc_id) && ($acc = $this->read($acc_id))) |
||
| 485 | { |
||
| 486 | $acc['accessory_of'] = -1; |
||
| 487 | $this->save($acc); |
||
| 488 | } |
||
| 489 | }; |
||
| 490 | $this->remove_picture($res_id); |
||
| 491 | Link::unlink(0,'resources',$res_id); |
||
| 492 | // delete the resource from the calendar |
||
| 493 | ExecMethod('calendar.calendar_so.deleteaccount','r'.$res_id); |
||
| 494 | return false; |
||
| 495 | } |
||
| 496 | return lang('Something went wrong by deleting resource'); |
||
| 497 | } |
||
| 498 | |||
| 499 | /** |
||
| 500 | * gets list of accessories for resource |
||
| 501 | * |
||
| 502 | * Cornelius Weiss <[email protected]> |
||
| 503 | * @param int $res_id id of resource |
||
| 504 | * @param boolean $deleted Include deleted accessories |
||
| 505 | * @return array |
||
| 506 | */ |
||
| 507 | function get_acc_list($res_id,$deleted=false) |
||
| 520 | } |
||
| 521 | |||
| 522 | /** |
||
| 523 | * Search for resources for calendar to select as participants |
||
| 524 | * |
||
| 525 | * Search and options match Link::query() |
||
| 526 | * |
||
| 527 | * Resources return actual resources as well as categories that match |
||
| 528 | * |
||
| 529 | * @param String $search - Search string |
||
| 530 | * @param Array $options - search options |
||
| 531 | * @see Link::query() |
||
| 532 | * |
||
| 533 | * @return Array List of ID => Title entries matching search string |
||
| 534 | */ |
||
| 535 | public static function calendar_search($search, $options) |
||
| 536 | { |
||
| 537 | $bo = new resources_bo(); |
||
| 538 | |||
| 539 | // Resources - call direct to avoid cache |
||
| 540 | $list = $bo->link_query($search, $options); |
||
| 541 | |||
| 542 | // Categories |
||
| 543 | $cats = $bo->acl->get_cats(Acl::READ); |
||
| 544 | foreach($cats as $cat_id => $cat) |
||
| 545 | { |
||
| 546 | if($cat && stripos($cat, $search) !== FALSE) |
||
| 547 | { |
||
| 548 | // Get resources for that category |
||
| 549 | if(!$options['exec']) |
||
| 550 | { |
||
| 551 | $resources = $bo->get_resources_by_category($cat_id); |
||
| 552 | } |
||
| 553 | else |
||
| 554 | { |
||
| 555 | $cat_options = $options; |
||
| 556 | $cat_options['cat_id'] = $cat_id; |
||
| 557 | $resources = $bo->link_query('',$cat_options); |
||
| 558 | } |
||
| 559 | |||
| 560 | // Edit dialog sends exec as an option, don't add categories |
||
| 561 | if(count($resources) && !$options['exec']) |
||
| 562 | { |
||
| 563 | $_resources = array_map( |
||
| 564 | function($id) { return 'r'.$id;}, |
||
| 565 | array_keys($resources) |
||
| 566 | ); |
||
| 567 | $list['cat-'.$cat_id] = array( |
||
| 568 | 'label' => $bo->acl->egw_cats->id2name($cat_id), |
||
| 569 | 'resources' => $_resources, |
||
| 570 | ); |
||
| 571 | } |
||
| 572 | else if ($resources && $options['exec']) |
||
| 573 | { |
||
| 574 | array_map( |
||
| 575 | function($id,$name) use (&$list) { if(!$list[''.$id]) $list[''.$id] = $name;}, |
||
| 576 | array_keys($resources), $resources |
||
| 577 | ); |
||
| 578 | } |
||
| 579 | } |
||
| 580 | } |
||
| 581 | |||
| 582 | return $list; |
||
| 583 | } |
||
| 584 | |||
| 585 | /** |
||
| 586 | * Get a list of resources (ID => name) matching a single category ID |
||
| 587 | * @param int $cat_id |
||
| 588 | * @return array() |
||
| 589 | */ |
||
| 590 | public function get_resources_by_category($cat_id) |
||
| 591 | { |
||
| 592 | $resources = array(); |
||
| 593 | $filter = array( |
||
| 594 | 'cat_id' => $cat_id, |
||
| 595 | //'accessory_of' => '-1' |
||
| 596 | 'deleted' => null |
||
| 597 | ); |
||
| 598 | $only_keys = 'res_id,name'; |
||
| 599 | $data = $this->so->search(array(),$only_keys,$order_by='name',$extra_cols='',$wildcard='%',$empty,$op='OR',false,$filter); |
||
| 600 | if(is_array($data) && $data) |
||
| 601 | { |
||
| 602 | foreach($data as $resource) |
||
| 603 | { |
||
| 604 | $resources[$resource['res_id']] = $resource['name']; |
||
| 605 | } |
||
| 606 | } |
||
| 607 | |||
| 608 | return $resources; |
||
| 609 | } |
||
| 610 | |||
| 611 | /** |
||
| 612 | * returns info about resource for calender |
||
| 613 | * @author Cornelius Weiss<[email protected]> |
||
| 614 | * @param int|array|string $res_id single id, array $num => $res_id or |
||
| 615 | * 'cat-<cat_id>' for the whole category |
||
| 616 | * @return array |
||
| 617 | */ |
||
| 618 | function get_calendar_info($res_id) |
||
| 619 | { |
||
| 620 | //error_log(__METHOD__ . "(".print_r($res_id,true).")"); |
||
| 621 | |||
| 622 | // Resource category |
||
| 623 | if(is_string($res_id) && strpos($res_id, 'cat-') === 0) |
||
| 624 | { |
||
| 625 | $cat_id = (int)substr($res_id, 4); |
||
| 626 | if(!$this->acl->is_permitted($cat_id, Acl::READ)) |
||
| 627 | { |
||
| 628 | return array(); |
||
| 629 | } |
||
| 630 | return array( array( |
||
| 631 | 'name' => $this->acl->get_cat_name($cat_id), |
||
| 632 | 'rights' => $this->acl->get_permissions($cat_id), |
||
| 633 | 'resources' => array_map( |
||
| 634 | function($id) { return 'r'.$id;}, |
||
| 635 | array_keys($this->get_resources_by_category($cat_id)) |
||
| 636 | ) |
||
| 637 | )); |
||
| 638 | } |
||
| 639 | |||
| 640 | if(!is_array($res_id) && $res_id < 1) return; |
||
| 641 | |||
| 642 | $data = $this->so->search(array('res_id' => $res_id),self::TITLE_COLS.',useable'); |
||
| 643 | if (!is_array($data)) |
||
| 644 | { |
||
| 645 | //error_log(__METHOD__." No Calendar Data found for Resource with id $res_id"); |
||
| 646 | return array(); |
||
| 647 | } |
||
| 648 | foreach($data as $num => &$resource) |
||
| 649 | { |
||
| 650 | $resource['rights'] = false; |
||
| 651 | foreach($this->cal_right_transform as $res_right => $cal_right) |
||
| 652 | { |
||
| 653 | if($this->acl->is_permitted($resource['cat_id'],$res_right)) |
||
| 654 | { |
||
| 655 | $resource['rights'] = $cal_right; |
||
| 656 | } |
||
| 657 | } |
||
| 658 | $resource['responsible'] = $this->acl->get_cat_admin($resource['cat_id']); |
||
| 659 | |||
| 660 | // preseed the cache |
||
| 661 | Link::set_cache('resources',$resource['res_id'],$t=$this->link_title($resource)); |
||
| 662 | } |
||
| 663 | return $data; |
||
| 664 | } |
||
| 665 | |||
| 666 | /** |
||
| 667 | * returns status for a new calendar entry depending on resources ACL |
||
| 668 | * @author Cornelius Weiss <[email protected]> |
||
| 669 | * @param int $res_id single id |
||
| 670 | * @return string|boolean false if resource not found, no read rights or not bookable, else A if user has direkt booking rights or U if no dirket booking |
||
| 671 | */ |
||
| 672 | function get_calendar_new_status($res_id) |
||
| 679 | } |
||
| 680 | |||
| 681 | /** |
||
| 682 | * @author Cornelius Weiss <[email protected]> |
||
| 683 | * query infolog for entries matching $pattern |
||
| 684 | * @param string|array $pattern if it's a string it is the string we will search for as a criteria, if it's an array we |
||
| 685 | * will seach for 'search' key in this array to get the string criteria. others keys handled are actually used |
||
| 686 | * for calendar disponibility. |
||
| 687 | * @param array $options Array of options for the search |
||
| 688 | * |
||
| 689 | */ |
||
| 690 | function link_query( $pattern, Array &$options = array() ) |
||
| 691 | { |
||
| 692 | if (is_array($pattern)) |
||
| 693 | { |
||
| 694 | $criteria =array('name' => $pattern['search'] |
||
| 695 | ,'short_description' => $pattern['search']); |
||
| 696 | } |
||
| 697 | else |
||
| 698 | { |
||
| 699 | $criteria = array('name' => $pattern |
||
| 700 | , 'short_description' => $pattern); |
||
| 701 | } |
||
| 702 | $only_keys = 'res_id,name,short_description,bookable,useable,quantity'; |
||
| 703 | |||
| 704 | // If no read access to any category, just stop |
||
| 705 | if(!$this->acl->get_cats(Acl::READ)) |
||
| 706 | { |
||
| 707 | $options['total'] = 0; |
||
| 708 | return array(); |
||
| 709 | } |
||
| 710 | $filter = array( |
||
| 711 | 'cat_id' => array_flip((array)$this->acl->get_cats(Acl::READ)), |
||
| 712 | //'accessory_of' => '-1' |
||
| 713 | 'deleted' => null |
||
| 714 | ); |
||
| 715 | $limit = false; |
||
| 716 | if($options['start'] || $options['num_rows']) { |
||
| 717 | $limit = array($options['start'], $options['num_rows']); |
||
| 718 | } |
||
| 719 | if($options['cat_id'] && in_array($options['cat_id'], $filter['cat_id'])) |
||
| 720 | { |
||
| 721 | $filter['cat_id'] = $options['cat_id']; |
||
| 722 | } |
||
| 723 | if($options['accessory_of']) |
||
| 724 | { |
||
| 725 | $filter['accessory_of'] = $options['accessory_of']; |
||
| 726 | } |
||
| 727 | $list = array(); |
||
| 728 | $data = $this->so->search($criteria,$only_keys,$order_by='name',$extra_cols='',$wildcard='%',$empty,$op='OR',$limit,$filter); |
||
| 729 | // we need to check availability of the searched resources in the calendar if $pattern ['exec'] contains some extra args |
||
| 730 | $show_conflict=False; |
||
| 731 | if ($data && $options['exec']) |
||
| 732 | { |
||
| 733 | // we'll use a cache for resources info taken from database |
||
| 734 | static $res_info_cache = array(); |
||
| 735 | $cal_info=$options['exec']; |
||
| 736 | if ( isset($cal_info['start']) && isset($cal_info['duration'])) |
||
| 737 | { |
||
| 738 | //get a calendar objet for reservations |
||
| 739 | if ( (!isset($this->bocal)) || !(is_object($this->bocal))) |
||
| 740 | { |
||
| 741 | $this->bocal = new calendar_bo(); |
||
| 742 | } |
||
| 743 | $start = new Api\DateTime($cal_info['start']); |
||
| 744 | $startarr= getdate($start->format('ts')); |
||
| 745 | if (isset($cal_info['whole_day']) && $cal_info['whole_day']) { |
||
| 746 | $startarr['hour'] = $startarr['minute'] = 0; |
||
| 747 | $start = new Api\DateTime($startarr); |
||
| 748 | $end = $start->format('ts') + 86399; |
||
| 749 | } else { |
||
| 750 | $start = $start->format('ts'); |
||
| 751 | $end = $start + ($cal_info['duration']); |
||
| 752 | } |
||
| 753 | |||
| 754 | // search events matching our timestamps |
||
| 755 | $resource_list=array(); |
||
| 756 | foreach($data as $num => $resource) |
||
| 757 | { |
||
| 758 | // we only need resources id for the search, but with a 'r' prefix |
||
| 759 | // now we take this loop to store a new resource array indexed with resource id |
||
| 760 | // and as we work for calendar we use only bookable resources |
||
| 761 | if ((isset($resource['bookable'])) && ($resource['bookable'])){ |
||
| 762 | $res_info_cache[$resource['res_id']]=$resource; |
||
| 763 | $resource_list[]='r'.$resource['res_id']; |
||
| 764 | } |
||
| 765 | } |
||
| 766 | $overlapping_events =& $this->bocal->search(array( |
||
| 767 | 'start' => $start, |
||
| 768 | 'end' => $end, |
||
| 769 | 'users' => $resource_list, |
||
| 770 | 'ignore_acl' => true, // otherwise we get only events readable by the user |
||
| 771 | 'enum_groups' => false, // otherwise group-events would not block time |
||
| 772 | )); |
||
| 773 | |||
| 774 | // parse theses overlapping events |
||
| 775 | foreach($overlapping_events as $event) |
||
| 776 | { |
||
| 777 | if ($event['non_blocking']) continue; // ignore non_blocking events |
||
| 778 | if (isset($cal_info['event_id']) && $event['id']==$cal_info['event_id']) { |
||
| 779 | continue; //ignore this event, it's the current edited event, no conflict by def |
||
| 780 | } |
||
| 781 | // now we are interested only on resources booked by theses events |
||
| 782 | if (isset($event['participants']) && is_array($event['participants'])){ |
||
| 783 | foreach($event['participants'] as $part_key => $part_detail){ |
||
| 784 | if ($part_key{0}=='r') |
||
| 785 | { //now we gatta resource here |
||
| 786 | //need to check the quantity of this resource |
||
| 787 | $resource_id=substr($part_key,1); |
||
| 788 | // if we do not find this resource in our indexed array it's certainly |
||
| 789 | // because it was unset, non bookable maybe |
||
| 790 | if (!isset($res_info_cache[$resource_id])) continue; |
||
| 791 | // to detect ressources with default to 1 quantity |
||
| 792 | if (!isset($res_info_cache[$resource_id]['useable'])) { |
||
| 793 | $res_info_cache[$resource_id]['useable'] = 1; |
||
| 794 | } |
||
| 795 | // now decrement this quantity useable |
||
| 796 | $quantity = 1; |
||
| 797 | $this->bocal->so->split_status($part_detail,$quantity); |
||
| 798 | |||
| 799 | $res_info_cache[$resource_id]['useable']-=$quantity; |
||
| 800 | } |
||
| 801 | } |
||
| 802 | } |
||
| 803 | } |
||
| 804 | } |
||
| 805 | } |
||
| 806 | if (isset($res_info_cache)) { |
||
| 807 | $show_conflict= $GLOBALS['egw_info']['user']['preferences']['calendar']['defaultresource_sel'] !== 'resources_without_conflict'; |
||
| 808 | // if we have this array indexed on resource id it means non-bookable resource are removed and we are working for calendar |
||
| 809 | // so we'll loop on this one and not $data |
||
| 810 | foreach($res_info_cache as $id => $resource) { |
||
| 811 | //maybe this resource is reserved |
||
| 812 | if ( ($resource['useable'] < 1) ) |
||
| 813 | { |
||
| 814 | if($show_conflict) |
||
| 815 | { |
||
| 816 | $list[$id] = ' ('.lang('conflict').') '.$resource['name']. ($resource['short_description'] ? ', ['.$resource['short_description'].']':''); |
||
| 817 | } |
||
| 818 | } |
||
| 819 | else |
||
| 820 | { |
||
| 821 | $list[$id] = $resource['name']. ($resource['short_description'] ? ', ['.$resource['short_description'].']':'') . |
||
| 822 | ($resource['useable'] > 1 ? " ( {$resource['useable']} / {$resource['quantity']} )" : ''); |
||
| 823 | } |
||
| 824 | } |
||
| 825 | } else { |
||
| 826 | // we are not working for the calendar, we loop on the initial $data |
||
| 827 | if (is_array($data)) { |
||
| 828 | foreach($data as $num => $resource) |
||
| 829 | { |
||
| 830 | $id=$resource['res_id']; |
||
| 831 | $list[$id] = $resource['name']. ($resource['short_description'] ? ', ['.$resource['short_description'].']':''); |
||
| 832 | } |
||
| 833 | } else { |
||
| 834 | error_log(__METHOD__." No Data found for Resource with id ".$resource['res_id']); |
||
| 835 | } |
||
| 836 | } |
||
| 837 | $options['total'] = $this->so->total; |
||
| 838 | return $list; |
||
| 839 | } |
||
| 840 | |||
| 841 | /** |
||
| 842 | * get title for an infolog entry identified by $res_id |
||
| 843 | * |
||
| 844 | * @author Cornelius Weiss <[email protected]> |
||
| 845 | * @param int|array $resource |
||
| 846 | * @return string|boolean string with title, null if resource does not exist or false if no perms to view it |
||
| 847 | */ |
||
| 848 | function link_title( $resource ) |
||
| 849 | { |
||
| 850 | if (!is_array($resource)) |
||
| 851 | { |
||
| 852 | if (!($resource = $this->read(array('res_id' => $resource)))) return $resource; |
||
| 853 | } |
||
| 854 | elseif (!$this->acl->is_permitted($resource['cat_id'],Acl::READ)) |
||
| 855 | { |
||
| 856 | return false; |
||
| 857 | } |
||
| 858 | return $resource['name']. ($resource['short_description'] ? ', ['.$resource['short_description'].']':''); |
||
| 859 | } |
||
| 860 | |||
| 861 | /** |
||
| 862 | * Columns displayed in title (or required for ACL) |
||
| 863 | * |
||
| 864 | */ |
||
| 865 | const TITLE_COLS = 'res_id,name,short_description,cat_id'; |
||
| 866 | |||
| 867 | /** |
||
| 868 | * get title for multiple contacts identified by $ids |
||
| 869 | * |
||
| 870 | * Is called as hook to participate in the linking. |
||
| 871 | * |
||
| 872 | * @param array $ids array with resource-id's |
||
| 873 | * @return array with titles, see link_title |
||
| 874 | */ |
||
| 875 | function link_titles(array $ids) |
||
| 876 | { |
||
| 877 | $titles = array(); |
||
| 878 | if (($resources =& $this->so->search(array('res_id' => $ids),self::TITLE_COLS))) |
||
| 879 | { |
||
| 880 | foreach($resources as $resource) |
||
| 881 | { |
||
| 882 | $titles[$resource['res_id']] = $this->link_title($resource); |
||
| 883 | } |
||
| 884 | } |
||
| 885 | // we assume all not returned contacts are not readable for the user (as we report all deleted contacts to Link) |
||
| 886 | foreach($ids as $id) |
||
| 887 | { |
||
| 888 | if (!isset($titles[$id])) |
||
| 889 | { |
||
| 890 | $titles[$id] = false; |
||
| 891 | } |
||
| 892 | } |
||
| 893 | return $titles; |
||
| 894 | } |
||
| 895 | |||
| 896 | /** |
||
| 897 | * saves a pictures in vfs |
||
| 898 | * |
||
| 899 | * Cornelius Weiss <[email protected]> |
||
| 900 | * @param array $file array with key => value |
||
| 901 | * @param int $resource_id |
||
| 902 | * @return mixed string with msg if somthing went wrong; nothing if all right |
||
| 903 | */ |
||
| 904 | function save_picture($file,$resource_id) |
||
| 935 | )); |
||
| 936 | } |
||
| 937 | |||
| 938 | /** |
||
| 939 | * get resource picture either from vfs or from symlink |
||
| 940 | * Cornelius Weiss <[email protected]> |
||
| 941 | * @param int|array $resource res-id or whole resource array |
||
| 942 | * @param bool $fullsize false = thumb, true = full pic |
||
| 943 | * @return string url of picture |
||
| 944 | */ |
||
| 945 | function get_picture($resource,$fullsize=false) |
||
| 980 | } |
||
| 981 | |||
| 982 | /** |
||
| 983 | * removes picture from vfs |
||
| 984 | * |
||
| 985 | * Cornelius Weiss <[email protected]> |
||
| 986 | * @param int $res_id id of resource |
||
| 987 | * @return bool succsess or not |
||
| 988 | */ |
||
| 989 | function remove_picture($res_id) |
||
| 996 | } |
||
| 997 | |||
| 998 | /** |
||
| 999 | * get_genpicturelist |
||
| 1000 | * gets all pictures from 'generic picutres dir' in selectbox style for eTemplate |
||
| 1001 | * |
||
| 1002 | * Cornelius Weiss <[email protected]> |
||
| 1003 | * @return array directory contens in eTemplates selectbox style |
||
| 1004 | */ |
||
| 1005 | function get_genpicturelist() |
||
| 1018 | } |
||
| 1019 | } |
||
| 1020 |