PageRenderTime 71ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 2ms

/wp-content/plugins/all-in-one-event-calendar/lib/iCal/iCalcreator-2.20/iCalcreator.class.php

https://github.com/dedavidd/piratenpartij.nl
PHP | 10543 lines | 8068 code | 31 blank | 2444 comment | 2377 complexity | ca019c5514c8c616bf64eacb28151ef2 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*********************************************************************************/
  3. /**
  4. *
  5. * This file is a PHP implementation of rfc2445/rfc5545.
  6. *
  7. * @copyright Copyright (c) 2007-2014 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
  8. * @link http://kigkonsult.se/iCalcreator/index.php
  9. * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
  10. * @package iCalcreator
  11. * @version v2.20
  12. */
  13. /**
  14. * This library is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU Lesser General Public
  16. * License as published by the Free Software Foundation; either
  17. * version 2.1 of the License, or (at your option) any later version.
  18. *
  19. * This library is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  22. * Lesser General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU Lesser General Public
  25. * License along with this library; if not, write to the Free Software
  26. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  27. */
  28. /*********************************************************************************/
  29. /**
  30. * Do NOT remove or change version!!
  31. *
  32. * @copyright Copyright (c) 2007-2014 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
  33. * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
  34. */
  35. define( 'ICALCREATOR_VERSION', 'iCalcreator 2.20' );
  36. /*********************************************************************************/
  37. /**
  38. * vcalendar class
  39. *
  40. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  41. * @since 2.9.6 - 2011-05-14
  42. */
  43. class vcalendar {
  44. // calendar property variables
  45. var $calscale;
  46. var $method;
  47. var $prodid;
  48. var $version;
  49. var $xprop;
  50. // container for calendar components
  51. var $components;
  52. // component config variables
  53. var $allowEmpty;
  54. var $unique_id;
  55. var $language;
  56. var $directory;
  57. var $filename;
  58. var $url;
  59. var $delimiter;
  60. var $nl;
  61. var $format;
  62. var $dtzid;
  63. // component internal variables
  64. var $attributeDelimiter;
  65. var $valueInit;
  66. // component xCal declaration container
  67. var $xcaldecl;
  68. /**
  69. * constructor for calendar object
  70. *
  71. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  72. * @since 2.9.6 - 2011-05-14
  73. * @param array $config
  74. * @return void
  75. */
  76. function vcalendar ( $config = array()) {
  77. $this->_makeVersion();
  78. $this->calscale = null;
  79. $this->method = null;
  80. $this->_makeUnique_id();
  81. $this->prodid = null;
  82. $this->xprop = array();
  83. $this->language = null;
  84. $this->directory = null;
  85. $this->filename = null;
  86. $this->url = null;
  87. $this->dtzid = null;
  88. /**
  89. * language = <Text identifying a language, as defined in [RFC 1766]>
  90. */
  91. if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
  92. $config['language'] = ICAL_LANG;
  93. if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
  94. if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
  95. if( !isset( $config['format'] )) $config['format'] = 'iCal';
  96. if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
  97. $this->setConfig( $config );
  98. $this->xcaldecl = array();
  99. $this->components = array();
  100. }
  101. /**
  102. * return iCalcreator version number
  103. *
  104. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  105. * @since 2.18.5 - 2013-08-29
  106. * @uses ICALCREATOR_VERSION
  107. * @return string
  108. */
  109. public static function iCalcreatorVersion() {
  110. return trim( substr( ICALCREATOR_VERSION, strpos( ICALCREATOR_VERSION, ' ' )));
  111. }
  112. /*********************************************************************************/
  113. /**
  114. * Property Name: CALSCALE
  115. */
  116. /**
  117. * creates formatted output for calendar property calscale
  118. *
  119. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  120. * @since 2.10.16 - 2011-10-28
  121. * @return string
  122. */
  123. function createCalscale() {
  124. if( empty( $this->calscale )) return FALSE;
  125. switch( $this->format ) {
  126. case 'xcal':
  127. return $this->nl.' calscale="'.$this->calscale.'"';
  128. break;
  129. default:
  130. return 'CALSCALE:'.$this->calscale.$this->nl;
  131. break;
  132. }
  133. }
  134. /**
  135. * set calendar property calscale
  136. *
  137. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  138. * @since 2.4.8 - 2008-10-21
  139. * @param string $value
  140. * @return void
  141. */
  142. function setCalscale( $value ) {
  143. if( empty( $value )) return FALSE;
  144. $this->calscale = $value;
  145. }
  146. /*********************************************************************************/
  147. /**
  148. * Property Name: METHOD
  149. */
  150. /**
  151. * creates formatted output for calendar property method
  152. *
  153. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  154. * @since 2.10.16 - 2011-10-28
  155. * @return string
  156. */
  157. function createMethod() {
  158. if( empty( $this->method )) return FALSE;
  159. switch( $this->format ) {
  160. case 'xcal':
  161. return $this->nl.' method="'.$this->method.'"';
  162. break;
  163. default:
  164. return 'METHOD:'.$this->method.$this->nl;
  165. break;
  166. }
  167. }
  168. /**
  169. * set calendar property method
  170. *
  171. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  172. * @since 2.4.8 - 2008-20-23
  173. * @param string $value
  174. * @return bool
  175. */
  176. function setMethod( $value ) {
  177. if( empty( $value )) return FALSE;
  178. $this->method = $value;
  179. return TRUE;
  180. }
  181. /*********************************************************************************/
  182. /**
  183. * Property Name: PRODID
  184. *
  185. */
  186. /**
  187. * creates formatted output for calendar property prodid
  188. *
  189. * @copyright copyright (c) 2007-2013 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
  190. * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
  191. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  192. * @since 2.12.11 - 2012-05-13
  193. * @return string
  194. */
  195. function createProdid() {
  196. if( !isset( $this->prodid ))
  197. $this->_makeProdid();
  198. switch( $this->format ) {
  199. case 'xcal':
  200. return $this->nl.' prodid="'.$this->prodid.'"';
  201. break;
  202. default:
  203. $toolbox = new calendarComponent();
  204. $toolbox->setConfig( $this->getConfig());
  205. return $toolbox->_createElement( 'PRODID', '', $this->prodid );
  206. break;
  207. }
  208. }
  209. /**
  210. * make default value for calendar prodid, do NOT alter or remove this method or invoke of this method
  211. *
  212. * @copyright copyright (c) 2007-2013 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
  213. * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
  214. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  215. * @since 2.6.8 - 2009-12-30
  216. * @return void
  217. */
  218. function _makeProdid() {
  219. $this->prodid = '-//'.$this->unique_id.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION.'//'.strtoupper( $this->language );
  220. }
  221. /**
  222. * Conformance: The property MUST be specified once in an iCalendar object.
  223. * Description: The vendor of the implementation SHOULD assure that this
  224. * is a globally unique identifier; using some technique such as an FPI
  225. * value, as defined in [ISO 9070].
  226. */
  227. /**
  228. * make default unique_id for calendar prodid
  229. *
  230. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  231. * @since 0.3.0 - 2006-08-10
  232. * @return void
  233. */
  234. function _makeUnique_id() {
  235. $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
  236. }
  237. /*********************************************************************************/
  238. /**
  239. * Property Name: VERSION
  240. *
  241. * Description: A value of "2.0" corresponds to this memo.
  242. */
  243. /**
  244. * creates formatted output for calendar property version
  245. *
  246. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  247. * @since 2.10.16 - 2011-10-28
  248. * @return string
  249. */
  250. function createVersion() {
  251. if( empty( $this->version ))
  252. $this->_makeVersion();
  253. switch( $this->format ) {
  254. case 'xcal':
  255. return $this->nl.' version="'.$this->version.'"';
  256. break;
  257. default:
  258. return 'VERSION:'.$this->version.$this->nl;
  259. break;
  260. }
  261. }
  262. /**
  263. * set default calendar version
  264. *
  265. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  266. * @since 0.3.0 - 2006-08-10
  267. * @return void
  268. */
  269. function _makeVersion() {
  270. $this->version = '2.0';
  271. }
  272. /**
  273. * set calendar version
  274. *
  275. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  276. * @since 2.4.8 - 2008-10-23
  277. * @param string $value
  278. * @return void
  279. */
  280. function setVersion( $value ) {
  281. if( empty( $value )) return FALSE;
  282. $this->version = $value;
  283. return TRUE;
  284. }
  285. /*********************************************************************************/
  286. /**
  287. * Property Name: x-prop
  288. */
  289. /**
  290. * creates formatted output for calendar property x-prop, iCal format only
  291. *
  292. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  293. * @since 2.16.21 - 2013-05-25
  294. * @return string
  295. */
  296. function createXprop() {
  297. if( empty( $this->xprop ) || !is_array( $this->xprop )) return FALSE;
  298. $output = null;
  299. $toolbox = new calendarComponent();
  300. $toolbox->setConfig( $this->getConfig());
  301. foreach( $this->xprop as $label => $xpropPart ) {
  302. if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) {
  303. if( $this->getConfig( 'allowEmpty' ))
  304. $output .= $toolbox->_createElement( $label );
  305. continue;
  306. }
  307. $attributes = $toolbox->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
  308. if( is_array( $xpropPart['value'] )) {
  309. foreach( $xpropPart['value'] as $pix => $theXpart )
  310. $xpropPart['value'][$pix] = iCalUtilityFunctions::_strrep( $theXpart, $this->format, $this->nl );
  311. $xpropPart['value'] = implode( ',', $xpropPart['value'] );
  312. }
  313. else
  314. $xpropPart['value'] = iCalUtilityFunctions::_strrep( $xpropPart['value'], $this->format, $this->nl );
  315. $output .= $toolbox->_createElement( $label, $attributes, $xpropPart['value'] );
  316. if( is_array( $toolbox->xcaldecl ) && ( 0 < count( $toolbox->xcaldecl ))) {
  317. foreach( $toolbox->xcaldecl as $localxcaldecl )
  318. $this->xcaldecl[] = $localxcaldecl;
  319. }
  320. }
  321. return $output;
  322. }
  323. /**
  324. * set calendar property x-prop
  325. *
  326. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  327. * @since 2.18.10 - 2013-09-04
  328. * @param string $label
  329. * @param string $value
  330. * @param array $params optional
  331. * @return bool
  332. */
  333. function setXprop( $label, $value, $params=FALSE ) {
  334. if( empty( $label ))
  335. return FALSE;
  336. $label = strtoupper( $label );
  337. if( 'X-' != substr( $label, 0, 2 ))
  338. return FALSE;
  339. if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
  340. $xprop = array( 'value' => $value );
  341. $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
  342. if( !is_array( $this->xprop ))
  343. $this->xprop = array();
  344. $this->xprop[$label] = $xprop;
  345. return TRUE;
  346. }
  347. /*********************************************************************************/
  348. /**
  349. * delete calendar property value
  350. *
  351. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  352. * @since 2.8.8 - 2011-03-15
  353. * @param mixed $propName, bool FALSE => X-property
  354. * @param int $propix, optional, if specific property is wanted in case of multiply occurences
  355. * @return bool, if successfull delete
  356. */
  357. function deleteProperty( $propName=FALSE, $propix=FALSE ) {
  358. $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
  359. if( !$propix )
  360. $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
  361. $this->propdelix[$propName] = --$propix;
  362. $return = FALSE;
  363. switch( $propName ) {
  364. case 'CALSCALE':
  365. if( isset( $this->calscale )) {
  366. $this->calscale = null;
  367. $return = TRUE;
  368. }
  369. break;
  370. case 'METHOD':
  371. if( isset( $this->method )) {
  372. $this->method = null;
  373. $return = TRUE;
  374. }
  375. break;
  376. default:
  377. $reduced = array();
  378. if( $propName != 'X-PROP' ) {
  379. if( !isset( $this->xprop[$propName] )) { unset( $this->propdelix[$propName] ); return FALSE; }
  380. foreach( $this->xprop as $k => $a ) {
  381. if(( $k != $propName ) && !empty( $a ))
  382. $reduced[$k] = $a;
  383. }
  384. }
  385. else {
  386. if( count( $this->xprop ) <= $propix ) return FALSE;
  387. $xpropno = 0;
  388. foreach( $this->xprop as $xpropkey => $xpropvalue ) {
  389. if( $propix != $xpropno )
  390. $reduced[$xpropkey] = $xpropvalue;
  391. $xpropno++;
  392. }
  393. }
  394. $this->xprop = $reduced;
  395. if( empty( $this->xprop )) {
  396. unset( $this->propdelix[$propName] );
  397. return FALSE;
  398. }
  399. return TRUE;
  400. }
  401. return $return;
  402. }
  403. /**
  404. * get calendar property value/params
  405. *
  406. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  407. * @since 2.18.10 - 2013-09-04
  408. * @param string $propName, optional
  409. * @param int $propix, optional, if specific property is wanted in case of multiply occurences
  410. * @param bool $inclParam=FALSE
  411. * @return mixed
  412. */
  413. function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) {
  414. $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
  415. if( 'X-PROP' == $propName ) {
  416. if( !$propix )
  417. $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
  418. $this->propix[$propName] = --$propix;
  419. }
  420. else {
  421. $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' );
  422. $vComps = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' );
  423. $dateFmt = '%04d%02d%02d';
  424. }
  425. switch( $propName ) {
  426. case 'ATTENDEE':
  427. case 'CATEGORIES':
  428. case 'CONTACT':
  429. case 'DTSTART':
  430. case 'GEOLOCATION':
  431. case 'LOCATION':
  432. case 'ORGANIZER':
  433. case 'PRIORITY':
  434. case 'RESOURCES':
  435. case 'STATUS':
  436. case 'SUMMARY':
  437. case 'RECURRENCE-ID-UID':
  438. case 'RELATED-TO':
  439. case 'R-UID':
  440. case 'UID':
  441. case 'URL':
  442. $output = array();
  443. foreach ( $this->components as $cix => $component) {
  444. if( !in_array( $component->objName, $vComps))
  445. continue;
  446. if( in_array( $propName, $mProps )) {
  447. $component->_getProperties( $propName, $output );
  448. continue;
  449. }
  450. elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) {
  451. if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' )))
  452. $content = $component->getProperty( 'UID' );
  453. }
  454. elseif( 'GEOLOCATION' == $propName ) {
  455. $content = ( FALSE === ( $loc = $component->getProperty( 'LOCATION' ))) ? '' : $loc.' ';
  456. if( FALSE === ( $geo = $component->getProperty( 'GEO' )))
  457. continue;
  458. $content .= iCalUtilityFunctions::_geo2str2( $geo['latitude'], iCalUtilityFunctions::$geoLatFmt ).
  459. iCalUtilityFunctions::_geo2str2( $geo['longitude'], iCalUtilityFunctions::$geoLongFmt ).'/';
  460. }
  461. elseif( FALSE === ( $content = $component->getProperty( $propName )))
  462. continue;
  463. if(( FALSE === $content ) || empty( $content ))
  464. continue;
  465. elseif( is_array( $content )) {
  466. if( isset( $content['year'] )) {
  467. $key = sprintf( $dateFmt, $content['year'], $content['month'], $content['day'] );
  468. if( !isset( $output[$key] ))
  469. $output[$key] = 1;
  470. else
  471. $output[$key] += 1;
  472. }
  473. else {
  474. foreach( $content as $partValue => $partCount ) {
  475. if( !isset( $output[$partValue] ))
  476. $output[$partValue] = $partCount;
  477. else
  478. $output[$partValue] += $partCount;
  479. }
  480. }
  481. } // end elseif( is_array( $content )) {
  482. elseif( !isset( $output[$content] ))
  483. $output[$content] = 1;
  484. else
  485. $output[$content] += 1;
  486. } // end foreach ( $this->components as $cix => $component)
  487. if( !empty( $output ))
  488. ksort( $output );
  489. return $output;
  490. break;
  491. case 'CALSCALE':
  492. return ( !empty( $this->calscale )) ? $this->calscale : FALSE;
  493. break;
  494. case 'METHOD':
  495. return ( !empty( $this->method )) ? $this->method : FALSE;
  496. break;
  497. case 'PRODID':
  498. if( empty( $this->prodid ))
  499. $this->_makeProdid();
  500. return $this->prodid;
  501. break;
  502. case 'VERSION':
  503. return ( !empty( $this->version )) ? $this->version : FALSE;
  504. break;
  505. default:
  506. if( $propName != 'X-PROP' ) {
  507. if( !isset( $this->xprop[$propName] )) return FALSE;
  508. return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
  509. : array( $propName, $this->xprop[$propName]['value'] );
  510. }
  511. else {
  512. if( empty( $this->xprop )) return FALSE;
  513. $xpropno = 0;
  514. foreach( $this->xprop as $xpropkey => $xpropvalue ) {
  515. if( $propix == $xpropno )
  516. return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
  517. : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
  518. else
  519. $xpropno++;
  520. }
  521. unset( $this->propix[$propName] );
  522. return FALSE; // not found ??
  523. }
  524. }
  525. return FALSE;
  526. }
  527. /**
  528. * general vcalendar property setting
  529. *
  530. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  531. * @since 2.2.13 - 2007-11-04
  532. * @param mixed $args variable number of function arguments,
  533. * first argument is ALWAYS component name,
  534. * second ALWAYS component value!
  535. * @return bool
  536. */
  537. function setProperty () {
  538. $numargs = func_num_args();
  539. if( 1 > $numargs )
  540. return FALSE;
  541. $arglist = func_get_args();
  542. $arglist[0] = strtoupper( $arglist[0] );
  543. switch( $arglist[0] ) {
  544. case 'CALSCALE':
  545. return $this->setCalscale( $arglist[1] );
  546. case 'METHOD':
  547. return $this->setMethod( $arglist[1] );
  548. case 'VERSION':
  549. return $this->setVersion( $arglist[1] );
  550. default:
  551. if( !isset( $arglist[1] )) $arglist[1] = null;
  552. if( !isset( $arglist[2] )) $arglist[2] = null;
  553. return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
  554. }
  555. return FALSE;
  556. }
  557. /*********************************************************************************/
  558. /**
  559. * get vcalendar config values or * calendar components
  560. *
  561. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  562. * @since 2.11.7 - 2012-01-12
  563. * @param mixed $config
  564. * @return value
  565. */
  566. function getConfig( $config = FALSE ) {
  567. if( !$config ) {
  568. $return = array();
  569. $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' );
  570. $return['DELIMITER'] = $this->getConfig( 'DELIMITER' );
  571. $return['DIRECTORY'] = $this->getConfig( 'DIRECTORY' );
  572. $return['FILENAME'] = $this->getConfig( 'FILENAME' );
  573. $return['DIRFILE'] = $this->getConfig( 'DIRFILE' );
  574. $return['FILESIZE'] = $this->getConfig( 'FILESIZE' );
  575. $return['FORMAT'] = $this->getConfig( 'FORMAT' );
  576. if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' )))
  577. $return['LANGUAGE'] = $lang;
  578. $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
  579. $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' );
  580. if( FALSE !== ( $url = $this->getConfig( 'URL' )))
  581. $return['URL'] = $url;
  582. $return['TZID'] = $this->getConfig( 'TZID' );
  583. return $return;
  584. }
  585. switch( strtoupper( $config )) {
  586. case 'ALLOWEMPTY':
  587. return $this->allowEmpty;
  588. break;
  589. case 'COMPSINFO':
  590. unset( $this->compix );
  591. $info = array();
  592. foreach( $this->components as $cix => $component ) {
  593. if( empty( $component )) continue;
  594. $info[$cix]['ordno'] = $cix + 1;
  595. $info[$cix]['type'] = $component->objName;
  596. $info[$cix]['uid'] = $component->getProperty( 'uid' );
  597. $info[$cix]['props'] = $component->getConfig( 'propinfo' );
  598. $info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
  599. }
  600. return $info;
  601. break;
  602. case 'DELIMITER':
  603. return $this->delimiter;
  604. break;
  605. case 'DIRECTORY':
  606. if( empty( $this->directory ) && ( '0' != $this->directory ))
  607. $this->directory = '.';
  608. return $this->directory;
  609. break;
  610. case 'DIRFILE':
  611. return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' );
  612. break;
  613. case 'FILEINFO':
  614. return array( $this->getConfig( 'directory' )
  615. , $this->getConfig( 'filename' )
  616. , $this->getConfig( 'filesize' ));
  617. break;
  618. case 'FILENAME':
  619. if( empty( $this->filename ) && ( '0' != $this->filename )) {
  620. if( 'xcal' == $this->format )
  621. $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. .
  622. else
  623. $this->filename = date( 'YmdHis' ).'.ics';
  624. }
  625. return $this->filename;
  626. break;
  627. case 'FILESIZE':
  628. $size = 0;
  629. if( empty( $this->url )) {
  630. $dirfile = $this->getConfig( 'dirfile' );
  631. if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile ))))
  632. $size = 0;
  633. clearstatcache();
  634. }
  635. return $size;
  636. break;
  637. case 'FORMAT':
  638. return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal';
  639. break;
  640. case 'LANGUAGE':
  641. /* get language for calendar component as defined in [RFC 1766] */
  642. return $this->language;
  643. break;
  644. case 'NL':
  645. case 'NEWLINECHAR':
  646. return $this->nl;
  647. break;
  648. case 'TZID':
  649. return $this->dtzid;
  650. break;
  651. case 'UNIQUE_ID':
  652. return $this->unique_id;
  653. break;
  654. case 'URL':
  655. if( !empty( $this->url ))
  656. return $this->url;
  657. else
  658. return FALSE;
  659. break;
  660. }
  661. }
  662. /**
  663. * general vcalendar config setting
  664. *
  665. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  666. * @since 2.18.12 - 2013-09-12
  667. * @param mixed $config
  668. * @param string $value
  669. * @return void
  670. */
  671. function setConfig( $config, $value = FALSE) {
  672. if( is_array( $config )) {
  673. $config = array_change_key_case( $config, CASE_UPPER );
  674. if( isset( $config['DELIMITER'] )) {
  675. if( FALSE === $this->setConfig( 'DELIMITER', $config['DELIMITER'] ))
  676. return FALSE;
  677. unset( $config['DELIMITER'] );
  678. }
  679. if( isset( $config['DIRECTORY'] )) {
  680. if( FALSE === $this->setConfig( 'DIRECTORY', $config['DIRECTORY'] ))
  681. return FALSE;
  682. unset( $config['DIRECTORY'] );
  683. }
  684. foreach( $config as $cKey => $cValue ) {
  685. if( FALSE === $this->setConfig( $cKey, $cValue ))
  686. return FALSE;
  687. }
  688. return TRUE;
  689. }
  690. else
  691. $res = FALSE;
  692. $config = strtoupper( $config );
  693. switch( $config ) {
  694. case 'ALLOWEMPTY':
  695. $this->allowEmpty = $value;
  696. $subcfg = array( 'ALLOWEMPTY' => $value );
  697. $res = TRUE;
  698. break;
  699. case 'DELIMITER':
  700. $this->delimiter = $value;
  701. return TRUE;
  702. break;
  703. case 'DIRECTORY':
  704. if( FALSE === ( $value = realpath( rtrim( trim( $value ), $this->delimiter ))))
  705. return FALSE;
  706. else {
  707. /* local directory */
  708. $this->directory = $value;
  709. $this->url = null;
  710. return TRUE;
  711. }
  712. break;
  713. case 'FILENAME':
  714. $value = trim( $value );
  715. $dirfile = $this->directory.$this->delimiter.$value;
  716. if( file_exists( $dirfile )) {
  717. /* local file exists */
  718. if( is_readable( $dirfile ) || is_writable( $dirfile )) {
  719. clearstatcache();
  720. $this->filename = $value;
  721. return TRUE;
  722. }
  723. else
  724. return FALSE;
  725. }
  726. elseif( is_readable( $this->directory ) || is_writable( $this->directory )) {
  727. /* read- or writable directory */
  728. clearstatcache();
  729. $this->filename = $value;
  730. return TRUE;
  731. }
  732. else
  733. return FALSE;
  734. break;
  735. case 'FORMAT':
  736. $value = trim( strtolower( $value ));
  737. if( 'xcal' == $value ) {
  738. $this->format = 'xcal';
  739. $this->attributeDelimiter = $this->nl;
  740. $this->valueInit = null;
  741. }
  742. else {
  743. $this->format = null;
  744. $this->attributeDelimiter = ';';
  745. $this->valueInit = ':';
  746. }
  747. $subcfg = array( 'FORMAT' => $value );
  748. $res = TRUE;
  749. break;
  750. case 'LANGUAGE': // set language for calendar component as defined in [RFC 1766]
  751. $value = trim( $value );
  752. $this->language = $value;
  753. $this->_makeProdid();
  754. $subcfg = array( 'LANGUAGE' => $value );
  755. $res = TRUE;
  756. break;
  757. case 'NL':
  758. case 'NEWLINECHAR':
  759. $this->nl = $value;
  760. if( 'xcal' == $value ) {
  761. $this->attributeDelimiter = $this->nl;
  762. $this->valueInit = null;
  763. }
  764. else {
  765. $this->attributeDelimiter = ';';
  766. $this->valueInit = ':';
  767. }
  768. $subcfg = array( 'NL' => $value );
  769. $res = TRUE;
  770. break;
  771. case 'TZID':
  772. $this->dtzid = $value;
  773. $subcfg = array( 'TZID' => $value );
  774. $res = TRUE;
  775. break;
  776. case 'UNIQUE_ID':
  777. $value = trim( $value );
  778. $this->unique_id = $value;
  779. $this->_makeProdid();
  780. $subcfg = array( 'UNIQUE_ID' => $value );
  781. $res = TRUE;
  782. break;
  783. case 'URL':
  784. /* remote file - URL */
  785. $value = str_replace( array( 'HTTP://', 'WEBCAL://', 'webcal://' ), 'http://', trim( $value ));
  786. $value = str_replace( 'HTTPS://', 'https://', trim( $value ));
  787. if(( 'http://' != substr( $value, 0, 7 )) && ( 'https://' != substr( $value, 0, 8 )))
  788. return FALSE;
  789. $this->directory = '.';
  790. $this->url = $value;
  791. if( '.ics' != strtolower( substr( $value, -4 )))
  792. unset( $this->filename );
  793. else
  794. $this->filename = $basename( $value );
  795. return TRUE;
  796. break;
  797. default: // any unvalid config key.. .
  798. return TRUE;
  799. }
  800. if( !$res ) return FALSE;
  801. if( isset( $subcfg ) && !empty( $this->components )) {
  802. foreach( $subcfg as $cfgkey => $cfgvalue ) {
  803. foreach( $this->components as $cix => $component ) {
  804. $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE );
  805. if( !$res )
  806. break 2;
  807. $this->components[$cix] = $component->copy(); // PHP4 compliant
  808. }
  809. }
  810. }
  811. return $res;
  812. }
  813. /*********************************************************************************/
  814. /**
  815. * add calendar component to container
  816. *
  817. * alias to setComponent
  818. *
  819. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  820. * @since 1.x.x - 2007-04-24
  821. * @param object $component calendar component
  822. * @return void
  823. */
  824. function addComponent( $component ) {
  825. $this->setComponent( $component );
  826. }
  827. /**
  828. * delete calendar component from container
  829. *
  830. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  831. * @since 2.8.8 - 2011-03-15
  832. * @param mixed $arg1 ordno / component type / component uid
  833. * @param mixed $arg2 optional, ordno if arg1 = component type
  834. * @return void
  835. */
  836. function deleteComponent( $arg1, $arg2=FALSE ) {
  837. $argType = $index = null;
  838. if ( ctype_digit( (string) $arg1 )) {
  839. $argType = 'INDEX';
  840. $index = (int) $arg1 - 1;
  841. }
  842. elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
  843. $argType = strtolower( $arg1 );
  844. $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
  845. }
  846. $cix1dC = 0;
  847. foreach ( $this->components as $cix => $component) {
  848. if( empty( $component )) continue;
  849. if(( 'INDEX' == $argType ) && ( $index == $cix )) {
  850. unset( $this->components[$cix] );
  851. return TRUE;
  852. }
  853. elseif( $argType == $component->objName ) {
  854. if( $index == $cix1dC ) {
  855. unset( $this->components[$cix] );
  856. return TRUE;
  857. }
  858. $cix1dC++;
  859. }
  860. elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
  861. unset( $this->components[$cix] );
  862. return TRUE;
  863. }
  864. }
  865. return FALSE;
  866. }
  867. /**
  868. * get calendar component from container
  869. *
  870. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  871. * @since 2.18.10 - 2013-09-04
  872. * @param mixed $arg1 optional, ordno/component type/ component uid
  873. * @param mixed $arg2 optional, ordno if arg1 = component type
  874. * @return object
  875. */
  876. function getComponent( $arg1=FALSE, $arg2=FALSE ) {
  877. $index = $argType = null;
  878. if ( !$arg1 ) { // first or next in component chain
  879. $argType = 'INDEX';
  880. $index = $this->compix['INDEX'] = ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
  881. }
  882. elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
  883. $arg2 = implode( '-', array_keys( $arg1 ));
  884. $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1;
  885. $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' );
  886. $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' );
  887. $mProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' );
  888. }
  889. elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain
  890. $argType = 'INDEX';
  891. $index = (int) $arg1;
  892. unset( $this->compix );
  893. }
  894. elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name
  895. unset( $this->compix['INDEX'] );
  896. $argType = strtolower( $arg1 );
  897. if( !$arg2 )
  898. $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
  899. elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
  900. $index = (int) $arg2;
  901. }
  902. elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument
  903. if( !$arg2 )
  904. $index = $this->compix[$arg1] = ( isset( $this->compix[$arg1] )) ? $this->compix[$arg1] + 1 : 1;
  905. elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
  906. $index = (int) $arg2;
  907. }
  908. if( isset( $index ))
  909. $index -= 1;
  910. $ckeys = array_keys( $this->components );
  911. if( !empty( $index) && ( $index > end( $ckeys )))
  912. return FALSE;
  913. $cix1gC = 0;
  914. foreach ( $this->components as $cix => $component) {
  915. if( empty( $component )) continue;
  916. if(( 'INDEX' == $argType ) && ( $index == $cix ))
  917. return $component->copy();
  918. elseif( $argType == $component->objName ) {
  919. if( $index == $cix1gC )
  920. return $component->copy();
  921. $cix1gC++;
  922. }
  923. elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
  924. $hit = array();
  925. $arg1 = array_change_key_case( $arg1, CASE_UPPER );
  926. foreach( $arg1 as $pName => $pValue ) {
  927. if( !in_array( $pName, $dateProps ) && !in_array( $pName, $otherProps ))
  928. continue;
  929. if( in_array( $pName, $mProps )) { // multiple occurrence
  930. $propValues = array();
  931. $component->_getProperties( $pName, $propValues );
  932. $propValues = array_keys( $propValues );
  933. $hit[] = ( in_array( $pValue, $propValues )) ? TRUE : FALSE;
  934. continue;
  935. } // end if(.. .// multiple occurrence
  936. if( FALSE === ( $value = $component->getProperty( $pName ))) { // single occurrence
  937. $hit[] = FALSE; // missing property
  938. continue;
  939. }
  940. if( 'SUMMARY' == $pName ) { // exists within (any case)
  941. $hit[] = ( FALSE !== stripos( $value, $pValue )) ? TRUE : FALSE;
  942. continue;
  943. }
  944. if( in_array( $pName, $dateProps )) {
  945. $valuedate = sprintf( '%04d%02d%02d', $value['year'], $value['month'], $value['day'] );
  946. if( 8 < strlen( $pValue )) {
  947. if( isset( $value['hour'] )) {
  948. if( 'T' == substr( $pValue, 8, 1 ))
  949. $pValue = str_replace( 'T', '', $pValue );
  950. $valuedate .= sprintf( '%02d%02d%02d', $value['hour'], $value['min'], $value['sec'] );
  951. }
  952. else
  953. $pValue = substr( $pValue, 0, 8 );
  954. }
  955. $hit[] = ( $pValue == $valuedate ) ? TRUE : FALSE;
  956. continue;
  957. }
  958. elseif( !is_array( $value ))
  959. $value = array( $value );
  960. foreach( $value as $part ) {
  961. $part = ( FALSE !== strpos( $part, ',' )) ? explode( ',', $part ) : array( $part );
  962. foreach( $part as $subPart ) {
  963. if( $pValue == $subPart ) {
  964. $hit[] = TRUE;
  965. continue 3;
  966. }
  967. }
  968. } // end foreach( $value as $part )
  969. $hit[] = FALSE; // no hit in property
  970. } // end foreach( $arg1 as $pName => $pValue )
  971. if( in_array( TRUE, $hit )) {
  972. if( $index == $cix1gC )
  973. return $component->copy();
  974. $cix1gC++;
  975. }
  976. } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
  977. elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { // UID
  978. if( $index == $cix1gC )
  979. return $component->copy();
  980. $cix1gC++;
  981. }
  982. } // end foreach ( $this->components.. .
  983. /* not found.. . */
  984. unset( $this->compix );
  985. return FALSE;
  986. }
  987. /**
  988. * create new calendar component, already included within calendar
  989. *
  990. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  991. * @since 2.6.33 - 2011-01-03
  992. * @param string $compType component type
  993. * @return object (reference)
  994. */
  995. function & newComponent( $compType ) {
  996. $config = $this->getConfig();
  997. $keys = array_keys( $this->components );
  998. $ix = end( $keys) + 1;
  999. switch( strtoupper( $compType )) {
  1000. case 'EVENT':
  1001. case 'VEVENT':
  1002. $this->components[$ix] = new vevent( $config );
  1003. break;
  1004. case 'TODO':
  1005. case 'VTODO':
  1006. $this->components[$ix] = new vtodo( $config );
  1007. break;
  1008. case 'JOURNAL':
  1009. case 'VJOURNAL':
  1010. $this->components[$ix] = new vjournal( $config );
  1011. break;
  1012. case 'FREEBUSY':
  1013. case 'VFREEBUSY':
  1014. $this->components[$ix] = new vfreebusy( $config );
  1015. break;
  1016. case 'TIMEZONE':
  1017. case 'VTIMEZONE':
  1018. array_unshift( $this->components, new vtimezone( $config ));
  1019. $ix = 0;
  1020. break;
  1021. default:
  1022. return FALSE;
  1023. }
  1024. return $this->components[$ix];
  1025. }
  1026. /**
  1027. * select components from calendar on date or selectOption basis
  1028. *
  1029. * Ensure DTSTART is set for every component.
  1030. * No date controls occurs.
  1031. *
  1032. * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
  1033. * @since 2.18.19 - 2014-02-01
  1034. * @param mixed $startY optional, start Year, default current Year ALT. array selecOptions ( *[ <propName> => <uniqueValue> ] )
  1035. * @param int $startM optional, start Month, default current Month
  1036. * @param int $startD optional, start Day, default current Day
  1037. * @param int $endY optional, end Year, default $startY
  1038. * @param int $endY optional, end Month, default $startM
  1039. * @param int $endY optional, end Day, default $startD
  1040. * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)
  1041. * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][]
  1042. * TRUE => output : array[] (ignores split)
  1043. * @param bool $any optional, TRUE (default) - select component(-s) that occurs within period
  1044. * FALSE - only component(-s) that starts within period
  1045. * @param bool $split optional, TRUE (default) - one component copy every DAY it occurs during the
  1046. * period (implies flat=FALSE)
  1047. * FALSE - one occurance of component only in output array
  1048. * @return array or FALSE
  1049. */
  1050. function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) {
  1051. /* check if empty calendar */
  1052. if( 0 >= count( $this->components )) return FALSE;
  1053. if( is_array( $startY ))
  1054. return $this->selectComponents2( $startY );
  1055. /* check default dates */
  1056. if( ! $startY ) $startY = date( 'Y' );
  1057. if( ! $startM ) $startM = date( 'm' );
  1058. if( ! $startD ) $startD = date( 'd' );
  1059. $startDate = mktime( 0, 0, 0, $startM, $startD, $startY );
  1060. if( ! $endY ) $endY = $startY;
  1061. if( ! $endM ) $endM = $startM;
  1062. if( ! $endD ) $endD = $startD;
  1063. $endDate = mktime( 23, 59, 59, $endM, $endD, $endY );
  1064. // echo 'selectComp arg='.date( 'Y-m-d H:i:s', $startDate).' -- '.date( 'Y-m-d H:i:s', $endDate)."<br>\n"; $tcnt = 0;// test ###
  1065. /* check component types */
  1066. $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' );
  1067. if( empty( $cType ))
  1068. $cType = $validTypes;
  1069. else {
  1070. if( ! is_array( $cType ))
  1071. $cType = array( $cType );
  1072. $cType = array_map( 'strtolower', $cType );
  1073. foreach( $cType as $cix => $theType ) {
  1074. $cType[$cix] = $theType;
  1075. if( !in_array( $theType, $validTypes ))
  1076. $cType[$cix] = 'vevent';
  1077. }
  1078. $cType = array_unique( $cType );
  1079. }
  1080. if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination
  1081. $split = FALSE;
  1082. if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination
  1083. $split = FALSE;
  1084. /* iterate components */
  1085. $result = array();
  1086. $this->sort( 'UID' );
  1087. $compUIDcmp = null;
  1088. $recurridList = array();
  1089. foreach ( $this->components as $cix => $component ) {
  1090. if( empty( $component )) continue;
  1091. unset( $start );
  1092. /* deselect unvalid type components */
  1093. if( !in_array( $component->objName, $cType ))
  1094. continue;
  1095. $start = $component->getProperty( 'dtstart', FALSE, TRUE );
  1096. /* select due when dtstart is missing */
  1097. if( empty( $start ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due', FALSE, TRUE ))))
  1098. continue;
  1099. if( empty( $start ))
  1100. continue;
  1101. if( ! isset( $start['value']['tz'] ) && isset( $start['params']['TZID'] ))
  1102. $start['value']['tz'] = $start['params']['TZID'];
  1103. $start = $start['value'];
  1104. $compUID = $component->getProperty( 'UID' );
  1105. if( $compUIDcmp != $compUID ) {
  1106. $compUIDcmp = $compUID;
  1107. unset( $exdatelist, $recurridList );
  1108. }
  1109. $SCbools = array( 'dtendExist' => FALSE, 'dueExist' => FALSE, 'durationExist' => FALSE, 'endAllDayEvent' => FALSE );
  1110. $recurrid = FALSE;
  1111. $dateFormat = array();
  1112. unset( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $workstart, $workend ); // clean up
  1113. $startWdate = iCalUtilityFunctions::_SCsetXCurrentDateZ( iCalUtilityFunctions::_date2timestamp( $start ), $start );
  1114. $dateFormat['start'] = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
  1115. /* get end date from dtend/due/duration properties */
  1116. $end = $component->getProperty( 'dtend', FALSE, TRUE );
  1117. if( !empty( $end )) {
  1118. $SCbools[ 'dtendExist'] = TRUE;
  1119. $dateFormat['end'] = ( isset( $end['value']['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
  1120. }
  1121. if( ! isset( $end['value']['tz'] ) && isset( $end['params']['TZID'] ))
  1122. $end['value']['tz'] = $end['params']['TZID'];
  1123. $end = $end['value'];
  1124. if( empty( $end ) && ( $component->objName == 'vtodo' )) {
  1125. $end = $component->getProperty( 'due' );
  1126. if( !empty( $end )) {
  1127. $SCbools[ 'dueExist'] = TRUE;
  1128. $dateFormat['end'] = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
  1129. }
  1130. }
  1131. if( !empty( $end ) && !isset( $end['hour'] )) {
  1132. /* a DTEND without time part regards an event that ends the day before,
  1133. for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
  1134. $SCbools[ 'endAllDayEvent'] = TRUE;
  1135. $endWdate = mktime( 23, 59, 59, $end['month'], ($end['day'] - 1), $end['year'] );
  1136. $end['year'] = date( 'Y', $endWdate );
  1137. $end['month'] = date( 'm', $endWdate );
  1138. $end['day'] = date( 'd', $endWdate );
  1139. $end['hour'] = 23;
  1140. $end['min'] = $end['sec'] = 59;
  1141. }
  1142. if( empty( $end )) {
  1143. $end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format
  1144. if( !empty( $end ))
  1145. if( isset( $start['tz'] ))
  1146. $end['tz'] = $start['tz'];
  1147. $SCbools[ 'durationExist'] = TRUE;
  1148. $dateFormat['end'] = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
  1149. // if( !empty($end)) echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br>\n"; // test ###
  1150. }
  1151. if( empty( $end )) { // assume one day duration if missing end date
  1152. $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
  1153. if( isset( $start['tz'] ))
  1154. $end['tz'] = $start['tz'];
  1155. }
  1156. // if( isset($end)) echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br>\n"; // test ###
  1157. $endWdate = iCalUtilityFunctions::_SCsetXCurrentDateZ( iCalUtilityFunctions::_date2timestamp( $end ), $end );
  1158. if( $endWdate < $startWdate ) { // MUST be after start date!!
  1159. $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
  1160. $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
  1161. }
  1162. $rdurWsecs = $endWdate - $startWdate; // compute event (component) duration in seconds
  1163. /* make a list of optional exclude dates for component occurence from exrule and exdate */
  1164. $exdatelist = array();
  1165. $workstart = iCalUtilityFunctions::_timestamp2date(( $startDate - $rdurWsecs ), 6);
  1166. $workend = iCalUtilityFunctions::_timestamp2date(( $endDate + $rdurWsecs ), 6);
  1167. while( FALSE !== ( $exrule = $component->getProperty( 'exrule' ))) // check exrule
  1168. iCalUtilityFunctions::_recur2date( $exdatelist, $exrule, $start, $workstart, $workend );
  1169. while( FALSE !== ( $exdate = $component->getProperty( 'exdate' ))) { // check exdate
  1170. foreach( $exdate as $theExdate ) {
  1171. $exWdate = iCalUtilityFunctions::_date2timestamp( $theExdate );
  1172. $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate )); // on a day-basis !!!
  1173. if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate ))
  1174. $exdatelist[$exWdate] = TRUE;
  1175. } // end - foreach( $exdate as $theExdate )
  1176. } // end - check exdate
  1177. /* check recurrence-id (note, a missing sequence is the same as sequence=0 so don't test for sequence), remove hit with reccurr-id date */
  1178. if( FALSE !== ( $recurrid = $component->getProperty( 'recurrence-id' ))) {
  1179. $recurrid = iCalUtilityFunctions::_date2timestamp( $recurrid );
  1180. $recurrid = mktime( 0, 0, 0, date( 'm', $recurrid ), date( 'd', $recurrid ), date( 'Y', $recurrid )); // on a day-basis !!!
  1181. $recurridList[$recurrid] = TRUE; // no recurring to start this day
  1182. // echo "adding comp no:$cix with date=".implode($start)." and recurrid=".implode($recurrid)." to recurridList id=$recurrid<br>\n"; // test ###
  1183. } // end recurrence-id/sequence test
  1184. /* select only components with.. . */
  1185. if(( !$any && ( $startWdate >= $startDate ) && ( $startWdate <= $endDate )) || // (dt)start within the period
  1186. ( $any && ( $startWdate < $endDate ) && ( $endWdate >= $startDate ))) { // occurs within the period
  1187. /* add the selected component (WITHIN valid dates) to output array */
  1188. if( $flat ) { // any=true/false, ignores split
  1189. if( !$recurrid )
  1190. $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id)
  1191. }
  1192. elseif( $split ) { // split the original component
  1193. if( $endWdate > $endDate )
  1194. $endWdate = $endDate; // use period end date
  1195. $rstart = ( $startWdate < $startDate ) ? $startDate : $startWdate; // use period start date
  1196. $startYMD = $rstartYMD = date( 'Ymd', $rstart );
  1197. $endYMD = date( 'Ymd', $endWdate );
  1198. $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
  1199. // echo "going to test comp no:$cix with rstartYMD=$rstartYMD, endYMD=$endYMD and checkDate($checkDate) with recurridList=".implode(',',array_keys($recurridList))."<br>\n"; // test ###
  1200. if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
  1201. while( $rstartYMD <= $endYMD ) { // iterate
  1202. if( isset( $exdatelist[$checkDate] ) || // exclude any recurrence date, found in the exdatelist
  1203. ( isset( $recurridList[$checkDate] ) && !$recurrid )) { // or in the recurridList, but not itself
  1204. // echo "skipping comp no:$cix with datestart=$rstartYMD and checkdate=$checkDate<br>\n"; // test ###
  1205. $rstart += ( 24 *3600 ); // step one day
  1206. $rstartYMD = date( 'Ymd', $rstart );
  1207. continue;
  1208. }
  1209. iCalUtilityFunctions::_SCsetXCurrentStart( $component, $dateFormat, $checkDate, $rstartYMD, $rstart, $startYMD, $start );
  1210. iCalUtilityFunctions::_SCsetXCurrentEnd( $component, $dateFormat, $rstart, $rstartYMD, $endWdate, $endYMD, $end, $SCbools );
  1211. $wd = getdate( $rstart );
  1212. $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output
  1213. $rstart += ( 24 *3600 ); // step one day
  1214. $rstartYMD = date( 'Ymd', $rstart );
  1215. $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
  1216. } // end while( $rstart <= $endWdate )
  1217. } // end if( !isset( $exdatelist[$checkDate] ))
  1218. } // end elseif( $split ) - else use component date
  1219. elseif( $recurrid && !$flat && !$any && !$split )
  1220. $continue = TRUE;
  1221. else { // !$flat && !$split, i.e. no flat array and DTSTART within period
  1222. $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!!
  1223. // echo "going to test comp no:$cix with checkDate=$checkDate with recurridList=".implode(',',array_keys($recurridList)); // test ###
  1224. if(( !$any || !isset( $exdatelist[$checkDate] )) && // exclude any recurrence date, found in exdatelist
  1225. ( !isset( $recurridList[$checkDate] ) || $recurrid )) { // or in the recurridList, but not itself
  1226. // echo " and copied to output<br>\n"; // test ###
  1227. $wd = getdate( $startWdate );
  1228. $result[$wd['year']][$wd['mon']][$wd['mday']][$compUID] = $component->copy(); // copy to output
  1229. }
  1230. }
  1231. } // end if(( $startWdate >= $startDate ) && ( $startWdate <= $endDate ))
  1232. /* if 'any' components, check components with reccurrence rules, removing all excluding dates */
  1233. if( TRUE === $any ) {
  1234. /* make a list of optional repeating dates for component occurence, rrule, rdate */
  1235. $recurlist = array();
  1236. while( FALSE !== ( $rrule = $component->getProperty( 'rrule' ))) // check rrule
  1237. iCalUtilityFunctions::_recur2date( $recurlist, $rrule, $start, $workstart, $workend );
  1238. foreach( $recurlist as $recurkey => $recurvalue ) // key=match date as timestamp
  1239. $recurlist[$recurkey] = $rdurWsecs; // add duration in seconds
  1240. while( FALSE !== ( $rdate = $component->getProperty( 'rdate' ))) { // check rdate
  1241. foreach( $rdate as $theRdate ) {
  1242. if( is_array( $theRdate ) && ( 2 == count( $theRdate )) && // all days within PERIOD
  1243. array_key_exists( '0', $theRdate ) && array_key_exists( '1', $theRdate )) {
  1244. $rstart = iCalUtilityFunctions::_date2timestamp( $theRdate[0] );
  1245. if(( $rstart < ( $startDate - $rdurWsecs )) || ( $rstart > $endDate ))
  1246. continue;
  1247. if( isset( $theRdate[1]['year'] )) // date-date period
  1248. $rend = iCalUtilityFunctions::_date2timestamp( $theRdate[1] );
  1249. else { // date-duration period
  1250. $rend = iCalUtilityFunctions::_duration2date(

Large files files are truncated, but you can click here to view the full file