[Pkg-owncloud-commits] [php-sabre-vobject] 31/43: Validator upgrades.
David Prévot
taffit at moszumanska.debian.org
Sat Jan 10 14:20:16 UTC 2015
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository php-sabre-vobject.
commit 17b76de88fa7fb58347978224ca3668a6c9e37a6
Author: Evert Pot <me at evertpot.com>
Date: Fri Jan 9 19:03:36 2015 -0500
Validator upgrades.
This comment adds two new options to the validator:
1. PROFILE_CARDDAV
2. PROFILE_CALDAV
Passing these options will tell the validator to assume that the
vcard/icalendar object needs to be validated specifically according to
the rules of caldav/carddav.
For vCards specifically this means that the UID is required for CardDAV,
but we only emit a warning without PROFILE_CARDDAV.
For iCalendar there's a bunch of new rules that we will eventually
integrate into sabredav.
This fixes #176.
---
lib/Component.php | 38 ++++----
lib/Component/VCalendar.php | 78 +++++++++++++---
lib/Component/VCard.php | 59 +++++++-----
lib/Node.php | 21 +++++
tests/VObject/Component/VCalendarTest.php | 150 ++++++++++++++++++++++++++++++
tests/VObject/Component/VCardTest.php | 112 ++++++++++++++++++++++
6 files changed, 401 insertions(+), 57 deletions(-)
diff --git a/lib/Component.php b/lib/Component.php
index c085a4c..42ced44 100644
--- a/lib/Component.php
+++ b/lib/Component.php
@@ -47,7 +47,7 @@ class Component extends Node {
* @param bool $defaults
* @return void
*/
- public function __construct(Document $root, $name, array $children = array(), $defaults = true) {
+ function __construct(Document $root, $name, array $children = array(), $defaults = true) {
$this->name = strtoupper($name);
$this->root = $root;
@@ -104,7 +104,7 @@ class Component extends Node {
*
* @return Node
*/
- public function add($a1, $a2 = null, $a3 = null) {
+ function add($a1, $a2 = null, $a3 = null) {
if ($a1 instanceof Node) {
if (!is_null($a2)) {
@@ -145,7 +145,7 @@ class Component extends Node {
* @param mixed $item
* @return void
*/
- public function remove($item) {
+ function remove($item) {
if (is_string($item)) {
$children = $this->select($item);
@@ -172,7 +172,7 @@ class Component extends Node {
*
* @return array
*/
- public function children() {
+ function children() {
return $this->children;
@@ -184,7 +184,7 @@ class Component extends Node {
*
* @return array
*/
- public function getComponents() {
+ function getComponents() {
$result = array();
foreach($this->children as $child) {
@@ -213,7 +213,7 @@ class Component extends Node {
* @param string $name
* @return array
*/
- public function select($name) {
+ function select($name) {
$group = null;
$name = strtoupper($name);
@@ -250,7 +250,7 @@ class Component extends Node {
*
* @return string
*/
- public function serialize() {
+ function serialize() {
$str = "BEGIN:" . $this->name . "\r\n";
@@ -324,7 +324,7 @@ class Component extends Node {
*
* @return array
*/
- public function jsonSerialize() {
+ function jsonSerialize() {
$components = array();
$properties = array();
@@ -371,7 +371,7 @@ class Component extends Node {
* @param string $name
* @return Property
*/
- public function __get($name) {
+ function __get($name) {
$matches = $this->select($name);
if (count($matches)===0) {
@@ -391,7 +391,7 @@ class Component extends Node {
* @param string $name
* @return bool
*/
- public function __isset($name) {
+ function __isset($name) {
$matches = $this->select($name);
return count($matches)>0;
@@ -411,7 +411,7 @@ class Component extends Node {
* @param mixed $value
* @return void
*/
- public function __set($name, $value) {
+ function __set($name, $value) {
$matches = $this->select($name);
$overWrite = count($matches)?key($matches):null;
@@ -441,7 +441,7 @@ class Component extends Node {
* @param string $name
* @return void
*/
- public function __unset($name) {
+ function __unset($name) {
$matches = $this->select($name);
foreach($matches as $k=>$child) {
@@ -461,7 +461,7 @@ class Component extends Node {
*
* @return void
*/
- public function __clone() {
+ function __clone() {
foreach($this->children as $key=>$child) {
$this->children[$key] = clone $child;
@@ -491,7 +491,7 @@ class Component extends Node {
*
* @var array
*/
- public function getValidationRules() {
+ function getValidationRules() {
return array();
@@ -502,6 +502,8 @@ class Component extends Node {
*
* The following options are supported:
* Node::REPAIR - May attempt to automatically repair the problem.
+ * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+ * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
*
* This method returns an array with detected problems.
* Every element has the following properties:
@@ -511,14 +513,14 @@ class Component extends Node {
* * node - A reference to the problematic node.
*
* The level means:
- * 1 - The issue was repaired (only happens if REPAIR was turned on)
- * 2 - An inconsequential issue
- * 3 - A severe issue.
+ * 1 - The issue was repaired (only happens if REPAIR was turned on).
+ * 2 - A warning.
+ * 3 - An error.
*
* @param int $options
* @return array
*/
- public function validate($options = 0) {
+ function validate($options = 0) {
$rules = $this->getValidationRules();
$defaults = $this->getDefaults();
diff --git a/lib/Component/VCalendar.php b/lib/Component/VCalendar.php
index b0051a3..57584b4 100644
--- a/lib/Component/VCalendar.php
+++ b/lib/Component/VCalendar.php
@@ -27,14 +27,14 @@ class VCalendar extends VObject\Document {
*
* @var string
*/
- static public $defaultName = 'VCALENDAR';
+ static $defaultName = 'VCALENDAR';
/**
* This is a list of components, and which classes they should map to.
*
* @var array
*/
- static public $componentMap = array(
+ static $componentMap = array(
'VALARM' => 'Sabre\\VObject\\Component\\VAlarm',
'VEVENT' => 'Sabre\\VObject\\Component\\VEvent',
'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy',
@@ -48,7 +48,7 @@ class VCalendar extends VObject\Document {
*
* @var array
*/
- static public $valueMap = array(
+ static $valueMap = array(
'BINARY' => 'Sabre\\VObject\\Property\\Binary',
'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean',
'CAL-ADDRESS' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
@@ -71,7 +71,7 @@ class VCalendar extends VObject\Document {
*
* @var array
*/
- static public $propertyMap = array(
+ static $propertyMap = array(
// Calendar properties
'CALSCALE' => 'Sabre\\VObject\\Property\\FlatText',
'METHOD' => 'Sabre\\VObject\\Property\\FlatText',
@@ -150,7 +150,7 @@ class VCalendar extends VObject\Document {
*
* @return void
*/
- public function getDocumentType() {
+ function getDocumentType() {
return self::ICALENDAR20;
@@ -166,7 +166,7 @@ class VCalendar extends VObject\Document {
* @param string $componentName filter by component name
* @return VObject\Component[]
*/
- public function getBaseComponents($componentName = null) {
+ function getBaseComponents($componentName = null) {
$components = array();
foreach($this->children as $component) {
@@ -200,7 +200,7 @@ class VCalendar extends VObject\Document {
* @param string $componentName filter by component name
* @return VObject\Component|null
*/
- public function getBaseComponent($componentName = null) {
+ function getBaseComponent($componentName = null) {
foreach($this->children as $component) {
@@ -245,7 +245,7 @@ class VCalendar extends VObject\Document {
* times.
* @return void
*/
- public function expand(DateTime $start, DateTime $end, DateTimeZone $timeZone = null) {
+ function expand(DateTime $start, DateTime $end, DateTimeZone $timeZone = null) {
$newEvents = array();
@@ -355,7 +355,7 @@ class VCalendar extends VObject\Document {
*
* @var array
*/
- public function getValidationRules() {
+ function getValidationRules() {
return array(
'PRODID' => 1,
@@ -369,16 +369,28 @@ class VCalendar extends VObject\Document {
/**
* Validates the node for correctness.
- * An array is returned with warnings.
*
- * Every item in the array has the following properties:
- * * level - (number between 1 and 3 with severity information)
- * * message - (human readable message)
- * * node - (reference to the offending node)
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+ * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
*
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on).
+ * 2 - A warning.
+ * 3 - An error.
+ *
+ * @param int $options
* @return array
*/
- public function validate($options = 0) {
+ function validate($options = 0) {
$warnings = parent::validate($options);
@@ -396,6 +408,9 @@ class VCalendar extends VObject\Document {
$uidList = array();
$componentsFound = 0;
+
+ $componentTypes = [];
+
foreach($this->children as $child) {
if($child instanceof Component) {
$componentsFound++;
@@ -403,6 +418,7 @@ class VCalendar extends VObject\Document {
if (!in_array($child->name, array('VEVENT', 'VTODO', 'VJOURNAL'))) {
continue;
}
+ $componentTypes[] = $child->name;
$uid = (string)$child->UID;
$isMaster = isset($child->{'RECURRENCE-ID'})?0:1;
@@ -434,6 +450,38 @@ class VCalendar extends VObject\Document {
);
}
+ if ($options & self::PROFILE_CALDAV) {
+ if (count($uidList)>1) {
+ $warnings[] = array(
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server may only have components with the same UID.',
+ 'node' => $this,
+ );
+ }
+ if (count(array_unique($componentTypes))===0) {
+ $warnings[] = array(
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL).',
+ 'node' => $this,
+ );
+ }
+ if (count(array_unique($componentTypes))>1) {
+ $warnings[] = array(
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL).',
+ 'node' => $this,
+ );
+ }
+
+ if (isset($this->METHOD)) {
+ $warnings[] = array(
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server MUST NOT have a METHOD property.',
+ 'node' => $this,
+ );
+ }
+ }
+
return $warnings;
}
diff --git a/lib/Component/VCard.php b/lib/Component/VCard.php
index 9285188..e468801 100644
--- a/lib/Component/VCard.php
+++ b/lib/Component/VCard.php
@@ -24,7 +24,7 @@ class VCard extends VObject\Document {
*
* @var string
*/
- static public $defaultName = 'VCARD';
+ static $defaultName = 'VCARD';
/**
* Caching the version number
@@ -38,7 +38,7 @@ class VCard extends VObject\Document {
*
* @var array
*/
- static public $valueMap = array(
+ static $valueMap = array(
'BINARY' => 'Sabre\\VObject\\Property\\Binary',
'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean',
'CONTENT-ID' => 'Sabre\\VObject\\Property\\FlatText', // vCard 2.1 only
@@ -62,7 +62,7 @@ class VCard extends VObject\Document {
*
* @var array
*/
- static public $propertyMap = array(
+ static $propertyMap = array(
// vCard 2.1 properties and up
'N' => 'Sabre\\VObject\\Property\\Text',
@@ -131,7 +131,7 @@ class VCard extends VObject\Document {
*
* @return void
*/
- public function getDocumentType() {
+ function getDocumentType() {
if (!$this->version) {
$version = (string)$this->VERSION;
@@ -169,7 +169,7 @@ class VCard extends VObject\Document {
* @param int $target
* @return VCard
*/
- public function convert($target) {
+ function convert($target) {
$converter = new VObject\VCardConverter();
return $converter->convert($this, $target);
@@ -204,7 +204,7 @@ class VCard extends VObject\Document {
* @param int $options
* @return array
*/
- public function validate($options = 0) {
+ function validate($options = 0) {
$warnings = array();
@@ -227,17 +227,37 @@ class VCard extends VObject\Document {
$this->VERSION = $versionMap[self::DEFAULT_VERSION];
}
}
+ if ($version === '2.1' && ($options & self::PROFILE_CARDDAV)) {
+ $warnings[] = array(
+ 'level' => 3,
+ 'message' => 'CardDAV servers are not allowed to accept vCard 2.1.',
+ 'node' => $this,
+ );
+ }
}
$uid = $this->select('UID');
- if (($options & self::REPAIR) && count($uid) === 0) {
- $this->UID = VObject\UUIDUtil::getUUID();
+ if (count($uid) === 0) {
+ if ($options & self::PROFILE_CARDDAV) {
+ // Required for CardDAV
+ $warningLevel = 3;
+ $message = 'vCards on CardDAV servers MUST have a UID property.';
+ } else {
+ // Not required for regular vcards
+ $warningLevel = 2;
+ $message = 'Adding a UID to a vCard property is recommended.';
+ }
+ if ($options & self::REPAIR) {
+ $this->UID = VObject\UUIDUtil::getUUID();
+ $warningLevel = 1;
+ }
$warnings[] = array(
- 'level' => 1,
- 'message' => 'The UID property must appear in the VCARD component exactly 1 time',
+ 'level' => $warningLevel,
+ 'message' => $message,
'node' => $this,
);
}
+
$fn = $this->select('FN');
if (count($fn)!==1) {
@@ -289,7 +309,7 @@ class VCard extends VObject\Document {
*
* @var array
*/
- public function getValidationRules() {
+ function getValidationRules() {
return array(
'ADR' => '*',
@@ -331,16 +351,7 @@ class VCard extends VObject\Document {
// validate function, which may also try to repair it.
// 'FN' => '+',
- // vcard actually specifies this as '?', but in most cases not
- // having a UID is highly undesirable. So here we're going against
- // the spec and make it required.
- //
- // I would be interested to hear if this is problematic for
- // anyone, or at least a usecase where this is undesirable.
- //
- // If so, I may have to add a facility that allows us to check
- // specifically for validity in the context of 'DAV'.
- 'UID' => '1',
+ 'UID' => '?',
);
}
@@ -358,7 +369,7 @@ class VCard extends VObject\Document {
* @param string $fieldName
* @return VObject\Property|null
*/
- public function preferred($propertyName) {
+ function preferred($propertyName) {
$preferred = null;
$lastPref = 101;
@@ -401,7 +412,7 @@ class VCard extends VObject\Document {
*
* @return array
*/
- public function jsonSerialize() {
+ function jsonSerialize() {
// A vcard does not have sub-components, so we're overriding this
// method to remove that array element.
@@ -424,7 +435,7 @@ class VCard extends VObject\Document {
* @param string $propertyName
* @return string
*/
- public function getClassNameForPropertyName($propertyName) {
+ function getClassNameForPropertyName($propertyName) {
$className = parent::getClassNameForPropertyName($propertyName);
// In vCard 4, BINARY no longer exists, and we need URI instead.
diff --git a/lib/Node.php b/lib/Node.php
index c047d54..77d3dc6 100644
--- a/lib/Node.php
+++ b/lib/Node.php
@@ -13,10 +13,31 @@ abstract class Node implements \IteratorAggregate, \ArrayAccess, \Countable {
/**
* The following constants are used by the validate() method.
+ *
+ * If REPAIR is set, the validator will attempt to repair any broken data
+ * (if possible).
*/
const REPAIR = 1;
/**
+ * If this option is set, the validator will operate on the vcards on the
+ * assumption that the vcards need to be valid for CardDAV.
+ *
+ * This means for example that the UID is required, whereas it is not for
+ * regular vcards.
+ */
+ const PROFILE_CARDDAV = 2;
+
+ /**
+ * If this option is set, the validator will operate on iCalendar objects
+ * on the assumption that the vcards need to be valid for CalDAV.
+ *
+ * This means for example that calendars can only contain objects with
+ * identical component types and UIDs.
+ */
+ const PROFILE_CALDAV = 4;
+
+ /**
* Reference to the parent object, if this is not the top object.
*
* @var Node
diff --git a/tests/VObject/Component/VCalendarTest.php b/tests/VObject/Component/VCalendarTest.php
index 31f1787..9c9db42 100644
--- a/tests/VObject/Component/VCalendarTest.php
+++ b/tests/VObject/Component/VCalendarTest.php
@@ -543,4 +543,154 @@ END:VCALENDAR
$this->assertNull($result);
}
+
+ function testNoComponents() {
+
+ $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:vobject
+END:VCALENDAR
+ICS;
+
+ $this->assertValidate(
+ $input,
+ 0,
+ 3,
+ "An iCalendar object must have at least 1 component."
+ );
+
+ }
+
+ function testCalDAVNoComponents() {
+
+ $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:vobject
+BEGIN:VTIMEZONE
+TZID:America/Toronto
+END:VTIMEZONE
+END:VCALENDAR
+ICS;
+
+ $this->assertValidate(
+ $input,
+ VCalendar::PROFILE_CALDAV,
+ 3,
+ "A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL)."
+ );
+
+ }
+
+ function testCalDAVMultiUID() {
+
+ $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:vobject
+BEGIN:VEVENT
+UID:foo
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VEVENT
+BEGIN:VEVENT
+UID:bar
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+ $this->assertValidate(
+ $input,
+ VCalendar::PROFILE_CALDAV,
+ 3,
+ "A calendar object on a CalDAV server may only have components with the same UID."
+ );
+
+ }
+
+ function testCalDAVMultiComponent() {
+
+ $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:vobject
+BEGIN:VEVENT
+UID:foo
+RECURRENCE-ID:20150109T185200Z
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VEVENT
+BEGIN:VTODO
+UID:foo
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VTODO
+END:VCALENDAR
+ICS;
+
+ $this->assertValidate(
+ $input,
+ VCalendar::PROFILE_CALDAV,
+ 3,
+ "A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL)."
+ );
+
+ }
+
+ function testCalDAVMETHOD() {
+
+ $input = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+METHOD:PUBLISH
+PRODID:vobject
+BEGIN:VEVENT
+UID:foo
+RECURRENCE-ID:20150109T185200Z
+DTSTAMP:20150109T184500Z
+DTSTART:20150109T184500Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+ $this->assertValidate(
+ $input,
+ VCalendar::PROFILE_CALDAV,
+ 3,
+ "A calendar object on a CalDAV server MUST NOT have a METHOD property."
+ );
+
+ }
+
+ function assertValidate($ics, $options, $expectedLevel, $expectedMessage = null) {
+
+ $vcal = VObject\Reader::read($ics);
+ $result = $vcal->validate($options);
+
+ $this->assertValidateResult($result, $expectedLevel, $expectedMessage);
+
+ }
+
+ function assertValidateResult($input, $expectedLevel, $expectedMessage = null) {
+
+ $messages = array();
+ foreach($input as $warning) {
+ $messages[] = $warning['message'];
+ }
+
+ if ($expectedLevel === 0) {
+ $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages));
+ } else {
+ $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages));
+
+ $this->assertEquals($expectedMessage, $input[0]['message']);
+ $this->assertEquals($expectedLevel, $input[0]['level']);
+ }
+
+ }
+
+
}
diff --git a/tests/VObject/Component/VCardTest.php b/tests/VObject/Component/VCardTest.php
index 032f275..d460ddc 100644
--- a/tests/VObject/Component/VCardTest.php
+++ b/tests/VObject/Component/VCardTest.php
@@ -173,4 +173,116 @@ VCF;
$this->assertNull($vcard->preferred('EMAIL'));
}
+
+ function testNoUIDCardDAV() {
+
+ $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+FN:John Doe
+END:VCARD
+VCF;
+ $this->assertValidate(
+ $vcard,
+ VCARD::PROFILE_CARDDAV,
+ 3,
+ 'vCards on CardDAV servers MUST have a UID property.'
+ );
+
+ }
+
+ function testNoUIDNoCardDAV() {
+
+ $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+FN:John Doe
+END:VCARD
+VCF;
+ $this->assertValidate(
+ $vcard,
+ 0,
+ 2,
+ 'Adding a UID to a vCard property is recommended.'
+ );
+
+ }
+ function testNoUIDNoCardDAVRepair() {
+
+ $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:4.0
+FN:John Doe
+END:VCARD
+VCF;
+ $this->assertValidate(
+ $vcard,
+ VCARD::REPAIR,
+ 1,
+ 'Adding a UID to a vCard property is recommended.'
+ );
+
+ }
+
+ function testVCard21CardDAV() {
+
+ $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:2.1
+FN:John Doe
+UID:foo
+END:VCARD
+VCF;
+ $this->assertValidate(
+ $vcard,
+ VCARD::PROFILE_CARDDAV,
+ 3,
+ 'CardDAV servers are not allowed to accept vCard 2.1.'
+ );
+
+ }
+
+ function testVCard21NoCardDAV() {
+
+ $vcard = <<<VCF
+BEGIN:VCARD
+VERSION:2.1
+FN:John Doe
+UID:foo
+END:VCARD
+VCF;
+ $this->assertValidate(
+ $vcard,
+ 0,
+ 0
+ );
+
+ }
+
+ function assertValidate($vcf, $options, $expectedLevel, $expectedMessage = null) {
+
+ $vcal = VObject\Reader::read($vcf);
+ $result = $vcal->validate($options);
+
+ $this->assertValidateResult($result, $expectedLevel, $expectedMessage);
+
+ }
+
+ function assertValidateResult($input, $expectedLevel, $expectedMessage = null) {
+
+ $messages = array();
+ foreach($input as $warning) {
+ $messages[] = $warning['message'];
+ }
+
+ if ($expectedLevel === 0) {
+ $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages));
+ } else {
+ $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages));
+
+ $this->assertEquals($expectedMessage, $input[0]['message']);
+ $this->assertEquals($expectedLevel, $input[0]['level']);
+ }
+
+ }
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/php-sabre-vobject.git
More information about the Pkg-owncloud-commits
mailing list