Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like calendar_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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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_bo, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 50 | class calendar_bo |
||
| 51 | { |
||
| 52 | /** |
||
| 53 | * Gives read access to the calendar, but all events the user is not participating are private! |
||
| 54 | * Used by addressbook. |
||
| 55 | */ |
||
| 56 | const ACL_READ_FOR_PARTICIPANTS = Acl::CUSTOM1; |
||
| 57 | /** |
||
| 58 | * Right to see free/busy data only |
||
| 59 | */ |
||
| 60 | const ACL_FREEBUSY = Acl::CUSTOM2; |
||
| 61 | /** |
||
| 62 | * Allows to invite an other user (if configured to be used!) |
||
| 63 | */ |
||
| 64 | const ACL_INVITE = Acl::CUSTOM3; |
||
| 65 | |||
| 66 | /** |
||
| 67 | * @var int $debug name of method to debug or level of debug-messages: |
||
| 68 | * False=Off as higher as more messages you get ;-) |
||
| 69 | * 1 = function-calls incl. parameters to general functions like search, read, write, delete |
||
| 70 | * 2 = function-calls to exported helper-functions like check_perms |
||
| 71 | * 4 = function-calls to exported conversation-functions like date2ts, date2array, ... |
||
| 72 | * 5 = function-calls to private functions |
||
| 73 | */ |
||
| 74 | var $debug=false; |
||
| 75 | |||
| 76 | /** |
||
| 77 | * @var int $now timestamp in server-time |
||
| 78 | */ |
||
| 79 | var $now; |
||
| 80 | |||
| 81 | /** |
||
| 82 | * @var int $now_su timestamp of actual user-time |
||
| 83 | */ |
||
| 84 | var $now_su; |
||
| 85 | |||
| 86 | /** |
||
| 87 | * @var array $cal_prefs calendar-specific prefs |
||
| 88 | */ |
||
| 89 | var $cal_prefs; |
||
| 90 | |||
| 91 | /** |
||
| 92 | * @var array $common_prefs common preferences |
||
| 93 | */ |
||
| 94 | var $common_prefs; |
||
| 95 | /** |
||
| 96 | * Custom fields read from the calendar config |
||
| 97 | * |
||
| 98 | * @var array |
||
| 99 | */ |
||
| 100 | var $customfields = array(); |
||
| 101 | /** |
||
| 102 | * @var int $user nummerical id of the current user-id |
||
| 103 | */ |
||
| 104 | var $user=0; |
||
| 105 | |||
| 106 | /** |
||
| 107 | * @var array $grants grants of the current user, array with user-id / ored-ACL-rights pairs |
||
| 108 | */ |
||
| 109 | var $grants=array(); |
||
| 110 | |||
| 111 | /** |
||
| 112 | * @var array $verbose_status translated 1-char status values to a verbose name, run through lang() by the constructor |
||
| 113 | */ |
||
| 114 | var $verbose_status = array( |
||
| 115 | 'A' => 'Accepted', |
||
| 116 | 'R' => 'Rejected', |
||
| 117 | 'T' => 'Tentative', |
||
| 118 | 'U' => 'No Response', |
||
| 119 | 'D' => 'Delegated', |
||
| 120 | 'G' => 'Group invitation', |
||
| 121 | ); |
||
| 122 | /** |
||
| 123 | * @var array recur_types translates MCAL recur-types to verbose labels |
||
| 124 | */ |
||
| 125 | var $recur_types = Array( |
||
| 126 | MCAL_RECUR_NONE => 'No recurrence', |
||
| 127 | MCAL_RECUR_DAILY => 'Daily', |
||
| 128 | MCAL_RECUR_WEEKLY => 'Weekly', |
||
| 129 | MCAL_RECUR_MONTHLY_WDAY => 'Monthly (by day)', |
||
| 130 | MCAL_RECUR_MONTHLY_MDAY => 'Monthly (by date)', |
||
| 131 | MCAL_RECUR_YEARLY => 'Yearly' |
||
| 132 | ); |
||
| 133 | /** |
||
| 134 | * @var array recur_days translates MCAL recur-days to verbose labels |
||
| 135 | */ |
||
| 136 | var $recur_days = array( |
||
| 137 | MCAL_M_MONDAY => 'Monday', |
||
| 138 | MCAL_M_TUESDAY => 'Tuesday', |
||
| 139 | MCAL_M_WEDNESDAY => 'Wednesday', |
||
| 140 | MCAL_M_THURSDAY => 'Thursday', |
||
| 141 | MCAL_M_FRIDAY => 'Friday', |
||
| 142 | MCAL_M_SATURDAY => 'Saturday', |
||
| 143 | MCAL_M_SUNDAY => 'Sunday', |
||
| 144 | ); |
||
| 145 | /** |
||
| 146 | * Standard iCal attendee roles |
||
| 147 | * |
||
| 148 | * @var array |
||
| 149 | */ |
||
| 150 | var $roles = array( |
||
| 151 | 'REQ-PARTICIPANT' => 'Requested', |
||
| 152 | 'CHAIR' => 'Chair', |
||
| 153 | 'OPT-PARTICIPANT' => 'Optional', |
||
| 154 | 'NON-PARTICIPANT' => 'None', |
||
| 155 | ); |
||
| 156 | /** |
||
| 157 | * Alarm times |
||
| 158 | * |
||
| 159 | * @var array |
||
| 160 | */ |
||
| 161 | var $alarms = array( |
||
| 162 | 300 => '5 Minutes', |
||
| 163 | 600 => '10 Minutes', |
||
| 164 | 900 => '15 Minutes', |
||
| 165 | 1800 => '30 Minutes', |
||
| 166 | 3600 => '1 Hour', |
||
| 167 | 7200 => '2 Hours', |
||
| 168 | 43200 => '12 Hours', |
||
| 169 | 86400 => '1 Day', |
||
| 170 | 172800 => '2 Days', |
||
| 171 | 604800 => '1 Week', |
||
| 172 | ); |
||
| 173 | /** |
||
| 174 | * @var array $resources registered scheduling resources of the calendar (gets cached in the session for performance reasons) |
||
| 175 | */ |
||
| 176 | var $resources; |
||
| 177 | /** |
||
| 178 | * @var array $cached_event here we do some caching to read single events only once |
||
| 179 | */ |
||
| 180 | protected static $cached_event = array(); |
||
| 181 | protected static $cached_event_date_format = false; |
||
| 182 | protected static $cached_event_date = 0; |
||
| 183 | |||
| 184 | /** |
||
| 185 | * Instance of the socal class |
||
| 186 | * |
||
| 187 | * @var calendar_so |
||
| 188 | */ |
||
| 189 | var $so; |
||
| 190 | /** |
||
| 191 | * Instance of the categories class |
||
| 192 | * |
||
| 193 | * @var Api\Categories |
||
| 194 | */ |
||
| 195 | var $categories; |
||
| 196 | /** |
||
| 197 | * Config values for "calendar", only used for horizont, regular calendar config is under phpgwapi |
||
| 198 | * |
||
| 199 | * @var array |
||
| 200 | */ |
||
| 201 | var $config; |
||
| 202 | |||
| 203 | /** |
||
| 204 | * Does a user require an extra invite grant, to be able to invite an other user, default no |
||
| 205 | * |
||
| 206 | * @var string 'all', 'groups' or null |
||
| 207 | */ |
||
| 208 | public $require_acl_invite = null; |
||
| 209 | |||
| 210 | /** |
||
| 211 | * Warnings to show in regular UI |
||
| 212 | * |
||
| 213 | * @var array |
||
| 214 | */ |
||
| 215 | var $warnings = array(); |
||
| 216 | |||
| 217 | /** |
||
| 218 | * Constructor |
||
| 219 | */ |
||
| 220 | function __construct() |
||
| 221 | { |
||
| 222 | if ($this->debug > 0) $this->debug_message('calendar_bo::bocal() started',True); |
||
| 223 | |||
| 224 | $this->so = new calendar_so(); |
||
| 225 | |||
| 226 | $this->common_prefs =& $GLOBALS['egw_info']['user']['preferences']['common']; |
||
| 227 | $this->cal_prefs =& $GLOBALS['egw_info']['user']['preferences']['calendar']; |
||
| 228 | |||
| 229 | $this->now = time(); |
||
| 230 | $this->now_su = Api\DateTime::server2user($this->now,'ts'); |
||
| 231 | |||
| 232 | $this->user = $GLOBALS['egw_info']['user']['account_id']; |
||
| 233 | |||
| 234 | $this->grants = $GLOBALS['egw']->acl->get_grants('calendar'); |
||
| 235 | |||
| 236 | if (!is_array($this->resources = Api\Cache::getSession('calendar', 'resources'))) |
||
| 237 | { |
||
| 238 | $this->resources = array(); |
||
| 239 | foreach(Api\Hooks::process('calendar_resources') as $app => $data) |
||
| 240 | { |
||
| 241 | if ($data && $data['type']) |
||
| 242 | { |
||
| 243 | $this->resources[$data['type']] = $data + array('app' => $app); |
||
| 244 | } |
||
| 245 | } |
||
| 246 | $this->resources['e'] = array( |
||
| 247 | 'type' => 'e', |
||
| 248 | 'info' => __CLASS__.'::email_info', |
||
| 249 | 'app' => 'email', |
||
| 250 | ); |
||
| 251 | $this->resources['l'] = array( |
||
| 252 | 'type' => 'l',// one char type-identifier for this resources |
||
| 253 | 'info' => __CLASS__ .'::mailing_lists',// info method, returns array with id, type & name for a given id |
||
| 254 | 'app' => 'Distribution list' |
||
| 255 | ); |
||
| 256 | $this->resources[''] = array( |
||
| 257 | 'type' => '', |
||
| 258 | 'app' => 'api-accounts', |
||
| 259 | ); |
||
| 260 | $this->resources['l'] = array( |
||
| 261 | 'type' => 'l',// one char type-identifier for this resources |
||
| 262 | 'info' => __CLASS__ .'::mailing_lists',// info method, returns array with id, type & name for a given id |
||
| 263 | 'app' => 'Distribution list' |
||
| 264 | ); |
||
| 265 | Api\Cache::setSession('calendar', 'resources', $this->resources); |
||
| 266 | } |
||
| 267 | //error_log(__METHOD__ . " registered resources=". array2string($this->resources)); |
||
| 268 | |||
| 269 | $this->config = Api\Config::read('calendar'); // only used for horizont, regular calendar config is under phpgwapi |
||
| 270 | $this->require_acl_invite = $GLOBALS['egw_info']['server']['require_acl_invite']; |
||
| 271 | |||
| 272 | $this->categories = new Api\Categories($this->user,'calendar'); |
||
| 273 | |||
| 274 | $this->customfields = Api\Storage\Customfields::get('calendar'); |
||
| 275 | |||
| 276 | foreach($this->alarms as $secs => &$label) |
||
| 277 | { |
||
| 278 | $label = self::secs2label($secs); |
||
| 279 | } |
||
| 280 | } |
||
| 281 | |||
| 282 | /** |
||
| 283 | * Generate translated label for a given number of seconds |
||
| 284 | * |
||
| 285 | * @param int $secs |
||
| 286 | * @return string |
||
| 287 | */ |
||
| 288 | static public function secs2label($secs) |
||
| 289 | { |
||
| 290 | if ($secs <= 3600) |
||
| 291 | { |
||
| 292 | $label = lang('%1 minutes', $secs/60); |
||
| 293 | } |
||
| 294 | elseif($secs <= 86400) |
||
| 295 | { |
||
| 296 | $label = lang('%1 hours', $secs/3600); |
||
| 297 | } |
||
| 298 | else |
||
| 299 | { |
||
| 300 | $label = lang('%1 days', $secs/86400); |
||
| 301 | } |
||
| 302 | return $label; |
||
| 303 | } |
||
| 304 | |||
| 305 | /** |
||
| 306 | * returns info about email addresses as participants |
||
| 307 | * |
||
| 308 | * @param int|array $ids single contact-id or array of id's |
||
| 309 | * @return array |
||
| 310 | */ |
||
| 311 | static function email_info($ids) |
||
| 312 | { |
||
| 313 | if (!$ids) return null; |
||
| 314 | |||
| 315 | $data = array(); |
||
| 316 | foreach((array)$ids as $id) |
||
| 317 | { |
||
| 318 | $email = $id; |
||
| 319 | $name = ''; |
||
| 320 | $matches = null; |
||
| 321 | View Code Duplication | if (preg_match('/^(.*) *<([a-z0-9_.@-]{8,})>$/iU',$email,$matches)) |
|
| 322 | { |
||
| 323 | $name = $matches[1]; |
||
| 324 | $email = $matches[2]; |
||
| 325 | } |
||
| 326 | $data[] = array( |
||
| 327 | 'res_id' => $id, |
||
| 328 | 'email' => $email, |
||
| 329 | 'rights' => self::ACL_READ_FOR_PARTICIPANTS, |
||
| 330 | 'name' => $name, |
||
| 331 | ); |
||
| 332 | } |
||
| 333 | //error_log(__METHOD__.'('.array2string($ids).')='.array2string($data).' '.function_backtrace()); |
||
| 334 | return $data; |
||
| 335 | } |
||
| 336 | |||
| 337 | /** |
||
| 338 | * returns info about mailing lists as participants |
||
| 339 | * |
||
| 340 | * @param int|array $ids single mailing list ID or array of id's |
||
| 341 | * @return array |
||
| 342 | */ |
||
| 343 | static function mailing_lists($ids) |
||
| 344 | { |
||
| 345 | if(!is_array($ids)) |
||
| 346 | { |
||
| 347 | $ids = array($ids); |
||
| 348 | } |
||
| 349 | $data = array(); |
||
| 350 | |||
| 351 | // Email list |
||
| 352 | $contacts_obj = new Api\Contacts(); |
||
| 353 | $bo = new calendar_bo(); |
||
| 354 | foreach($ids as $id) |
||
| 355 | { |
||
| 356 | $list = $contacts_obj->read_list((int)$id); |
||
| 357 | |||
| 358 | $data[] = array( |
||
| 359 | 'res_id' => $id, |
||
| 360 | 'rights' => self::ACL_READ_FOR_PARTICIPANTS, |
||
| 361 | 'name' => $list['list_name'], |
||
| 362 | 'resources' => $bo->enum_mailing_list('l'.$id, false, false) |
||
| 363 | ); |
||
| 364 | } |
||
| 365 | |||
| 366 | return $data; |
||
| 367 | } |
||
| 368 | |||
| 369 | /** |
||
| 370 | * Enumerates the contacts in a contact list, and returns the list of contact IDs |
||
| 371 | * |
||
| 372 | * This is used to enable mailing lists as owner/participant |
||
| 373 | * |
||
| 374 | * @param string $id Mailing list participant ID, which is the mailing list |
||
| 375 | * ID prefixed with 'l' |
||
| 376 | * @param boolean $ignore_acl = false Flag to skip ACL checks |
||
| 377 | * @param boolean $use_freebusy =true should freebusy rights are taken into account, default true, can be set to false eg. for a search |
||
| 378 | * |
||
| 379 | * @return array |
||
| 380 | */ |
||
| 381 | public function enum_mailing_list($id, $ignore_acl= false, $use_freebusy = true) |
||
| 382 | { |
||
| 383 | $contact_list = array(); |
||
| 384 | $contacts = new Api\Contacts(); |
||
| 385 | if($contacts->check_list((int)substr($id,1), ACL::READ) || (int)substr($id,1) < 0) |
||
| 386 | { |
||
| 387 | $options = array('list' => substr($id,1)); |
||
| 388 | $lists = $contacts->search('',true,'','','',false,'AND',false,$options); |
||
| 389 | if(!$lists) |
||
| 390 | { |
||
| 391 | return $contact_list; |
||
| 392 | } |
||
| 393 | foreach($lists as &$contact) |
||
| 394 | { |
||
| 395 | // Check for user account |
||
| 396 | if (($account_id = $GLOBALS['egw']->accounts->name2id($contact['id'],'person_id'))) |
||
| 397 | { |
||
| 398 | $contact = ''.$account_id; |
||
| 399 | } |
||
| 400 | else |
||
| 401 | { |
||
| 402 | $contact = 'c'.$contact['id']; |
||
| 403 | } |
||
| 404 | View Code Duplication | if ($ignore_acl || $this->check_perms(ACL::READ|self::ACL_READ_FOR_PARTICIPANTS|($use_freebusy?self::ACL_FREEBUSY:0),0,$contact)) |
|
| 405 | { |
||
| 406 | if ($contact && !in_array($contact,$contact_list)) // already added? |
||
| 407 | { |
||
| 408 | $contact_list[] = $contact; |
||
| 409 | } |
||
| 410 | } |
||
| 411 | } |
||
| 412 | } |
||
| 413 | return $contact_list; |
||
| 414 | } |
||
| 415 | |||
| 416 | /** |
||
| 417 | * Add group-members as participants with status 'G' |
||
| 418 | * |
||
| 419 | * @param array $event event-array |
||
| 420 | * @return int number of added participants |
||
| 421 | */ |
||
| 422 | function enum_groups(&$event) |
||
| 423 | { |
||
| 424 | $added = 0; |
||
| 425 | foreach(array_keys($event['participants']) as $uid) |
||
| 426 | { |
||
| 427 | if (is_numeric($uid) && $GLOBALS['egw']->accounts->get_type($uid) == 'g' && |
||
| 428 | ($members = $GLOBALS['egw']->accounts->members($uid, true))) |
||
| 429 | { |
||
| 430 | foreach($members as $member) |
||
| 431 | { |
||
| 432 | if (!isset($event['participants'][$member])) |
||
| 433 | { |
||
| 434 | $event['participants'][$member] = 'G'; |
||
| 435 | ++$added; |
||
| 436 | } |
||
| 437 | } |
||
| 438 | } |
||
| 439 | } |
||
| 440 | return $added; |
||
| 441 | } |
||
| 442 | |||
| 443 | /** |
||
| 444 | * Resolve users to add memberships for users and members for groups |
||
| 445 | * |
||
| 446 | * @param int|array $_users |
||
| 447 | * @param boolean $no_enum_groups =true |
||
| 448 | * @param boolean $ignore_acl =false |
||
| 449 | * @param boolean $use_freebusy =true should freebusy rights are taken into account, default true, can be set to false eg. for a search |
||
| 450 | * @return array of user-ids |
||
| 451 | */ |
||
| 452 | private function resolve_users($_users, $no_enum_groups=true, $ignore_acl=false, $use_freebusy=true) |
||
| 453 | { |
||
| 454 | if (!is_array($_users)) |
||
| 455 | { |
||
| 456 | $_users = $_users ? array($_users) : array(); |
||
| 457 | } |
||
| 458 | // only query calendars of users, we have READ-grants from |
||
| 459 | $users = array(); |
||
| 460 | foreach($_users as $user) |
||
| 461 | { |
||
| 462 | $user = trim($user); |
||
| 463 | |||
| 464 | // Handle email lists |
||
| 465 | if(!is_numeric($user) && $user[0] == 'l') |
||
| 466 | { |
||
| 467 | foreach($this->enum_mailing_list($user, $ignore_acl, $use_freebusy) as $contact) |
||
| 468 | { |
||
| 469 | if ($contact && !in_array($contact,$users)) // already added? |
||
| 470 | { |
||
| 471 | $users[] = $contact; |
||
| 472 | } |
||
| 473 | } |
||
| 474 | continue; |
||
| 475 | } |
||
| 476 | if ($ignore_acl || $this->check_perms(ACL::READ|self::ACL_READ_FOR_PARTICIPANTS|($use_freebusy?self::ACL_FREEBUSY:0),0,$user)) |
||
| 477 | { |
||
| 478 | if ($user && !in_array($user,$users)) // already added? |
||
| 479 | { |
||
| 480 | // General expansion check |
||
| 481 | if (!is_numeric($user) && $this->resources[$user[0]]['info']) |
||
| 482 | { |
||
| 483 | $info = $this->resource_info($user); |
||
| 484 | if($info && $info['resources']) |
||
| 485 | { |
||
| 486 | foreach($info['resources'] as $_user) |
||
| 487 | { |
||
| 488 | if($_user && !in_array($_user, $users)) |
||
| 489 | { |
||
| 490 | $users[] = $_user; |
||
| 491 | } |
||
| 492 | } |
||
| 493 | continue; |
||
| 494 | } |
||
| 495 | } |
||
| 496 | $users[] = $user; |
||
| 497 | } |
||
| 498 | } |
||
| 499 | elseif ($GLOBALS['egw']->accounts->get_type($user) != 'g') |
||
| 500 | { |
||
| 501 | continue; // for non-groups (eg. users), we stop here if we have no read-rights |
||
| 502 | } |
||
| 503 | // the further code is only for real users |
||
| 504 | if (!is_numeric($user)) continue; |
||
| 505 | |||
| 506 | // for groups we have to include the members |
||
| 507 | if ($GLOBALS['egw']->accounts->get_type($user) == 'g') |
||
| 508 | { |
||
| 509 | if ($no_enum_groups) continue; |
||
| 510 | |||
| 511 | $members = $GLOBALS['egw']->accounts->members($user, true); |
||
| 512 | View Code Duplication | if (is_array($members)) |
|
| 513 | { |
||
| 514 | foreach($members as $member) |
||
| 515 | { |
||
| 516 | // use only members which gave the user a read-grant |
||
| 517 | if (!in_array($member, $users) && |
||
| 518 | ($ignore_acl || $this->check_perms(Acl::READ|($use_freebusy?self::ACL_FREEBUSY:0),0,$member))) |
||
| 519 | { |
||
| 520 | $users[] = $member; |
||
| 521 | } |
||
| 522 | } |
||
| 523 | } |
||
| 524 | } |
||
| 525 | View Code Duplication | else // for users we have to include all the memberships, to get the group-events |
|
| 526 | { |
||
| 527 | $memberships = $GLOBALS['egw']->accounts->memberships($user, true); |
||
| 528 | if (is_array($memberships)) |
||
| 529 | { |
||
| 530 | foreach($memberships as $group) |
||
| 531 | { |
||
| 532 | if (!in_array($group,$users)) |
||
| 533 | { |
||
| 534 | $users[] = $group; |
||
| 535 | } |
||
| 536 | } |
||
| 537 | } |
||
| 538 | } |
||
| 539 | } |
||
| 540 | return $users; |
||
| 541 | } |
||
| 542 | |||
| 543 | /** |
||
| 544 | * Searches / lists calendar entries, including repeating ones |
||
| 545 | * |
||
| 546 | * @param array $params array with the following keys |
||
| 547 | * start date startdate of the search/list, defaults to today |
||
| 548 | * end date enddate of the search/list, defaults to start + one day |
||
| 549 | * users int|array integer user-id or array of user-id's to use, defaults to the current user |
||
| 550 | * cat_id int|array category-id or array of cat-id's (incl. all sub-categories), default 0 = all |
||
| 551 | * filter string all (not rejected), accepted, unknown, tentative, rejected, hideprivate or everything (incl. rejected, deleted) |
||
| 552 | * query string pattern so search for, if unset or empty all matching entries are returned (no search) |
||
| 553 | * Please Note: a search never returns repeating events more then once AND does not honor start+end date !!! |
||
| 554 | * daywise boolean on True it returns an array with YYYYMMDD strings as keys and an array with events |
||
| 555 | * (events spanning multiple days are returned each day again (!)) otherwise it returns one array with |
||
| 556 | * the events (default), not honored in a search ==> always returns an array of events! |
||
| 557 | * date_format string date-formats: 'ts'=timestamp (default), 'array'=array, or string with format for date |
||
| 558 | * offset boolean|int false (default) to return all entries or integer offset to return only a limited result |
||
| 559 | * enum_recuring boolean if true or not set (default) or daywise is set, each recurence of a recuring events is returned, |
||
| 560 | * otherwise the original recuring event (with the first start- + enddate) is returned |
||
| 561 | * num_rows int number of entries to return, default or if 0, max_entries from the prefs |
||
| 562 | * order column-names plus optional DESC|ASC separted by comma |
||
| 563 | * ignore_acl if set and true no check_perms for a general Acl::READ grants is performed |
||
| 564 | * enum_groups boolean if set and true, group-members will be added as participants with status 'G' |
||
| 565 | * cols string|array columns to select, if set an iterator will be returned |
||
| 566 | * append string to append to the query, eg. GROUP BY |
||
| 567 | * cfs array if set, query given custom fields or all for empty array, none are returned, if not set (default) |
||
| 568 | * master_only boolean default false, true only take into account participants/status from master (for AS) |
||
| 569 | * @param string $sql_filter =null sql to be and'ed into query (fully quoted), default none |
||
| 570 | * @return iterator|array|boolean array of events or array with YYYYMMDD strings / array of events pairs (depending on $daywise param) |
||
| 571 | * or false if there are no read-grants from _any_ of the requested users or iterator/recordset if cols are given |
||
| 572 | */ |
||
| 573 | function &search($params,$sql_filter=null) |
||
| 574 | { |
||
| 575 | $params_in = $params; |
||
| 576 | |||
| 577 | $params['sql_filter'] = $sql_filter; // dont allow to set it via UI or xmlrpc |
||
| 578 | |||
| 579 | // check if any resource wants to hook into |
||
| 580 | foreach($this->resources as $data) |
||
| 581 | { |
||
| 582 | if (isset($data['search_filter'])) |
||
| 583 | { |
||
| 584 | $params = ExecMethod($data['search_filter'],$params); |
||
| 585 | } |
||
| 586 | } |
||
| 587 | |||
| 588 | if (!isset($params['users']) || !$params['users'] || |
||
| 589 | count($params['users']) == 1 && isset($params['users'][0]) && !$params['users'][0]) // null or '' casted to an array |
||
| 590 | { |
||
| 591 | // for a search use all account you have read grants from |
||
| 592 | $params['users'] = $params['query'] ? array_keys($this->grants) : $this->user; |
||
| 593 | } |
||
| 594 | // resolve users to add memberships for users and members for groups |
||
| 595 | // for search, do NOT use freebusy rights, as it would allow to probe the content of event entries |
||
| 596 | $users = $this->resolve_users($params['users'], $params['filter'] == 'no-enum-groups', $params['ignore_acl'], empty($params['query'])); |
||
| 597 | |||
| 598 | // supply so with private_grants, to not query them again from the database |
||
| 599 | if (!empty($params['query'])) |
||
| 600 | { |
||
| 601 | $params['private_grants'] = array(); |
||
| 602 | foreach($this->grants as $user => $rights) |
||
| 603 | { |
||
| 604 | if ($rights & Acl::PRIVAT) $params['private_grants'][] = $user; |
||
| 605 | } |
||
| 606 | } |
||
| 607 | |||
| 608 | // replace (by so not understood filter 'no-enum-groups' with 'default' filter |
||
| 609 | if ($params['filter'] == 'no-enum-groups') |
||
| 610 | { |
||
| 611 | $params['filter'] = 'default'; |
||
| 612 | } |
||
| 613 | // if we have no grants from the given user(s), we directly return no events / an empty array, |
||
| 614 | // as calling the so-layer without users would give the events of all users (!) |
||
| 615 | if (!count($users) && !$params['ignore_acl']) |
||
| 616 | { |
||
| 617 | return false; |
||
| 618 | } |
||
| 619 | if (isset($params['start'])) $start = $this->date2ts($params['start']); |
||
| 620 | |||
| 621 | if (isset($params['end'])) |
||
| 622 | { |
||
| 623 | $end = $this->date2ts($params['end']); |
||
| 624 | $this->check_move_horizont($end); |
||
| 625 | } |
||
| 626 | $daywise = !isset($params['daywise']) ? False : !!$params['daywise']; |
||
| 627 | $params['enum_recuring'] = $enum_recuring = $daywise || !isset($params['enum_recuring']) || !!$params['enum_recuring']; |
||
| 628 | $cat_id = isset($params['cat_id']) ? $params['cat_id'] : 0; |
||
| 629 | $filter = isset($params['filter']) ? $params['filter'] : 'all'; |
||
| 630 | $offset = isset($params['offset']) && $params['offset'] !== false ? (int) $params['offset'] : false; |
||
| 631 | // socal::search() returns rejected group-invitations, as only the user not also the group is rejected |
||
| 632 | // as we cant remove them efficiantly in SQL, we kick them out here, but only if just one user is displayed |
||
| 633 | $users_in = (array)$params_in['users']; |
||
| 634 | $remove_rejected_by_user = !in_array($filter,array('all','rejected','everything')) && |
||
| 635 | count($users_in) == 1 && $users_in[0] > 0 ? $users_in[0] : null; |
||
| 636 | //error_log(__METHOD__.'('.array2string($params_in).", $sql_filter) params[users]=".array2string($params['users']).' --> remove_rejected_by_user='.array2string($remove_rejected_by_user)); |
||
| 637 | |||
| 638 | if ($this->debug && ($this->debug > 1 || $this->debug == 'search')) |
||
| 639 | { |
||
| 640 | $this->debug_message('calendar_bo::search(%1) start=%2, end=%3, daywise=%4, cat_id=%5, filter=%6, query=%7, offset=%8, num_rows=%9, order=%10, sql_filter=%11)', |
||
| 641 | True,$params,$start,$end,$daywise,$cat_id,$filter,$params['query'],$offset,(int)$params['num_rows'],$params['order'],$params['sql_filter']); |
||
| 642 | } |
||
| 643 | // date2ts(,true) converts to server time, db2data converts again to user-time |
||
| 644 | $events =& $this->so->search(isset($start) ? $this->date2ts($start,true) : null,isset($end) ? $this->date2ts($end,true) : null, |
||
| 645 | $users,$cat_id,$filter,$offset,(int)$params['num_rows'],$params,$remove_rejected_by_user); |
||
| 646 | |||
| 647 | if (isset($params['cols'])) |
||
| 648 | { |
||
| 649 | return $events; |
||
| 650 | } |
||
| 651 | $this->total = $this->so->total; |
||
| 652 | $this->db2data($events,isset($params['date_format']) ? $params['date_format'] : 'ts'); |
||
| 653 | |||
| 654 | //echo "<p align=right>remove_rejected_by_user=$remove_rejected_by_user, filter=$filter, params[users]=".print_r($param['users'])."</p>\n"; |
||
| 655 | foreach($events as $id => $event) |
||
| 656 | { |
||
| 657 | if ($params['enum_groups'] && $this->enum_groups($event)) |
||
| 658 | { |
||
| 659 | $events[$id] = $event; |
||
| 660 | } |
||
| 661 | $matches = null; |
||
| 662 | if (!(int)$event['id'] && preg_match('/^([a-z_]+)([0-9]+)$/',$event['id'],$matches)) |
||
| 663 | { |
||
| 664 | $is_private = self::integration_get_private($matches[1],$matches[2],$event); |
||
| 665 | } |
||
| 666 | else |
||
| 667 | { |
||
| 668 | $is_private = !$this->check_perms(Acl::READ,$event); |
||
| 669 | } |
||
| 670 | if (!$params['ignore_acl'] && ($is_private || (!$event['public'] && $filter == 'hideprivate'))) |
||
| 671 | { |
||
| 672 | $this->clear_private_infos($events[$id],$users); |
||
| 673 | } |
||
| 674 | } |
||
| 675 | |||
| 676 | if ($daywise) |
||
| 677 | { |
||
| 678 | if ($this->debug && ($this->debug > 2 || $this->debug == 'search')) |
||
| 679 | { |
||
| 680 | $this->debug_message('socalendar::search daywise sorting from %1 to %2 of %3',False,$start,$end,$events); |
||
| 681 | } |
||
| 682 | // create empty entries for each day in the reported time |
||
| 683 | for($ts = $start; $ts <= $end; $ts += DAY_s) // good enough for array creation, but see while loop below. |
||
| 684 | { |
||
| 685 | $daysEvents[$this->date2string($ts)] = array(); |
||
| 686 | } |
||
| 687 | foreach($events as $k => $event) |
||
| 688 | { |
||
| 689 | $e_start = max($this->date2ts($event['start']),$start); |
||
| 690 | // $event['end']['raw']-1 to allow events to end on a full hour/day without the need to enter it as minute=59 |
||
| 691 | $e_end = min($this->date2ts($event['end'])-1,$end); |
||
| 692 | |||
| 693 | // add event to each day in the reported time |
||
| 694 | $ts = $e_start; |
||
| 695 | // $ts += DAY_s in a 'for' loop does not work for daylight savings in week view |
||
| 696 | // because the day is longer than DAY_s: Fullday events will be added twice. |
||
| 697 | $ymd = null; |
||
| 698 | while ($ts <= $e_end) |
||
| 699 | { |
||
| 700 | $daysEvents[$ymd = $this->date2string($ts)][] =& $events[$k]; |
||
| 701 | $ts = strtotime("+1 day",$ts); |
||
| 702 | } |
||
| 703 | if ($ymd != ($last = $this->date2string($e_end))) |
||
| 704 | { |
||
| 705 | $daysEvents[$last][] =& $events[$k]; |
||
| 706 | } |
||
| 707 | } |
||
| 708 | $events =& $daysEvents; |
||
| 709 | if ($this->debug && ($this->debug > 2 || $this->debug == 'search')) |
||
| 710 | { |
||
| 711 | $this->debug_message('socalendar::search daywise events=%1',False,$events); |
||
| 712 | } |
||
| 713 | } |
||
| 714 | if ($this->debug && ($this->debug > 0 || $this->debug == 'search')) |
||
| 715 | { |
||
| 716 | $this->debug_message('calendar_bo::search(%1)=%2',True,$params,$events); |
||
| 717 | } |
||
| 718 | //error_log(__METHOD__."() returning ".count($events)." entries, total=$this->total ".function_backtrace()); |
||
| 719 | return $events; |
||
| 720 | } |
||
| 721 | |||
| 722 | /** |
||
| 723 | * Get integration data for a given app of a part (value for a certain key) of it |
||
| 724 | * |
||
| 725 | * @param string $app |
||
| 726 | * @param string $part |
||
| 727 | * @return array |
||
| 728 | */ |
||
| 729 | static function integration_get_data($app,$part=null) |
||
| 730 | { |
||
| 731 | static $integration_data=null; |
||
| 732 | |||
| 733 | if (!isset($integration_data)) |
||
| 734 | { |
||
| 735 | $integration_data = calendar_so::get_integration_data(); |
||
| 736 | } |
||
| 737 | |||
| 738 | if (!isset($integration_data[$app])) return null; |
||
| 739 | |||
| 740 | return $part ? $integration_data[$app][$part] : $integration_data[$app]; |
||
| 741 | } |
||
| 742 | |||
| 743 | /** |
||
| 744 | * Get private attribute for an integration event |
||
| 745 | * |
||
| 746 | * Attribute 'is_private' is either a boolean value, eg. false to make all events of $app public |
||
| 747 | * or an ExecMethod callback with parameters $id,$event |
||
| 748 | * |
||
| 749 | * @param string $app |
||
| 750 | * @param int|string $id |
||
| 751 | * @return string |
||
| 752 | */ |
||
| 753 | static function integration_get_private($app,$id,$event) |
||
| 754 | { |
||
| 755 | $app_data = self::integration_get_data($app,'is_private'); |
||
| 756 | |||
| 757 | // no method, fall back to link title |
||
| 758 | if (is_null($app_data)) |
||
| 759 | { |
||
| 760 | $is_private = !Link::title($app,$id); |
||
| 761 | } |
||
| 762 | // boolean value to make all events of $app public (false) or private (true) |
||
| 763 | elseif (is_bool($app_data)) |
||
| 764 | { |
||
| 765 | $is_private = $app_data; |
||
| 766 | } |
||
| 767 | else |
||
| 768 | { |
||
| 769 | $is_private = (bool)ExecMethod2($app_data,$id,$event); |
||
| 770 | } |
||
| 771 | //echo '<p>'.__METHOD__."($app,$id,) app_data=".array2string($app_data).' returning '.array2string($is_private)."</p>\n"; |
||
| 772 | return $is_private; |
||
| 773 | } |
||
| 774 | |||
| 775 | /** |
||
| 776 | * Clears all non-private info from a privat event |
||
| 777 | * |
||
| 778 | * That function only returns the infos allowed to be viewed by people without Acl::PRIVAT grants |
||
| 779 | * |
||
| 780 | * @param array &$event |
||
| 781 | * @param array $allowed_participants ids of the allowed participants, eg. the ones the search is over or eg. the owner of the calendar |
||
| 782 | */ |
||
| 783 | function clear_private_infos(&$event,$allowed_participants = array()) |
||
| 784 | { |
||
| 785 | if ($event == false) return; |
||
| 786 | if (!is_array($event['participants'])) error_log(__METHOD__.'('.array2string($event).', '.array2string($allowed_participants).') NO PARTICIPANTS '.function_backtrace()); |
||
| 787 | |||
| 788 | $event = array( |
||
| 789 | 'id' => $event['id'], |
||
| 790 | 'start' => $event['start'], |
||
| 791 | 'end' => $event['end'], |
||
| 792 | 'whole_day' => $event['whole_day'], |
||
| 793 | 'tzid' => $event['tzid'], |
||
| 794 | 'title' => lang('private'), |
||
| 795 | 'modified' => $event['modified'], |
||
| 796 | 'owner' => $event['owner'], |
||
| 797 | 'uid' => $event['uid'], |
||
| 798 | 'etag' => $event['etag'], |
||
| 799 | 'participants' => array_intersect_key($event['participants'],array_flip($allowed_participants)), |
||
| 800 | 'public'=> 0, |
||
| 801 | 'category' => $event['category'], // category is visible anyway, eg. by using planner by cat |
||
| 802 | 'non_blocking' => $event['non_blocking'], |
||
| 803 | 'caldav_name' => $event['caldav_name'], |
||
| 804 | // we need full recurrence information, as they are relevant free/busy information |
||
| 805 | )+($event['recur_type'] ? array( |
||
| 806 | 'recur_type' => $event['recur_type'], |
||
| 807 | 'recur_interval' => $event['recur_interval'], |
||
| 808 | 'recur_data' => $event['recur_data'], |
||
| 809 | 'recur_enddate' => $event['recur_enddate'], |
||
| 810 | 'recur_exception'=> $event['recur_exception'], |
||
| 811 | ):array( |
||
| 812 | 'reference' => $event['reference'], |
||
| 813 | 'recurrence' => $event['recurrence'], |
||
| 814 | )); |
||
| 815 | } |
||
| 816 | |||
| 817 | /** |
||
| 818 | * check and evtl. move the horizont (maximum date for unlimited recuring events) to a new date |
||
| 819 | * |
||
| 820 | * @internal automaticaly called by search |
||
| 821 | * @param mixed $_new_horizont time to set the horizont to (user-time) |
||
| 822 | */ |
||
| 823 | function check_move_horizont($_new_horizont) |
||
| 824 | { |
||
| 825 | if ((int) $this->debug >= 2 || $this->debug == 'check_move_horizont') |
||
| 826 | { |
||
| 827 | $this->debug_message('calendar_bo::check_move_horizont(%1) horizont=%2',true,$_new_horizont,(int)$this->config['horizont']); |
||
| 828 | } |
||
| 829 | $new_horizont = $this->date2ts($_new_horizont,true); // now we are in server-time, where this function operates |
||
| 830 | |||
| 831 | if ($new_horizont <= $this->config['horizont']) // no move necessary |
||
| 832 | { |
||
| 833 | if ($this->debug == 'check_move_horizont') $this->debug_message('calendar_bo::check_move_horizont(%1) horizont=%2 is bigger ==> nothing to do',true,$new_horizont,(int)$this->config['horizont']); |
||
| 834 | return; |
||
| 835 | } |
||
| 836 | if (!empty($GLOBALS['egw_info']['server']['calendar_horizont'])) |
||
| 837 | { |
||
| 838 | $maxdays = abs($GLOBALS['egw_info']['server']['calendar_horizont']); |
||
| 839 | } |
||
| 840 | if (empty($maxdays)) $maxdays = 1000; // old default |
||
| 841 | if ($new_horizont > time()+$maxdays*DAY_s) // some user tries to "look" more then the maximum number of days in the future |
||
| 842 | { |
||
| 843 | if ($this->debug == 'check_move_horizont') $this->debug_message('calendar_bo::check_move_horizont(%1) horizont=%2 new horizont more then %3 days from now --> ignoring it',true,$new_horizont,(int)$this->config['horizont'],$maxdays); |
||
| 844 | $this->warnings['horizont'] = lang('Requested date %1 outside allowed range of %2 days: recurring events obmitted!', Api\DateTime::to($new_horizont,true), $maxdays); |
||
| 845 | return; |
||
| 846 | } |
||
| 847 | if ($new_horizont < time()+31*DAY_s) |
||
| 848 | { |
||
| 849 | $new_horizont = time()+31*DAY_s; |
||
| 850 | } |
||
| 851 | $old_horizont = $this->config['horizont']; |
||
| 852 | $this->config['horizont'] = $new_horizont; |
||
| 853 | |||
| 854 | // create further recurrences for all recurring and not yet (at the old horizont) ended events |
||
| 855 | if (($recuring = $this->so->unfinished_recuring($old_horizont))) |
||
| 856 | { |
||
| 857 | @set_time_limit(0); // disable time-limit, in case it takes longer to calculate the recurrences |
||
| 858 | foreach($this->read(array_keys($recuring)) as $cal_id => $event) |
||
| 859 | { |
||
| 860 | if ($this->debug == 'check_move_horizont') |
||
| 861 | { |
||
| 862 | $this->debug_message('calendar_bo::check_move_horizont(%1): calling set_recurrences(%2,%3)',true,$new_horizont,$event,$old_horizont); |
||
| 863 | } |
||
| 864 | // insert everything behind max(cal_start), which can be less then $old_horizont because of bugs in the past |
||
| 865 | $this->set_recurrences($event,Api\DateTime::server2user($recuring[$cal_id]+1)); // set_recurences operates in user-time! |
||
| 866 | } |
||
| 867 | } |
||
| 868 | // update the horizont |
||
| 869 | Api\Config::save_value('horizont',$this->config['horizont'],'calendar'); |
||
| 870 | |||
| 871 | if ($this->debug == 'check_move_horizont') $this->debug_message('calendar_bo::check_move_horizont(%1) new horizont=%2, exiting',true,$new_horizont,(int)$this->config['horizont']); |
||
| 872 | } |
||
| 873 | |||
| 874 | /** |
||
| 875 | * set all recurrences for an event until the defined horizont $this->config['horizont'] |
||
| 876 | * |
||
| 877 | * This methods operates in usertime, while $this->config['horizont'] is in servertime! |
||
| 878 | * |
||
| 879 | * @param array $event |
||
| 880 | * @param mixed $start =0 minimum start-time for new recurrences or !$start = since the start of the event |
||
| 881 | */ |
||
| 882 | function set_recurrences($event,$start=0) |
||
| 883 | { |
||
| 884 | View Code Duplication | if ($this->debug && ((int) $this->debug >= 2 || $this->debug == 'set_recurrences' || $this->debug == 'check_move_horizont')) |
|
| 885 | { |
||
| 886 | $this->debug_message('calendar_bo::set_recurrences(%1,%2)',true,$event,$start); |
||
| 887 | } |
||
| 888 | // check if the caller gave us enough information and if not read it from the DB |
||
| 889 | if (!isset($event['participants']) || !isset($event['start']) || !isset($event['end'])) |
||
| 890 | { |
||
| 891 | list(,$event_read) = each($this->so->read($event['id'])); |
||
| 892 | if (!isset($event['participants'])) |
||
| 893 | { |
||
| 894 | $event['participants'] = $event_read['participants']; |
||
| 895 | } |
||
| 896 | if (!isset($event['start']) || !isset($event['end'])) |
||
| 897 | { |
||
| 898 | $event['start'] = $event_read['start']; |
||
| 899 | $event['end'] = $event_read['end']; |
||
| 900 | } |
||
| 901 | } |
||
| 902 | if (!$start) $start = $event['start']; |
||
| 903 | |||
| 904 | $events = array(); |
||
| 905 | $this->insert_all_recurrences($event,$start,$this->date2usertime($this->config['horizont']),$events); |
||
| 906 | |||
| 907 | $exceptions = array(); |
||
| 908 | foreach((array)$event['recur_exception'] as $exception) |
||
| 909 | { |
||
| 910 | $exceptions[] = Api\DateTime::to($exception, true); // true = date |
||
| 911 | } |
||
| 912 | foreach($events as $event) |
||
| 913 | { |
||
| 914 | $is_exception = in_array(Api\DateTime::to($event['start'], true), $exceptions); |
||
| 915 | $start = $this->date2ts($event['start'],true); |
||
| 916 | if ($event['whole_day']) |
||
| 917 | { |
||
| 918 | $start = new Api\DateTime($event['start'], Api\DateTime::$server_timezone); |
||
| 919 | $start->setTime(0,0,0); |
||
| 920 | $start = $start->format('ts'); |
||
| 921 | $time = $this->so->startOfDay(new Api\DateTime($event['end'], Api\DateTime::$user_timezone)); |
||
| 922 | $time->setTime(23, 59, 59); |
||
| 923 | $end = $this->date2ts($time,true); |
||
| 924 | } |
||
| 925 | else |
||
| 926 | { |
||
| 927 | $end = $this->date2ts($event['end'],true); |
||
| 928 | } |
||
| 929 | //error_log(__METHOD__."() start=".Api\DateTime::to($start).", is_exception=".array2string($is_exception)); |
||
| 930 | $this->so->recurrence($event['id'], $start, $end, $event['participants'], $is_exception); |
||
| 931 | } |
||
| 932 | } |
||
| 933 | |||
| 934 | /** |
||
| 935 | * Convert data read from the db, eg. convert server to user-time |
||
| 936 | * |
||
| 937 | * Also make sure all timestamps comming from DB as string are converted to integer, |
||
| 938 | * to avoid misinterpretation by Api\DateTime as Ymd string. |
||
| 939 | * |
||
| 940 | * @param array &$events array of event-arrays (reference) |
||
| 941 | * @param $date_format ='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, 'array'=array or string with date-format |
||
| 942 | */ |
||
| 943 | function db2data(&$events,$date_format='ts') |
||
| 1019 | |||
| 1020 | /** |
||
| 1021 | * convert a date from server to user-time |
||
| 1022 | * |
||
| 1023 | * @param int $ts timestamp in server-time |
||
| 1024 | * @param string $date_format ='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, 'array'=array or string with date-format |
||
| 1025 | * @return mixed depending of $date_format |
||
| 1026 | */ |
||
| 1027 | function date2usertime($ts,$date_format='ts') |
||
| 1033 | |||
| 1034 | /** |
||
| 1035 | * Reads a calendar-entry |
||
| 1036 | * |
||
| 1037 | * @param int|array|string $ids id or array of id's of the entries to read, or string with a single uid |
||
| 1038 | * @param mixed $date =null date to specify a single event of a series |
||
| 1039 | * @param boolean $ignore_acl should we ignore the acl, default False for a single id, true for multiple id's |
||
| 1040 | * @param string $date_format ='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in servertime, 'array'=array, or string with date-format |
||
| 1041 | * @param array|int $clear_private_infos_users =null if not null, return events with self::ACL_FREEBUSY too, |
||
| 1042 | * but call clear_private_infos() with the given users |
||
| 1043 | * @return boolean|array event or array of id => event pairs, false if the acl-check went wrong, null if $ids not found |
||
| 1044 | */ |
||
| 1045 | function read($ids,$date=null,$ignore_acl=False,$date_format='ts',$clear_private_infos_users=null) |
||
| 1094 | |||
| 1095 | /** |
||
| 1096 | * Inserts all repetions of $event in the timespan between $start and $end into $events |
||
| 1097 | * |
||
| 1098 | * The new entries are just appended to $events, so $events is no longer sorted by startdate !!! |
||
| 1099 | * |
||
| 1100 | * Recurrences get calculated by rrule iterator implemented in calendar_rrule class. |
||
| 1101 | * |
||
| 1102 | * @param array $event repeating event whos repetions should be inserted |
||
| 1103 | * @param mixed $start start-date |
||
| 1104 | * @param mixed $end end-date |
||
| 1105 | * @param array $events where the repetions get inserted |
||
| 1106 | * @param array $recur_exceptions with date (in Ymd) as key (and True as values), seems not to be used anymore |
||
| 1107 | */ |
||
| 1108 | function insert_all_recurrences($event,$_start,$end,&$events) |
||
| 1180 | |||
| 1181 | /** |
||
| 1182 | * Adds one repetion of $event for $date_ymd to the $events array, after adjusting its start- and end-time |
||
| 1183 | * |
||
| 1184 | * @param array $events array in which the event gets inserted |
||
| 1185 | * @param array $event event to insert, it has start- and end-date of the first recurrence, not of $date_ymd |
||
| 1186 | * @param int|string $date_ymd of the date of the event |
||
| 1187 | */ |
||
| 1188 | function add_adjusted_event(&$events,$event,$date_ymd) |
||
| 1210 | |||
| 1211 | /** |
||
| 1212 | * Fetch information about a resource |
||
| 1213 | * |
||
| 1214 | * We do some caching here, as the resource itself might not do it. |
||
| 1215 | * |
||
| 1216 | * @param string $uid string with one-letter resource-type and numerical resource-id, eg. "r19" |
||
| 1217 | * @return array|boolean array with keys res_id,cat_id,name,useable (name definied by max_quantity in $this->resources),rights,responsible or false if $uid is not found |
||
| 1218 | */ |
||
| 1219 | function resource_info($uid) |
||
| 1259 | |||
| 1260 | /** |
||
| 1261 | * Checks if the current user has the necessary ACL rights |
||
| 1262 | * |
||
| 1263 | * The check is performed on an event or generally on the cal of an other user |
||
| 1264 | * |
||
| 1265 | * Note: Participating in an event is considered as haveing read-access on that event, |
||
| 1266 | * even if you have no general read-grant from that user. |
||
| 1267 | * |
||
| 1268 | * @param int $needed necessary ACL right: Acl::{READ|EDIT|DELETE} |
||
| 1269 | * @param mixed $event event as array or the event-id or 0 for a general check |
||
| 1270 | * @param int $other uid to check (if event==0) or 0 to check against $this->user |
||
| 1271 | * @param string $date_format ='ts' date-format used for reading: 'ts'=timestamp, 'array'=array, 'string'=iso8601 string for xmlrpc |
||
| 1272 | * @param mixed $date_to_read =null date used for reading, internal param for the caching |
||
| 1273 | * @param int $user =null for which user to check, default current user |
||
| 1274 | * @return boolean true permission granted, false for permission denied or null if event not found |
||
| 1275 | */ |
||
| 1276 | function check_perms($needed,$event=0,$other=0,$date_format='ts',$date_to_read=null,$user=null) |
||
| 1374 | |||
| 1375 | /** |
||
| 1376 | * Converts several date-types to a timestamp and optionally converts user- to server-time |
||
| 1377 | * |
||
| 1378 | * @param mixed $date date to convert, should be one of the following types |
||
| 1379 | * string (!) in form YYYYMMDD or iso8601 YYYY-MM-DDThh:mm:ss or YYYYMMDDThhmmss |
||
| 1380 | * int already a timestamp |
||
| 1381 | * array with keys 'second', 'minute', 'hour', 'day' or 'mday' (depricated !), 'month' and 'year' |
||
| 1382 | * @param boolean $user2server =False conversion between user- and server-time; default False == Off |
||
| 1383 | */ |
||
| 1384 | static function date2ts($date,$user2server=False) |
||
| 1388 | |||
| 1389 | /** |
||
| 1390 | * Converts a date to an array and optionally converts server- to user-time |
||
| 1391 | * |
||
| 1392 | * @param mixed $date date to convert |
||
| 1393 | * @param boolean $server2user conversation between user- and server-time default False == Off |
||
| 1394 | * @return array with keys 'second', 'minute', 'hour', 'day', 'month', 'year', 'raw' (timestamp) and 'full' (Ymd-string) |
||
| 1395 | */ |
||
| 1396 | static function date2array($date,$server2user=False) |
||
| 1400 | |||
| 1401 | /** |
||
| 1402 | * Converts a date as timestamp or array to a date-string and optionaly converts server- to user-time |
||
| 1403 | * |
||
| 1404 | * @param mixed $date integer timestamp or array with ('year','month',..,'second') to convert |
||
| 1405 | * @param boolean $server2user conversation between user- and server-time default False == Off, not used if $format ends with \Z |
||
| 1406 | * @param string $format ='Ymd' format of the date to return, eg. 'Y-m-d\TH:i:sO' (2005-11-01T15:30:00+0100) |
||
| 1407 | * @return string date formatted according to $format |
||
| 1408 | */ |
||
| 1409 | static function date2string($date,$server2user=False,$format='Ymd') |
||
| 1413 | |||
| 1414 | /** |
||
| 1415 | * Formats a date given as timestamp or array |
||
| 1416 | * |
||
| 1417 | * @param mixed $date integer timestamp or array with ('year','month',..,'second') to convert |
||
| 1418 | * @param string|boolean $format ='' default common_prefs[dateformat], common_prefs[timeformat], false=time only, true=date only |
||
| 1419 | * @return string the formated date (incl. time) |
||
| 1420 | */ |
||
| 1421 | static function format_date($date,$format='') |
||
| 1425 | |||
| 1426 | /** |
||
| 1427 | * Gives out a debug-message with certain parameters |
||
| 1428 | * |
||
| 1429 | * All permanent debug-messages in the calendar should be done by this function !!! |
||
| 1430 | * (In future they may be logged or sent as xmlrpc-faults back.) |
||
| 1431 | * |
||
| 1432 | * Permanent debug-message need to make sure NOT to give secret information like passwords !!! |
||
| 1433 | * |
||
| 1434 | * This function do NOT honor the setting of the debug variable, you may use it like |
||
| 1435 | * if ($this->debug > N) $this->debug_message('Error ;-)'); |
||
| 1436 | * |
||
| 1437 | * The parameters get formated depending on their type. ACL-values need a ACL_TYPE_IDENTIFER prefix. |
||
| 1438 | * |
||
| 1439 | * @param string $msg message with parameters/variables like lang(), eg. '%1' |
||
| 1440 | * @param boolean $backtrace =True include a function-backtrace, default True=On |
||
| 1441 | * should only be set to False=Off, if your code ensures a call with backtrace=On was made before !!! |
||
| 1442 | * @param mixed $param a variable number of parameters, to be inserted in $msg |
||
| 1443 | * arrays get serialized with print_r() ! |
||
| 1444 | */ |
||
| 1445 | static function debug_message($msg,$backtrace=True) |
||
| 1503 | |||
| 1504 | /** |
||
| 1505 | * Formats one or two dates (range) as long date (full monthname), optionaly with a time |
||
| 1506 | * |
||
| 1507 | * @param mixed $_first first date |
||
| 1508 | * @param mixed $last =0 last date if != 0 (default) |
||
| 1509 | * @param boolean $display_time =false should a time be displayed too |
||
| 1510 | * @param boolean $display_day =false should a day-name prefix the date, eg. monday June 20, 2006 |
||
| 1511 | * @return string with formated date |
||
| 1512 | */ |
||
| 1513 | function long_date($_first,$last=0,$display_time=false,$display_day=false) |
||
| 1598 | |||
| 1599 | /** |
||
| 1600 | * Displays a timespan, eg. $both ? "10:00 - 13:00: 3h" (10:00 am - 1 pm: 3h) : "10:00 3h" (10:00 am 3h) |
||
| 1601 | * |
||
| 1602 | * @param int $start_m start time in minutes since 0h |
||
| 1603 | * @param int $end_m end time in minutes since 0h |
||
| 1604 | * @param boolean $both =false display the end-time too, duration is always displayed |
||
| 1605 | */ |
||
| 1606 | function timespan($start_m,$end_m,$both=false) |
||
| 1626 | |||
| 1627 | /** |
||
| 1628 | * Converts a participant into a (readable) user- or resource-name |
||
| 1629 | * |
||
| 1630 | * @param string|int $id id of user or resource |
||
| 1631 | * @param string|boolean $use_type =false type-letter or false |
||
| 1632 | * @param boolean $append_email =false append email (Name <email>) |
||
| 1633 | * @return string with name |
||
| 1634 | */ |
||
| 1635 | function participant_name($id,$use_type=false, $append_email=false) |
||
| 1661 | |||
| 1662 | /** |
||
| 1663 | * Converts participants array of an event into array of (readable) participant-names with status |
||
| 1664 | * |
||
| 1665 | * @param array $event event-data |
||
| 1666 | * @param boolean $long_status =false should the long/verbose status or an icon be use |
||
| 1667 | * @param boolean $show_group_invitation =false show group-invitations (status == 'G') or not (default) |
||
| 1668 | * @return array with id / names with status pairs |
||
| 1669 | */ |
||
| 1670 | function participants($event,$long_status=false,$show_group_invitation=false) |
||
| 1737 | |||
| 1738 | /** |
||
| 1739 | * Converts category string of an event into array of (readable) category-names |
||
| 1740 | * |
||
| 1741 | * @param string $category cat-id (multiple id's commaseparated) |
||
| 1742 | * @param int $color color of the category, if multiple cats, the color of the last one with color is returned |
||
| 1743 | * @return array with id / names |
||
| 1744 | */ |
||
| 1745 | function categories($category,&$color) |
||
| 1774 | |||
| 1775 | /** |
||
| 1776 | * This is called only by list_cals(). It was moved here to remove fatal error in php5 beta4 |
||
| 1777 | */ |
||
| 1778 | private static function _list_cals_add($id,&$users,&$groups) |
||
| 1800 | |||
| 1801 | /** |
||
| 1802 | * generate list of user- / group-calendars for the selectbox in the header |
||
| 1803 | * |
||
| 1804 | * @return array alphabeticaly sorted array with users first and then groups: array('grantor'=>$id,'value'=>['g_'.]$id,'name'=>$name) |
||
| 1805 | */ |
||
| 1806 | function list_cals() |
||
| 1810 | |||
| 1811 | /** |
||
| 1812 | * generate list of user- / group-calendars or a given user |
||
| 1813 | * |
||
| 1814 | * @param int $user account_id of user to generate list for |
||
| 1815 | * @param array $grants =null calendar grants from user, or null to query them from acl class |
||
| 1816 | */ |
||
| 1817 | public static function list_calendars($user, array $grants=null) |
||
| 1846 | |||
| 1847 | /** |
||
| 1848 | * Compare function for sort by value of key 'name' |
||
| 1849 | * |
||
| 1850 | * @param array $a |
||
| 1851 | * @param array $b |
||
| 1852 | * @return int |
||
| 1853 | */ |
||
| 1854 | public static function name_cmp(array $a, array $b) |
||
| 1858 | |||
| 1859 | /** |
||
| 1860 | * Convert the recurrence-information of an event, into a human readable string |
||
| 1861 | * |
||
| 1862 | * @param array $event |
||
| 1863 | * @return string |
||
| 1864 | */ |
||
| 1865 | function recure2string($event) |
||
| 1870 | |||
| 1871 | /** |
||
| 1872 | * Read the holidays for a given $year |
||
| 1873 | * |
||
| 1874 | * The holidays get cached in the session (performance), so changes in holidays or birthdays do NOT affect a current session!!! |
||
| 1875 | * |
||
| 1876 | * @param int $year =0 year, defaults to 0 = current year |
||
| 1877 | * @return array indexed with Ymd of array of holidays. A holiday is an array with the following fields: |
||
| 1878 | * name: string |
||
| 1879 | * title: optional string with description |
||
| 1880 | * day: numerical day in month |
||
| 1881 | * month: numerical month |
||
| 1882 | * occurence: numerical year or 0 for every year |
||
| 1883 | */ |
||
| 1884 | function read_holidays($year=0) |
||
| 1909 | |||
| 1910 | /** |
||
| 1911 | * Get translated calendar event fields, presenting as link title options |
||
| 1912 | * |
||
| 1913 | * @param type $event |
||
| 1914 | * @return array array of selected calendar fields |
||
| 1915 | */ |
||
| 1916 | public static function get_link_options ($event = array()) |
||
| 1930 | |||
| 1931 | /** |
||
| 1932 | * get title for an event identified by $event |
||
| 1933 | * |
||
| 1934 | * Is called as hook to participate in the linking |
||
| 1935 | * |
||
| 1936 | * @param int|array $entry int cal_id or array with event |
||
| 1937 | * @param string|boolean string with title, null if not found or false if not read perms |
||
| 1938 | */ |
||
| 1939 | function link_title($event) |
||
| 1985 | |||
| 1986 | /** |
||
| 1987 | * query calendar for events matching $pattern |
||
| 1988 | * |
||
| 1989 | * Is called as hook to participate in the linking |
||
| 1990 | * |
||
| 1991 | * @param string $pattern pattern to search |
||
| 1992 | * @return array with cal_id - title pairs of the matching entries |
||
| 1993 | */ |
||
| 1994 | function link_query($pattern, Array &$options = array()) |
||
| 2012 | |||
| 2013 | /** |
||
| 2014 | * Check access to the file store |
||
| 2015 | * |
||
| 2016 | * @param int $id id of entry |
||
| 2017 | * @param int $check Acl::READ for read and Acl::EDIT for write or delete access |
||
| 2018 | * @param string $rel_path =null currently not used in calendar |
||
| 2019 | * @param int $user =null for which user to check, default current user |
||
| 2020 | * @return boolean true if access is granted or false otherwise |
||
| 2021 | */ |
||
| 2022 | function file_access($id,$check,$rel_path,$user=null) |
||
| 2028 | |||
| 2029 | /** |
||
| 2030 | * sets the default prefs, if they are not already set (on a per pref. basis) |
||
| 2031 | * |
||
| 2032 | * It sets a flag in the app-session-data to be called only once per session |
||
| 2033 | */ |
||
| 2034 | function check_set_default_prefs() |
||
| 2074 | |||
| 2075 | /** |
||
| 2076 | * Get the freebusy URL of a user |
||
| 2077 | * |
||
| 2078 | * @param int|string $user account_id or account_lid |
||
| 2079 | * @param string $pw =null password |
||
| 2080 | */ |
||
| 2081 | static function freebusy_url($user='',$pw=null) |
||
| 2101 | |||
| 2102 | /** |
||
| 2103 | * Check if the event is the whole day |
||
| 2104 | * |
||
| 2105 | * @param array $event event |
||
| 2106 | * @return boolean true if whole day event, false othwerwise |
||
| 2107 | */ |
||
| 2108 | public static function isWholeDay($event) |
||
| 2116 | |||
| 2117 | /** |
||
| 2118 | * Get the etag for an entry |
||
| 2119 | * |
||
| 2120 | * As all update routines (incl. set_status and add/delete alarms) update (series master) modified timestamp, |
||
| 2121 | * we do NOT need any special handling for series master anymore |
||
| 2122 | * |
||
| 2123 | * @param array|int|string $entry array with event or cal_id, or cal_id:recur_date for virtual exceptions |
||
| 2124 | * @param string &$schedule_tag=null on return schedule-tag (egw_cal.cal_id:egw_cal.cal_etag, no participant modifications!) |
||
| 2125 | * @return string|boolean string with etag or false |
||
| 2126 | */ |
||
| 2127 | function get_etag($entry, &$schedule_tag=null) |
||
| 2140 | |||
| 2141 | /** |
||
| 2142 | * Query ctag for calendar |
||
| 2143 | * |
||
| 2144 | * @param int|string|array $user integer user-id or array of user-id's to use, defaults to the current user |
||
| 2145 | * @param string $filter ='owner' all (not rejected), accepted, unknown, tentative, rejected or hideprivate |
||
| 2146 | * @param boolean $master_only =false only check recurance master (egw_cal_user.recur_date=0) |
||
| 2147 | * @return integer |
||
| 2148 | */ |
||
| 2149 | public function get_ctag($user, $filter='owner', $master_only=false) |
||
| 2160 | |||
| 2161 | /** |
||
| 2162 | * Hook for infolog to set some extra data and links |
||
| 2163 | * |
||
| 2164 | * @param array $data event-array preset by infolog plus |
||
| 2165 | * @param int $data[id] cal_id |
||
| 2166 | * @return array with key => value pairs to set in new event and link_app/link_id arrays |
||
| 2167 | */ |
||
| 2168 | function infolog_set($data) |
||
| 2212 | |||
| 2213 | /** |
||
| 2214 | * Hook for timesheet to set some extra data and links |
||
| 2215 | * |
||
| 2216 | * @param array $data |
||
| 2217 | * @param int $data[id] cal_id:recurrence |
||
| 2218 | * @return array with key => value pairs to set in new timesheet and link_app/link_id arrays |
||
| 2219 | */ |
||
| 2220 | function timesheet_set($data) |
||
| 2247 | } |
||
| 2248 |