Overview

Packages

  • awl
    • AuthPlugin
    • AwlDatabase
    • Browser
    • classEditor
    • DataEntry
    • DataUpdate
    • EMail
    • iCalendar
    • MenuSet
    • PgQuery
    • Session
    • Translation
    • User
    • Utilities
    • Validation
    • vCalendar
    • vComponent
    • XMLDocument
    • XMLElement
  • None
  • PHP

Classes

  • vComponent
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: /**
  3:  * A Class for handling vCalendar & vCard data.
  4:  *
  5:  * When parsed the underlying structure is roughly as follows:
  6:  *
  7:  *   vComponent( array(vComponent), array(vProperty) )
  8:  *
  9:  * @package awl
 10:  * @subpackage vComponent
 11:  * @author Milan Medlik <milan@morphoss.com>
 12:  * @copyright Morphoss Ltd <http://www.morphoss.com/>
 13:  * @license   http://gnu.org/copyleft/lgpl.html GNU LGPL v2 or later
 14:  *
 15:  */
 16: 
 17:     include_once('vObject.php');
 18:     //include_once('HeapLines.php');
 19:     include_once('vProperty.php');
 20: 
 21:     class vComponent extends vObject{
 22: 
 23:         private $components;
 24:         private $properties;
 25:         private $type;
 26:         private $iterator;
 27:         private $seekBegin;
 28:         private $seekEnd;
 29:         private $propertyLocation;
 30: 
 31:         const KEYBEGIN = 'BEGIN:';
 32:         const KEYBEGINLENGTH = 6;
 33:         const KEYEND = "END:";
 34:         const KEYENDLENGTH = 4;
 35:         const VEOL = "\r\n";
 36: 
 37:         public static $PREPARSED = false;
 38: 
 39:         function __construct($propstring=null, &$refData=null){
 40:             parent::__construct($master);
 41: 
 42:             unset($this->type);
 43: 
 44:             if(isset($propstring) && gettype($propstring) == 'string'){
 45:                 $this->initFromText($propstring);
 46:             } else if(isset($refData)){
 47:                 if(gettype($refData) == 'string'){
 48:                     $this->initFromText($refData);
 49:                 } else if(gettype($refData) == 'object') {
 50:                     $this->initFromIterator($refData);
 51:                 }
 52:             } else {
 53:                 //$text = '';
 54:                 //$this->initFromText($text);
 55:             }
 56: 
 57: 
 58: //            if(isset($this->iterator)){
 59: //                $this->parseFrom($this->iterator);
 60: //            }
 61: 
 62: 
 63:         }
 64: 
 65:         function initFromIterator(&$iterator, $begin = -1){
 66:             $this->iterator = &$iterator;
 67: 
 68:             //$this->seekBegin = $this->iterator->key();
 69: 
 70: 
 71: 
 72:             $iterator = $this->iterator;
 73:             do {
 74:                 $line = $iterator->current();
 75:                 $seek = $iterator->key();
 76: 
 77:                 $posStart = strpos(strtoupper($line), vComponent::KEYBEGIN);
 78:                 if($posStart !== false && $posStart == 0){
 79:                     if(!isset($this->type)){
 80:                         $this->seekBegin = $seek;
 81: 
 82:                         $this->type = strtoupper(substr($line, vComponent::KEYBEGINLENGTH));
 83:                     }
 84:                 } else {
 85: 
 86:                     $posEnd = strpos(strtoupper($line), vComponent::KEYEND);
 87:                     if($posEnd !== false && $posEnd == 0){
 88:                         $thisEnd = strtoupper(substr($line, vComponent::KEYENDLENGTH));
 89:                         if($thisEnd == $this->type){
 90:                             $this->seekEnd = $seek;
 91:                             //$iterator->next();
 92:                             $len = strlen($this->type);
 93:                             $last = $this->type[$len-1];
 94:                             if($last == "\r"){
 95:                                 $this->type = strtoupper(substr($this->type, 0, $len-1));
 96:                             }
 97:                             break;
 98:                         }
 99: 
100:                     } else {
101:                         //$this->properties[] = new vProperty(null, $iterator, $seek);
102:                     }
103:                 }
104: 
105: 
106: 
107: 
108:                 $iterator->next();
109:             } while($iterator->valid());
110:             //$this->parseFrom($iterator);
111: 
112:         }
113: 
114:         public function getIterator(){
115:             return $this->iterator;
116:         }
117: 
118:         function initFromText(&$plainText){
119:             $plain2 = $this->UnwrapComponent($plainText);
120: 
121:             //$file = fopen('data.out.tmp', 'w');
122:             //$plain3 = preg_replace('{\r?\n}', '\r\n', $plain2 );
123:             //fwrite($file, $plain2);
124:             //fclose($file);
125:             //$lines = &explode(PHP_EOL, $plain2);
126: //            $arrayData = new ArrayObject($lines);
127: //            $this->iterator = &$arrayData->getIterator();
128: //            $this->initFromIterator($this->iterator, 0);
129: //            unset($plain);
130: //            unset($iterator);
131: //            unset($arrayData);
132: //            unset($lines);
133: 
134:             // Note that we can't use PHP_EOL here, since the line splitting should handle *either* of CR, CRLF or LF line endings.
135:             $arrayOfLines = new ArrayObject(preg_split('{\r?\n}', $plain2));
136:             $this->iterator = $arrayOfLines->getIterator();
137:             unset($plain2);
138:             //$this->initFromIterator($this->iterator);
139:             //$this->iterator = new HeapLines($plain);
140: 
141:             //$this->initFromIterator(new HeapLines($plain), 0);
142:             $this->parseFrom($this->iterator);
143: 
144:         }
145: 
146:         function rewind(){
147:             if(isset($this->iterator) && isset($this->seekBegin)){
148:                 $this->iterator->seek($this->seekBegin);
149:             }
150:         }
151: 
152: 
153:         /**
154:          * fill arrays with components and properties if they are empty.
155:          *
156:          * basicaly the object are just pointer to memory with input data
157:          * (iterator with seek address on start and end)
158:          * but when you want get information of any components
159:          * or properties is necessary call this function first
160:          *
161:          * @see GetComponents(), ComponentsCount(), GetProperties(), PropertiesCount()
162:          */
163:         function explode(){
164:             if(!isset($this->properties) && !isset($this->components) && $this->isValid()){
165:                 unset($this->properties);
166:                 unset($this->components);
167:                 unset($this->type);
168:                 $this->rewind();
169:                 $this->parseFrom($this->iterator);
170:             }
171:         }
172: 
173:         function close(){
174: 
175:             if(isset($this->components)){
176:                 foreach($this->components as $comp){
177:                     $comp->close();
178:                 }
179:             }
180: 
181:             if($this->isValid()){
182:                 unset($this->properties);
183:                 unset($this->components);
184:             }
185: 
186: 
187: 
188:         }
189: 
190:         function parseFrom(&$iterator){
191: 
192: 
193:             $begin = $iterator->key();
194:             $typelen = 0;
195:             //$count = $lines->count();
196: 
197:             do{
198:                 $line = $iterator->current();
199:                 //$line = substr($current, 0, strlen($current) -1);
200:                 $end = $iterator->key();
201: 
202:                 $pos = strpos(strtoupper($line), vComponent::KEYBEGIN);
203:                 $callnext = true;
204:                 if($pos !== false && $pos == 0) {
205:                     $type = strtoupper(substr($line, vComponent::KEYBEGINLENGTH));
206: 
207:                     if($typelen !== 0 && strncmp($this->type, $type, $typelen) !== 0){
208:                         $this->components[] = new vComponent(null, $iterator);
209:                         $callnext = false;
210:                     } else {
211:                         // in special cases when is "\r" on end remove it
212:                         // We should probably throw an error if we get here, because the
213:                         // thing that splits stuff should not be giving us this.
214:                         $typelen = strlen($type);
215:                         if($type[$typelen-1] == "\r"){
216:                             $typelen--;
217:                             $this->type = substr($type, 0, $typelen);
218:                         } else {
219:                             $this->type = $type;
220:                         }
221: 
222: 
223:                         //$iterator->offsetUnset($end);
224:                         //$iterator->seek($begin);
225:                         //$callnext = false;
226:                     }
227: 
228:                 } else {
229:                     $pos = strpos(strtoupper($line), vComponent::KEYEND);
230: 
231:                     if($pos !== false && $pos == 0) {
232:                         $this->seekBegin = $begin;
233:                         $this->seekEnd = $end;
234:                         //$iterator->offsetUnset($end);
235:                         //$iterator->seek($end-2);
236:                         //$line2 = $iterator->current();
237:                         //$this->seekEnd = $iterator->key();
238: 
239:                         //$callnext = false;
240:                         //$newheap = $lines->createLineHeapFrom($start, $end);
241:                         //$testfistline = $newheap->substr(0);
242:                         //echo "end:" . $this->key . "[$start, $end]<br>";
243:                         //$lines->nextLine();
244:                         //$iterator->offsetUnset($end);
245:                         return;
246:                     } else {
247: //                    $prstart = $lines->getSwheretartLineOnHeap();
248: //                    $prend =
249: //$this->properties[] = new vProperty("AHOJ");
250:                         $parameters = preg_split( '(:|;)', $line);
251:                         $possiblename = strtoupper(array_shift( $parameters ));
252:                         $this->properties[] = new vProperty($possiblename, $this->master, $iterator, $end);
253:                         //echo $this->key . ' property line' . "[$prstart,$prend]<br>";
254: 
255:                     }
256:                 }
257: 
258: //                if($callnext){
259: //                    $iterator->next();
260: //                }
261:                 //if($callnext)
262:                 //    $iterator->offsetUnset($end);
263:                 if($iterator->valid())
264:                     $iterator->next();
265: 
266:             } while($iterator->valid() && ( !isset($this->seekEnd) || $this->seekEnd > $end ) );
267:             //$lines->getEndLineOnHeap();
268: 
269: 
270:         }
271: 
272: 
273: 
274:         /**
275:          * count of component
276:          * @return int
277:          */
278:         public function ComponentCount(){
279:             $this->explode();
280:             return isset($this->components) ? count($this->components) : 0;
281:         }
282: 
283:         /**
284:          * count of component
285:          * @return int
286:          */
287:         public function propertiesCount(){
288:             $this->explode();
289:             return isset($this->properties) ? count($this->properties) : 0;
290:         }
291: 
292:         /**
293:          * @param $position
294:          * @return null - whet is position out of range
295:          */
296:         public function getComponentAt($position){
297:             $this->explode();
298:             if($this->ComponentCount() > $position){
299:                 return $this->components[$position];
300:             } else {
301:                 return null;
302:             }
303:         }
304: 
305:         function getPropertyAt($position){
306:             $this->explode();
307:             if($this->propertiesCount() > $position){
308:                 return $this->properties[$position];
309:             } else {
310:                 return null;
311:             }
312: 
313:         }
314: 
315:         function clearPropertyAt($position) {
316:             $this->explode();
317:             if($this->isValid()){
318:                 $this->invalidate();
319:             }
320: 
321:             $i=0;  // property index/position is relative to current vComponent
322:             foreach( $this->properties AS $k => $v ) {
323:                 if ( $i == $position ) {
324:                     unset($this->properties[$k]);
325:                 }
326:                 $i++;
327:             }
328:             $this->properties = array_values($this->properties);
329:         }
330: 
331: 
332:         /**
333:          * Return the type of component which this is
334:          */
335:         function GetType() {
336:             return $this->type;
337:         }
338: 
339: 
340:         /**
341:          * Set the type of component which this is
342:          */
343:         function SetType( $type ) {
344:             if ( $this->isValid() ) {
345:                 $this->invalidate();
346:             };
347:             $this->type = strtoupper($type);
348:             return $this->type;
349:         }
350: 
351: 
352:         /**
353:          * Collect an array of all parameters of our properties which are the specified type
354:          * Mainly used for collecting the full variety of references TZIDs
355:          */
356:         function CollectParameterValues( $parameter_name ) {
357:             $this->explode();
358:             $values = array();
359:             if(isset($this->components)){
360:                 foreach( $this->components AS $k => $v ) {
361:                     $also = $v->CollectParameterValues($parameter_name);
362:                     $values = array_merge( $values, $also );
363:                 }
364:             }
365:             if(isset($this->properties)){
366:                 foreach( $this->properties AS $k => $v ) {
367:                     $also = $v->GetParameterValue($parameter_name);
368:                     if ( isset($also) && $also != "" ) {
369: //        dbg_error_log( 'vComponent', "::CollectParameterValues(%s) : Found '%s'", $parameter_name, $also);
370:                         $values[$also] = 1;
371:                     }
372:                 }
373:             }
374: 
375:             return $values;
376:         }
377: 
378: 
379:         /**
380:          * Return the first instance of a property of this name
381:          */
382:         function GetProperty( $type ) {
383:             $this->explode();
384:             foreach( $this->properties AS $k => $v ) {
385:                 if ( is_object($v) && $v->Name() == $type ) {
386:                     return $v;
387:                 }
388:                 else if ( !is_object($v) ) {
389:                     dbg_error_log("ERROR", 'vComponent::GetProperty(): Trying to get %s on %s which is not an object!', $type, $v );
390:                 }
391:             }
392:             /** So we can call methods on the result of this, make sure we always return a vProperty of some kind */
393:             return null;
394:         }
395: 
396:         /**
397:          * Return the value of the first instance of a property of this name, or null
398:          */
399:         function GetPValue( $type ) {
400:             $this->explode();
401:             $p = $this->GetProperty($type);
402:             if ( isset($p) ) return $p->Value();
403:             return null;
404:         }
405: 
406: 
407:         /**
408:          * Get all properties, or the properties matching a particular type, or matching an
409:          * array associating property names with true values: array( 'PROPERTY' => true, 'PROPERTY2' => true )
410:          */
411:         function GetProperties( $type = null ) {
412:             // the properties in base are with name
413:             // it was setted in parseFrom(&interator)
414:             if(!isset($this->properties)){
415:                 $this->explode();
416:             }
417:             $properties = array();
418:             $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
419:             foreach( $this->properties AS $k => $v ) {
420:                 $name = $v->Name(); //get Property name (string)
421:                 $name = preg_replace( '/^.*[.]/', '', $name ); //for grouped properties we remove prefix itemX.
422:                 if ( $type == null || (isset($testtypes[$name]) && $testtypes[$name])) {
423:                     $properties[] = $v;
424:                 }
425:             }
426:             return $properties;
427:         }
428: 
429:         /**
430:          * Return an array of properties matching the specified path
431:          *
432:          * @return array An array of vProperty within the tree which match the path given, in the form
433:          *  [/]COMPONENT[/...]/PROPERTY in a syntax kind of similar to our poor man's XML queries. We
434:          *  also allow COMPONENT and PROPERTY to be !COMPONENT and !PROPERTY for ++fun.
435:          *
436:          * @note At some point post PHP4 this could be re-done with an iterator, which should be more efficient for common use cases.
437:          */
438:         function GetPropertiesByPath( $path ) {
439:             $properties = array();
440:             dbg_error_log( 'vComponent', "GetPropertiesByPath: Querying within '%s' for path '%s'", $this->type, $path );
441:             if ( !preg_match( '#(/?)(!?)([^/]+)(/?.*)$#', $path, $matches ) ) return $properties;
442: 
443:             $anchored = ($matches[1] == '/');
444:             $inverted = ($matches[2] == '!');
445:             $ourtest = $matches[3];
446:             $therest = $matches[4];
447:             dbg_error_log( 'vComponent', "GetPropertiesByPath: Matches: %s -- %s -- %s -- %s\n", $matches[1], $matches[2], $matches[3], $matches[4] );
448:             if ( $ourtest == '*' || (($ourtest == $this->type) !== $inverted) && $therest != '' ) {
449:                 if ( preg_match( '#^/(!?)([^/]+)$#', $therest, $matches ) ) {
450:                     $normmatch = ($matches[1] =='');
451:                     $proptest  = $matches[2];
452: 
453:                     $thisproperties = $this->GetProperties();
454:                     if(isset($thisproperties) && count($thisproperties) > 0){
455:                         foreach( $thisproperties AS $k => $v ) {
456:                             if ( $proptest == '*' || (($v->Name() == $proptest) === $normmatch ) ) {
457:                                 $properties[] = $v;
458:                             }
459:                         }
460:                     }
461: 
462:                 }
463:                 else {
464:                     /**
465:                      * There is more to the path, so we recurse into that sub-part
466:                      */
467:                     foreach( $this->GetComponents() AS $k => $v ) {
468:                         $properties = array_merge( $properties, $v->GetPropertiesByPath($therest) );
469:                     }
470:                 }
471:             }
472: 
473:             if ( ! $anchored ) {
474:                 /**
475:                  * Our input $path was not rooted, so we recurse further
476:                  */
477:                 foreach( $this->GetComponents() AS $k => $v ) {
478:                     $properties = array_merge( $properties, $v->GetPropertiesByPath($path) );
479:                 }
480:             }
481:             dbg_error_log('vComponent', "GetPropertiesByPath: Found %d within '%s' for path '%s'\n", count($properties), $this->type, $path );
482:             return $properties;
483:         }
484: 
485:         /**
486:          * Clear all properties, or the properties matching a particular type
487:          * @param string|array $type The type of property - omit for all properties - or an
488:          * array associating property names with true values: array( 'PROPERTY' => true, 'PROPERTY2' => true )
489:          */
490:         function ClearProperties( $type = null ) {
491:             $this->explode();
492:             if($this->isValid()){
493:                 $this->invalidate();
494:             }
495: 
496:             if ( $type != null ) {
497:                 $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
498:                 // First remove all the existing ones of that type
499:                 foreach( $this->properties AS $k => $v ) {
500:                     if ( isset($testtypes[$v->Name()]) && $testtypes[$v->Name()] ) {
501:                         unset($this->properties[$k]);
502: 
503:                     }
504:                 }
505:                 $this->properties = array_values($this->properties);
506:             }
507:             else {
508: 
509:                 $this->properties = array();
510:             }
511:         }
512: 
513:         /**
514:          * Set all properties, or the ones matching a particular type
515:          */
516:         function SetProperties( $new_properties, $type = null ) {
517:             $this->explode();
518:             $this->ClearProperties($type);
519:             foreach( $new_properties AS $k => $v ) {
520:                 $this->properties[] = $v;
521:             }
522:         }
523: 
524:         /**
525:          * Adds a new property
526:          *
527:          * @param vProperty $new_property The new property to append to the set, or a string with the name
528:          * @param string $value The value of the new property (default: param 1 is an vProperty with everything
529:          * @param array $parameters The key/value parameter pairs (default: none, or param 1 is an vProperty with everything)
530:          */
531:         function AddProperty( $new_property, $value = null, $parameters = null ) {
532:             $this->explode();
533:             if ( isset($value) && gettype($new_property) == 'string' ) {
534:                 $new_prop = new vProperty('', $this->master);
535:                 $new_prop->Name($new_property);
536:                 $new_prop->Value($value);
537:                 if ( $parameters != null ) {
538:                     $new_prop->Parameters($parameters);
539:                 }
540: //      dbg_error_log('vComponent'," Adding new property '%s'", $new_prop->Render() );
541:                 $this->properties[] = $new_prop;
542:             }
543:             else if ( $new_property instanceof vProperty ) {
544:                 $this->properties[] = $new_property;
545:                 $new_property->setMaster($this->master);
546:             }
547: 
548:             if($this->isValid()){
549:                 $this->invalidate();
550:             }
551:         }
552: 
553:         /**
554:          * Get all sub-components, or at least get those matching a type, or failling to match,
555:          * should the second parameter be set to false. Component types may be a string or an array
556:          * associating property names with true values: array( 'TYPE' => true, 'TYPE2' => true )
557:          *
558:          * @param mixed $type The type(s) to match (default: All)
559:          * @param boolean $normal_match Set to false to invert the match (default: true)
560:          * @return array an array of the sub-components
561:          */
562:         function GetComponents( $type = null, $normal_match = true ) {
563:             $this->explode();
564:             $components = isset($this->components) ? $this->components : array();
565: 
566:             if ( $type != null ) {
567:                 //$components = $this->components;
568:                 $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
569:                 foreach( $components AS $k => $v ) {
570: //        printf( "Type: %s, %s, %s\n", $v->GetType(),
571: //                 ($normal_match && isset($testtypes[$v->GetType()]) && $testtypes[$v->GetType()] ? 'true':'false'),
572: //                 ( !$normal_match && (!isset($testtypes[$v->GetType()]) || !$testtypes[$v->GetType()]) ? 'true':'false')
573: //               );
574:                     if ( !($normal_match && isset($testtypes[$v->GetType()]) && $testtypes[$v->GetType()] )
575:                         && !( !$normal_match && (!isset($testtypes[$v->GetType()]) || !$testtypes[$v->GetType()])) ) {
576:                         unset($components[$k]);
577:                     }
578:                 }
579:                 $components = array_values($components);
580:             }
581: //    print_r($components);
582:             return $components;
583:         }
584: 
585: 
586:         /**
587:          * Clear all components, or the components matching a particular type
588:          * @param string $type The type of component - omit for all components
589:          */
590:         function ClearComponents( $type = null ) {
591:             if($this->isValid()){
592:                 $this->explode();
593:             }
594: 
595: 
596:             if ( $type != null && !empty($this->components)) {
597:                 $testtypes = (gettype($type) == 'string' ? array( $type => true ) : $type );
598:                 // First remove all the existing ones of that type
599:                 foreach( $this->components AS $k => $v ) {
600:                     $this->components[$k]->ClearComponents($testtypes);
601:                     if ( isset($testtypes[$v->GetType()]) && $testtypes[$v->GetType()] ) {
602:                         unset($this->components[$k]);
603:                         if ( $this->isValid()) {
604:                             $this->invalidate();
605:                         }
606:                     }
607: 
608:                 }
609:             }
610:             else {
611:                 $this->components = array();
612:                 if ( $this->isValid()) {
613:                     $this->invalidate();
614:                 }
615:             }
616: 
617:             return $this->isValid();
618:         }
619: 
620:         /**
621:          * Sets some or all sub-components of the component to the supplied new components
622:          *
623:          * @param array of vComponent $new_components The new components to replace the existing ones
624:          * @param string $type The type of components to be replaced.  Defaults to null, which means all components will be replaced.
625:          */
626:         function SetComponents( $new_component, $type = null ) {
627:             $this->explode();
628:             if ( $this->isValid()) {
629:                 $this->invalidate();
630:             }
631:             if ( empty($type) ) {
632:                 $this->components = $new_component;
633:                 return;
634:             }
635: 
636:             $this->ClearComponents($type);
637:             foreach( $new_component AS $k => $v ) {
638:                 $this->components[] = $v;
639:             }
640:         }
641: 
642:         /**
643:          * Adds a new subcomponent
644:          *
645:          * @param vComponent $new_component The new component to append to the set
646:          */
647:         public function AddComponent( $new_component ) {
648:             $this->explode();
649:             if ( is_array($new_component) && count($new_component) == 0 ) return;
650: 
651:             if ( $this->isValid()) {
652:                 $this->invalidate();
653:             }
654: 
655:             try {
656:                 if ( is_array($new_component) ) {
657:                     foreach( $new_component AS $k => $v ) {
658:                         $this->components[] = $v;
659:                         if ( !method_exists($v,'setMaster') ) fatal('Component to be added must be a vComponent');
660:                         $v->setMaster($this->master);
661:                     }
662:                 }
663:                 else {
664:                     if ( !method_exists($new_component,'setMaster') ) fatal('Component to be added must be a vComponent');
665:                     $new_component->setMaster($this->master);
666:                     $this->components[] = $new_component;
667:                 }
668:             }
669:             catch( Exception $e ) {
670:                 fatal();
671:             }
672:         }
673: 
674: 
675:         /**
676:          * Mask components, removing any that are not of the types in the list
677:          * @param array $keep An array of component types to be kept
678:          * @param boolean $recursive (default true) Whether to recursively MaskComponents on the ones we find
679:          */
680:         function MaskComponents( $keep, $recursive = true ) {
681:             $this->explode();
682:             if(!isset($this->components)){
683:                 return ;
684:             }
685: 
686:             foreach( $this->components AS $k => $v ) {
687:                 if ( !isset($keep[$v->GetType()]) ) {
688:                     unset($this->components[$k]);
689:                     if ( $this->isValid()) {
690:                         $this->invalidate();
691:                     }
692:                 }
693:                 else if ( $recursive ) {
694:                     $v->MaskComponents($keep);
695:                 }
696:             }
697:         }
698: 
699:         /**
700:          * Mask properties, removing any that are not in the list
701:          * @param array $keep An array of property names to be kept
702:          * @param array $component_list An array of component types to check within
703:          */
704:         function MaskProperties( $keep, $component_list=null ) {
705:             $this->explode();
706:             if ( !isset($component_list) || isset($component_list[$this->type]) ) {
707:                 foreach( $this->properties AS $k => $v ) {
708:                     if ( !isset($keep[$v->Name()]) || !$keep[$v->Name()] ) {
709:                         unset($this->properties[$k]);
710:                         if ( $this->isValid()) {
711:                             $this->invalidate();
712:                         }
713:                     }
714:                 }
715:             }
716:             if(isset($this->components)){
717:                 foreach( $this->components AS $k => $v ) {
718:                     $v->MaskProperties($keep, $component_list);
719:                 }
720:             }
721: 
722:         }
723: 
724:         /**
725:          * This imposes the (CRLF + linear space) wrapping specified in RFC2445. According
726:          * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising
727:          * XML parsers often muck with it and may remove the CR.  We output RFC2445 compliance.
728:          *
729:          * In order to preserve pre-existing wrapping in the component, we split the incoming
730:          * string on line breaks before running wordwrap over each component of that.
731:          */
732:         function WrapComponent( $content ) {
733:             $strs = preg_split( "/\r?\n/", $content );
734:             $wrapped = "";
735:             foreach ($strs as $str) {
736: //                print "Before: >>$str<<, len(".strlen($str).")\n";
737:                 $wrapped_bit = (strlen($str) == 72 ? $str : preg_replace( '/(.{72})/u', '$1'."\r\n ", $str )) .self::VEOL;
738: //                print "After: >>$wrapped_bit<<\n";
739:                 $wrapped .= $wrapped_bit;
740:             }
741:             return $wrapped;
742:         }
743: 
744:         /**
745:          * This unescapes the (CRLF + linear space) wrapping specified in RFC2445. According
746:          * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising
747:          * XML parsers often muck with it and may remove the CR.  We accept either case.
748:          */
749:         function UnwrapComponent( &$content ) {
750:             return preg_replace('/\r?\n[ \t]/', '', $content );
751:         }
752: 
753: 
754:         /**
755:          * Render vComponent without wrap lines
756:          * @param null $restricted_properties
757:          * @param bool $force_rendering
758:          * @return string
759:          */
760:         protected function RenderWithoutWrap($restricted_properties = null, $force_rendering = false){
761:             $unrolledComponents = isset($this->components);
762:             $rendered = vComponent::KEYBEGIN . $this->type . self::VEOL;
763: 
764: 
765:             if($this->isValid()){
766:                 $rendered .= $this->RenderWithoutWrapFromIterator($unrolledComponents);
767:             } else {
768:                 $rendered .= $this->RenderWithoutWrapFromObjects();
769:             }
770: 
771:             if($unrolledComponents){
772:                 //$count = 0;
773:                 foreach($this->components as $component){
774:                     //$component->explode();
775:                     //$count++;
776:                     $component_render = $component->RenderWithoutWrap( null, $force_rendering );
777:                     if(strlen($component_render) > 0){
778:                         $rendered .= $component_render . self::VEOL;
779:                     }
780: 
781:                     //$component->close();
782: 
783:                 }
784:             }
785: 
786:             return $rendered . vComponent::KEYEND . $this->type;
787:         }
788: 
789:         /**
790:          * Let render property by property
791:          * @return string
792:          */
793:         protected function RenderWithoutWrapFromObjects(){
794:             $rendered = '';
795:             if(isset($this->properties)){
796:                 foreach( $this->properties AS $k => $v ) {
797:                     if ( method_exists($v, 'Render') ) {
798:                         $forebug = $v->Render() . self::VEOL;
799:                         $rendered .= $forebug;
800:                     }
801:                 }
802:             }
803: 
804:             return $rendered;
805:         }
806: 
807:         /**
808:          * take source data in Iterator and recreate to string
809:          * @param boolean $unroledComponents - have any components
810:          * @return string - rendered object
811:          */
812:         protected function RenderWithoutWrapFromIterator($unrolledComponents){
813:             $this->rewind();
814:             $rendered = '';
815:             $lentype = 0;
816: 
817:             if(isset($this->type)){
818:                 $lentype = strlen($this->type);
819:             }
820: 
821:             $iterator = $this->iterator;
822:             $inInnerObject = 0;
823:             do {
824:                 $line = $iterator->current() . self::VEOL;
825:                 $seek = $iterator->key();
826: 
827:                 $posStart = strpos($line, vComponent::KEYBEGIN);
828:                 if($posStart !== false && $posStart == 0){
829:                     $type = substr($line, vComponent::KEYBEGINLENGTH);
830:                     if(!isset($this->type)){
831:                         //$this->seekBegin = $seek;
832:                         $this->type = $type;
833:                         $lentype = strlen($this->type);
834:                     } else if(strncmp($type, $this->type, $lentype) != 0){
835:                         // dont render line which is owned
836:                         // by inner commponent -> inner component *BEGIN*
837:                         if($unrolledComponents){
838:                             $inInnerObject++;
839:                         } else {
840:                             $rendered .= $line ;
841:                         }
842:                     }
843:                 } else {
844: 
845:                     $posEnd = strpos($line, vComponent::KEYEND);
846:                     if($posEnd !== false && $posEnd == 0){
847:                         $thisEnd = substr($line, vComponent::KEYENDLENGTH);
848:                         if(strncmp($thisEnd, $this->type, $lentype) == 0){
849:                             // Current object end
850:                             $this->seekEnd = $seek;
851:                             //$iterator->next();
852:                             break;
853:                         }else if($unrolledComponents){
854:                             // dont render line which is owned
855:                             // by inner commponent -> inner component *END*
856:                             $inInnerObject--;
857:                         } else {
858:                             $rendered .= $line;
859:                         }
860: 
861:                     } else if($inInnerObject === 0 || !$unrolledComponents){
862:                         $rendered .= $line;
863:                     }
864:                 }
865:                 $iterator->next();
866:             } while($iterator->valid() && ( !isset($this->seekEnd) || $this->seekEnd > $seek));
867: 
868: 
869:             return $rendered;
870: 
871:         }
872: 
873: 
874:         /**
875:          * render object to string with wraped lines
876:          * @param null $restricted_properties
877:          * @param bool $force_rendering
878:          * @return string - rendered object
879:          */
880:         function Render($restricted_properties = null, $force_rendering = false){
881:             return $this->WrapComponent($this->RenderWithoutWrap($restricted_properties, $force_rendering));
882:             //return $this->InternalRender($restricted_properties, $force_rendering);
883:         }
884: 
885:         function isValid(){
886:             if($this->valid){
887:                 if(isset($this->components)){
888:                     foreach($this->components as $comp){
889:                         if(!$comp->isValid()){
890:                             return false;
891:                         }
892:                     }
893:                 }
894: 
895:                 return true;
896:             }
897:             return false;
898:         }
899: 
900:  /**
901:    * Test a PROP-FILTER or COMP-FILTER and return a true/false
902:    * COMP-FILTER (is-defined | is-not-defined | (time-range?, prop-filter*, comp-filter*))
903:    * PROP-FILTER (is-defined | is-not-defined | ((time-range | text-match)?, param-filter*))
904:    *
905:    * @param array $filter An array of XMLElement defining the filter
906:    *
907:    * @return boolean Whether or not this vComponent passes the test
908:    */
909:   function TestFilter( $filters ) {
910:     foreach( $filters AS $k => $v ) {
911:       $tag = $v->GetNSTag();
912: //      dbg_error_log( 'vCalendar', ":TestFilter: '%s' ", $tag );
913:       switch( $tag ) {
914:         case 'urn:ietf:params:xml:ns:caldav:is-defined':
915:         case 'urn:ietf:params:xml:ns:carddav:is-defined':
916:           if ( count($this->properties) == 0 && count($this->components) == 0 ) return false;
917:           break;
918: 
919:         case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
920:         case 'urn:ietf:params:xml:ns:carddav:is-not-defined':
921:           if ( count($this->properties) > 0 || count($this->components) > 0 ) return false;
922:           break;
923: 
924:         case 'urn:ietf:params:xml:ns:caldav:comp-filter':
925:         case 'urn:ietf:params:xml:ns:carddav:comp-filter':
926:           $subcomponents = $this->GetComponents($v->GetAttribute('name'));
927:           $subfilter = $v->GetContent();
928: //          dbg_error_log( 'vCalendar', ":TestFilter: Found '%d' (of %d) subs of type '%s'",
929: //                       count($subcomponents), count($this->components), $v->GetAttribute('name') );
930:           $subtag = $subfilter[0]->GetNSTag();
931:           if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-not-defined'
932:             || $subtag == 'urn:ietf:params:xml:ns:carddav:is-not-defined' ) {
933:             if ( count($properties) > 0 ) {
934: //              dbg_error_log( 'vComponent', ":TestFilter: Wanted none => false" );
935:               return false;
936:             }
937:           }
938:           else if ( count($subcomponents) == 0 ) {
939:             if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-defined'
940:               || $subtag == 'urn:ietf:params:xml:ns:carddav:is-defined' ) {
941: //              dbg_error_log( 'vComponent', ":TestFilter: Wanted some => false" );
942:               return false;
943:             }
944:             else {
945: //              dbg_error_log( 'vCalendar', ":TestFilter: Wanted something from missing sub-components => false" );
946:               $negate = $subfilter[0]->GetAttribute("negate-condition");
947:               if ( empty($negate) || strtolower($negate) != 'yes' ) return false;
948:             }
949:           }
950:           else {
951:             foreach( $subcomponents AS $kk => $subcomponent ) {
952:               if ( ! $subcomponent->TestFilter($subfilter) ) return false;
953:             }
954:           }
955:           break;
956: 
957:         case 'urn:ietf:params:xml:ns:carddav:prop-filter':
958:         case 'urn:ietf:params:xml:ns:caldav:prop-filter':
959:           $subfilter = $v->GetContent();
960:           $properties = $this->GetProperties($v->GetAttribute("name"));
961:           dbg_error_log( 'vCalendar', ":TestFilter: Found '%d' props of type '%s'", count($properties), $v->GetAttribute('name') );
962:           $subtag = $subfilter[0]->GetNSTag();
963:           if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-not-defined'
964:             || $subtag == 'urn:ietf:params:xml:ns:carddav:is-not-defined' ) {
965:             if ( count($properties) > 0 ) {
966: //              dbg_error_log( 'vCalendar', ":TestFilter: Wanted none => false" );
967:               return false;
968:             }
969:           }
970:           else if ( count($properties) == 0 ) {
971:             if ( $subtag == 'urn:ietf:params:xml:ns:caldav:is-defined'
972:               || $subtag == 'urn:ietf:params:xml:ns:carddav:is-defined' ) {
973: //              dbg_error_log( 'vCalendar', ":TestFilter: Wanted some => false" );
974:               return false;
975:             }
976:             else {
977: //              dbg_error_log( 'vCalendar', ":TestFilter: Wanted '%s' from missing sub-properties => false", $subtag );
978:               $negate = $subfilter[0]->GetAttribute("negate-condition");
979:               if ( empty($negate) || strtolower($negate) != 'yes' ) return false;
980:             }
981:           }
982:           else {
983:             foreach( $properties AS $kk => $property ) {
984:               if ( !$property->TestFilter($subfilter) ) return false;
985:             }
986:           }
987:           break;
988:       }
989:     }
990:     return true;
991:   }
992: 
993:     }
994: 
995: 
996: 
AWL API documentation generated by ApiGen 2.8.0