mirror of https://github.com/nextcloud/calendar
503 lines
12 KiB
PHP
503 lines
12 KiB
PHP
<?php
|
|
|
|
namespace OCA\Calendar\Sabre\VObject;
|
|
|
|
/**
|
|
* Property
|
|
*
|
|
* A property is always in a KEY:VALUE structure, and may optionally contain
|
|
* parameters.
|
|
*
|
|
* @copyright Copyright (C) 2007-2013 fruux GmbH. All rights reserved.
|
|
* @author Evert Pot (http://evertpot.com/)
|
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
*/
|
|
abstract class Property extends Node {
|
|
|
|
/**
|
|
* Property name.
|
|
*
|
|
* This will contain a string such as DTSTART, SUMMARY, FN.
|
|
*
|
|
* @var string
|
|
*/
|
|
public $name;
|
|
|
|
/**
|
|
* Property group.
|
|
*
|
|
* This is only used in vcards
|
|
*
|
|
* @var string
|
|
*/
|
|
public $group;
|
|
|
|
/**
|
|
* List of parameters
|
|
*
|
|
* @var array
|
|
*/
|
|
public $parameters = array();
|
|
|
|
/**
|
|
* Current value
|
|
*
|
|
* @var mixed
|
|
*/
|
|
protected $value;
|
|
|
|
/**
|
|
* In case this is a multi-value property. This string will be used as a
|
|
* delimiter.
|
|
*
|
|
* @var string|null
|
|
*/
|
|
public $delimiter = ';';
|
|
|
|
/**
|
|
* Creates the generic property.
|
|
*
|
|
* Parameters must be specified in key=>value syntax.
|
|
*
|
|
* @param Component $root The root document
|
|
* @param string $name
|
|
* @param string|array|null $value
|
|
* @param array $parameters List of parameters
|
|
* @param string $group The vcard property group
|
|
* @return void
|
|
*/
|
|
public function __construct(Component $root, $name, $value = null, array $parameters = array(), $group = null) {
|
|
|
|
$this->name = $name;
|
|
$this->group = $group;
|
|
|
|
$this->root = $root;
|
|
|
|
if (!is_null($value)) {
|
|
$this->setValue($value);
|
|
}
|
|
|
|
foreach($parameters as $k=>$v) {
|
|
$this->add($k, $v);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Updates the current value.
|
|
*
|
|
* This may be either a single, or multiple strings in an array.
|
|
*
|
|
* @param string|array $value
|
|
* @return void
|
|
*/
|
|
public function setValue($value) {
|
|
|
|
$this->value = $value;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns the current value.
|
|
*
|
|
* This method will always return a singular value. If this was a
|
|
* multi-value object, some decision will be made first on how to represent
|
|
* it as a string.
|
|
*
|
|
* To get the correct multi-value version, use getParts.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getValue() {
|
|
|
|
if (is_array($this->value)) {
|
|
if (count($this->value)==0) {
|
|
return null;
|
|
} elseif (count($this->value)===1) {
|
|
return $this->value[0];
|
|
} else {
|
|
return $this->getRawMimeDirValue($this->value);
|
|
}
|
|
} else {
|
|
return $this->value;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Sets a multi-valued property.
|
|
*
|
|
* @param array $parts
|
|
* @return void
|
|
*/
|
|
public function setParts(array $parts) {
|
|
|
|
$this->value = $parts;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns a multi-valued property.
|
|
*
|
|
* This method always returns an array, if there was only a single value,
|
|
* it will still be wrapped in an array.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getParts() {
|
|
|
|
if (is_null($this->value)) {
|
|
return array();
|
|
} elseif (is_array($this->value)) {
|
|
return $this->value;
|
|
} else {
|
|
return array($this->value);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Adds a new parameter, and returns the new item.
|
|
*
|
|
* If a parameter with same name already existed, the values will be
|
|
* combined.
|
|
* If nameless parameter is added, we try to guess it's name.
|
|
*
|
|
* @param string $name
|
|
* @param string|null|array $value
|
|
* @return Node
|
|
*/
|
|
public function add($name, $value = null) {
|
|
$noName = false;
|
|
if ($name === null) {
|
|
$name = Parameter::guessParameterNameByValue($value);
|
|
$noName = true;
|
|
}
|
|
|
|
if (isset($this->parameters[strtoupper($name)])) {
|
|
$this->parameters[strtoupper($name)]->addValue($value);
|
|
}
|
|
else {
|
|
$param = new Parameter($this->root, $name, $value);
|
|
$param->noName = $noName;
|
|
$this->parameters[$param->name] = $param;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns an iterable list of children
|
|
*
|
|
* @return array
|
|
*/
|
|
public function parameters() {
|
|
|
|
return $this->parameters;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns the type of value.
|
|
*
|
|
* This corresponds to the VALUE= parameter. Every property also has a
|
|
* 'default' valueType.
|
|
*
|
|
* @return string
|
|
*/
|
|
abstract public function getValueType();
|
|
|
|
/**
|
|
* Sets a raw value coming from a mimedir (iCalendar/vCard) file.
|
|
*
|
|
* This has been 'unfolded', so only 1 line will be passed. Unescaping is
|
|
* not yet done, but parameters are not included.
|
|
*
|
|
* @param string $val
|
|
* @return void
|
|
*/
|
|
abstract public function setRawMimeDirValue($val);
|
|
|
|
/**
|
|
* Returns a raw mime-dir representation of the value.
|
|
*
|
|
* @return string
|
|
*/
|
|
abstract public function getRawMimeDirValue();
|
|
|
|
/**
|
|
* Turns the object back into a serialized blob.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function serialize() {
|
|
|
|
$str = $this->name;
|
|
if ($this->group) $str = $this->group . '.' . $this->name;
|
|
|
|
foreach($this->parameters as $param) {
|
|
|
|
$str.=';' . $param->serialize();
|
|
|
|
}
|
|
|
|
$str.=':' . $this->getRawMimeDirValue();
|
|
|
|
$out = '';
|
|
while(strlen($str)>0) {
|
|
if (strlen($str)>75) {
|
|
$out.= mb_strcut($str,0,75,'utf-8') . "\r\n";
|
|
$str = ' ' . mb_strcut($str,75,strlen($str),'utf-8');
|
|
} else {
|
|
$out.=$str . "\r\n";
|
|
$str='';
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns the value, in the format it should be encoded for json.
|
|
*
|
|
* This method must always return an array.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getJsonValue() {
|
|
|
|
return $this->getParts();
|
|
|
|
}
|
|
|
|
/**
|
|
* Sets the json value, as it would appear in a jCard or jCal object.
|
|
*
|
|
* The value must always be an array.
|
|
*
|
|
* @param array $value
|
|
* @return void
|
|
*/
|
|
public function setJsonValue(array $value) {
|
|
|
|
if (count($value)===1) {
|
|
$this->setValue(reset($value));
|
|
} else {
|
|
$this->setValue($value);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* This method returns an array, with the representation as it should be
|
|
* encoded in json. This is used to create jCard or jCal documents.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function jsonSerialize() {
|
|
|
|
$parameters = array();
|
|
|
|
foreach($this->parameters as $parameter) {
|
|
if ($parameter->name === 'VALUE') {
|
|
continue;
|
|
}
|
|
$parameters[strtolower($parameter->name)] = $parameter->jsonSerialize();
|
|
}
|
|
// In jCard, we need to encode the property-group as a separate 'group'
|
|
// parameter.
|
|
if ($this->group) {
|
|
$parameters['group'] = $this->group;
|
|
}
|
|
|
|
return array_merge(
|
|
array(
|
|
strtolower($this->name),
|
|
(object)$parameters,
|
|
strtolower($this->getValueType()),
|
|
),
|
|
$this->getJsonValue()
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Called when this object is being cast to a string.
|
|
*
|
|
* If the property only had a single value, you will get just that. In the
|
|
* case the property had multiple values, the contents will be escaped and
|
|
* combined with ,.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function __toString() {
|
|
|
|
return (string)$this->getValue();
|
|
|
|
}
|
|
|
|
/* ArrayAccess interface {{{ */
|
|
|
|
/**
|
|
* Checks if an array element exists
|
|
*
|
|
* @param mixed $name
|
|
* @return bool
|
|
*/
|
|
public function offsetExists($name) {
|
|
|
|
if (is_int($name)) return parent::offsetExists($name);
|
|
|
|
$name = strtoupper($name);
|
|
|
|
foreach($this->parameters as $parameter) {
|
|
if ($parameter->name == $name) return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns a parameter.
|
|
*
|
|
* If the parameter does not exist, null is returned.
|
|
*
|
|
* @param string $name
|
|
* @return Node
|
|
*/
|
|
public function offsetGet($name) {
|
|
|
|
if (is_int($name)) return parent::offsetGet($name);
|
|
$name = strtoupper($name);
|
|
|
|
if (!isset($this->parameters[$name])) {
|
|
return null;
|
|
}
|
|
|
|
return $this->parameters[$name];
|
|
|
|
}
|
|
|
|
/**
|
|
* Creates a new parameter
|
|
*
|
|
* @param string $name
|
|
* @param mixed $value
|
|
* @return void
|
|
*/
|
|
public function offsetSet($name, $value) {
|
|
|
|
if (is_int($name)) {
|
|
parent::offsetSet($name, $value);
|
|
// @codeCoverageIgnoreStart
|
|
// This will never be reached, because an exception is always
|
|
// thrown.
|
|
return;
|
|
// @codeCoverageIgnoreEnd
|
|
}
|
|
|
|
$param = new Parameter($this->root, $name, $value);
|
|
$this->parameters[$param->name] = $param;
|
|
|
|
}
|
|
|
|
/**
|
|
* Removes one or more parameters with the specified name
|
|
*
|
|
* @param string $name
|
|
* @return void
|
|
*/
|
|
public function offsetUnset($name) {
|
|
|
|
if (is_int($name)) {
|
|
parent::offsetUnset($name);
|
|
// @codeCoverageIgnoreStart
|
|
// This will never be reached, because an exception is always
|
|
// thrown.
|
|
return;
|
|
// @codeCoverageIgnoreEnd
|
|
}
|
|
|
|
unset($this->parameters[strtoupper($name)]);
|
|
|
|
}
|
|
/* }}} */
|
|
|
|
/**
|
|
* This method is automatically called when the object is cloned.
|
|
* Specifically, this will ensure all child elements are also cloned.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __clone() {
|
|
|
|
foreach($this->parameters as $key=>$child) {
|
|
$this->parameters[$key] = clone $child;
|
|
$this->parameters[$key]->parent = $this;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Validates the node for correctness.
|
|
*
|
|
* The following options are supported:
|
|
* - Node::REPAIR - If something is broken, and automatic repair may
|
|
* be attempted.
|
|
*
|
|
* 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)
|
|
*
|
|
* @param int $options
|
|
* @return array
|
|
*/
|
|
public function validate($options = 0) {
|
|
|
|
$warnings = array();
|
|
|
|
// Checking if our value is UTF-8
|
|
if (!StringUtil::isUTF8($this->getRawMimeDirValue())) {
|
|
$warnings[] = array(
|
|
'level' => 1,
|
|
'message' => 'Property is not valid UTF-8!',
|
|
'node' => $this,
|
|
);
|
|
if ($options & self::REPAIR) {
|
|
$this->setRawMimeDirValue(StringUtil::convertToUTF8($this->getRawMimeDirValue()));
|
|
}
|
|
}
|
|
|
|
// Checking if the propertyname does not contain any invalid bytes.
|
|
if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) {
|
|
$warnings[] = array(
|
|
'level' => 1,
|
|
'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed',
|
|
'node' => $this,
|
|
);
|
|
if ($options & self::REPAIR) {
|
|
// Uppercasing and converting underscores to dashes.
|
|
$this->name = strtoupper(
|
|
str_replace('_', '-', $this->name)
|
|
);
|
|
// Removing every other invalid character
|
|
$this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Validating inner parameters
|
|
foreach($this->parameters as $param) {
|
|
$warnings = array_merge($warnings, $param->validate($options));
|
|
}
|
|
|
|
return $warnings;
|
|
|
|
}
|
|
|
|
}
|