mirror of https://github.com/nextcloud/calendar
initial commit
This commit is contained in:
commit
99f2e03b6d
|
@ -0,0 +1,735 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
use
|
||||
InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* This is the CLI interface for sabre-vobject.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Cli {
|
||||
|
||||
/**
|
||||
* No output
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $quiet = false;
|
||||
|
||||
/**
|
||||
* Help display
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $showHelp = false;
|
||||
|
||||
/**
|
||||
* Wether to spit out 'mimedir' or 'json' format.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $format;
|
||||
|
||||
/**
|
||||
* JSON pretty print
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $pretty;
|
||||
|
||||
/**
|
||||
* Source file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $inputPath;
|
||||
|
||||
/**
|
||||
* Destination file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $outputPath;
|
||||
|
||||
/**
|
||||
* output stream
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $stdout;
|
||||
|
||||
/**
|
||||
* stdin
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $stdin;
|
||||
|
||||
/**
|
||||
* stderr
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $stderr;
|
||||
|
||||
/**
|
||||
* Input format (one of json or mimedir)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $inputFormat;
|
||||
|
||||
/**
|
||||
* Makes the parser less strict.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $forgiving = false;
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function main(array $argv) {
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
// We cannot easily test this, so we'll skip it. Pretty basic anyway.
|
||||
|
||||
if (!$this->stderr) {
|
||||
$this->stderr = STDERR;
|
||||
}
|
||||
if (!$this->stdout) {
|
||||
$this->stdout = STDOUT;
|
||||
}
|
||||
if (!$this->stdin) {
|
||||
$this->stdin = STDIN;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
|
||||
try {
|
||||
|
||||
list($options, $positional) = $this->parseArguments($argv);
|
||||
|
||||
if (isset($options['q'])) {
|
||||
$this->quiet = true;
|
||||
}
|
||||
$this->log($this->colorize('green', "sabre-vobject ") . $this->colorize('yellow', Version::VERSION));
|
||||
|
||||
foreach($options as $name=>$value) {
|
||||
|
||||
switch($name) {
|
||||
|
||||
case 'q' :
|
||||
// Already handled earlier.
|
||||
break;
|
||||
case 'h' :
|
||||
case 'help' :
|
||||
$this->showHelp();
|
||||
return 0;
|
||||
break;
|
||||
case 'format' :
|
||||
switch($value) {
|
||||
|
||||
// jcard/jcal documents
|
||||
case 'jcard' :
|
||||
case 'jcal' :
|
||||
|
||||
// specific document versions
|
||||
case 'vcard21' :
|
||||
case 'vcard30' :
|
||||
case 'vcard40' :
|
||||
case 'icalendar20' :
|
||||
|
||||
// specific formats
|
||||
case 'json' :
|
||||
case 'mimedir' :
|
||||
|
||||
// icalendar/vcad
|
||||
case 'icalendar' :
|
||||
case 'vcard' :
|
||||
$this->format = $value;
|
||||
break;
|
||||
|
||||
default :
|
||||
throw new InvalidArgumentException('Unknown format: ' . $value);
|
||||
|
||||
}
|
||||
break;
|
||||
case 'pretty' :
|
||||
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
|
||||
$this->pretty = true;
|
||||
}
|
||||
break;
|
||||
case 'forgiving' :
|
||||
$this->forgiving = true;
|
||||
break;
|
||||
case 'inputformat' :
|
||||
switch($value) {
|
||||
// json formats
|
||||
case 'jcard' :
|
||||
case 'jcal' :
|
||||
case 'json' :
|
||||
$this->inputFormat = 'json';
|
||||
break;
|
||||
|
||||
// mimedir formats
|
||||
case 'mimedir' :
|
||||
case 'icalendar' :
|
||||
case 'vcard' :
|
||||
case 'vcard21' :
|
||||
case 'vcard30' :
|
||||
case 'vcard40' :
|
||||
case 'icalendar20' :
|
||||
|
||||
$this->inputFormat = 'mimedir';
|
||||
break;
|
||||
|
||||
default :
|
||||
throw new InvalidArgumentException('Unknown format: ' . $value);
|
||||
|
||||
}
|
||||
break;
|
||||
default :
|
||||
throw new InvalidArgumentException('Unknown option: ' . $name);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (count($positional) === 0) {
|
||||
$this->showHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (count($positional) === 1) {
|
||||
throw new InvalidArgumentException('Inputfile is a required argument');
|
||||
}
|
||||
|
||||
if (count($positional) > 3) {
|
||||
throw new InvalidArgumentException('Too many arguments');
|
||||
}
|
||||
|
||||
if (!in_array($positional[0], array('validate','repair','convert','color'))) {
|
||||
throw new InvalidArgumentException('Uknown command: ' . $positional[0]);
|
||||
}
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->showHelp();
|
||||
$this->log('Error: ' . $e->getMessage(),'red');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$command = $positional[0];
|
||||
|
||||
$this->inputPath = $positional[1];
|
||||
$this->outputPath = isset($positional[2])?$positional[2]:'-';
|
||||
|
||||
if ($this->outputPath !== '-') {
|
||||
$this->stdout = fopen($this->outputPath,'w');
|
||||
}
|
||||
|
||||
if (!$this->inputFormat) {
|
||||
if (substr($this->inputPath,-5)==='.json') {
|
||||
$this->inputFormat = 'json';
|
||||
} else {
|
||||
$this->inputFormat = 'mimedir';
|
||||
}
|
||||
}
|
||||
if (!$this->format) {
|
||||
if (substr($this->outputPath,-5)==='.json') {
|
||||
$this->format = 'json';
|
||||
} else {
|
||||
$this->format = 'mimedir';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$realCode = 0;
|
||||
|
||||
try {
|
||||
|
||||
while($input = $this->readInput()) {
|
||||
|
||||
$returnCode = $this->$command($input);
|
||||
if ($returnCode!==0) $realCode = $returnCode;
|
||||
|
||||
}
|
||||
|
||||
} catch (EofException $e) {
|
||||
// end of file
|
||||
} catch (\Exception $e) {
|
||||
$this->log('Error: ' . $e->getMessage(),'red');
|
||||
return 2;
|
||||
}
|
||||
|
||||
return $realCode;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the help message.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function showHelp() {
|
||||
|
||||
$this->log('Usage:', 'yellow');
|
||||
$this->log(" vobject [options] command [arguments]");
|
||||
$this->log('');
|
||||
$this->log('Options:', 'yellow');
|
||||
$this->log($this->colorize('green', ' -q ') . "Don't output anything.");
|
||||
$this->log($this->colorize('green', ' -help -h ') . "Display this help message.");
|
||||
$this->log($this->colorize('green', ' --format ') . "Convert to a specific format. Must be one of: vcard, vcard21,");
|
||||
$this->log($this->colorize('green', ' --forgiving ') . "Makes the parser less strict.");
|
||||
$this->log(" vcard30, vcard40, icalendar20, jcal, jcard, json, mimedir.");
|
||||
$this->log($this->colorize('green', ' --inputformat ') . "If the input format cannot be guessed from the extension, it");
|
||||
$this->log(" must be specified here.");
|
||||
// Only PHP 5.4 and up
|
||||
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
|
||||
$this->log($this->colorize('green', ' --pretty ') . "json pretty-print.");
|
||||
}
|
||||
$this->log('');
|
||||
$this->log('Commands:', 'yellow');
|
||||
$this->log($this->colorize('green', ' validate') . ' source_file Validates a file for correctness.');
|
||||
$this->log($this->colorize('green', ' repair') . ' source_file [output_file] Repairs a file.');
|
||||
$this->log($this->colorize('green', ' convert') . ' source_file [output_file] Converts a file.');
|
||||
$this->log($this->colorize('green', ' color') . ' source_file Colorize a file, useful for debbugging.');
|
||||
$this->log(<<<HELP
|
||||
|
||||
If source_file is set as '-', STDIN will be used.
|
||||
If output_file is omitted, STDOUT will be used.
|
||||
All other output is sent to STDERR.
|
||||
|
||||
HELP
|
||||
);
|
||||
|
||||
$this->log('Examples:', 'yellow');
|
||||
$this->log(' vobject convert contact.vcf contact.json');
|
||||
$this->log(' vobject convert --format=vcard40 old.vcf new.vcf');
|
||||
$this->log(' vobject convert --inputformat=json --format=mimedir - -');
|
||||
$this->log(' vobject color calendar.ics');
|
||||
$this->log('');
|
||||
$this->log('https://github.com/fruux/sabre-vobject','purple');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a VObject file
|
||||
*
|
||||
* @param Component $vObj
|
||||
* @return int
|
||||
*/
|
||||
protected function validate($vObj) {
|
||||
|
||||
$returnCode = 0;
|
||||
|
||||
switch($vObj->name) {
|
||||
case 'VCALENDAR' :
|
||||
$this->log("iCalendar: " . (string)$vObj->VERSION);
|
||||
break;
|
||||
case 'VCARD' :
|
||||
$this->log("vCard: " . (string)$vObj->VERSION);
|
||||
break;
|
||||
}
|
||||
|
||||
$warnings = $vObj->validate();
|
||||
if (!count($warnings)) {
|
||||
$this->log(" No warnings!");
|
||||
} else {
|
||||
|
||||
$returnCode = 2;
|
||||
foreach($warnings as $warn) {
|
||||
|
||||
$this->log(" " . $warn['message']);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $returnCode;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Repairs a VObject file
|
||||
*
|
||||
* @param Component $vObj
|
||||
* @return int
|
||||
*/
|
||||
protected function repair($vObj) {
|
||||
|
||||
$returnCode = 0;
|
||||
|
||||
switch($vObj->name) {
|
||||
case 'VCALENDAR' :
|
||||
$this->log("iCalendar: " . (string)$vObj->VERSION);
|
||||
break;
|
||||
case 'VCARD' :
|
||||
$this->log("vCard: " . (string)$vObj->VERSION);
|
||||
break;
|
||||
}
|
||||
|
||||
$warnings = $vObj->validate(Node::REPAIR);
|
||||
if (!count($warnings)) {
|
||||
$this->log(" No warnings!");
|
||||
} else {
|
||||
foreach($warnings as $warn) {
|
||||
|
||||
$returnCode = 2;
|
||||
$this->log(" " . $warn['message']);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
fwrite($this->stdout, $vObj->serialize());
|
||||
|
||||
return $returnCode;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a vObject file to a new format.
|
||||
*
|
||||
* @param Component $vObj
|
||||
* @return int
|
||||
*/
|
||||
protected function convert($vObj) {
|
||||
|
||||
$json = false;
|
||||
$convertVersion = null;
|
||||
$forceInput = null;
|
||||
|
||||
switch($this->format) {
|
||||
case 'json' :
|
||||
$json = true;
|
||||
if ($vObj->name === 'VCARD') {
|
||||
$convertVersion = Document::VCARD40;
|
||||
}
|
||||
break;
|
||||
case 'jcard' :
|
||||
$json = true;
|
||||
$forceInput = 'VCARD';
|
||||
$convertVersion = Document::VCARD40;
|
||||
break;
|
||||
case 'jcal' :
|
||||
$json = true;
|
||||
$forceInput = 'VCALENDAR';
|
||||
break;
|
||||
case 'mimedir' :
|
||||
case 'icalendar' :
|
||||
case 'icalendar20' :
|
||||
case 'vcard' :
|
||||
break;
|
||||
case 'vcard21' :
|
||||
$convertVersion = Document::VCARD21;
|
||||
break;
|
||||
case 'vcard30' :
|
||||
$convertVersion = Document::VCARD30;
|
||||
break;
|
||||
case 'vcard40' :
|
||||
$convertVersion = Document::VCARD40;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if ($forceInput && $vObj->name !== $forceInput) {
|
||||
throw new \Exception('You cannot convert a ' . strtolower($vObj->name) . ' to ' . $this->format);
|
||||
}
|
||||
if ($convertVersion) {
|
||||
$vObj = $vObj->convert($convertVersion);
|
||||
}
|
||||
if ($json) {
|
||||
$jsonOptions = 0;
|
||||
if ($this->pretty) {
|
||||
$jsonOptions = JSON_PRETTY_PRINT;
|
||||
}
|
||||
fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions));
|
||||
} else {
|
||||
fwrite($this->stdout, $vObj->serialize());
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorizes a file
|
||||
*
|
||||
* @param Component $vObj
|
||||
* @return int
|
||||
*/
|
||||
protected function color($vObj) {
|
||||
|
||||
fwrite($this->stdout, $this->serializeComponent($vObj));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ansi color string for a color name.
|
||||
*
|
||||
* @param string $color
|
||||
* @return string
|
||||
*/
|
||||
protected function colorize($color, $str, $resetTo = 'default') {
|
||||
|
||||
$colors = array(
|
||||
'cyan' => '1;36',
|
||||
'red' => '1;31',
|
||||
'yellow' => '1;33',
|
||||
'blue' => '0;34',
|
||||
'green' => '0;32',
|
||||
'default' => '0',
|
||||
'purple' => '0;35',
|
||||
);
|
||||
return "\033[" . $colors[$color] . 'm' . $str . "\033[".$colors[$resetTo]."m";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out a string in specific color.
|
||||
*
|
||||
* @param string $color
|
||||
* @param string $str
|
||||
* @return void
|
||||
*/
|
||||
protected function cWrite($color, $str) {
|
||||
|
||||
fwrite($this->stdout, $this->colorize($color, $str));
|
||||
|
||||
}
|
||||
|
||||
protected function serializeComponent(Component $vObj) {
|
||||
|
||||
$this->cWrite('cyan', 'BEGIN');
|
||||
$this->cWrite('red', ':');
|
||||
$this->cWrite('yellow', $vObj->name . "\n");
|
||||
|
||||
/**
|
||||
* Gives a component a 'score' for sorting purposes.
|
||||
*
|
||||
* This is solely used by the childrenSort method.
|
||||
*
|
||||
* A higher score means the item will be lower in the list.
|
||||
* To avoid score collisions, each "score category" has a reasonable
|
||||
* space to accomodate elements. The $key is added to the $score to
|
||||
* preserve the original relative order of elements.
|
||||
*
|
||||
* @param int $key
|
||||
* @param array $array
|
||||
* @return int
|
||||
*/
|
||||
$sortScore = function($key, $array) {
|
||||
|
||||
if ($array[$key] instanceof Component) {
|
||||
|
||||
// We want to encode VTIMEZONE first, this is a personal
|
||||
// preference.
|
||||
if ($array[$key]->name === 'VTIMEZONE') {
|
||||
$score=300000000;
|
||||
return $score+$key;
|
||||
} else {
|
||||
$score=400000000;
|
||||
return $score+$key;
|
||||
}
|
||||
} else {
|
||||
// Properties get encoded first
|
||||
// VCARD version 4.0 wants the VERSION property to appear first
|
||||
if ($array[$key] instanceof Property) {
|
||||
if ($array[$key]->name === 'VERSION') {
|
||||
$score=100000000;
|
||||
return $score+$key;
|
||||
} else {
|
||||
// All other properties
|
||||
$score=200000000;
|
||||
return $score+$key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$tmp = $vObj->children;
|
||||
uksort($vObj->children, function($a, $b) use ($sortScore, $tmp) {
|
||||
|
||||
$sA = $sortScore($a, $tmp);
|
||||
$sB = $sortScore($b, $tmp);
|
||||
|
||||
return $sA - $sB;
|
||||
|
||||
});
|
||||
|
||||
foreach($vObj->children as $child) {
|
||||
if ($child instanceof Component) {
|
||||
$this->serializeComponent($child);
|
||||
} else {
|
||||
$this->serializeProperty($child);
|
||||
}
|
||||
}
|
||||
|
||||
$this->cWrite('cyan', 'END');
|
||||
$this->cWrite('red', ':');
|
||||
$this->cWrite('yellow', $vObj->name . "\n");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorizes a property.
|
||||
*
|
||||
* @param Property $property
|
||||
* @return void
|
||||
*/
|
||||
protected function serializeProperty(Property $property) {
|
||||
|
||||
if ($property->group) {
|
||||
$this->cWrite('default', $property->group);
|
||||
$this->cWrite('red', '.');
|
||||
}
|
||||
|
||||
$str = '';
|
||||
$this->cWrite('yellow', $property->name);
|
||||
|
||||
foreach($property->parameters as $param) {
|
||||
|
||||
$this->cWrite('red',';');
|
||||
$this->cWrite('blue', $param->serialize());
|
||||
|
||||
}
|
||||
$this->cWrite('red',':');
|
||||
|
||||
if ($property instanceof Property\Binary) {
|
||||
|
||||
$this->cWrite('default', 'embedded binary stripped. (' . strlen($property->getValue()) . ' bytes)');
|
||||
|
||||
} else {
|
||||
|
||||
$parts = $property->getParts();
|
||||
$first1 = true;
|
||||
// Looping through property values
|
||||
foreach($parts as $part) {
|
||||
if ($first1) {
|
||||
$first1 = false;
|
||||
} else {
|
||||
$this->cWrite('red', $property->delimiter);
|
||||
}
|
||||
$first2 = true;
|
||||
// Looping through property sub-values
|
||||
foreach((array)$part as $subPart) {
|
||||
if ($first2) {
|
||||
$first2 = false;
|
||||
} else {
|
||||
// The sub-value delimiter is always comma
|
||||
$this->cWrite('red', ',');
|
||||
}
|
||||
|
||||
$subPart = strtr($subPart, array(
|
||||
'\\' => $this->colorize('purple', '\\\\', 'green'),
|
||||
';' => $this->colorize('purple', '\;', 'green'),
|
||||
',' => $this->colorize('purple', '\,', 'green'),
|
||||
"\n" => $this->colorize('purple', "\\n\n\t", 'green'),
|
||||
"\r" => "",
|
||||
));
|
||||
|
||||
$this->cWrite('green', $subPart);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$this->cWrite("default", "\n");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the list of arguments.
|
||||
*
|
||||
* @param array $argv
|
||||
* @return void
|
||||
*/
|
||||
protected function parseArguments(array $argv) {
|
||||
|
||||
$positional = array();
|
||||
$options = array();
|
||||
|
||||
for($ii=0; $ii < count($argv); $ii++) {
|
||||
|
||||
// Skipping the first argument.
|
||||
if ($ii===0) continue;
|
||||
|
||||
$v = $argv[$ii];
|
||||
|
||||
if (substr($v,0,2)==='--') {
|
||||
// This is a long-form option.
|
||||
$optionName = substr($v,2);
|
||||
$optionValue = true;
|
||||
if (strpos($optionName,'=')) {
|
||||
list($optionName, $optionValue) = explode('=', $optionName);
|
||||
}
|
||||
$options[$optionName] = $optionValue;
|
||||
} elseif (substr($v,0,1) === '-' && strlen($v)>1) {
|
||||
// This is a short-form option.
|
||||
foreach(str_split(substr($v,1)) as $option) {
|
||||
$options[$option] = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$positional[] = $v;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array($options, $positional);
|
||||
|
||||
}
|
||||
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Reads the input file
|
||||
*
|
||||
* @return Component
|
||||
*/
|
||||
protected function readInput() {
|
||||
|
||||
if (!$this->parser) {
|
||||
if ($this->inputPath!=='-') {
|
||||
$this->stdin = fopen($this->inputPath,'r');
|
||||
}
|
||||
|
||||
if ($this->inputFormat === 'mimedir') {
|
||||
$this->parser = new Parser\MimeDir($this->stdin, ($this->forgiving?Reader::OPTION_FORGIVING:0));
|
||||
} else {
|
||||
$this->parser = new Parser\Json($this->stdin, ($this->forgiving?Reader::OPTION_FORGIVING:0));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->parser->parse();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to STDERR.
|
||||
*
|
||||
* @param string $msg
|
||||
* @return void
|
||||
*/
|
||||
protected function log($msg, $color = 'default') {
|
||||
|
||||
if (!$this->quiet) {
|
||||
if ($color!=='default') {
|
||||
$msg = $this->colorize($color, $msg);
|
||||
}
|
||||
fwrite($this->stderr, $msg . "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,473 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Component
|
||||
*
|
||||
* A component represents a group of properties, such as VCALENDAR, VEVENT, or
|
||||
* VCARD.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Component extends Node {
|
||||
|
||||
/**
|
||||
* Component name.
|
||||
*
|
||||
* This will contain a string such as VEVENT, VTODO, VCALENDAR, VCARD.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* A list of properties and/or sub-components.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $children = array();
|
||||
|
||||
/**
|
||||
* Creates a new component.
|
||||
*
|
||||
* You can specify the children either in key=>value syntax, in which case
|
||||
* properties will automatically be created, or you can just pass a list of
|
||||
* Component and Property object.
|
||||
*
|
||||
* By default, a set of sensible values will be added to the component. For
|
||||
* an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
|
||||
* ensure that this does not happen, set $defaults to false.
|
||||
*
|
||||
* @param Document $root
|
||||
* @param string $name such as VCALENDAR, VEVENT.
|
||||
* @param array $children
|
||||
* @param bool $defaults
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Document $root, $name, array $children = array(), $defaults = true) {
|
||||
|
||||
$this->name = strtoupper($name);
|
||||
$this->root = $root;
|
||||
|
||||
if ($defaults) {
|
||||
$children = array_merge($this->getDefaults(), $children);
|
||||
}
|
||||
|
||||
foreach($children as $k=>$child) {
|
||||
if ($child instanceof Node) {
|
||||
|
||||
// Component or Property
|
||||
$this->add($child);
|
||||
} else {
|
||||
|
||||
// Property key=>value
|
||||
$this->add($k, $child);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new property or component, and returns the new item.
|
||||
*
|
||||
* This method has 3 possible signatures:
|
||||
*
|
||||
* add(Component $comp) // Adds a new component
|
||||
* add(Property $prop) // Adds a new property
|
||||
* add($name, $value, array $parameters = array()) // Adds a new property
|
||||
* add($name, array $children = array()) // Adds a new component
|
||||
* by name.
|
||||
*
|
||||
* @return Node
|
||||
*/
|
||||
public function add($a1, $a2 = null, $a3 = null) {
|
||||
|
||||
if ($a1 instanceof Node) {
|
||||
if (!is_null($a2)) {
|
||||
throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node');
|
||||
}
|
||||
$a1->parent = $this;
|
||||
$this->children[] = $a1;
|
||||
|
||||
return $a1;
|
||||
|
||||
} elseif(is_string($a1)) {
|
||||
|
||||
$item = $this->root->create($a1, $a2, $a3);
|
||||
$item->parent = $this;
|
||||
$this->children[] = $item;
|
||||
|
||||
return $item;
|
||||
|
||||
} else {
|
||||
|
||||
throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes a component or property from this component.
|
||||
*
|
||||
* You can either specify the item by name (like DTSTART), in which case
|
||||
* all properties/components with that name will be removed, or you can
|
||||
* pass an instance of a property or component, in which case only that
|
||||
* exact item will be removed.
|
||||
*
|
||||
* The removed item will be returned. In case there were more than 1 items
|
||||
* removed, only the last one will be returned.
|
||||
*
|
||||
* @param mixed $item
|
||||
* @return void
|
||||
*/
|
||||
public function remove($item) {
|
||||
|
||||
if (is_string($item)) {
|
||||
$children = $this->select($item);
|
||||
foreach($children as $k=>$child) {
|
||||
unset($this->children[$k]);
|
||||
}
|
||||
return $child;
|
||||
} else {
|
||||
foreach($this->children as $k => $child) {
|
||||
if ($child===$item) {
|
||||
unset($this->children[$k]);
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterable list of children
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function children() {
|
||||
|
||||
return $this->children;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method only returns a list of sub-components. Properties are
|
||||
* ignored.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getComponents() {
|
||||
|
||||
$result = array();
|
||||
foreach($this->children as $child) {
|
||||
if ($child instanceof Component) {
|
||||
$result[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with elements that match the specified name.
|
||||
*
|
||||
* This function is also aware of MIME-Directory groups (as they appear in
|
||||
* vcards). This means that if a property is grouped as "HOME.EMAIL", it
|
||||
* will also be returned when searching for just "EMAIL". If you want to
|
||||
* search for a property in a specific group, you can select on the entire
|
||||
* string ("HOME.EMAIL"). If you want to search on a specific property that
|
||||
* has not been assigned a group, specify ".EMAIL".
|
||||
*
|
||||
* Keys are retained from the 'children' array, which may be confusing in
|
||||
* certain cases.
|
||||
*
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function select($name) {
|
||||
|
||||
$group = null;
|
||||
$name = strtoupper($name);
|
||||
if (strpos($name,'.')!==false) {
|
||||
list($group,$name) = explode('.', $name, 2);
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach($this->children as $key=>$child) {
|
||||
|
||||
if (
|
||||
strtoupper($child->name) === $name &&
|
||||
(is_null($group) || ( $child instanceof Property && strtoupper($child->group) === $group))
|
||||
) {
|
||||
|
||||
$result[$key] = $child;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
reset($result);
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the object back into a serialized blob.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize() {
|
||||
|
||||
$str = "BEGIN:" . $this->name . "\r\n";
|
||||
|
||||
/**
|
||||
* Gives a component a 'score' for sorting purposes.
|
||||
*
|
||||
* This is solely used by the childrenSort method.
|
||||
*
|
||||
* A higher score means the item will be lower in the list.
|
||||
* To avoid score collisions, each "score category" has a reasonable
|
||||
* space to accomodate elements. The $key is added to the $score to
|
||||
* preserve the original relative order of elements.
|
||||
*
|
||||
* @param int $key
|
||||
* @param array $array
|
||||
* @return int
|
||||
*/
|
||||
$sortScore = function($key, $array) {
|
||||
|
||||
if ($array[$key] instanceof Component) {
|
||||
|
||||
// We want to encode VTIMEZONE first, this is a personal
|
||||
// preference.
|
||||
if ($array[$key]->name === 'VTIMEZONE') {
|
||||
$score=300000000;
|
||||
return $score+$key;
|
||||
} else {
|
||||
$score=400000000;
|
||||
return $score+$key;
|
||||
}
|
||||
} else {
|
||||
// Properties get encoded first
|
||||
// VCARD version 4.0 wants the VERSION property to appear first
|
||||
if ($array[$key] instanceof Property) {
|
||||
if ($array[$key]->name === 'VERSION') {
|
||||
$score=100000000;
|
||||
return $score+$key;
|
||||
} else {
|
||||
// All other properties
|
||||
$score=200000000;
|
||||
return $score+$key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$tmp = $this->children;
|
||||
uksort($this->children, function($a, $b) use ($sortScore, $tmp) {
|
||||
|
||||
$sA = $sortScore($a, $tmp);
|
||||
$sB = $sortScore($b, $tmp);
|
||||
|
||||
return $sA - $sB;
|
||||
|
||||
});
|
||||
|
||||
foreach($this->children as $child) $str.=$child->serialize();
|
||||
$str.= "END:" . $this->name . "\r\n";
|
||||
|
||||
return $str;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
$components = array();
|
||||
$properties = array();
|
||||
|
||||
foreach($this->children as $child) {
|
||||
if ($child instanceof Component) {
|
||||
$components[] = $child->jsonSerialize();
|
||||
} else {
|
||||
$properties[] = $child->jsonSerialize();
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
strtolower($this->name),
|
||||
$properties,
|
||||
$components
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should return a list of default property values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaults() {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/* Magic property accessors {{{ */
|
||||
|
||||
/**
|
||||
* Using 'get' you will either get a property or component.
|
||||
*
|
||||
* If there were no child-elements found with the specified name,
|
||||
* null is returned.
|
||||
*
|
||||
* To use this, this may look something like this:
|
||||
*
|
||||
* $event = $calendar->VEVENT;
|
||||
*
|
||||
* @param string $name
|
||||
* @return Property
|
||||
*/
|
||||
public function __get($name) {
|
||||
|
||||
$matches = $this->select($name);
|
||||
if (count($matches)===0) {
|
||||
return null;
|
||||
} else {
|
||||
$firstMatch = current($matches);
|
||||
/** @var $firstMatch Property */
|
||||
$firstMatch->setIterator(new ElementList(array_values($matches)));
|
||||
return $firstMatch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if a sub-element with the specified name exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name) {
|
||||
|
||||
$matches = $this->select($name);
|
||||
return count($matches)>0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the setter method you can add properties or subcomponents
|
||||
*
|
||||
* You can either pass a Component, Property
|
||||
* object, or a string to automatically create a Property.
|
||||
*
|
||||
* If the item already exists, it will be removed. If you want to add
|
||||
* a new item with the same name, always use the add() method.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function __set($name, $value) {
|
||||
|
||||
$matches = $this->select($name);
|
||||
$overWrite = count($matches)?key($matches):null;
|
||||
|
||||
if ($value instanceof Component || $value instanceof Property) {
|
||||
$value->parent = $this;
|
||||
if (!is_null($overWrite)) {
|
||||
$this->children[$overWrite] = $value;
|
||||
} else {
|
||||
$this->children[] = $value;
|
||||
}
|
||||
} elseif (is_scalar($value) || is_array($value) || is_null($value)) {
|
||||
$property = $this->root->create($name,$value);
|
||||
$property->parent = $this;
|
||||
if (!is_null($overWrite)) {
|
||||
$this->children[$overWrite] = $property;
|
||||
} else {
|
||||
$this->children[] = $property;
|
||||
}
|
||||
} else {
|
||||
throw new \InvalidArgumentException('You must pass a \\Sabre\\VObject\\Component, \\Sabre\\VObject\\Property or scalar type');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all properties and components within this component with the
|
||||
* specified name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function __unset($name) {
|
||||
|
||||
$matches = $this->select($name);
|
||||
foreach($matches as $k=>$child) {
|
||||
|
||||
unset($this->children[$k]);
|
||||
$child->parent = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/**
|
||||
* 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->children as $key=>$child) {
|
||||
$this->children[$key] = clone $child;
|
||||
$this->children[$key]->parent = $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the node for correctness.
|
||||
*
|
||||
* The following options are supported:
|
||||
* - Node::REPAIR - If something is broken, an 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) {
|
||||
|
||||
$result = array();
|
||||
foreach($this->children as $child) {
|
||||
$result = array_merge($result, $child->validate($options));
|
||||
}
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Component;
|
||||
use OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VAlarm component
|
||||
*
|
||||
* This component contains some additional functionality specific for VALARMs.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VAlarm extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Returns a DateTime object when this alarm is going to trigger.
|
||||
*
|
||||
* This ignores repeated alarm, only the first trigger is returned.
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getEffectiveTriggerTime() {
|
||||
|
||||
$trigger = $this->TRIGGER;
|
||||
if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
|
||||
$triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER);
|
||||
$related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START';
|
||||
|
||||
$parentComponent = $this->parent;
|
||||
if ($related === 'START') {
|
||||
|
||||
if ($parentComponent->name === 'VTODO') {
|
||||
$propName = 'DUE';
|
||||
} else {
|
||||
$propName = 'DTSTART';
|
||||
}
|
||||
|
||||
$effectiveTrigger = clone $parentComponent->$propName->getDateTime();
|
||||
$effectiveTrigger->add($triggerDuration);
|
||||
} else {
|
||||
if ($parentComponent->name === 'VTODO') {
|
||||
$endProp = 'DUE';
|
||||
} elseif ($parentComponent->name === 'VEVENT') {
|
||||
$endProp = 'DTEND';
|
||||
} else {
|
||||
throw new \LogicException('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT');
|
||||
}
|
||||
|
||||
if (isset($parentComponent->$endProp)) {
|
||||
$effectiveTrigger = clone $parentComponent->$endProp->getDateTime();
|
||||
$effectiveTrigger->add($triggerDuration);
|
||||
} elseif (isset($parentComponent->DURATION)) {
|
||||
$effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
|
||||
$duration = VObject\DateTimeParser::parseDuration($parentComponent->DURATION);
|
||||
$effectiveTrigger->add($duration);
|
||||
$effectiveTrigger->add($triggerDuration);
|
||||
} else {
|
||||
$effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
|
||||
$effectiveTrigger->add($triggerDuration);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$effectiveTrigger = $trigger->getDateTime();
|
||||
}
|
||||
return $effectiveTrigger;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if the event falls in the specified
|
||||
* time-range. This is used for filtering purposes.
|
||||
*
|
||||
* The rules used to determine if an event falls within the specified
|
||||
* time-range is based on the CalDAV specification.
|
||||
*
|
||||
* @param \DateTime $start
|
||||
* @param \DateTime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isInTimeRange(\DateTime $start, \DateTime $end) {
|
||||
|
||||
$effectiveTrigger = $this->getEffectiveTriggerTime();
|
||||
|
||||
if (isset($this->DURATION)) {
|
||||
$duration = VObject\DateTimeParser::parseDuration($this->DURATION);
|
||||
$repeat = (string)$this->repeat;
|
||||
if (!$repeat) {
|
||||
$repeat = 1;
|
||||
}
|
||||
|
||||
$period = new \DatePeriod($effectiveTrigger, $duration, (int)$repeat);
|
||||
|
||||
foreach($period as $occurrence) {
|
||||
|
||||
if ($start <= $occurrence && $end > $occurrence) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return ($start <= $effectiveTrigger && $end > $effectiveTrigger);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,369 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Component;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject,
|
||||
OCA\Calendar\Sabre\VObject\Component;
|
||||
|
||||
/**
|
||||
* The VCalendar component
|
||||
*
|
||||
* This component adds functionality to a component, specific for a VCALENDAR.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VCalendar extends VObject\Document {
|
||||
|
||||
/**
|
||||
* The default name for this component.
|
||||
*
|
||||
* This should be 'VCALENDAR' or 'VCARD'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
static public $defaultName = 'VCALENDAR';
|
||||
|
||||
/**
|
||||
* This is a list of components, and which classes they should map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $componentMap = array(
|
||||
'VEVENT' => 'Sabre\\VObject\\Component\\VEvent',
|
||||
'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy',
|
||||
'VJOURNAL' => 'Sabre\\VObject\\Component\\VJournal',
|
||||
'VTODO' => 'Sabre\\VObject\\Component\\VTodo',
|
||||
'VALARM' => 'Sabre\\VObject\\Component\\VAlarm',
|
||||
);
|
||||
|
||||
/**
|
||||
* List of value-types, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $valueMap = array(
|
||||
'BINARY' => 'Sabre\\VObject\\Property\\Binary',
|
||||
'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean',
|
||||
'CAL-ADDRESS' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
|
||||
'DATE' => 'Sabre\\VObject\\Property\\ICalendar\\Date',
|
||||
'DATE-TIME' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
|
||||
'FLOAT' => 'Sabre\\VObject\\Property\\Float',
|
||||
'INTEGER' => 'Sabre\\VObject\\Property\\Integer',
|
||||
'PERIOD' => 'Sabre\\VObject\\Property\\ICalendar\\Period',
|
||||
'RECUR' => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
|
||||
'TEXT' => 'Sabre\\VObject\\Property\\Text',
|
||||
'TIME' => 'Sabre\\VObject\\Property\\Time',
|
||||
'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
|
||||
'URI' => 'Sabre\\VObject\\Property\\Uri',
|
||||
'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset',
|
||||
);
|
||||
|
||||
/**
|
||||
* List of properties, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $propertyMap = array(
|
||||
// Calendar properties
|
||||
'CALSCALE' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'METHOD' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'PRODID' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
|
||||
// Component properties
|
||||
'ATTACH' => 'Sabre\\VObject\\Property\\Binary',
|
||||
'CATEGORIES' => 'Sabre\\VObject\\Property\\Text',
|
||||
'CLASS' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'COMMENT' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'DESCRIPTION' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'GEO' => 'Sabre\\VObject\\Property\\Float',
|
||||
'LOCATION' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'PERCENT-COMPLETE' => 'Sabre\\VObject\\Property\\Integer',
|
||||
'PRIORITY' => 'Sabre\\VObject\\Property\\Integer',
|
||||
'RESOURCES' => 'Sabre\\VObject\\Property\\Text',
|
||||
'STATUS' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'SUMMARY' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
|
||||
// Date and Time Component Properties
|
||||
'COMPLETED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DTEND' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DUE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DTSTART' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
|
||||
'FREEBUSY' => 'Sabre\\VObject\\Property\\ICalendar\\Period',
|
||||
'TRANSP' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
|
||||
// Time Zone Component Properties
|
||||
'TZID' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'TZNAME' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'TZOFFSETFROM' => 'Sabre\\VObject\\Property\\UtcOffset',
|
||||
'TZOFFSETTO' => 'Sabre\\VObject\\Property\\UtcOffset',
|
||||
'TZURL' => 'Sabre\\VObject\\Property\\Uri',
|
||||
|
||||
// Relationship Component Properties
|
||||
'ATTENDEE' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
|
||||
'CONTACT' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'ORGANIZER' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
|
||||
'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'RELATED-TO' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'URL' => 'Sabre\\VObject\\Property\\Uri',
|
||||
'UID' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
|
||||
// Recurrence Component Properties
|
||||
'EXDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'RDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'RRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
|
||||
'EXRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545
|
||||
|
||||
// Alarm Component Properties
|
||||
'ACTION' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'REPEAT' => 'Sabre\\VObject\\Property\\Integer',
|
||||
'TRIGGER' => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
|
||||
|
||||
// Change Management Component Properties
|
||||
'CREATED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DTSTAMP' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'SEQUENCE' => 'Sabre\\VObject\\Property\\Integer',
|
||||
|
||||
// Request Status
|
||||
'REQUEST-STATUS' => 'Sabre\\VObject\\Property\\Text',
|
||||
|
||||
// Additions from draft-daboo-valarm-extensions-04
|
||||
'ALARM-AGENT' => 'Sabre\\VObject\\Property\\Text',
|
||||
'ACKNOWLEDGED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'PROXIMITY' => 'Sabre\\VObject\\Property\\Text',
|
||||
'DEFAULT-ALARM' => 'Sabre\\VObject\\Property\\Boolean',
|
||||
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the current document type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getDocumentType() {
|
||||
|
||||
return self::ICALENDAR20;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all 'base components'. For instance, if an Event has
|
||||
* a recurrence rule, and one instance is overridden, the overridden event
|
||||
* will have the same UID, but will be excluded from this list.
|
||||
*
|
||||
* VTIMEZONE components will always be excluded.
|
||||
*
|
||||
* @param string $componentName filter by component name
|
||||
* @return array
|
||||
*/
|
||||
public function getBaseComponents($componentName = null) {
|
||||
|
||||
$components = array();
|
||||
foreach($this->children as $component) {
|
||||
|
||||
if (!$component instanceof VObject\Component)
|
||||
continue;
|
||||
|
||||
if (isset($component->{'RECURRENCE-ID'}))
|
||||
continue;
|
||||
|
||||
if ($componentName && $component->name !== strtoupper($componentName))
|
||||
continue;
|
||||
|
||||
if ($component->name === 'VTIMEZONE')
|
||||
continue;
|
||||
|
||||
$components[] = $component;
|
||||
|
||||
}
|
||||
|
||||
return $components;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If this calendar object, has events with recurrence rules, this method
|
||||
* can be used to expand the event into multiple sub-events.
|
||||
*
|
||||
* Each event will be stripped from it's recurrence information, and only
|
||||
* the instances of the event in the specified timerange will be left
|
||||
* alone.
|
||||
*
|
||||
* In addition, this method will cause timezone information to be stripped,
|
||||
* and normalized to UTC.
|
||||
*
|
||||
* This method will alter the VCalendar. This cannot be reversed.
|
||||
*
|
||||
* This functionality is specifically used by the CalDAV standard. It is
|
||||
* possible for clients to request expand events, if they are rather simple
|
||||
* clients and do not have the possibility to calculate recurrences.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @return void
|
||||
*/
|
||||
public function expand(\DateTime $start, \DateTime $end) {
|
||||
|
||||
$newEvents = array();
|
||||
|
||||
foreach($this->select('VEVENT') as $key=>$vevent) {
|
||||
|
||||
if (isset($vevent->{'RECURRENCE-ID'})) {
|
||||
unset($this->children[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!$vevent->rrule) {
|
||||
unset($this->children[$key]);
|
||||
if ($vevent->isInTimeRange($start, $end)) {
|
||||
$newEvents[] = $vevent;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$uid = (string)$vevent->uid;
|
||||
if (!$uid) {
|
||||
throw new \LogicException('Event did not have a UID!');
|
||||
}
|
||||
|
||||
$it = new VObject\RecurrenceIterator($this, $vevent->uid);
|
||||
$it->fastForward($start);
|
||||
|
||||
while($it->valid() && $it->getDTStart() < $end) {
|
||||
|
||||
if ($it->getDTEnd() > $start) {
|
||||
|
||||
$newEvents[] = $it->getEventObject();
|
||||
|
||||
}
|
||||
$it->next();
|
||||
|
||||
}
|
||||
unset($this->children[$key]);
|
||||
|
||||
}
|
||||
|
||||
// Setting all properties to UTC time.
|
||||
foreach($newEvents as $newEvent) {
|
||||
|
||||
foreach($newEvent->children as $child) {
|
||||
if ($child instanceof VObject\Property\ICalendar\DateTime && $child->hasTime()) {
|
||||
$dt = $child->getDateTimes();
|
||||
// We only need to update the first timezone, because
|
||||
// setDateTimes will match all other timezones to the
|
||||
// first.
|
||||
$dt[0]->setTimeZone(new \DateTimeZone('UTC'));
|
||||
$child->setDateTimes($dt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->add($newEvent);
|
||||
|
||||
}
|
||||
|
||||
// Removing all VTIMEZONE components
|
||||
unset($this->VTIMEZONE);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should return a list of default property values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaults() {
|
||||
|
||||
return array(
|
||||
'VERSION' => '2.0',
|
||||
'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
|
||||
'CALSCALE' => 'GREGORIAN',
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validate($options = 0) {
|
||||
|
||||
$warnings = array();
|
||||
|
||||
$version = $this->select('VERSION');
|
||||
if (count($version)!==1) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'The VERSION property must appear in the VCALENDAR component exactly 1 time',
|
||||
'node' => $this,
|
||||
);
|
||||
} else {
|
||||
if ((string)$this->VERSION !== '2.0') {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
}
|
||||
$version = $this->select('PRODID');
|
||||
if (count($version)!==1) {
|
||||
$warnings[] = array(
|
||||
'level' => 2,
|
||||
'message' => 'The PRODID property must appear in the VCALENDAR component exactly 1 time',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
if (count($this->CALSCALE) > 1) {
|
||||
$warnings[] = array(
|
||||
'level' => 2,
|
||||
'message' => 'The CALSCALE property must not be specified more than once.',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
if (count($this->METHOD) > 1) {
|
||||
$warnings[] = array(
|
||||
'level' => 2,
|
||||
'message' => 'The METHOD property must not be specified more than once.',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
|
||||
$componentsFound = 0;
|
||||
foreach($this->children as $child) {
|
||||
if($child instanceof Component) {
|
||||
$componentsFound++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($componentsFound===0) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'An iCalendar object must have at least 1 component.',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
$warnings,
|
||||
parent::validate()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,352 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Component;
|
||||
|
||||
use OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* The VCard component
|
||||
*
|
||||
* This component represents the BEGIN:VCARD and END:VCARD found in every
|
||||
* vcard.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VCard extends VObject\Document {
|
||||
|
||||
/**
|
||||
* The default name for this component.
|
||||
*
|
||||
* This should be 'VCALENDAR' or 'VCARD'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
static public $defaultName = 'VCARD';
|
||||
|
||||
/**
|
||||
* Caching the version number
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $version = null;
|
||||
|
||||
/**
|
||||
* List of value-types, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $valueMap = array(
|
||||
'BINARY' => 'Sabre\\VObject\\Property\\Binary',
|
||||
'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean',
|
||||
'CONTENT-ID' => 'Sabre\\VObject\\Property\\FlatText', // vCard 2.1 only
|
||||
'DATE' => 'Sabre\\VObject\\Property\\VCard\\Date',
|
||||
'DATE-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateTime',
|
||||
'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only
|
||||
'FLOAT' => 'Sabre\\VObject\\Property\\Float',
|
||||
'INTEGER' => 'Sabre\\VObject\\Property\\Integer',
|
||||
'LANGUAGE-TAG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
|
||||
'TIMESTAMP' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
|
||||
'TEXT' => 'Sabre\\VObject\\Property\\Text',
|
||||
'TIME' => 'Sabre\\VObject\\Property\\Time',
|
||||
'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
|
||||
'URI' => 'Sabre\\VObject\\Property\\Uri',
|
||||
'URL' => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only
|
||||
'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset',
|
||||
);
|
||||
|
||||
/**
|
||||
* List of properties, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $propertyMap = array(
|
||||
|
||||
// vCard 2.1 properties and up
|
||||
'N' => 'Sabre\\VObject\\Property\\Text',
|
||||
'FN' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'PHOTO' => 'Sabre\\VObject\\Property\\Binary', // Todo: we should add a class for Binary values.
|
||||
'BDAY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
|
||||
'ADR' => 'Sabre\\VObject\\Property\\Text',
|
||||
'LABEL' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
|
||||
'TEL' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'EMAIL' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'MAILER' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
|
||||
'GEO' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'TITLE' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'ROLE' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'LOGO' => 'Sabre\\VObject\\Property\\Binary',
|
||||
// 'AGENT' => 'Sabre\\VObject\\Property\\', // Todo: is an embedded vCard. Probably rare, so
|
||||
// not supported at the moment
|
||||
'ORG' => 'Sabre\\VObject\\Property\\Text',
|
||||
'NOTE' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'REV' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
|
||||
'SOUND' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'URL' => 'Sabre\\VObject\\Property\\Uri',
|
||||
'UID' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'KEY' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'TZ' => 'Sabre\\VObject\\Property\\Text',
|
||||
|
||||
// vCard 3.0 properties
|
||||
'CATEGORIES' => 'Sabre\\VObject\\Property\\Text',
|
||||
'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'PRODID' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'NICKNAME' => 'Sabre\\VObject\\Property\\Text',
|
||||
'CLASS' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
|
||||
|
||||
// rfc2739 properties
|
||||
'FBURL' => 'Sabre\\VObject\\Property\\Uri',
|
||||
'CAPURI' => 'Sabre\\VObject\\Property\\Uri',
|
||||
'CALURI' => 'Sabre\\VObject\\Property\\Uri',
|
||||
|
||||
// rfc4770 properties
|
||||
'IMPP' => 'Sabre\\VObject\\Property\\Uri',
|
||||
|
||||
// vCard 4.0 properties
|
||||
'XML' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
'ANNIVERSARY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
|
||||
'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text',
|
||||
'LANG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
|
||||
'GENDER' => 'Sabre\\VObject\\Property\\Text',
|
||||
'KIND' => 'Sabre\\VObject\\Property\\FlatText',
|
||||
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the current document type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getDocumentType() {
|
||||
|
||||
if (!$this->version) {
|
||||
$version = (string)$this->VERSION;
|
||||
switch($version) {
|
||||
case '2.1' :
|
||||
$this->version = self::VCARD21;
|
||||
break;
|
||||
case '3.0' :
|
||||
$this->version = self::VCARD30;
|
||||
break;
|
||||
case '4.0' :
|
||||
$this->version = self::VCARD40;
|
||||
break;
|
||||
default :
|
||||
$this->version = self::UNKNOWN;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $this->version;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the document to a different vcard version.
|
||||
*
|
||||
* Use one of the VCARD constants for the target. This method will return
|
||||
* a copy of the vcard in the new version.
|
||||
*
|
||||
* At the moment the only supported conversion is from 3.0 to 4.0.
|
||||
*
|
||||
* If input and output version are identical, a clone is returned.
|
||||
*
|
||||
* @param int $target
|
||||
* @return VCard
|
||||
*/
|
||||
public function convert($target) {
|
||||
|
||||
$converter = new VObject\VCardConverter();
|
||||
return $converter->convert($this, $target);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* VCards with version 2.1, 3.0 and 4.0 are found.
|
||||
*
|
||||
* If the VCARD doesn't know its version, 2.1 is assumed.
|
||||
*/
|
||||
const DEFAULT_VERSION = self::VCARD21;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
$versionMap = array(
|
||||
self::VCARD21 => '2.1',
|
||||
self::VCARD30 => '3.0',
|
||||
self::VCARD40 => '4.0',
|
||||
);
|
||||
|
||||
$version = $this->select('VERSION');
|
||||
if (count($version)!==1) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'The VERSION property must appear in the VCARD component exactly 1 time',
|
||||
'node' => $this,
|
||||
);
|
||||
if ($options & self::REPAIR) {
|
||||
$this->VERSION = $versionMap[self::DEFAULT_VERSION];
|
||||
}
|
||||
} else {
|
||||
$version = (string)$this->VERSION;
|
||||
if ($version!=='2.1' && $version!=='3.0' && $version!=='4.0') {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
|
||||
'node' => $this,
|
||||
);
|
||||
if ($options & self::REPAIR) {
|
||||
$this->VERSION = $versionMap[self::DEFAULT_VERSION];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$fn = $this->select('FN');
|
||||
if (count($fn)!==1) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'The FN property must appear in the VCARD component exactly 1 time',
|
||||
'node' => $this,
|
||||
);
|
||||
if (($options & self::REPAIR) && count($fn) === 0) {
|
||||
// We're going to try to see if we can use the contents of the
|
||||
// N property.
|
||||
if (isset($this->N)) {
|
||||
$value = explode(';', (string)$this->N);
|
||||
if (isset($value[1]) && $value[1]) {
|
||||
$this->FN = $value[1] . ' ' . $value[0];
|
||||
} else {
|
||||
$this->FN = $value[0];
|
||||
}
|
||||
|
||||
// Otherwise, the ORG property may work
|
||||
} elseif (isset($this->ORG)) {
|
||||
$this->FN = (string)$this->ORG;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
parent::validate($options),
|
||||
$warnings
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a preferred field.
|
||||
*
|
||||
* VCards can indicate wether a field such as ADR, TEL or EMAIL is
|
||||
* preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x
|
||||
* being a number between 1 and 100).
|
||||
*
|
||||
* If neither of those parameters are specified, the first is returned, if
|
||||
* a field with that name does not exist, null is returned.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @return VObject\Property|null
|
||||
*/
|
||||
public function preferred($propertyName) {
|
||||
|
||||
$preferred = null;
|
||||
$lastPref = 101;
|
||||
foreach($this->select($propertyName) as $field) {
|
||||
|
||||
$pref = 101;
|
||||
if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) {
|
||||
$pref = 1;
|
||||
} elseif (isset($field['PREF'])) {
|
||||
$pref = $field['PREF']->getValue();
|
||||
}
|
||||
|
||||
if ($pref < $lastPref || is_null($preferred)) {
|
||||
$preferred = $field;
|
||||
$lastPref = $pref;
|
||||
}
|
||||
|
||||
}
|
||||
return $preferred;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should return a list of default property values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaults() {
|
||||
|
||||
return array(
|
||||
'VERSION' => '3.0',
|
||||
'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
// A vcard does not have sub-components, so we're overriding this
|
||||
// method to remove that array element.
|
||||
$properties = array();
|
||||
|
||||
foreach($this->children as $child) {
|
||||
$properties[] = $child->jsonSerialize();
|
||||
}
|
||||
|
||||
return array(
|
||||
strtolower($this->name),
|
||||
$properties,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default class for a property name.
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @return string
|
||||
*/
|
||||
public function getClassNameForPropertyName($propertyName) {
|
||||
|
||||
$className = parent::getClassNameForPropertyName($propertyName);
|
||||
// In vCard 4, BINARY no longer exists, and we need URI instead.
|
||||
|
||||
if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType()===self::VCARD40) {
|
||||
return 'Sabre\\VObject\\Property\\Uri';
|
||||
}
|
||||
return $className;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Component;
|
||||
|
||||
use OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VEvent component
|
||||
*
|
||||
* This component contains some additional functionality specific for VEVENT's.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VEvent extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if the event falls in the specified
|
||||
* time-range. This is used for filtering purposes.
|
||||
*
|
||||
* The rules used to determine if an event falls within the specified
|
||||
* time-range is based on the CalDAV specification.
|
||||
*
|
||||
* @param \DateTime $start
|
||||
* @param \DateTime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isInTimeRange(\DateTime $start, \DateTime $end) {
|
||||
|
||||
if ($this->RRULE) {
|
||||
$it = new VObject\RecurrenceIterator($this);
|
||||
$it->fastForward($start);
|
||||
|
||||
// We fast-forwarded to a spot where the end-time of the
|
||||
// recurrence instance exceeded the start of the requested
|
||||
// time-range.
|
||||
//
|
||||
// If the starttime of the recurrence did not exceed the
|
||||
// end of the time range as well, we have a match.
|
||||
return ($it->getDTStart() < $end && $it->getDTEnd() > $start);
|
||||
|
||||
}
|
||||
|
||||
$effectiveStart = $this->DTSTART->getDateTime();
|
||||
if (isset($this->DTEND)) {
|
||||
|
||||
// The DTEND property is considered non inclusive. So for a 3 day
|
||||
// event in july, dtstart and dtend would have to be July 1st and
|
||||
// July 4th respectively.
|
||||
//
|
||||
// See:
|
||||
// http://tools.ietf.org/html/rfc5545#page-54
|
||||
$effectiveEnd = $this->DTEND->getDateTime();
|
||||
|
||||
} elseif (isset($this->DURATION)) {
|
||||
$effectiveEnd = clone $effectiveStart;
|
||||
$effectiveEnd->add( VObject\DateTimeParser::parseDuration($this->DURATION) );
|
||||
} elseif (!$this->DTSTART->hasTime()) {
|
||||
$effectiveEnd = clone $effectiveStart;
|
||||
$effectiveEnd->modify('+1 day');
|
||||
} else {
|
||||
$effectiveEnd = clone $effectiveStart;
|
||||
}
|
||||
return (
|
||||
($start <= $effectiveEnd) && ($end > $effectiveStart)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Component;
|
||||
|
||||
use OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* The VFreeBusy component
|
||||
*
|
||||
* This component adds functionality to a component, specific for VFREEBUSY
|
||||
* components.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VFreeBusy extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Checks based on the contained FREEBUSY information, if a timeslot is
|
||||
* available.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param Datetime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isFree(\DateTime $start, \Datetime $end) {
|
||||
|
||||
foreach($this->select('FREEBUSY') as $freebusy) {
|
||||
|
||||
// We are only interested in FBTYPE=BUSY (the default),
|
||||
// FBTYPE=BUSY-TENTATIVE or FBTYPE=BUSY-UNAVAILABLE.
|
||||
if (isset($freebusy['FBTYPE']) && strtoupper(substr((string)$freebusy['FBTYPE'],0,4))!=='BUSY') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The freebusy component can hold more than 1 value, separated by
|
||||
// commas.
|
||||
$periods = explode(',', (string)$freebusy);
|
||||
|
||||
foreach($periods as $period) {
|
||||
// Every period is formatted as [start]/[end]. The start is an
|
||||
// absolute UTC time, the end may be an absolute UTC time, or
|
||||
// duration (relative) value.
|
||||
list($busyStart, $busyEnd) = explode('/', $period);
|
||||
|
||||
$busyStart = VObject\DateTimeParser::parse($busyStart);
|
||||
$busyEnd = VObject\DateTimeParser::parse($busyEnd);
|
||||
if ($busyEnd instanceof \DateInterval) {
|
||||
$tmp = clone $busyStart;
|
||||
$tmp->add($busyEnd);
|
||||
$busyEnd = $tmp;
|
||||
}
|
||||
|
||||
if($start < $busyEnd && $end > $busyStart) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Component;
|
||||
|
||||
use OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VJournal component
|
||||
*
|
||||
* This component contains some additional functionality specific for VJOURNALs.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VJournal extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if the event falls in the specified
|
||||
* time-range. This is used for filtering purposes.
|
||||
*
|
||||
* The rules used to determine if an event falls within the specified
|
||||
* time-range is based on the CalDAV specification.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isInTimeRange(\DateTime $start, \DateTime $end) {
|
||||
|
||||
$dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
|
||||
if ($dtstart) {
|
||||
$effectiveEnd = clone $dtstart;
|
||||
if (!$this->DTSTART->hasTime()) {
|
||||
$effectiveEnd->modify('+1 day');
|
||||
}
|
||||
|
||||
return ($start <= $effectiveEnd && $end > $dtstart);
|
||||
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Component;
|
||||
|
||||
use OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VTodo component
|
||||
*
|
||||
* This component contains some additional functionality specific for VTODOs.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VTodo extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if the event falls in the specified
|
||||
* time-range. This is used for filtering purposes.
|
||||
*
|
||||
* The rules used to determine if an event falls within the specified
|
||||
* time-range is based on the CalDAV specification.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isInTimeRange(\DateTime $start, \DateTime $end) {
|
||||
|
||||
$dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
|
||||
$duration = isset($this->DURATION)?VObject\DateTimeParser::parseDuration($this->DURATION):null;
|
||||
$due = isset($this->DUE)?$this->DUE->getDateTime():null;
|
||||
$completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null;
|
||||
$created = isset($this->CREATED)?$this->CREATED->getDateTime():null;
|
||||
|
||||
if ($dtstart) {
|
||||
if ($duration) {
|
||||
$effectiveEnd = clone $dtstart;
|
||||
$effectiveEnd->add($duration);
|
||||
return $start <= $effectiveEnd && $end > $dtstart;
|
||||
} elseif ($due) {
|
||||
return
|
||||
($start < $due || $start <= $dtstart) &&
|
||||
($end > $dtstart || $end >= $due);
|
||||
} else {
|
||||
return $start <= $dtstart && $end > $dtstart;
|
||||
}
|
||||
}
|
||||
if ($due) {
|
||||
return ($start < $due && $end >= $due);
|
||||
}
|
||||
if ($completed && $created) {
|
||||
return
|
||||
($start <= $created || $start <= $completed) &&
|
||||
($end >= $created || $end >= $completed);
|
||||
}
|
||||
if ($completed) {
|
||||
return ($start <= $completed && $end >= $completed);
|
||||
}
|
||||
if ($created) {
|
||||
return ($end > $created);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,415 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* DateTimeParser
|
||||
*
|
||||
* This class is responsible for parsing the several different date and time
|
||||
* formats iCalendar and vCards have.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class DateTimeParser {
|
||||
|
||||
/**
|
||||
* Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
|
||||
*
|
||||
* Specifying a reference timezone is optional. It will only be used
|
||||
* if the non-UTC format is used. The argument is used as a reference, the
|
||||
* returned DateTime object will still be in the UTC timezone.
|
||||
*
|
||||
* @param string $dt
|
||||
* @param DateTimeZone $tz
|
||||
* @return DateTime
|
||||
*/
|
||||
static public function parseDateTime($dt,\DateTimeZone $tz = null) {
|
||||
|
||||
// Format is YYYYMMDD + "T" + hhmmss
|
||||
$result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches);
|
||||
|
||||
if (!$result) {
|
||||
throw new \LogicException('The supplied iCalendar datetime value is incorrect: ' . $dt);
|
||||
}
|
||||
|
||||
if ($matches[7]==='Z' || is_null($tz)) {
|
||||
$tz = new \DateTimeZone('UTC');
|
||||
}
|
||||
$date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz);
|
||||
|
||||
// Still resetting the timezone, to normalize everything to UTC
|
||||
// $date->setTimeZone(new \DateTimeZone('UTC'));
|
||||
return $date;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar (rfc5545) formatted date and returns a DateTime object
|
||||
*
|
||||
* @param string $date
|
||||
* @return DateTime
|
||||
*/
|
||||
static public function parseDate($date) {
|
||||
|
||||
// Format is YYYYMMDD
|
||||
$result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])$/',$date,$matches);
|
||||
|
||||
if (!$result) {
|
||||
throw new \LogicException('The supplied iCalendar date value is incorrect: ' . $date);
|
||||
}
|
||||
|
||||
$date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new \DateTimeZone('UTC'));
|
||||
return $date;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar (RFC5545) formatted duration value.
|
||||
*
|
||||
* This method will either return a DateTimeInterval object, or a string
|
||||
* suitable for strtotime or DateTime::modify.
|
||||
*
|
||||
* @param string $duration
|
||||
* @param bool $asString
|
||||
* @return DateInterval|string
|
||||
*/
|
||||
static public function parseDuration($duration, $asString = false) {
|
||||
|
||||
$result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches);
|
||||
if (!$result) {
|
||||
throw new \LogicException('The supplied iCalendar duration value is incorrect: ' . $duration);
|
||||
}
|
||||
|
||||
if (!$asString) {
|
||||
$invert = false;
|
||||
if ($matches['plusminus']==='-') {
|
||||
$invert = true;
|
||||
}
|
||||
|
||||
|
||||
$parts = array(
|
||||
'week',
|
||||
'day',
|
||||
'hour',
|
||||
'minute',
|
||||
'second',
|
||||
);
|
||||
foreach($parts as $part) {
|
||||
$matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0;
|
||||
}
|
||||
|
||||
|
||||
// We need to re-construct the $duration string, because weeks and
|
||||
// days are not supported by DateInterval in the same string.
|
||||
$duration = 'P';
|
||||
$days = $matches['day'];
|
||||
if ($matches['week']) {
|
||||
$days+=$matches['week']*7;
|
||||
}
|
||||
if ($days)
|
||||
$duration.=$days . 'D';
|
||||
|
||||
if ($matches['minute'] || $matches['second'] || $matches['hour']) {
|
||||
$duration.='T';
|
||||
|
||||
if ($matches['hour'])
|
||||
$duration.=$matches['hour'].'H';
|
||||
|
||||
if ($matches['minute'])
|
||||
$duration.=$matches['minute'].'M';
|
||||
|
||||
if ($matches['second'])
|
||||
$duration.=$matches['second'].'S';
|
||||
|
||||
}
|
||||
|
||||
if ($duration==='P') {
|
||||
$duration = 'PT0S';
|
||||
}
|
||||
$iv = new \DateInterval($duration);
|
||||
if ($invert) $iv->invert = true;
|
||||
|
||||
return $iv;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
$parts = array(
|
||||
'week',
|
||||
'day',
|
||||
'hour',
|
||||
'minute',
|
||||
'second',
|
||||
);
|
||||
|
||||
$newDur = '';
|
||||
foreach($parts as $part) {
|
||||
if (isset($matches[$part]) && $matches[$part]) {
|
||||
$newDur.=' '.$matches[$part] . ' ' . $part . 's';
|
||||
}
|
||||
}
|
||||
|
||||
$newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
|
||||
if ($newDur === '+') { $newDur = '+0 seconds'; };
|
||||
return $newDur;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses either a Date or DateTime, or Duration value.
|
||||
*
|
||||
* @param string $date
|
||||
* @param DateTimeZone|string $referenceTZ
|
||||
* @return DateTime|DateInterval
|
||||
*/
|
||||
static public function parse($date, $referenceTZ = null) {
|
||||
|
||||
if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) {
|
||||
return self::parseDuration($date);
|
||||
} elseif (strlen($date)===8) {
|
||||
return self::parseDate($date);
|
||||
} else {
|
||||
return self::parseDateTime($date, $referenceTZ);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method parses a vCard date and or time value.
|
||||
*
|
||||
* This can be used for the DATE, DATE-TIME, TIMESTAMP and
|
||||
* DATE-AND-OR-TIME value.
|
||||
*
|
||||
* This method returns an array, not a DateTime value.
|
||||
*
|
||||
* The elements in the array are in the following order:
|
||||
* year, month, date, hour, minute, second, timezone
|
||||
*
|
||||
* Almost any part of the string may be omitted. It's for example legal to
|
||||
* just specify seconds, leave out the year, etc.
|
||||
*
|
||||
* Timezone is either returned as 'Z' or as '+08:00'
|
||||
*
|
||||
* For any non-specified values null is returned.
|
||||
*
|
||||
* List of date formats that are supported:
|
||||
* YYYY
|
||||
* YYYY-MM
|
||||
* YYYYMMDD
|
||||
* --MMDD
|
||||
* ---DD
|
||||
*
|
||||
* YYYY-MM-DD
|
||||
* --MM-DD
|
||||
* ---DD
|
||||
*
|
||||
* List of supported time formats:
|
||||
*
|
||||
* HH
|
||||
* HHMM
|
||||
* HHMMSS
|
||||
* -MMSS
|
||||
* --SS
|
||||
*
|
||||
* HH
|
||||
* HH:MM
|
||||
* HH:MM:SS
|
||||
* -MM:SS
|
||||
* --SS
|
||||
*
|
||||
* A full basic-format date-time string looks like :
|
||||
* 20130603T133901
|
||||
*
|
||||
* A full extended-format date-time string looks like :
|
||||
* 2013-06-03T13:39:01
|
||||
*
|
||||
* Times may be postfixed by a timezone offset. This can be either 'Z' for
|
||||
* UTC, or a string like -0500 or +1100.
|
||||
*
|
||||
* @param string $date
|
||||
* @return array
|
||||
*/
|
||||
static public function parseVCardDateTime($date) {
|
||||
|
||||
$regex = '/^
|
||||
(?: # date part
|
||||
(?:
|
||||
(?: (?P<year> [0-9]{4}) (?: -)?| --)
|
||||
(?P<month> [0-9]{2})?
|
||||
|---)
|
||||
(?P<date> [0-9]{2})?
|
||||
)?
|
||||
(?:T # time part
|
||||
(?P<hour> [0-9]{2} | -)
|
||||
(?P<minute> [0-9]{2} | -)?
|
||||
(?P<second> [0-9]{2})?
|
||||
|
||||
(?P<timezone> # timezone offset
|
||||
|
||||
Z | (?: \+|-)(?: [0-9]{4})
|
||||
|
||||
)?
|
||||
|
||||
)?
|
||||
$/x';
|
||||
|
||||
|
||||
if (!preg_match($regex, $date, $matches)) {
|
||||
|
||||
// Attempting to parse the extended format.
|
||||
$regex = '/^
|
||||
(?: # date part
|
||||
(?: (?P<year> [0-9]{4}) - | -- )
|
||||
(?P<month> [0-9]{2}) -
|
||||
(?P<date> [0-9]{2})
|
||||
)?
|
||||
(?:T # time part
|
||||
|
||||
(?: (?P<hour> [0-9]{2}) : | -)
|
||||
(?: (?P<minute> [0-9]{2}) : | -)?
|
||||
(?P<second> [0-9]{2})?
|
||||
|
||||
(?P<timezone> # timezone offset
|
||||
|
||||
Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2})
|
||||
|
||||
)?
|
||||
|
||||
)?
|
||||
$/x';
|
||||
|
||||
if (!preg_match($regex, $date, $matches)) {
|
||||
throw new \InvalidArgumentException('Invalid vCard date-time string: ' . $date);
|
||||
}
|
||||
|
||||
}
|
||||
$parts = array(
|
||||
'year',
|
||||
'month',
|
||||
'date',
|
||||
'hour',
|
||||
'minute',
|
||||
'second',
|
||||
'timezone'
|
||||
);
|
||||
|
||||
$result = array();
|
||||
foreach($parts as $part) {
|
||||
|
||||
if (empty($matches[$part])) {
|
||||
$result[$part] = null;
|
||||
} elseif ($matches[$part] === '-' || $matches[$part] === '--') {
|
||||
$result[$part] = null;
|
||||
} else {
|
||||
$result[$part] = $matches[$part];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method parses a vCard TIME value.
|
||||
*
|
||||
* This method returns an array, not a DateTime value.
|
||||
*
|
||||
* The elements in the array are in the following order:
|
||||
* hour, minute, second, timezone
|
||||
*
|
||||
* Almost any part of the string may be omitted. It's for example legal to
|
||||
* just specify seconds, leave out the hour etc.
|
||||
*
|
||||
* Timezone is either returned as 'Z' or as '+08:00'
|
||||
*
|
||||
* For any non-specified values null is returned.
|
||||
*
|
||||
* List of supported time formats:
|
||||
*
|
||||
* HH
|
||||
* HHMM
|
||||
* HHMMSS
|
||||
* -MMSS
|
||||
* --SS
|
||||
*
|
||||
* HH
|
||||
* HH:MM
|
||||
* HH:MM:SS
|
||||
* -MM:SS
|
||||
* --SS
|
||||
*
|
||||
* A full basic-format time string looks like :
|
||||
* 133901
|
||||
*
|
||||
* A full extended-format time string looks like :
|
||||
* 13:39:01
|
||||
*
|
||||
* Times may be postfixed by a timezone offset. This can be either 'Z' for
|
||||
* UTC, or a string like -0500 or +11:00.
|
||||
*
|
||||
* @param string $date
|
||||
* @return array
|
||||
*/
|
||||
static public function parseVCardTime($date) {
|
||||
|
||||
$regex = '/^
|
||||
(?P<hour> [0-9]{2} | -)
|
||||
(?P<minute> [0-9]{2} | -)?
|
||||
(?P<second> [0-9]{2})?
|
||||
|
||||
(?P<timezone> # timezone offset
|
||||
|
||||
Z | (?: \+|-)(?: [0-9]{4})
|
||||
|
||||
)?
|
||||
$/x';
|
||||
|
||||
|
||||
if (!preg_match($regex, $date, $matches)) {
|
||||
|
||||
// Attempting to parse the extended format.
|
||||
$regex = '/^
|
||||
(?: (?P<hour> [0-9]{2}) : | -)
|
||||
(?: (?P<minute> [0-9]{2}) : | -)?
|
||||
(?P<second> [0-9]{2})?
|
||||
|
||||
(?P<timezone> # timezone offset
|
||||
|
||||
Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2})
|
||||
|
||||
)?
|
||||
$/x';
|
||||
|
||||
if (!preg_match($regex, $date, $matches)) {
|
||||
throw new \InvalidArgumentException('Invalid vCard time string: ' . $date);
|
||||
}
|
||||
|
||||
}
|
||||
$parts = array(
|
||||
'hour',
|
||||
'minute',
|
||||
'second',
|
||||
'timezone'
|
||||
);
|
||||
|
||||
$result = array();
|
||||
foreach($parts as $part) {
|
||||
|
||||
if (empty($matches[$part])) {
|
||||
$result[$part] = null;
|
||||
} elseif ($matches[$part] === '-') {
|
||||
$result[$part] = null;
|
||||
} else {
|
||||
$result[$part] = $matches[$part];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Document
|
||||
*
|
||||
* A document is just like a component, except that it's also the top level
|
||||
* element.
|
||||
*
|
||||
* Both a VCALENDAR and a VCARD are considered documents.
|
||||
*
|
||||
* This class also provides a registry for document types.
|
||||
*
|
||||
* @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 Document extends Component {
|
||||
|
||||
/**
|
||||
* Unknown document type
|
||||
*/
|
||||
const UNKNOWN = 1;
|
||||
|
||||
/**
|
||||
* vCalendar 1.0
|
||||
*/
|
||||
const VCALENDAR10 = 2;
|
||||
|
||||
/**
|
||||
* iCalendar 2.0
|
||||
*/
|
||||
const ICALENDAR20 = 3;
|
||||
|
||||
/**
|
||||
* vCard 2.1
|
||||
*/
|
||||
const VCARD21 = 4;
|
||||
|
||||
/**
|
||||
* vCard 3.0
|
||||
*/
|
||||
const VCARD30 = 5;
|
||||
|
||||
/**
|
||||
* vCard 4.0
|
||||
*/
|
||||
const VCARD40 = 6;
|
||||
|
||||
/**
|
||||
* The default name for this component.
|
||||
*
|
||||
* This should be 'VCALENDAR' or 'VCARD'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
static public $defaultName;
|
||||
|
||||
/**
|
||||
* List of properties, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $propertyMap = array();
|
||||
|
||||
/**
|
||||
* List of components, along with which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $componentMap = array();
|
||||
|
||||
/**
|
||||
* List of value-types, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $valueMap = array();
|
||||
|
||||
/**
|
||||
* Creates a new document.
|
||||
*
|
||||
* We're changing the default behavior slightly here. First, we don't want
|
||||
* to have to specify a name (we already know it), and we want to allow
|
||||
* children to be specified in the first argument.
|
||||
*
|
||||
* But, the default behavior also works.
|
||||
*
|
||||
* So the two sigs:
|
||||
*
|
||||
* new Document(array $children = array(), $defaults = true);
|
||||
* new Document(string $name, array $children = array(), $defaults = true)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
$args = func_get_args();
|
||||
if (count($args)===0 || is_array($args[0])) {
|
||||
array_unshift($args, $this, static::$defaultName);
|
||||
call_user_func_array(array('parent', '__construct'), $args);
|
||||
} else {
|
||||
array_unshift($args, $this);
|
||||
call_user_func_array(array('parent', '__construct'), $args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current document type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getDocumentType() {
|
||||
|
||||
return self::UNKNOWN;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new component or property.
|
||||
*
|
||||
* If it's a known component, we will automatically call createComponent.
|
||||
* otherwise, we'll assume it's a property and call createProperty instead.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $arg1,... Unlimited number of args
|
||||
* @return mixed
|
||||
*/
|
||||
public function create($name) {
|
||||
|
||||
if (isset(static::$componentMap[strtoupper($name)])) {
|
||||
|
||||
return call_user_func_array(array($this,'createComponent'), func_get_args());
|
||||
|
||||
} else {
|
||||
|
||||
return call_user_func_array(array($this,'createProperty'), func_get_args());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new component
|
||||
*
|
||||
* This method automatically searches for the correct component class, based
|
||||
* on its name.
|
||||
*
|
||||
* You can specify the children either in key=>value syntax, in which case
|
||||
* properties will automatically be created, or you can just pass a list of
|
||||
* Component and Property object.
|
||||
*
|
||||
* By default, a set of sensible values will be added to the component. For
|
||||
* an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
|
||||
* ensure that this does not happen, set $defaults to false.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $children
|
||||
* @param bool $defaults
|
||||
* @return Component
|
||||
*/
|
||||
public function createComponent($name, array $children = null, $defaults = true) {
|
||||
|
||||
$name = strtoupper($name);
|
||||
$class = 'Sabre\\VObject\\Component';
|
||||
|
||||
if (isset(static::$componentMap[$name])) {
|
||||
$class=static::$componentMap[$name];
|
||||
}
|
||||
if (is_null($children)) $children = array();
|
||||
|
||||
if(strpos($class, 'OCA\\Calendar\\') !== 0) {
|
||||
$class = 'OCA\\Calendar\\' . $class;
|
||||
}
|
||||
|
||||
return new $class($this, $name, $children, $defaults);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for creating new properties
|
||||
*
|
||||
* This method automatically searches for the correct property class, based
|
||||
* on its name.
|
||||
*
|
||||
* You can specify the parameters either in key=>value syntax, in which case
|
||||
* parameters will automatically be created, or you can just pass a list of
|
||||
* Parameter objects.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @param string $valueType Force a specific valuetype, such as URI or TEXT
|
||||
* @return Property
|
||||
*/
|
||||
public function createProperty($name, $value = null, array $parameters = null, $valueType = null) {
|
||||
|
||||
// If there's a . in the name, it means it's prefixed by a groupname.
|
||||
if (($i=strpos($name,'.'))!==false) {
|
||||
$group = substr($name, 0, $i);
|
||||
$name = strtoupper(substr($name, $i+1));
|
||||
} else {
|
||||
$name = strtoupper($name);
|
||||
$group = null;
|
||||
}
|
||||
|
||||
$class = null;
|
||||
|
||||
if ($valueType) {
|
||||
// The valueType argument comes first to figure out the correct
|
||||
// class.
|
||||
$class = $this->getClassNameForPropertyValue($valueType);
|
||||
}
|
||||
|
||||
if (is_null($class) && isset($parameters['VALUE'])) {
|
||||
// If a VALUE parameter is supplied, we should use that.
|
||||
$class = $this->getClassNameForPropertyValue($parameters['VALUE']);
|
||||
}
|
||||
if (is_null($class)) {
|
||||
$class = $this->getClassNameForPropertyName($name);
|
||||
}
|
||||
if (is_null($parameters)) $parameters = array();
|
||||
|
||||
if(strpos($class, 'OCA\\Calendar\\') !== 0) {
|
||||
$class = 'OCA\\Calendar\\' . $class;
|
||||
}
|
||||
|
||||
return new $class($this, $name, $value, $parameters, $group);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a full class-name for a value parameter.
|
||||
*
|
||||
* For instance, DTSTART may have VALUE=DATE. In that case we will look in
|
||||
* our valueMap table and return the appropriate class name.
|
||||
*
|
||||
* This method returns null if we don't have a specialized class.
|
||||
*
|
||||
* @param string $valueParam
|
||||
* @return void
|
||||
*/
|
||||
public function getClassNameForPropertyValue($valueParam) {
|
||||
|
||||
$valueParam = strtoupper($valueParam);
|
||||
if (isset(static::$valueMap[$valueParam])) {
|
||||
return static::$valueMap[$valueParam];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default class for a property name.
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @return string
|
||||
*/
|
||||
public function getClassNameForPropertyName($propertyName) {
|
||||
|
||||
if (isset(static::$propertyMap[$propertyName])) {
|
||||
return 'OCA\\Calendar\\' . static::$propertyMap[$propertyName];
|
||||
} else {
|
||||
return 'OCA\\Calendar\\Sabre\\VObject\\Property\\Unknown';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VObject ElementList
|
||||
*
|
||||
* This class represents a list of elements. Lists are the result of queries,
|
||||
* such as doing $vcalendar->vevent where there's multiple VEVENT objects.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class ElementList implements \Iterator, \Countable, \ArrayAccess {
|
||||
|
||||
/**
|
||||
* Inner elements
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $elements = array();
|
||||
|
||||
/**
|
||||
* Creates the element list.
|
||||
*
|
||||
* @param array $elements
|
||||
*/
|
||||
public function __construct(array $elements) {
|
||||
|
||||
$this->elements = $elements;
|
||||
|
||||
}
|
||||
|
||||
/* {{{ Iterator interface */
|
||||
|
||||
/**
|
||||
* Current position
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $key = 0;
|
||||
|
||||
/**
|
||||
* Returns current item in iteration
|
||||
*
|
||||
* @return Element
|
||||
*/
|
||||
public function current() {
|
||||
|
||||
return $this->elements[$this->key];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* To the next item in the iterator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next() {
|
||||
|
||||
$this->key++;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current iterator key
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function key() {
|
||||
|
||||
return $this->key;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current position in the iterator is a valid one
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid() {
|
||||
|
||||
return isset($this->elements[$this->key]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewinds the iterator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind() {
|
||||
|
||||
$this->key = 0;
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ Countable interface */
|
||||
|
||||
/**
|
||||
* Returns the number of elements
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count() {
|
||||
|
||||
return count($this->elements);
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ArrayAccess Interface */
|
||||
|
||||
|
||||
/**
|
||||
* Checks if an item exists through ArrayAccess.
|
||||
*
|
||||
* @param int $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
|
||||
return isset($this->elements[$offset]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an item through ArrayAccess.
|
||||
*
|
||||
* @param int $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset) {
|
||||
|
||||
return $this->elements[$offset];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item through ArrayAccess.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($offset,$value) {
|
||||
|
||||
throw new \LogicException('You can not add new objects to an ElementList');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
|
||||
throw new \LogicException('You can not remove objects from an ElementList');
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Exception thrown by parser when the end of the stream has been reached,
|
||||
* before this was expected.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class EofException extends ParseException { }
|
|
@ -0,0 +1,321 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
use OCA\Calendar\Sabre\VObject\Component\VCalendar;
|
||||
|
||||
/**
|
||||
* This class helps with generating FREEBUSY reports based on existing sets of
|
||||
* objects.
|
||||
*
|
||||
* It only looks at VEVENT and VFREEBUSY objects from the sourcedata, and
|
||||
* generates a single VFREEBUSY object.
|
||||
*
|
||||
* VFREEBUSY components are described in RFC5545, The rules for what should
|
||||
* go in a single freebusy report is taken from RFC4791, section 7.10.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class FreeBusyGenerator {
|
||||
|
||||
/**
|
||||
* Input objects
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $objects;
|
||||
|
||||
/**
|
||||
* Start of range
|
||||
*
|
||||
* @var DateTime|null
|
||||
*/
|
||||
protected $start;
|
||||
|
||||
/**
|
||||
* End of range
|
||||
*
|
||||
* @var DateTime|null
|
||||
*/
|
||||
protected $end;
|
||||
|
||||
/**
|
||||
* VCALENDAR object
|
||||
*
|
||||
* @var Component
|
||||
*/
|
||||
protected $baseObject;
|
||||
|
||||
/**
|
||||
* Creates the generator.
|
||||
*
|
||||
* Check the setTimeRange and setObjects methods for details about the
|
||||
* arguments.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @param mixed $objects
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(\DateTime $start = null, \DateTime $end = null, $objects = null) {
|
||||
|
||||
if ($start && $end) {
|
||||
$this->setTimeRange($start, $end);
|
||||
}
|
||||
|
||||
if ($objects) {
|
||||
$this->setObjects($objects);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the VCALENDAR object.
|
||||
*
|
||||
* If this is set, it will not be generated for you. You are responsible
|
||||
* for setting things like the METHOD, CALSCALE, VERSION, etc..
|
||||
*
|
||||
* The VFREEBUSY object will be automatically added though.
|
||||
*
|
||||
* @param Component $vcalendar
|
||||
* @return void
|
||||
*/
|
||||
public function setBaseObject(Component $vcalendar) {
|
||||
|
||||
$this->baseObject = $vcalendar;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input objects
|
||||
*
|
||||
* You must either specify a valendar object as a strong, or as the parse
|
||||
* Component.
|
||||
* It's also possible to specify multiple objects as an array.
|
||||
*
|
||||
* @param mixed $objects
|
||||
* @return void
|
||||
*/
|
||||
public function setObjects($objects) {
|
||||
|
||||
if (!is_array($objects)) {
|
||||
$objects = array($objects);
|
||||
}
|
||||
|
||||
$this->objects = array();
|
||||
foreach($objects as $object) {
|
||||
|
||||
if (is_string($object)) {
|
||||
$this->objects[] = Reader::read($object);
|
||||
} elseif ($object instanceof Component) {
|
||||
$this->objects[] = $object;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time range
|
||||
*
|
||||
* Any freebusy object falling outside of this time range will be ignored.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @return void
|
||||
*/
|
||||
public function setTimeRange(\DateTime $start = null, \DateTime $end = null) {
|
||||
|
||||
$this->start = $start;
|
||||
$this->end = $end;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the input data and returns a correct VFREEBUSY object, wrapped in
|
||||
* a VCALENDAR.
|
||||
*
|
||||
* @return Component
|
||||
*/
|
||||
public function getResult() {
|
||||
|
||||
$busyTimes = array();
|
||||
|
||||
foreach($this->objects as $object) {
|
||||
|
||||
foreach($object->getBaseComponents() as $component) {
|
||||
|
||||
switch($component->name) {
|
||||
|
||||
case 'VEVENT' :
|
||||
|
||||
$FBTYPE = 'BUSY';
|
||||
if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) {
|
||||
break;
|
||||
}
|
||||
if (isset($component->STATUS)) {
|
||||
$status = strtoupper($component->STATUS);
|
||||
if ($status==='CANCELLED') {
|
||||
break;
|
||||
}
|
||||
if ($status==='TENTATIVE') {
|
||||
$FBTYPE = 'BUSY-TENTATIVE';
|
||||
}
|
||||
}
|
||||
|
||||
$times = array();
|
||||
|
||||
if ($component->RRULE) {
|
||||
|
||||
$iterator = new RecurrenceIterator($object, (string)$component->uid);
|
||||
if ($this->start) {
|
||||
$iterator->fastForward($this->start);
|
||||
}
|
||||
|
||||
$maxRecurrences = 200;
|
||||
|
||||
while($iterator->valid() && --$maxRecurrences) {
|
||||
|
||||
$startTime = $iterator->getDTStart();
|
||||
if ($this->end && $startTime > $this->end) {
|
||||
break;
|
||||
}
|
||||
$times[] = array(
|
||||
$iterator->getDTStart(),
|
||||
$iterator->getDTEnd(),
|
||||
);
|
||||
|
||||
$iterator->next();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$startTime = $component->DTSTART->getDateTime();
|
||||
if ($this->end && $startTime > $this->end) {
|
||||
break;
|
||||
}
|
||||
$endTime = null;
|
||||
if (isset($component->DTEND)) {
|
||||
$endTime = $component->DTEND->getDateTime();
|
||||
} elseif (isset($component->DURATION)) {
|
||||
$duration = DateTimeParser::parseDuration((string)$component->DURATION);
|
||||
$endTime = clone $startTime;
|
||||
$endTime->add($duration);
|
||||
} elseif (!$component->DTSTART->hasTime()) {
|
||||
$endTime = clone $startTime;
|
||||
$endTime->modify('+1 day');
|
||||
} else {
|
||||
// The event had no duration (0 seconds)
|
||||
break;
|
||||
}
|
||||
|
||||
$times[] = array($startTime, $endTime);
|
||||
|
||||
}
|
||||
|
||||
foreach($times as $time) {
|
||||
|
||||
if ($this->end && $time[0] > $this->end) break;
|
||||
if ($this->start && $time[1] < $this->start) break;
|
||||
|
||||
$busyTimes[] = array(
|
||||
$time[0],
|
||||
$time[1],
|
||||
$FBTYPE,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'VFREEBUSY' :
|
||||
foreach($component->FREEBUSY as $freebusy) {
|
||||
|
||||
$fbType = isset($freebusy['FBTYPE'])?strtoupper($freebusy['FBTYPE']):'BUSY';
|
||||
|
||||
// Skipping intervals marked as 'free'
|
||||
if ($fbType==='FREE')
|
||||
continue;
|
||||
|
||||
$values = explode(',', $freebusy);
|
||||
foreach($values as $value) {
|
||||
list($startTime, $endTime) = explode('/', $value);
|
||||
$startTime = DateTimeParser::parseDateTime($startTime);
|
||||
|
||||
if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') {
|
||||
$duration = DateTimeParser::parseDuration($endTime);
|
||||
$endTime = clone $startTime;
|
||||
$endTime->add($duration);
|
||||
} else {
|
||||
$endTime = DateTimeParser::parseDateTime($endTime);
|
||||
}
|
||||
|
||||
if($this->start && $this->start > $endTime) continue;
|
||||
if($this->end && $this->end < $startTime) continue;
|
||||
$busyTimes[] = array(
|
||||
$startTime,
|
||||
$endTime,
|
||||
$fbType
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($this->baseObject) {
|
||||
$calendar = $this->baseObject;
|
||||
} else {
|
||||
$calendar = new VCalendar();
|
||||
}
|
||||
|
||||
$vfreebusy = $calendar->createComponent('VFREEBUSY');
|
||||
$calendar->add($vfreebusy);
|
||||
|
||||
if ($this->start) {
|
||||
$dtstart = $calendar->createProperty('DTSTART');
|
||||
$dtstart->setDateTime($this->start);
|
||||
$vfreebusy->add($dtstart);
|
||||
}
|
||||
if ($this->end) {
|
||||
$dtend = $calendar->createProperty('DTEND');
|
||||
$dtend->setDateTime($this->end);
|
||||
$vfreebusy->add($dtend);
|
||||
}
|
||||
$dtstamp = $calendar->createProperty('DTSTAMP');
|
||||
$dtstamp->setDateTime(new \DateTime('now', new \DateTimeZone('UTC')));
|
||||
$vfreebusy->add($dtstamp);
|
||||
|
||||
foreach($busyTimes as $busyTime) {
|
||||
|
||||
$busyTime[0]->setTimeZone(new \DateTimeZone('UTC'));
|
||||
$busyTime[1]->setTimeZone(new \DateTimeZone('UTC'));
|
||||
|
||||
$prop = $calendar->createProperty(
|
||||
'FREEBUSY',
|
||||
$busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
|
||||
);
|
||||
$prop['FBTYPE'] = $busyTime[2];
|
||||
$vfreebusy->add($prop);
|
||||
|
||||
}
|
||||
|
||||
return $calendar;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* A node is the root class for every element in an iCalendar of vCard object.
|
||||
*
|
||||
* @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 Node implements \IteratorAggregate, \ArrayAccess, \Countable {
|
||||
|
||||
/**
|
||||
* The following constants are used by the validate() method.
|
||||
*/
|
||||
const REPAIR = 1;
|
||||
|
||||
/**
|
||||
* Reference to the parent object, if this is not the top object.
|
||||
*
|
||||
* @var Node
|
||||
*/
|
||||
public $parent;
|
||||
|
||||
/**
|
||||
* Iterator override
|
||||
*
|
||||
* @var ElementList
|
||||
*/
|
||||
protected $iterator = null;
|
||||
|
||||
/**
|
||||
* The root document
|
||||
*
|
||||
* @var Component
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* Serializes the node into a mimedir format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract function serialize();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
abstract function jsonSerialize();
|
||||
|
||||
/* {{{ IteratorAggregator interface */
|
||||
|
||||
/**
|
||||
* Returns the iterator for this object
|
||||
*
|
||||
* @return ElementList
|
||||
*/
|
||||
public function getIterator() {
|
||||
|
||||
if (!is_null($this->iterator))
|
||||
return $this->iterator;
|
||||
|
||||
return new ElementList(array($this));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the overridden iterator
|
||||
*
|
||||
* Note that this is not actually part of the iterator interface
|
||||
*
|
||||
* @param ElementList $iterator
|
||||
* @return void
|
||||
*/
|
||||
public function setIterator(ElementList $iterator) {
|
||||
|
||||
$this->iterator = $iterator;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ Countable interface */
|
||||
|
||||
/**
|
||||
* Returns the number of elements
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count() {
|
||||
|
||||
$it = $this->getIterator();
|
||||
return $it->count();
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ArrayAccess Interface */
|
||||
|
||||
|
||||
/**
|
||||
* Checks if an item exists through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
|
||||
$iterator = $this->getIterator();
|
||||
return $iterator->offsetExists($offset);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an item through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset) {
|
||||
|
||||
$iterator = $this->getIterator();
|
||||
return $iterator->offsetGet($offset);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($offset,$value) {
|
||||
|
||||
$iterator = $this->getIterator();
|
||||
$iterator->offsetSet($offset,$value);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
//
|
||||
// This method always throws an exception, so we ignore the closing
|
||||
// brace
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
/**
|
||||
* Sets an item through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
|
||||
$iterator = $this->getIterator();
|
||||
$iterator->offsetUnset($offset);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
//
|
||||
// This method always throws an exception, so we ignore the closing
|
||||
// brace
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
/* }}} */
|
||||
}
|
|
@ -0,0 +1,343 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
use
|
||||
ArrayObject;
|
||||
|
||||
/**
|
||||
* VObject Parameter
|
||||
*
|
||||
* This class represents a parameter. A parameter is always tied to a property.
|
||||
* In the case of:
|
||||
* DTSTART;VALUE=DATE:20101108
|
||||
* VALUE=DATE would be the parameter name and value.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Parameter extends Node {
|
||||
|
||||
/**
|
||||
* Parameter name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* vCard 2.1 allows parameters to be encoded without a name.
|
||||
*
|
||||
* We can deduce the parameter name based on it's value.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $noName = false;
|
||||
|
||||
/**
|
||||
* Parameter value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Sets up the object.
|
||||
*
|
||||
* It's recommended to use the create:: factory method instead.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*/
|
||||
public function __construct(Document $root, $name, $value = null) {
|
||||
|
||||
$this->name = strtoupper($name);
|
||||
$this->root = $root;
|
||||
if (is_null($name)) {
|
||||
$this->noName = true;
|
||||
$this->name = static::guessParameterNameByValue($value);
|
||||
}
|
||||
$this->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess property name by value, can be used for vCard 2.1 nameless parameters.
|
||||
*
|
||||
* Figuring out what the name should have been. Note that a ton of
|
||||
* these are rather silly in 2013 and would probably rarely be
|
||||
* used, but we like to be complete.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function guessParameterNameByValue($value) {
|
||||
switch(strtoupper($value)) {
|
||||
|
||||
// Encodings
|
||||
case '7-BIT' :
|
||||
case 'QUOTED-PRINTABLE' :
|
||||
case 'BASE64' :
|
||||
$name = 'ENCODING';
|
||||
break;
|
||||
|
||||
// Common types
|
||||
case 'WORK' :
|
||||
case 'HOME' :
|
||||
case 'PREF' :
|
||||
|
||||
// Delivery Label Type
|
||||
case 'DOM' :
|
||||
case 'INTL' :
|
||||
case 'POSTAL' :
|
||||
case 'PARCEL' :
|
||||
|
||||
// Telephone types
|
||||
case 'VOICE' :
|
||||
case 'FAX' :
|
||||
case 'MSG' :
|
||||
case 'CELL' :
|
||||
case 'PAGER' :
|
||||
case 'BBS' :
|
||||
case 'MODEM' :
|
||||
case 'CAR' :
|
||||
case 'ISDN' :
|
||||
case 'VIDEO' :
|
||||
|
||||
// EMAIL types (lol)
|
||||
case 'AOL' :
|
||||
case 'APPLELINK' :
|
||||
case 'ATTMAIL' :
|
||||
case 'CIS' :
|
||||
case 'EWORLD' :
|
||||
case 'INTERNET' :
|
||||
case 'IBMMAIL' :
|
||||
case 'MCIMAIL' :
|
||||
case 'POWERSHARE' :
|
||||
case 'PRODIGY' :
|
||||
case 'TLX' :
|
||||
case 'X400' :
|
||||
|
||||
// Photo / Logo format types
|
||||
case 'GIF' :
|
||||
case 'CGM' :
|
||||
case 'WMF' :
|
||||
case 'BMP' :
|
||||
case 'DIB' :
|
||||
case 'PICT' :
|
||||
case 'TIFF' :
|
||||
case 'PDF ':
|
||||
case 'PS' :
|
||||
case 'JPEG' :
|
||||
case 'MPEG' :
|
||||
case 'MPEG2' :
|
||||
case 'AVI' :
|
||||
case 'QTIME' :
|
||||
|
||||
// Sound Digital Audio Type
|
||||
case 'WAVE' :
|
||||
case 'PCM' :
|
||||
case 'AIFF' :
|
||||
|
||||
// Key types
|
||||
case 'X509' :
|
||||
case 'PGP' :
|
||||
$name = 'TYPE';
|
||||
break;
|
||||
|
||||
// Value types
|
||||
case 'INLINE' :
|
||||
case 'URL' :
|
||||
case 'CONTENT-ID' :
|
||||
case 'CID' :
|
||||
$name = 'VALUE';
|
||||
break;
|
||||
|
||||
default:
|
||||
$name = '';
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 string, or null. If there were multiple
|
||||
* values, it will automatically concatinate them (separated by comma).
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getValue() {
|
||||
|
||||
if (is_array($this->value)) {
|
||||
return implode(',' , $this->value);
|
||||
} else {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets multiple values for this parameter.
|
||||
*
|
||||
* @param array $value
|
||||
* @return void
|
||||
*/
|
||||
public function setParts(array $value) {
|
||||
|
||||
$this->value = $value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all values for this parameter.
|
||||
*
|
||||
* If there were no values, an empty array will be returned.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParts() {
|
||||
|
||||
if (is_array($this->value)) {
|
||||
return $this->value;
|
||||
} elseif (is_null($this->value)) {
|
||||
return array();
|
||||
} else {
|
||||
return array($this->value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value to this parameter
|
||||
*
|
||||
* If the argument is specified as an array, all items will be added to the
|
||||
* parameter value list.
|
||||
*
|
||||
* @param string|array $part
|
||||
* @return void
|
||||
*/
|
||||
public function addValue($part) {
|
||||
|
||||
if (is_null($this->value)) {
|
||||
$this->value = $part;
|
||||
} else {
|
||||
$this->value = array_merge((array)$this->value, (array)$part);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this parameter contains the specified value.
|
||||
*
|
||||
* This is a case-insensitive match. It makes sense to call this for for
|
||||
* instance the TYPE parameter, to see if it contains a keyword such as
|
||||
* 'WORK' or 'FAX'.
|
||||
*
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public function has($value) {
|
||||
|
||||
return in_array(
|
||||
strtolower($value),
|
||||
array_map('strtolower', (array)$this->value)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the object back into a serialized blob.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize() {
|
||||
|
||||
$value = $this->getParts();
|
||||
|
||||
if (count($value)===0) {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
if ($this->root->getDocumentType() === Document::VCARD21 && $this->noName) {
|
||||
|
||||
return implode(';', $value);
|
||||
|
||||
}
|
||||
|
||||
return $this->name . '=' . array_reduce($value, function($out, $item) {
|
||||
|
||||
if (!is_null($out)) $out.=',';
|
||||
|
||||
// If there's no special characters in the string, we'll use the simple
|
||||
// format
|
||||
if (!preg_match('#(?: [\n":;\^,] )#x', $item)) {
|
||||
return $out.$item;
|
||||
} else {
|
||||
// Enclosing in double-quotes, and using RFC6868 for encoding any
|
||||
// special characters
|
||||
$out.='"' . strtr($item, array(
|
||||
'^' => '^^',
|
||||
"\n" => '^n',
|
||||
'"' => '^\'',
|
||||
)) . '"';
|
||||
return $out;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
return $this->value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this object is being cast to a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
|
||||
return $this->getValue();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the iterator for this object
|
||||
*
|
||||
* @return ElementList
|
||||
*/
|
||||
public function getIterator() {
|
||||
|
||||
if (!is_null($this->iterator))
|
||||
return $this->iterator;
|
||||
|
||||
return $this->iterator = new ArrayObject((array)$this->value);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Exception thrown by Reader if an invalid object was attempted to be parsed.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class ParseException extends \Exception { }
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Parser;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Component\VCalendar,
|
||||
OCA\Calendar\Sabre\VObject\Component\VCard,
|
||||
OCA\Calendar\Sabre\VObject\ParseException,
|
||||
OCA\Calendar\Sabre\VObject\EofException;
|
||||
|
||||
/**
|
||||
* Json Parser.
|
||||
*
|
||||
* This parser parses both the jCal and jCard formats.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Json extends Parser {
|
||||
|
||||
/**
|
||||
* The input data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $input;
|
||||
|
||||
/**
|
||||
* Root component
|
||||
*
|
||||
* @var Document
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* This method starts the parsing process.
|
||||
*
|
||||
* If the input was not supplied during construction, it's possible to pass
|
||||
* it here instead.
|
||||
*
|
||||
* If either input or options are not supplied, the defaults will be used.
|
||||
*
|
||||
* @param resource|string|array|null $input
|
||||
* @param int|null $options
|
||||
* @return array
|
||||
*/
|
||||
public function parse($input = null, $options = null) {
|
||||
|
||||
if (!is_null($input)) {
|
||||
$this->setInput($input);
|
||||
}
|
||||
if (is_null($this->input)) {
|
||||
throw new EofException('End of input stream, or no input supplied');
|
||||
}
|
||||
|
||||
if (!is_null($options)) {
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
switch($this->input[0]) {
|
||||
case 'vcalendar' :
|
||||
$this->root = new VCalendar(array(), false);
|
||||
break;
|
||||
case 'vcard' :
|
||||
$this->root = new VCard(array(), false);
|
||||
break;
|
||||
default :
|
||||
throw new ParseException('The root component must either be a vcalendar, or a vcard');
|
||||
|
||||
}
|
||||
foreach($this->input[1] as $prop) {
|
||||
$this->root->add($this->parseProperty($prop));
|
||||
}
|
||||
if (isset($this->input[2])) foreach($this->input[2] as $comp) {
|
||||
$this->root->add($this->parseComponent($comp));
|
||||
}
|
||||
|
||||
// Resetting the input so we can throw an feof exception the next time.
|
||||
$this->input = null;
|
||||
|
||||
return $this->root;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a component
|
||||
*
|
||||
* @param array $jComp
|
||||
* @return \Sabre\VObject\Component
|
||||
*/
|
||||
public function parseComponent(array $jComp) {
|
||||
|
||||
// We can remove $self from PHP 5.4 onward.
|
||||
$self = $this;
|
||||
|
||||
$properties = array_map(function($jProp) use ($self) {
|
||||
return $self->parseProperty($jProp);
|
||||
}, $jComp[1]);
|
||||
|
||||
if (isset($jComp[2])) {
|
||||
|
||||
$components = array_map(function($jComp) use ($self) {
|
||||
return $self->parseComponent($jComp);
|
||||
}, $jComp[2]);
|
||||
|
||||
} else $components = array();
|
||||
|
||||
return $this->root->createComponent(
|
||||
$jComp[0],
|
||||
array_merge( $properties, $components),
|
||||
$defaults = false
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses properties.
|
||||
*
|
||||
* @param array $jProp
|
||||
* @return \Sabre\VObject\Property
|
||||
*/
|
||||
public function parseProperty(array $jProp) {
|
||||
|
||||
list(
|
||||
$propertyName,
|
||||
$parameters,
|
||||
$valueType
|
||||
) = $jProp;
|
||||
|
||||
$propertyName = strtoupper($propertyName);
|
||||
|
||||
// This is the default class we would be using if we didn't know the
|
||||
// value type. We're using this value later in this function.
|
||||
$defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName);
|
||||
|
||||
$parameters = (array)$parameters;
|
||||
|
||||
$value = array_slice($jProp, 3);
|
||||
|
||||
$valueType = strtoupper($valueType);
|
||||
|
||||
if (isset($parameters['group'])) {
|
||||
$propertyName = $parameters['group'] . '.' . $propertyName;
|
||||
unset($parameters['group']);
|
||||
}
|
||||
|
||||
$prop = $this->root->createProperty($propertyName, null, $parameters, $valueType);
|
||||
$prop->setJsonValue($value);
|
||||
|
||||
// We have to do something awkward here. FlatText as well as Text
|
||||
// represents TEXT values. We have to normalize these here. In the
|
||||
// future we can get rid of FlatText once we're allowed to break BC
|
||||
// again.
|
||||
if ($defaultPropertyClass === 'Sabre\VObject\Property\FlatText') {
|
||||
$defaultPropertyClass = 'Sabre\VObject\Property\Text';
|
||||
}
|
||||
|
||||
// If the value type we received (e.g.: TEXT) was not the default value
|
||||
// type for the given property (e.g.: BDAY), we need to add a VALUE=
|
||||
// parameter.
|
||||
if ($defaultPropertyClass !== get_class($prop)) {
|
||||
$prop["VALUE"] = $valueType;
|
||||
}
|
||||
|
||||
return $prop;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input data
|
||||
*
|
||||
* @param resource|string|array $input
|
||||
* @return void
|
||||
*/
|
||||
public function setInput($input) {
|
||||
|
||||
if (is_resource($input)) {
|
||||
$input = stream_get_contents($input);
|
||||
}
|
||||
if (is_string($input)) {
|
||||
$input = json_decode($input);
|
||||
}
|
||||
$this->input = $input;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,602 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Parser;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\ParseException,
|
||||
OCA\Calendar\Sabre\VObject\EofException,
|
||||
OCA\Calendar\Sabre\VObject\Component,
|
||||
OCA\Calendar\Sabre\VObject\Property,
|
||||
OCA\Calendar\Sabre\VObject\Component\VCalendar,
|
||||
OCA\Calendar\Sabre\VObject\Component\VCard;
|
||||
|
||||
/**
|
||||
* MimeDir parser.
|
||||
*
|
||||
* This class parses iCalendar/vCard files and returns an array.
|
||||
*
|
||||
* The array is identical to the format jCard/jCal use.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class MimeDir extends Parser {
|
||||
|
||||
/**
|
||||
* The input stream.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $input;
|
||||
|
||||
/**
|
||||
* Root component
|
||||
*
|
||||
* @var Component
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* Parses an iCalendar or vCard file
|
||||
*
|
||||
* Pass a stream or a string. If null is parsed, the existing buffer is
|
||||
* used.
|
||||
*
|
||||
* @param string|resource|null $input
|
||||
* @param int|null $options
|
||||
* @return array
|
||||
*/
|
||||
public function parse($input = null, $options = null) {
|
||||
|
||||
$this->root = null;
|
||||
if (!is_null($input)) {
|
||||
|
||||
$this->setInput($input);
|
||||
|
||||
}
|
||||
|
||||
if (!is_null($options)) $this->options = $options;
|
||||
|
||||
$this->parseDocument();
|
||||
|
||||
return $this->root;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input buffer. Must be a string or stream.
|
||||
*
|
||||
* @param resource|string $input
|
||||
* @return void
|
||||
*/
|
||||
public function setInput($input) {
|
||||
|
||||
// Resetting the parser
|
||||
$this->lineIndex = 0;
|
||||
$this->startLine = 0;
|
||||
|
||||
if (is_string($input)) {
|
||||
// Convering to a stream.
|
||||
$stream = fopen('php://temp', 'r+');
|
||||
fwrite($stream, $input);
|
||||
rewind($stream);
|
||||
$this->input = $stream;
|
||||
} else {
|
||||
$this->input = $input;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an entire document.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function parseDocument() {
|
||||
|
||||
$line = $this->readLine();
|
||||
switch(strtoupper($line)) {
|
||||
case 'BEGIN:VCALENDAR' :
|
||||
$class = isset(VCalendar::$componentMap['VCALENDAR'])
|
||||
? VCalendar::$componentMap[$name]
|
||||
: '\\OCA\\Calendar\\Sabre\\VObject\\Component\\VCalendar';
|
||||
break;
|
||||
case 'BEGIN:VCARD' :
|
||||
$class = isset(VCard::$componentMap['VCARD'])
|
||||
? VCard::$componentMap['VCARD']
|
||||
: '\\OCA\\Calendar\\Sabre\\VObject\\Component\\VCard';
|
||||
break;
|
||||
default :
|
||||
throw new ParseException('This parser only supports VCARD and VCALENDAR files');
|
||||
}
|
||||
|
||||
$this->root = new $class(array(), false);
|
||||
|
||||
while(true) {
|
||||
|
||||
// Reading until we hit END:
|
||||
$line = $this->readLine();
|
||||
if (strtoupper(substr($line,0,4)) === 'END:') {
|
||||
break;
|
||||
}
|
||||
$result = $this->parseLine($line);
|
||||
if ($result) {
|
||||
$this->root->add($result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$name = strtoupper(substr($line, 4));
|
||||
if ($name!==$this->root->name) {
|
||||
throw new ParseException('Invalid MimeDir file. expected: "END:' . $this->root->name . '" got: "END:' . $name . '"');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a line, and if it hits a component, it will also attempt to parse
|
||||
* the entire component
|
||||
*
|
||||
* @param string $line Unfolded line
|
||||
* @return Node
|
||||
*/
|
||||
protected function parseLine($line) {
|
||||
|
||||
// Start of a new component
|
||||
if (strtoupper(substr($line, 0, 6)) === 'BEGIN:') {
|
||||
|
||||
$component = $this->root->createComponent(substr($line,6), array(), false);
|
||||
|
||||
while(true) {
|
||||
|
||||
// Reading until we hit END:
|
||||
$line = $this->readLine();
|
||||
if (strtoupper(substr($line,0,4)) === 'END:') {
|
||||
break;
|
||||
}
|
||||
$result = $this->parseLine($line);
|
||||
if ($result) {
|
||||
$component->add($result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$name = strtoupper(substr($line, 4));
|
||||
if ($name!==$component->name) {
|
||||
throw new ParseException('Invalid MimeDir file. expected: "END:' . $component->name . '" got: "END:' . $name . '"');
|
||||
}
|
||||
|
||||
return $component;
|
||||
|
||||
} else {
|
||||
|
||||
// Property reader
|
||||
$property = $this->readProperty($line);
|
||||
if (!$property) {
|
||||
// Ignored line
|
||||
return false;
|
||||
}
|
||||
return $property;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to look ahead 1 line every time to see if we need to 'unfold'
|
||||
* the next line.
|
||||
*
|
||||
* If that was not the case, we store it here.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
protected $lineBuffer;
|
||||
|
||||
/**
|
||||
* The real current line number.
|
||||
*/
|
||||
protected $lineIndex = 0;
|
||||
|
||||
/**
|
||||
* In the case of unfolded lines, this property holds the line number for
|
||||
* the start of the line.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $startLine = 0;
|
||||
|
||||
/**
|
||||
* Contains a 'raw' representation of the current line.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rawLine;
|
||||
|
||||
/**
|
||||
* Reads a single line from the buffer.
|
||||
*
|
||||
* This method strips any newlines and also takes care of unfolding.
|
||||
*
|
||||
* @throws \Sabre\VObject\EofException
|
||||
* @return string
|
||||
*/
|
||||
protected function readLine() {
|
||||
|
||||
if (!is_null($this->lineBuffer)) {
|
||||
$rawLine = $this->lineBuffer;
|
||||
$this->lineBuffer = null;
|
||||
} else {
|
||||
do {
|
||||
$rawLine = fgets($this->input);
|
||||
if ($rawLine === false && feof($this->input)) {
|
||||
throw new EofException('End of document reached prematurely');
|
||||
}
|
||||
$rawLine = rtrim($rawLine, "\r\n");
|
||||
} while ($rawLine === ''); // Skipping empty lines
|
||||
$this->lineIndex++;
|
||||
}
|
||||
$line = $rawLine;
|
||||
|
||||
$this->startLine = $this->lineIndex;
|
||||
|
||||
// Looking ahead for folded lines.
|
||||
while (true) {
|
||||
|
||||
$nextLine = rtrim(fgets($this->input), "\r\n");
|
||||
$this->lineIndex++;
|
||||
if (!$nextLine) {
|
||||
break;
|
||||
}
|
||||
if ($nextLine[0] === "\t" || $nextLine[0] === " ") {
|
||||
$line .= substr($nextLine, 1);
|
||||
$rawLine .= "\n " . substr($nextLine, 1);
|
||||
} else {
|
||||
$this->lineBuffer = $nextLine;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
$this->rawLine = $rawLine;
|
||||
return $line;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a property or component from a line.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function readProperty($line) {
|
||||
|
||||
if ($this->options & self::OPTION_FORGIVING) {
|
||||
$propNameToken = 'A-Z0-9\-\._\\/';
|
||||
} else {
|
||||
$propNameToken = 'A-Z0-9\-\.';
|
||||
}
|
||||
|
||||
$paramNameToken = 'A-Z0-9\-';
|
||||
$safeChar = '^";:,';
|
||||
$qSafeChar = '^"';
|
||||
|
||||
$regex = "/
|
||||
^(?P<name> [$propNameToken]+ ) (?=[;:]) # property name
|
||||
|
|
||||
(?<=:)(?P<propValue> .*)$ # property value
|
||||
|
|
||||
;(?P<paramName> [$paramNameToken]+) (?=[=;:]) # parameter name
|
||||
|
|
||||
(=|,)(?P<paramValue> # parameter value
|
||||
(?: [$safeChar]*) |
|
||||
\"(?: [$qSafeChar]+)\"
|
||||
) (?=[;:,])
|
||||
/xi";
|
||||
|
||||
//echo $regex, "\n"; die();
|
||||
preg_match_all($regex, $line, $matches, PREG_SET_ORDER );
|
||||
|
||||
$property = array(
|
||||
'name' => null,
|
||||
'parameters' => array(),
|
||||
'value' => null
|
||||
);
|
||||
|
||||
$lastParam = null;
|
||||
|
||||
/**
|
||||
* Looping through all the tokens.
|
||||
*
|
||||
* Note that we are looping through them in reverse order, because if a
|
||||
* sub-pattern matched, the subsequent named patterns will not show up
|
||||
* in the result.
|
||||
*/
|
||||
foreach($matches as $match) {
|
||||
|
||||
if (isset($match['paramValue'])) {
|
||||
if ($match['paramValue'] && $match['paramValue'][0] === '"') {
|
||||
$value = substr($match['paramValue'], 1, -1);
|
||||
} else {
|
||||
$value = $match['paramValue'];
|
||||
}
|
||||
|
||||
$value = $this->unescapeParam($value);
|
||||
|
||||
if (is_null($property['parameters'][$lastParam])) {
|
||||
$property['parameters'][$lastParam] = $value;
|
||||
} elseif (is_array($property['parameters'][$lastParam])) {
|
||||
$property['parameters'][$lastParam][] = $value;
|
||||
} else {
|
||||
$property['parameters'][$lastParam] = array(
|
||||
$property['parameters'][$lastParam],
|
||||
$value
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isset($match['paramName'])) {
|
||||
$lastParam = strtoupper($match['paramName']);
|
||||
if (!isset($property['parameters'][$lastParam])) {
|
||||
$property['parameters'][$lastParam] = null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (isset($match['propValue'])) {
|
||||
$property['value'] = $match['propValue'];
|
||||
continue;
|
||||
}
|
||||
if (isset($match['name']) && $match['name']) {
|
||||
$property['name'] = strtoupper($match['name']);
|
||||
continue;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new \LogicException('This code should not be reachable');
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
}
|
||||
|
||||
if (is_null($property['value']) || !$property['name']) {
|
||||
if ($this->options & self::OPTION_IGNORE_INVALID_LINES) {
|
||||
return false;
|
||||
}
|
||||
throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions');
|
||||
}
|
||||
|
||||
// vCard 2.1 states that parameters may appear without a name, and only
|
||||
// a value. We can deduce the value based on it's name.
|
||||
//
|
||||
// Our parser will get those as parameters without a value instead, so
|
||||
// we're filtering these parameters out first.
|
||||
$namedParameters = array();
|
||||
$namelessParameters = array();
|
||||
|
||||
foreach($property['parameters'] as $name=>$value) {
|
||||
if (!is_null($value)) {
|
||||
$namedParameters[$name] = $value;
|
||||
} else {
|
||||
$namelessParameters[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
$propObj = $this->root->createProperty($property['name'], null, $namedParameters);
|
||||
|
||||
foreach($namelessParameters as $namelessParameter) {
|
||||
$propObj->add(null, $namelessParameter);
|
||||
}
|
||||
|
||||
if (strtoupper($propObj['ENCODING']) === 'QUOTED-PRINTABLE') {
|
||||
$propObj->setQuotedPrintableValue($this->extractQuotedPrintableValue());
|
||||
} else {
|
||||
$propObj->setRawMimeDirValue($property['value']);
|
||||
}
|
||||
|
||||
return $propObj;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescapes a property value.
|
||||
*
|
||||
* vCard 2.1 says:
|
||||
* * Semi-colons must be escaped in some property values, specifically
|
||||
* ADR, ORG and N.
|
||||
* * Semi-colons must be escaped in parameter values, because semi-colons
|
||||
* are also use to separate values.
|
||||
* * No mention of escaping backslashes with another backslash.
|
||||
* * newlines are not escaped either, instead QUOTED-PRINTABLE is used to
|
||||
* span values over more than 1 line.
|
||||
*
|
||||
* vCard 3.0 says:
|
||||
* * (rfc2425) Backslashes, newlines (\n or \N) and comma's must be
|
||||
* escaped, all time time.
|
||||
* * Comma's are used for delimeters in multiple values
|
||||
* * (rfc2426) Adds to to this that the semi-colon MUST also be escaped,
|
||||
* as in some properties semi-colon is used for separators.
|
||||
* * Properties using semi-colons: N, ADR, GEO, ORG
|
||||
* * Both ADR and N's individual parts may be broken up further with a
|
||||
* comma.
|
||||
* * Properties using commas: NICKNAME, CATEGORIES
|
||||
*
|
||||
* vCard 4.0 (rfc6350) says:
|
||||
* * Commas must be escaped.
|
||||
* * Semi-colons may be escaped, an unescaped semi-colon _may_ be a
|
||||
* delimiter, depending on the property.
|
||||
* * Backslashes must be escaped
|
||||
* * Newlines must be escaped as either \N or \n.
|
||||
* * Some compound properties may contain multiple parts themselves, so a
|
||||
* comma within a semi-colon delimited property may also be unescaped
|
||||
* to denote multiple parts _within_ the compound property.
|
||||
* * Text-properties using semi-colons: N, ADR, ORG, CLIENTPIDMAP.
|
||||
* * Text-properties using commas: NICKNAME, RELATED, CATEGORIES, PID.
|
||||
*
|
||||
* Even though the spec says that commas must always be escaped, the
|
||||
* example for GEO in Section 6.5.2 seems to violate this.
|
||||
*
|
||||
* iCalendar 2.0 (rfc5545) says:
|
||||
* * Commas or semi-colons may be used as delimiters, depending on the
|
||||
* property.
|
||||
* * Commas, semi-colons, backslashes, newline (\N or \n) are always
|
||||
* escaped, unless they are delimiters.
|
||||
* * Colons shall not be escaped.
|
||||
* * Commas can be considered the 'default delimiter' and is described as
|
||||
* the delimiter in cases where the order of the multiple values is
|
||||
* insignificant.
|
||||
* * Semi-colons are described as the delimiter for 'structured values'.
|
||||
* They are specifically used in Semi-colons are used as a delimiter in
|
||||
* REQUEST-STATUS, RRULE, GEO and EXRULE. EXRULE is deprecated however.
|
||||
*
|
||||
* Now for the parameters
|
||||
*
|
||||
* If delimiter is not set (null) this method will just return a string.
|
||||
* If it's a comma or a semi-colon the string will be split on those
|
||||
* characters, and always return an array.
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $delimiter
|
||||
* @return string|string[]
|
||||
*/
|
||||
static public function unescapeValue($input, $delimiter = ';') {
|
||||
|
||||
$regex = '# (?: (\\\\ (?: \\\\ | N | n | ; | , ) )';
|
||||
if ($delimiter) {
|
||||
$regex .= ' | (' . $delimiter . ')';
|
||||
}
|
||||
$regex .= ') #x';
|
||||
|
||||
$matches = preg_split($regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
|
||||
|
||||
$resultArray = array();
|
||||
$result = '';
|
||||
|
||||
foreach($matches as $match) {
|
||||
|
||||
switch ($match) {
|
||||
case '\\\\' :
|
||||
$result .='\\';
|
||||
break;
|
||||
case '\N' :
|
||||
case '\n' :
|
||||
$result .="\n";
|
||||
break;
|
||||
case '\;' :
|
||||
$result .=';';
|
||||
break;
|
||||
case '\,' :
|
||||
$result .=',';
|
||||
break;
|
||||
case $delimiter :
|
||||
$resultArray[] = $result;
|
||||
$result = '';
|
||||
break;
|
||||
default :
|
||||
$result .= $match;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$resultArray[] = $result;
|
||||
return $delimiter ? $resultArray : $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescapes a parameter value.
|
||||
*
|
||||
* vCard 2.1:
|
||||
* * Does not mention a mechanism for this. In addition, double quotes
|
||||
* are never used to wrap values.
|
||||
* * This means that parameters can simply not contain colons or
|
||||
* semi-colons.
|
||||
*
|
||||
* vCard 3.0 (rfc2425, rfc2426):
|
||||
* * Parameters _may_ be surrounded by double quotes.
|
||||
* * If this is not the case, semi-colon, colon and comma may simply not
|
||||
* occur (the comma used for multiple parameter values though).
|
||||
* * If it is surrounded by double-quotes, it may simply not contain
|
||||
* double-quotes.
|
||||
* * This means that a parameter can in no case encode double-quotes, or
|
||||
* newlines.
|
||||
*
|
||||
* vCard 4.0 (rfc6350)
|
||||
* * Behavior seems to be identical to vCard 3.0
|
||||
*
|
||||
* iCalendar 2.0 (rfc5545)
|
||||
* * Behavior seems to be identical to vCard 3.0
|
||||
*
|
||||
* Parameter escaping mechanism (rfc6868) :
|
||||
* * This rfc describes a new way to escape parameter values.
|
||||
* * New-line is encoded as ^n
|
||||
* * ^ is encoded as ^^.
|
||||
* * " is encoded as ^'
|
||||
*
|
||||
* @param string $input
|
||||
* @return void
|
||||
*/
|
||||
private function unescapeParam($input) {
|
||||
|
||||
return
|
||||
preg_replace_callback('#(\^(\^|n|\'))#',function($matches) {
|
||||
switch($matches[2]) {
|
||||
case 'n' :
|
||||
return "\n";
|
||||
case '^' :
|
||||
return '^';
|
||||
case '\'' :
|
||||
return '"';
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}, $input);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full quoted printable value.
|
||||
*
|
||||
* We need a special method for this, because newlines have both a meaning
|
||||
* in vCards, and in QuotedPrintable.
|
||||
*
|
||||
* This method does not do any decoding.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function extractQuotedPrintableValue() {
|
||||
|
||||
// We need to parse the raw line again to get the start of the value.
|
||||
//
|
||||
// We are basically looking for the first colon (:), but we need to
|
||||
// skip over the parameters first, as they may contain one.
|
||||
$regex = '/^
|
||||
(?: [^:])+ # Anything but a colon
|
||||
(?: "[^"]")* # A parameter in double quotes
|
||||
: # start of the value we really care about
|
||||
(.*)$
|
||||
/xs';
|
||||
|
||||
preg_match($regex, $this->rawLine, $matches);
|
||||
|
||||
$value = $matches[1];
|
||||
// Removing the first whitespace character from every line. Kind of
|
||||
// like unfolding, but we keep the newline.
|
||||
$value = str_replace("\n ", "\n", $value);
|
||||
|
||||
// Microsoft products don't always correctly fold lines, they may be
|
||||
// missing a whitespace. So if 'forgiving' is turned on, we will take
|
||||
// those as well.
|
||||
if ($this->options & self::OPTION_FORGIVING) {
|
||||
while(substr($value,-1) === '=') {
|
||||
// Reading the line
|
||||
$this->readLine();
|
||||
// Grabbing the raw form
|
||||
$value.="\n" . $this->rawLine;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Parser;
|
||||
|
||||
/**
|
||||
* Abstract parser.
|
||||
*
|
||||
* This class serves as a base-class for the different parsers.
|
||||
*
|
||||
* @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 Parser {
|
||||
|
||||
/**
|
||||
* Turning on this option makes the parser more forgiving.
|
||||
*
|
||||
* In the case of the MimeDir parser, this means that the parser will
|
||||
* accept slashes and underscores in property names, and it will also
|
||||
* attempt to fix Microsoft vCard 2.1's broken line folding.
|
||||
*/
|
||||
const OPTION_FORGIVING = 1;
|
||||
|
||||
/**
|
||||
* If this option is turned on, any lines we cannot parse will be ignored
|
||||
* by the reader.
|
||||
*/
|
||||
const OPTION_IGNORE_INVALID_LINES = 2;
|
||||
|
||||
/**
|
||||
* Bitmask of parser options
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Creates the parser.
|
||||
*
|
||||
* Optionally, it's possible to parse the input stream here.
|
||||
*
|
||||
* @param mixed $input
|
||||
* @param int $options Any parser options (OPTION constants).
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($input = null, $options = 0) {
|
||||
|
||||
if (!is_null($input)) {
|
||||
$this->setInput($input);
|
||||
}
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method starts the parsing process.
|
||||
*
|
||||
* If the input was not supplied during construction, it's possible to pass
|
||||
* it here instead.
|
||||
*
|
||||
* If either input or options are not supplied, the defaults will be used.
|
||||
*
|
||||
* @param mixed $input
|
||||
* @param int|null $options
|
||||
* @return array
|
||||
*/
|
||||
abstract public function parse($input = null, $options = null);
|
||||
|
||||
/**
|
||||
* Sets the input data
|
||||
*
|
||||
* @param mixed $input
|
||||
* @return void
|
||||
*/
|
||||
abstract public function setInput($input);
|
||||
|
||||
}
|
|
@ -0,0 +1,502 @@
|
|||
<?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;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
use
|
||||
LogicException,
|
||||
OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
/**
|
||||
* BINARY property
|
||||
*
|
||||
* This object represents BINARY values.
|
||||
*
|
||||
* Binary values are most commonly used by the iCalendar ATTACH property, and
|
||||
* the vCard PHOTO property.
|
||||
*
|
||||
* This property will transparently encode and decode to base64.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Binary extends Property {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = null;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
if(is_array($value)) {
|
||||
|
||||
if(count($value) === 1) {
|
||||
$this->value = $value[0];
|
||||
} else {
|
||||
throw new \InvalidArgumentException('The argument must either be a string or an array with only one child');
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$this->value = $value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$this->value = base64_decode($val);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
return base64_encode($this->value);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return 'BINARY';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 array(base64_encode($this->getValue()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
$value = array_map('base64_decode', $value);
|
||||
parent::setJsonValue($value);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
/**
|
||||
* Boolean property
|
||||
*
|
||||
* This object represents BOOLEAN values. These are always the case-insenstive
|
||||
* string TRUE or FALSE.
|
||||
*
|
||||
* Automatic conversion to PHP's true and false are done.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Boolean extends Property {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$val = strtoupper($val)==='TRUE'?true:false;
|
||||
$this->setValue($val);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
return $this->value?'TRUE':'FALSE';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return 'BOOLEAN';
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
/**
|
||||
* FlatText property
|
||||
*
|
||||
* This object represents certain TEXT values.
|
||||
*
|
||||
* Specifically, this property is used for text values where there is only 1
|
||||
* part. Semi-colons and colons will be de-escaped when deserializing, but if
|
||||
* any semi-colons or commas appear without a backslash, we will not assume
|
||||
* that they are delimiters.
|
||||
*
|
||||
* vCard 2.1 specifically has a whole bunch of properties where this may
|
||||
* happen, as it only defines a delimiter for a few properties.
|
||||
*
|
||||
* vCard 4.0 states something similar. An unescaped semi-colon _may_ be a
|
||||
* delimiter, depending on the property.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class FlatText extends Text {
|
||||
|
||||
/**
|
||||
* Field separator
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $delimiter = ',';
|
||||
|
||||
/**
|
||||
* Sets the value as a quoted-printable encoded string.
|
||||
*
|
||||
* Overriding this so we're not splitting on a ; delimiter.
|
||||
*
|
||||
* @param string $val
|
||||
* @return void
|
||||
*/
|
||||
public function setQuotedPrintableValue($val) {
|
||||
|
||||
$val = quoted_printable_decode($val);
|
||||
$this->setValue($val);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
/**
|
||||
* Float property
|
||||
*
|
||||
* This object represents FLOAT values. These can be 1 or more floating-point
|
||||
* numbers.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Float extends Property {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = ';';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$val = explode($this->delimiter, $val);
|
||||
foreach($val as &$item) {
|
||||
$item = (float)$item;
|
||||
}
|
||||
$this->setParts($val);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
return implode(
|
||||
$this->delimiter,
|
||||
$this->getParts()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "FLOAT";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value, in the format it should be encoded for json.
|
||||
*
|
||||
* This method must always return an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsonValue() {
|
||||
|
||||
$val = array_map(function($item) {
|
||||
|
||||
return (float)$item;
|
||||
|
||||
}, $this->getParts());
|
||||
|
||||
// Special-casing the GEO property.
|
||||
//
|
||||
// See:
|
||||
// http://tools.ietf.org/html/draft-ietf-jcardcal-jcal-04#section-3.4.1.2
|
||||
if ($this->name==='GEO') {
|
||||
return array($val);
|
||||
} else {
|
||||
return $val;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\ICalendar;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property\Text;
|
||||
|
||||
/**
|
||||
* CalAddress property
|
||||
*
|
||||
* This object encodes CAL-ADDRESS values, as defined in rfc5545
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class CalAddress extends Text {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = null;
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return 'CAL-ADDRESS';
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\ICalendar;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property,
|
||||
OCA\Calendar\Sabre\VObject\Parser\MimeDir,
|
||||
OCA\Calendar\Sabre\VObject\DateTimeParser,
|
||||
OCA\Calendar\Sabre\VObject\TimeZoneUtil;
|
||||
|
||||
/**
|
||||
* DateTime property
|
||||
*
|
||||
* This object represents DATE values, as defined here:
|
||||
*
|
||||
* http://tools.ietf.org/html/rfc5545#section-3.3.5
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Date extends DateTime {
|
||||
|
||||
}
|
|
@ -0,0 +1,308 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\ICalendar;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property,
|
||||
OCA\Calendar\Sabre\VObject\Parser\MimeDir,
|
||||
OCA\Calendar\Sabre\VObject\DateTimeParser,
|
||||
OCA\Calendar\Sabre\VObject\TimeZoneUtil;
|
||||
|
||||
/**
|
||||
* DateTime property
|
||||
*
|
||||
* This object represents DATE-TIME values, as defined here:
|
||||
*
|
||||
* http://tools.ietf.org/html/rfc5545#section-3.3.4
|
||||
*
|
||||
* This particular object has a bit of hackish magic that it may also in some
|
||||
* cases represent a DATE value. This is because it's a common usecase to be
|
||||
* able to change a DATE-TIME into a DATE.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class DateTime extends Property {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = ',';
|
||||
|
||||
/**
|
||||
* Sets a multi-valued property.
|
||||
*
|
||||
* You may also specify DateTime objects here.
|
||||
*
|
||||
* @param array $parts
|
||||
* @return void
|
||||
*/
|
||||
public function setParts(array $parts) {
|
||||
|
||||
if (isset($parts[0]) && $parts[0] instanceof \DateTime) {
|
||||
$this->setDateTimes($parts);
|
||||
} else {
|
||||
parent::setParts($parts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current value.
|
||||
*
|
||||
* This may be either a single, or multiple strings in an array.
|
||||
*
|
||||
* Instead of strings, you may also use DateTime here.
|
||||
*
|
||||
* @param string|array|\DateTime $value
|
||||
* @return void
|
||||
*/
|
||||
public function setValue($value) {
|
||||
|
||||
if (is_array($value) && isset($value[0]) && $value[0] instanceof \DateTime) {
|
||||
$this->setDateTimes($value);
|
||||
} elseif ($value instanceof \DateTime) {
|
||||
$this->setDateTimes(array($value));
|
||||
} else {
|
||||
parent::setValue($value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$this->setValue(explode($this->delimiter, $val));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
return implode($this->delimiter, $this->getParts());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is a DATE-TIME value, false if it's a DATE.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTime() {
|
||||
|
||||
return strtoupper((string)$this['VALUE']) !== 'DATE';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a date-time value.
|
||||
*
|
||||
* Note that if this property contained more than 1 date-time, only the
|
||||
* first will be returned. To get an array with multiple values, call
|
||||
* getDateTimes.
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getDateTime() {
|
||||
|
||||
$dt = $this->getDateTimes();
|
||||
if (!$dt) return null;
|
||||
|
||||
return $dt[0];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns multiple date-time values.
|
||||
*
|
||||
* @return \DateTime[]
|
||||
*/
|
||||
public function getDateTimes() {
|
||||
|
||||
// Finding the timezone.
|
||||
$tz = $this['TZID'];
|
||||
|
||||
if ($tz) {
|
||||
$tz = TimeZoneUtil::getTimeZone((string)$tz, $this->root);
|
||||
}
|
||||
|
||||
$dts = array();
|
||||
foreach($this->getParts() as $part) {
|
||||
$dts[] = DateTimeParser::parse($part, $tz);
|
||||
}
|
||||
return $dts;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property as a DateTime object.
|
||||
*
|
||||
* @param \DateTime $dt
|
||||
* @param bool isFloating If set to true, timezones will be ignored.
|
||||
* @return void
|
||||
*/
|
||||
public function setDateTime(\DateTime $dt, $isFloating = false) {
|
||||
|
||||
$this->setDateTimes(array($dt), $isFloating);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property as multiple date-time objects.
|
||||
*
|
||||
* The first value will be used as a reference for the timezones, and all
|
||||
* the otehr values will be adjusted for that timezone
|
||||
*
|
||||
* @param \DateTime[] $dt
|
||||
* @param bool isFloating If set to true, timezones will be ignored.
|
||||
* @return void
|
||||
*/
|
||||
public function setDateTimes(array $dt, $isFloating = false) {
|
||||
|
||||
$values = array();
|
||||
|
||||
if($this->hasTime()) {
|
||||
|
||||
$tz = null;
|
||||
$isUtc = false;
|
||||
|
||||
foreach($dt as $d) {
|
||||
|
||||
if ($isFloating) {
|
||||
$values[] = $d->format('Ymd\\THis');
|
||||
continue;
|
||||
}
|
||||
if (is_null($tz)) {
|
||||
$tz = $d->getTimeZone();
|
||||
$isUtc = in_array($tz->getName() , array('UTC', 'GMT', 'Z'));
|
||||
if (!$isUtc) {
|
||||
$this->offsetSet('TZID', $tz->getName());
|
||||
}
|
||||
} else {
|
||||
$d->setTimeZone($tz);
|
||||
}
|
||||
|
||||
if ($isUtc) {
|
||||
$values[] = $d->format('Ymd\\THis\\Z');
|
||||
} else {
|
||||
$values[] = $d->format('Ymd\\THis');
|
||||
}
|
||||
|
||||
}
|
||||
if ($isUtc || $isFloating) {
|
||||
$this->offsetUnset('TZID');
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
foreach($dt as $d) {
|
||||
|
||||
$values[] = $d->format('Ymd');
|
||||
|
||||
}
|
||||
$this->offsetUnset('TZID');
|
||||
|
||||
}
|
||||
|
||||
$this->value = $values;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return $this->hasTime()?'DATE-TIME':'DATE';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value, in the format it should be encoded for json.
|
||||
*
|
||||
* This method must always return an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsonValue() {
|
||||
|
||||
$dts = $this->getDateTimes();
|
||||
$hasTime = $this->hasTime();
|
||||
|
||||
$tz = $dts[0]->getTimeZone();
|
||||
$isUtc = in_array($tz->getName() , array('UTC', 'GMT', 'Z'));
|
||||
|
||||
return array_map(function($dt) use ($hasTime, $isUtc) {
|
||||
|
||||
if ($hasTime) {
|
||||
return $dt->format('Y-m-d\\TH:i:s') . ($isUtc?'Z':'');
|
||||
} else {
|
||||
return $dt->format('Y-m-d');
|
||||
}
|
||||
|
||||
}, $dts);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
// dates and times in jCal have one difference to dates and times in
|
||||
// iCalendar. In jCal date-parts are separated by dashes, and
|
||||
// time-parts are separated by colons. It makes sense to just remove
|
||||
// those.
|
||||
$this->setValue(array_map(function($item) {
|
||||
|
||||
return strtr($item, array(':'=>'', '-'=>''));
|
||||
|
||||
}, $value));
|
||||
|
||||
}
|
||||
/**
|
||||
* We need to intercept offsetSet, because it may be used to alter the
|
||||
* VALUE from DATE-TIME to DATE or vice-versa.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($name, $value) {
|
||||
|
||||
parent::offsetSet($name, $value);
|
||||
if (strtoupper($name)!=='VALUE') {
|
||||
return;
|
||||
}
|
||||
|
||||
// This will ensure that dates are correctly encoded.
|
||||
$this->setDateTimes($this->getDateTimes());
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\ICalendar;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property,
|
||||
OCA\Calendar\Sabre\VObject\Parser\MimeDir,
|
||||
OCA\Calendar\Sabre\VObject\DateTimeParser;
|
||||
|
||||
/**
|
||||
* Duration property
|
||||
*
|
||||
* This object represents DURATION values, as defined here:
|
||||
*
|
||||
* http://tools.ietf.org/html/rfc5545#section-3.3.6
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Duration extends Property {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = ',';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$this->setValue(explode($this->delimiter, $val));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
return implode($this->delimiter, $this->getParts());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return 'DURATION';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a DateInterval representation of the Duration property.
|
||||
*
|
||||
* If the property has more than one value, only the first is returned.
|
||||
*
|
||||
* @return \DateInterval
|
||||
*/
|
||||
public function getDateInterval() {
|
||||
|
||||
$parts = $this->getParts();
|
||||
$value = $parts[0];
|
||||
return DateTimeParser::parseDuration($value);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\ICalendar;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property,
|
||||
OCA\Calendar\Sabre\VObject\Parser\MimeDir,
|
||||
OCA\Calendar\Sabre\VObject\DateTimeParser;
|
||||
|
||||
/**
|
||||
* Period property
|
||||
*
|
||||
* This object represents PERIOD values, as defined here:
|
||||
*
|
||||
* http://tools.ietf.org/html/rfc5545#section-3.8.2.6
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Period extends Property {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = ',';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$this->setValue(explode($this->delimiter, $val));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
return implode($this->delimiter, $this->getParts());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "PERIOD";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
$value = array_map(function($item) {
|
||||
|
||||
return strtr(implode('/', $item), array(':' => '', '-' => ''));
|
||||
|
||||
}, $value);
|
||||
parent::setJsonValue($value);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = array();
|
||||
foreach($this->getParts() as $item) {
|
||||
|
||||
list($start, $end) = explode('/', $item, 2);
|
||||
|
||||
$start = DateTimeParser::parseDateTime($start);
|
||||
|
||||
// This is a duration value.
|
||||
if ($end[0]==='P') {
|
||||
$return[] = array(
|
||||
$start->format('Y-m-d\\TH:i:s'),
|
||||
$end
|
||||
);
|
||||
} else {
|
||||
$end = DateTimeParser::parseDateTime($end);
|
||||
$return[] = array(
|
||||
$start->format('Y-m-d\\TH:i:s'),
|
||||
$end->format('Y-m-d\\TH:i:s'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\ICalendar;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property,
|
||||
OCA\Calendar\Sabre\VObject\Parser\MimeDir;
|
||||
|
||||
/**
|
||||
* Recur property
|
||||
*
|
||||
* This object represents RECUR properties.
|
||||
* These values are just used for RRULE and the now deprecated EXRULE.
|
||||
*
|
||||
* The RRULE property may look something like this:
|
||||
*
|
||||
* RRULE:FREQ=MONTHLY;BYDAY=1,2,3;BYHOUR=5.
|
||||
*
|
||||
* This property exposes this as a key=>value array that is accessible using
|
||||
* getParts, and may be set using setParts.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Recur extends Property {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
// If we're getting the data from json, we'll be receiving an object
|
||||
if ($value instanceof \StdClass) {
|
||||
$value = (array)$value;
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$newVal = array();
|
||||
foreach($value as $k=>$v) {
|
||||
|
||||
if (is_string($v)) {
|
||||
$v = strtoupper($v);
|
||||
|
||||
// The value had multiple sub-values
|
||||
if (strpos($v,',')!==false) {
|
||||
$v = explode(',', $v);
|
||||
}
|
||||
} else {
|
||||
$v = array_map('strtoupper', $v);
|
||||
}
|
||||
|
||||
$newVal[strtoupper($k)] = $v;
|
||||
}
|
||||
$this->value = $newVal;
|
||||
} elseif (is_string($value)) {
|
||||
$value = strtoupper($value);
|
||||
$newValue = array();
|
||||
foreach(explode(';', $value) as $part) {
|
||||
|
||||
// Skipping empty parts.
|
||||
if (empty($part)) {
|
||||
continue;
|
||||
}
|
||||
list($partName, $partValue) = explode('=', $part);
|
||||
|
||||
// The value itself had multiple values..
|
||||
if (strpos($partValue,',')!==false) {
|
||||
$partValue=explode(',', $partValue);
|
||||
}
|
||||
$newValue[$partName] = $partValue;
|
||||
|
||||
}
|
||||
$this->value = $newValue;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('You must either pass a string, or a key=>value array');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
$out = array();
|
||||
foreach($this->value as $key=>$value) {
|
||||
$out[] = $key . '=' . (is_array($value)?implode(',', $value):$value);
|
||||
}
|
||||
return strtoupper(implode(';',$out));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a multi-valued property.
|
||||
*
|
||||
* @param array $parts
|
||||
* @return void
|
||||
*/
|
||||
public function setParts(array $parts) {
|
||||
|
||||
$this->setValue($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() {
|
||||
|
||||
return $this->value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$this->setValue($val);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
return $this->getValue();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "RECUR";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value, in the format it should be encoded for json.
|
||||
*
|
||||
* This method must always return an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsonValue() {
|
||||
|
||||
$values = array();
|
||||
foreach($this->getParts() as $k=>$v) {
|
||||
$values[strtolower($k)] = $v;
|
||||
}
|
||||
return array($values);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
/**
|
||||
* Integer property
|
||||
*
|
||||
* This object represents INTEGER values. These are always a single integer.
|
||||
* They may be preceeded by either + or -.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Integer extends Property {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$this->setValue((int)$val);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
return $this->value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "INTEGER";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 array((int)$this->getValue());
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property,
|
||||
OCA\Calendar\Sabre\VObject\Component,
|
||||
OCA\Calendar\Sabre\VObject\Parser\MimeDir,
|
||||
OCA\Calendar\Sabre\VObject\Document;
|
||||
|
||||
/**
|
||||
* Text property
|
||||
*
|
||||
* This object represents TEXT values.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Text extends Property {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $delimiter = ',';
|
||||
|
||||
/**
|
||||
* List of properties that are considered 'structured'.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $structuredValues = array(
|
||||
// vCard
|
||||
'N',
|
||||
'ADR',
|
||||
'ORG',
|
||||
'GENDER',
|
||||
|
||||
// iCalendar
|
||||
'REQUEST-STATUS',
|
||||
);
|
||||
|
||||
/**
|
||||
* Some text components have a minimum number of components.
|
||||
*
|
||||
* N must for instance be represented as 5 components, separated by ;, even
|
||||
* if the last few components are unused.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $minimumPropertyValues = array(
|
||||
'N' => 5,
|
||||
'ADR' => 7,
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates the property.
|
||||
*
|
||||
* You can specify the parameters either in key=>value syntax, in which case
|
||||
* parameters will automatically be created, or you can just pass a list of
|
||||
* Parameter objects.
|
||||
*
|
||||
* @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) {
|
||||
|
||||
// There's two types of multi-valued text properties:
|
||||
// 1. multivalue properties.
|
||||
// 2. structured value properties
|
||||
//
|
||||
// The former is always separated by a comma, the latter by semi-colon.
|
||||
if (in_array($name, $this->structuredValues)) {
|
||||
$this->delimiter = ';';
|
||||
}
|
||||
|
||||
parent::__construct($root, $name, $value, $parameters, $group);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$this->setValue(MimeDir::unescapeValue($val, $this->delimiter));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value as a quoted-printable encoded string.
|
||||
*
|
||||
* @param string $val
|
||||
* @return void
|
||||
*/
|
||||
public function setQuotedPrintableValue($val) {
|
||||
|
||||
$val = quoted_printable_decode($val);
|
||||
|
||||
// Quoted printable only appears in vCard 2.1, and the only character
|
||||
// that may be escaped there is ;. So we are simply splitting on just
|
||||
// that.
|
||||
//
|
||||
// We also don't have to unescape \\, so all we need to look for is a ;
|
||||
// that's not preceeded with a \.
|
||||
$regex = '# (?<!\\\\) ; #x';
|
||||
$matches = preg_split($regex, $val);
|
||||
$this->setValue($matches);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
$val = $this->getParts();
|
||||
|
||||
if (isset($this->minimumPropertyValues[$this->name])) {
|
||||
$val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
|
||||
}
|
||||
|
||||
foreach($val as &$item) {
|
||||
|
||||
if (!is_array($item)) {
|
||||
$item = array($item);
|
||||
}
|
||||
|
||||
foreach($item as &$subItem) {
|
||||
$subItem = strtr($subItem, array(
|
||||
'\\' => '\\\\',
|
||||
';' => '\;',
|
||||
',' => '\,',
|
||||
"\n" => '\n',
|
||||
"\r" => "",
|
||||
));
|
||||
}
|
||||
$item = implode(',', $item);
|
||||
|
||||
}
|
||||
|
||||
return implode($this->delimiter, $val);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value, in the format it should be encoded for json.
|
||||
*
|
||||
* This method must always return an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsonValue() {
|
||||
|
||||
// Structured text values should always be returned as a single
|
||||
// array-item. Multi-value text should be returned as multiple items in
|
||||
// the top-array.
|
||||
if (in_array($this->name, $this->structuredValues)) {
|
||||
return array($this->getParts());
|
||||
} else {
|
||||
return $this->getParts();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "TEXT";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the object back into a serialized blob.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize() {
|
||||
|
||||
// We need to kick in a special type of encoding, if it's a 2.1 vcard.
|
||||
if ($this->root->getDocumentType() !== Document::VCARD21) {
|
||||
return parent::serialize();
|
||||
}
|
||||
|
||||
$val = $this->getParts();
|
||||
|
||||
if (isset($this->minimumPropertyValues[$this->name])) {
|
||||
$val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
|
||||
}
|
||||
|
||||
// Imploding multiple parts into a single value, and splitting the
|
||||
// values with ;.
|
||||
if (count($val)>1) {
|
||||
foreach($val as $k=>$v) {
|
||||
$val[$k] = str_replace(';','\;', $v);
|
||||
}
|
||||
$val = implode(';', $val);
|
||||
} else {
|
||||
$val = $val[0];
|
||||
}
|
||||
|
||||
$str = $this->name;
|
||||
if ($this->group) $str = $this->group . '.' . $this->name;
|
||||
foreach($this->parameters as $param) {
|
||||
|
||||
if ($param->getValue() === 'QUOTED-PRINTABLE') {
|
||||
continue;
|
||||
}
|
||||
$str.=';' . $param->serialize();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// If the resulting value contains a \n, we must encode it as
|
||||
// quoted-printable.
|
||||
if (strpos($val,"\n") !== false) {
|
||||
|
||||
$str.=';ENCODING=QUOTED-PRINTABLE:';
|
||||
$lastLine=$str;
|
||||
$out = null;
|
||||
|
||||
// The PHP built-in quoted-printable-encode does not correctly
|
||||
// encode newlines for us. Specifically, the \r\n sequence must in
|
||||
// vcards be encoded as =0D=OA and we must insert soft-newlines
|
||||
// every 75 bytes.
|
||||
for($ii=0;$ii<strlen($val);$ii++) {
|
||||
$ord = ord($val[$ii]);
|
||||
// These characters are encoded as themselves.
|
||||
if ($ord >= 32 && $ord <=126) {
|
||||
$lastLine.=$val[$ii];
|
||||
} else {
|
||||
$lastLine.='=' . strtoupper(bin2hex($val[$ii]));
|
||||
}
|
||||
if (strlen($lastLine)>=75) {
|
||||
// Soft line break
|
||||
$out.=$lastLine. "=\r\n ";
|
||||
$lastLine = null;
|
||||
}
|
||||
|
||||
}
|
||||
if (!is_null($lastLine)) $out.= $lastLine . "\r\n";
|
||||
return $out;
|
||||
|
||||
} else {
|
||||
$str.=':' . $val;
|
||||
$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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = parent::validate($options);
|
||||
|
||||
if (isset($this->minimumPropertyValues[$this->name])) {
|
||||
|
||||
$minimum = $this->minimumPropertyValues[$this->name];
|
||||
$parts = $this->getParts();
|
||||
if (count($parts) < $minimum) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'This property must have at least ' . $minimum . ' components. It only has ' . count($parts),
|
||||
'node' => $this,
|
||||
);
|
||||
if ($options & self::REPAIR) {
|
||||
$parts = array_pad($parts, $minimum, '');
|
||||
$this->setParts($parts);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return $warnings;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
use OCA\Calendar\Sabre\VObject\DateTimeParser;
|
||||
|
||||
/**
|
||||
* Time property
|
||||
*
|
||||
* This object encodes TIME values.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Time extends Text {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = null;
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "TIME";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value, in the format it should be encoded for json.
|
||||
*
|
||||
* This method must always return an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsonValue() {
|
||||
|
||||
$parts = DateTimeParser::parseVCardTime($this->getValue());
|
||||
|
||||
$timeStr = '';
|
||||
|
||||
// Hour
|
||||
if (!is_null($parts['hour'])) {
|
||||
$timeStr.=$parts['hour'];
|
||||
|
||||
if (!is_null($parts['minute'])) {
|
||||
$timeStr.=':';
|
||||
}
|
||||
} else {
|
||||
// We know either minute or second _must_ be set, so we insert a
|
||||
// dash for an empty value.
|
||||
$timeStr.='-';
|
||||
}
|
||||
|
||||
// Minute
|
||||
if (!is_null($parts['minute'])) {
|
||||
$timeStr.=$parts['minute'];
|
||||
|
||||
if (!is_null($parts['second'])) {
|
||||
$timeStr.=':';
|
||||
}
|
||||
} else {
|
||||
if (isset($parts['second'])) {
|
||||
// Dash for empty minute
|
||||
$timeStr.='-';
|
||||
}
|
||||
}
|
||||
|
||||
// Second
|
||||
if (!is_null($parts['second'])) {
|
||||
$timeStr.=$parts['second'];
|
||||
}
|
||||
|
||||
// Timezone
|
||||
if (!is_null($parts['timezone'])) {
|
||||
$timeStr.=$parts['timezone'];
|
||||
}
|
||||
|
||||
return array($timeStr);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property,
|
||||
OCA\Calendar\Sabre\VObject\Component,
|
||||
OCA\Calendar\Sabre\VObject\Parser\MimeDir,
|
||||
OCA\Calendar\Sabre\VObject\Document;
|
||||
|
||||
/**
|
||||
* Unknown property
|
||||
*
|
||||
* This object represents any properties not recognized by the parser.
|
||||
* This type of value has been introduced by the jCal, jCard specs.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Unknown extends Text {
|
||||
|
||||
/**
|
||||
* 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 array($this->getRawMimeDirValue());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "UNKNOWN";
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
use OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
/**
|
||||
* URI property
|
||||
*
|
||||
* This object encodes URI values. vCard 2.1 calls these URL.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Uri extends Property {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = null;
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "URI";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$this->value = $val;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
if (is_array($this->value)) {
|
||||
return $this->value[0];
|
||||
} else {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
/**
|
||||
* UtcOffset property
|
||||
*
|
||||
* This object encodes UTC-OFFSET values.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class UtcOffset extends Text {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = null;
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "UTC-OFFSET";
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\VCard;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\DateTimeParser;
|
||||
|
||||
/**
|
||||
* Date property
|
||||
*
|
||||
* This object encodes vCard DATE values.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class Date extends DateAndOrTime {
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "DATE";
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\VCard;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\DateTimeParser,
|
||||
OCA\Calendar\Sabre\VObject\Property\Text;
|
||||
|
||||
/**
|
||||
* DateAndOrTime property
|
||||
*
|
||||
* This object encodes DATE-AND-OR-TIME values.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class DateAndOrTime extends Text {
|
||||
|
||||
/**
|
||||
* Field separator
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
public $delimiter = null;
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "DATE-AND-OR-TIME";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value, in the format it should be encoded for json.
|
||||
*
|
||||
* This method must always return an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsonValue() {
|
||||
|
||||
$parts = DateTimeParser::parseVCardDateTime($this->getValue());
|
||||
|
||||
$dateStr = '';
|
||||
|
||||
// Year
|
||||
if (!is_null($parts['year'])) {
|
||||
$dateStr.=$parts['year'];
|
||||
|
||||
if (!is_null($parts['month'])) {
|
||||
// If a year and a month is set, we need to insert a separator
|
||||
// dash.
|
||||
$dateStr.='-';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!is_null($parts['month']) || !is_null($parts['date'])) {
|
||||
// Inserting two dashes
|
||||
$dateStr.='--';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Month
|
||||
|
||||
if (!is_null($parts['month'])) {
|
||||
$dateStr.=$parts['month'];
|
||||
|
||||
if (isset($parts['date'])) {
|
||||
// If month and date are set, we need the separator dash.
|
||||
$dateStr.='-';
|
||||
}
|
||||
} else {
|
||||
if (isset($parts['date'])) {
|
||||
// If the month is empty, and a date is set, we need a 'empty
|
||||
// dash'
|
||||
$dateStr.='-';
|
||||
}
|
||||
}
|
||||
|
||||
// Date
|
||||
if (!is_null($parts['date'])) {
|
||||
$dateStr.=$parts['date'];
|
||||
}
|
||||
|
||||
|
||||
// Early exit if we don't have a time string.
|
||||
if (is_null($parts['hour']) && is_null($parts['minute']) && is_null($parts['second'])) {
|
||||
return array($dateStr);
|
||||
}
|
||||
|
||||
$dateStr.='T';
|
||||
|
||||
// Hour
|
||||
if (!is_null($parts['hour'])) {
|
||||
$dateStr.=$parts['hour'];
|
||||
|
||||
if (!is_null($parts['minute'])) {
|
||||
$dateStr.=':';
|
||||
}
|
||||
} else {
|
||||
// We know either minute or second _must_ be set, so we insert a
|
||||
// dash for an empty value.
|
||||
$dateStr.='-';
|
||||
}
|
||||
|
||||
// Minute
|
||||
if (!is_null($parts['minute'])) {
|
||||
$dateStr.=$parts['minute'];
|
||||
|
||||
if (!is_null($parts['second'])) {
|
||||
$dateStr.=':';
|
||||
}
|
||||
} else {
|
||||
if (isset($parts['second'])) {
|
||||
// Dash for empty minute
|
||||
$dateStr.='-';
|
||||
}
|
||||
}
|
||||
|
||||
// Second
|
||||
if (!is_null($parts['second'])) {
|
||||
$dateStr.=$parts['second'];
|
||||
}
|
||||
|
||||
// Timezone
|
||||
if (!is_null($parts['timezone'])) {
|
||||
$dateStr.=$parts['timezone'];
|
||||
}
|
||||
|
||||
return array($dateStr);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\VCard;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\DateTimeParser;
|
||||
|
||||
/**
|
||||
* DateTime property
|
||||
*
|
||||
* This object encodes DATE-TIME values for vCards.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class DateTime extends DateAndOrTime {
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "DATE-TIME";
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\VCard;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\Property;
|
||||
|
||||
/**
|
||||
* LanguageTag property
|
||||
*
|
||||
* This object represents LANGUAGE-TAG values as used in vCards.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class LanguageTag extends Property {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function setRawMimeDirValue($val) {
|
||||
|
||||
$this->setValue($val);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raw mime-dir representation of the value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawMimeDirValue() {
|
||||
|
||||
return $this->value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "LANGUAGE-TAG";
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Property\VCard;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject\DateTimeParser,
|
||||
OCA\Calendar\Sabre\VObject\Property\Text;
|
||||
|
||||
/**
|
||||
* TimeStamp property
|
||||
*
|
||||
* This object encodes TIMESTAMP values.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class TimeStamp extends Text {
|
||||
|
||||
/**
|
||||
* In case this is a multi-value property. This string will be used as a
|
||||
* delimiter.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $delimiter = null;
|
||||
|
||||
/**
|
||||
* Returns the type of value.
|
||||
*
|
||||
* This corresponds to the VALUE= parameter. Every property also has a
|
||||
* 'default' valueType.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValueType() {
|
||||
|
||||
return "TIMESTAMP";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value, in the format it should be encoded for json.
|
||||
*
|
||||
* This method must always return an array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsonValue() {
|
||||
|
||||
$parts = DateTimeParser::parseVCardDateTime($this->getValue());
|
||||
|
||||
$dateStr =
|
||||
$parts['year'] . '-' .
|
||||
$parts['month'] . '-' .
|
||||
$parts['date'] . 'T' .
|
||||
$parts['hour'] . ':' .
|
||||
$parts['minute'] . ':' .
|
||||
$parts['second'];
|
||||
|
||||
// Timezone
|
||||
if (!is_null($parts['timezone'])) {
|
||||
$dateStr.=$parts['timezone'];
|
||||
}
|
||||
|
||||
return array($dateStr);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* iCalendar/vCard/jCal/jCard reader object.
|
||||
*
|
||||
* This object provides a few (static) convenience methods to quickly access
|
||||
* the parsers.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Reader {
|
||||
|
||||
/**
|
||||
* If this option is passed to the reader, it will be less strict about the
|
||||
* validity of the lines.
|
||||
*/
|
||||
const OPTION_FORGIVING = 1;
|
||||
|
||||
/**
|
||||
* If this option is turned on, any lines we cannot parse will be ignored
|
||||
* by the reader.
|
||||
*/
|
||||
const OPTION_IGNORE_INVALID_LINES = 2;
|
||||
|
||||
/**
|
||||
* Parses a vCard or iCalendar object, and returns the top component.
|
||||
*
|
||||
* The options argument is a bitfield. Pass any of the OPTIONS constant to
|
||||
* alter the parsers' behaviour.
|
||||
*
|
||||
* You can either supply a string, or a readable stream for input.
|
||||
*
|
||||
* @param string|resource $data
|
||||
* @param int $options
|
||||
* @return Document
|
||||
*/
|
||||
static function read($data, $options = 0) {
|
||||
|
||||
$parser = new Parser\MimeDir();
|
||||
$result = $parser->parse($data, $options);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a jCard or jCal object, and returns the top component.
|
||||
*
|
||||
* The options argument is a bitfield. Pass any of the OPTIONS constant to
|
||||
* alter the parsers' behaviour.
|
||||
*
|
||||
* You can either a string, a readable stream, or an array for it's input.
|
||||
* Specifying the array is useful if json_decode was already called on the
|
||||
* input.
|
||||
*
|
||||
* @param string|resource|array $data
|
||||
* @param int $options
|
||||
* @return Node
|
||||
*/
|
||||
static function readJson($data, $options = 0) {
|
||||
|
||||
$parser = new Parser\Json();
|
||||
$result = $parser->parse($data, $options);
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Splitter;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject,
|
||||
OCA\Calendar\Sabre\VObject\Component\VCalendar;
|
||||
|
||||
/**
|
||||
* Splitter
|
||||
*
|
||||
* This class is responsible for splitting up iCalendar objects.
|
||||
*
|
||||
* This class expects a single VCALENDAR object with one or more
|
||||
* calendar-objects inside. Objects with identical UID's will be combined into
|
||||
* a single object.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Dominik Tobschall
|
||||
* @author Armin Hackmann
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class ICalendar implements SplitterInterface {
|
||||
|
||||
/**
|
||||
* Timezones
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $vtimezones = array();
|
||||
|
||||
/**
|
||||
* iCalendar objects
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $objects = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The splitter should receive an readable file stream as it's input.
|
||||
*
|
||||
* @param resource $input
|
||||
* @param int $options Parser options, see the OPTIONS constants.
|
||||
*/
|
||||
public function __construct($input, $options = 0) {
|
||||
|
||||
$data = VObject\Reader::read($input, $options);
|
||||
$vtimezones = array();
|
||||
$components = array();
|
||||
|
||||
foreach($data->children() as $component) {
|
||||
if (!$component instanceof VObject\Component) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all timezones
|
||||
if ($component->name === 'VTIMEZONE') {
|
||||
$this->vtimezones[(string)$component->TZID] = $component;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get component UID for recurring Events search
|
||||
if($component->UID) {
|
||||
$uid = (string)$component->UID;
|
||||
} else {
|
||||
// Generating a random UID
|
||||
$uid = sha1(microtime()) . '-vobjectimport';
|
||||
}
|
||||
|
||||
// Take care of recurring events
|
||||
if (!array_key_exists($uid, $this->objects)) {
|
||||
$this->objects[$uid] = new VCalendar();
|
||||
}
|
||||
|
||||
$this->objects[$uid]->add(clone $component);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Every time getNext() is called, a new object will be parsed, until we
|
||||
* hit the end of the stream.
|
||||
*
|
||||
* When the end is reached, null will be returned.
|
||||
*
|
||||
* @return Sabre\VObject\Component|null
|
||||
*/
|
||||
public function getNext() {
|
||||
|
||||
if($object=array_shift($this->objects)) {
|
||||
|
||||
// create our baseobject
|
||||
$object->version = '2.0';
|
||||
$object->prodid = '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN';
|
||||
$object->calscale = 'GREGORIAN';
|
||||
|
||||
// add vtimezone information to obj (if we have it)
|
||||
foreach ($this->vtimezones as $vtimezone) {
|
||||
$object->add($vtimezone);
|
||||
}
|
||||
|
||||
return $object;
|
||||
|
||||
} else {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Splitter;
|
||||
|
||||
/**
|
||||
* VObject splitter
|
||||
*
|
||||
* The splitter is responsible for reading a large vCard or iCalendar object,
|
||||
* and splitting it into multiple objects.
|
||||
*
|
||||
* This is for example for Card and CalDAV, which require every event and vcard
|
||||
* to exist in their own objects, instead of one large one.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Dominik Tobschall
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface SplitterInterface {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The splitter should receive an readable file stream as it's input.
|
||||
*
|
||||
* @param resource $input
|
||||
*/
|
||||
function __construct($input);
|
||||
|
||||
/**
|
||||
* Every time getNext() is called, a new object will be parsed, until we
|
||||
* hit the end of the stream.
|
||||
*
|
||||
* When the end is reached, null will be returned.
|
||||
*
|
||||
* @return Sabre\VObject\Component|null
|
||||
*/
|
||||
function getNext();
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject\Splitter;
|
||||
|
||||
use
|
||||
OCA\Calendar\Sabre\VObject,
|
||||
OCA\Calendar\Sabre\VObject\Parser\MimeDir;
|
||||
|
||||
/**
|
||||
* Splitter
|
||||
*
|
||||
* This class is responsible for splitting up VCard objects.
|
||||
*
|
||||
* It is assumed that the input stream contains 1 or more VCARD objects. This
|
||||
* class checks for BEGIN:VCARD and END:VCARD and parses each encountered
|
||||
* component individually.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Dominik Tobschall
|
||||
* @author Armin Hackmann
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VCard implements SplitterInterface {
|
||||
|
||||
/**
|
||||
* File handle
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $input;
|
||||
|
||||
/**
|
||||
* Persistent parser
|
||||
*
|
||||
* @var MimeDir
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The splitter should receive an readable file stream as it's input.
|
||||
*
|
||||
* @param resource $input
|
||||
* @param int $options Parser options, see the OPTIONS constants.
|
||||
*/
|
||||
public function __construct($input, $options = 0) {
|
||||
|
||||
$this->input = $input;
|
||||
$this->parser = new MimeDir($input, $options);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Every time getNext() is called, a new object will be parsed, until we
|
||||
* hit the end of the stream.
|
||||
*
|
||||
* When the end is reached, null will be returned.
|
||||
*
|
||||
* @return Sabre\VObject\Component|null
|
||||
*/
|
||||
public function getNext() {
|
||||
|
||||
try {
|
||||
$object = $this->parser->parse();
|
||||
} catch (VObject\EofException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $object;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Useful utilities for working with various strings.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class StringUtil {
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if a string is valid UTF-8
|
||||
*
|
||||
* @param string $str
|
||||
* @return bool
|
||||
*/
|
||||
static function isUTF8($str) {
|
||||
|
||||
// First check.. mb_check_encoding
|
||||
if (!mb_check_encoding($str, 'UTF-8')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Control characters
|
||||
if (preg_match('%(?:[\x00-\x08\x0B-\x0C\x0E\x0F])%', $str)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tries its best to convert the input string to UTF-8.
|
||||
*
|
||||
* Currently only ISO-5991-1 input and UTF-8 input is supported, but this
|
||||
* may be expanded upon if we receive other examples.
|
||||
*
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
static function convertToUTF8($str) {
|
||||
|
||||
$encoding = mb_detect_encoding($str , array('UTF-8','ISO-8859-1'), true);
|
||||
|
||||
if ($encoding === 'ISO-8859-1') {
|
||||
$newStr = utf8_encode($str);
|
||||
} else {
|
||||
$newStr = $str;
|
||||
}
|
||||
|
||||
// Removing any control characters
|
||||
return (preg_replace('%(?:[\x00-\x08\x0B-\x0C\x0E\x0F])%', '', $newStr));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,527 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Time zone name translation
|
||||
*
|
||||
* This file translates well-known time zone names into "Olson database" time zone names.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Frank Edelhaeuser (fedel@users.sourceforge.net)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class TimeZoneUtil {
|
||||
|
||||
public static $map = array(
|
||||
|
||||
// from http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html
|
||||
// snapshot taken on 2012/01/16
|
||||
|
||||
// windows
|
||||
'AUS Central Standard Time'=>'Australia/Darwin',
|
||||
'AUS Eastern Standard Time'=>'Australia/Sydney',
|
||||
'Afghanistan Standard Time'=>'Asia/Kabul',
|
||||
'Alaskan Standard Time'=>'America/Anchorage',
|
||||
'Arab Standard Time'=>'Asia/Riyadh',
|
||||
'Arabian Standard Time'=>'Asia/Dubai',
|
||||
'Arabic Standard Time'=>'Asia/Baghdad',
|
||||
'Argentina Standard Time'=>'America/Buenos_Aires',
|
||||
'Armenian Standard Time'=>'Asia/Yerevan',
|
||||
'Atlantic Standard Time'=>'America/Halifax',
|
||||
'Azerbaijan Standard Time'=>'Asia/Baku',
|
||||
'Azores Standard Time'=>'Atlantic/Azores',
|
||||
'Bangladesh Standard Time'=>'Asia/Dhaka',
|
||||
'Canada Central Standard Time'=>'America/Regina',
|
||||
'Cape Verde Standard Time'=>'Atlantic/Cape_Verde',
|
||||
'Caucasus Standard Time'=>'Asia/Yerevan',
|
||||
'Cen. Australia Standard Time'=>'Australia/Adelaide',
|
||||
'Central America Standard Time'=>'America/Guatemala',
|
||||
'Central Asia Standard Time'=>'Asia/Almaty',
|
||||
'Central Brazilian Standard Time'=>'America/Cuiaba',
|
||||
'Central Europe Standard Time'=>'Europe/Budapest',
|
||||
'Central European Standard Time'=>'Europe/Warsaw',
|
||||
'Central Pacific Standard Time'=>'Pacific/Guadalcanal',
|
||||
'Central Standard Time'=>'America/Chicago',
|
||||
'Central Standard Time (Mexico)'=>'America/Mexico_City',
|
||||
'China Standard Time'=>'Asia/Shanghai',
|
||||
'Dateline Standard Time'=>'Etc/GMT+12',
|
||||
'E. Africa Standard Time'=>'Africa/Nairobi',
|
||||
'E. Australia Standard Time'=>'Australia/Brisbane',
|
||||
'E. Europe Standard Time'=>'Europe/Minsk',
|
||||
'E. South America Standard Time'=>'America/Sao_Paulo',
|
||||
'Eastern Standard Time'=>'America/New_York',
|
||||
'Egypt Standard Time'=>'Africa/Cairo',
|
||||
'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg',
|
||||
'FLE Standard Time'=>'Europe/Kiev',
|
||||
'Fiji Standard Time'=>'Pacific/Fiji',
|
||||
'GMT Standard Time'=>'Europe/London',
|
||||
'GTB Standard Time'=>'Europe/Istanbul',
|
||||
'Georgian Standard Time'=>'Asia/Tbilisi',
|
||||
'Greenland Standard Time'=>'America/Godthab',
|
||||
'Greenwich Standard Time'=>'Atlantic/Reykjavik',
|
||||
'Hawaiian Standard Time'=>'Pacific/Honolulu',
|
||||
'India Standard Time'=>'Asia/Calcutta',
|
||||
'Iran Standard Time'=>'Asia/Tehran',
|
||||
'Israel Standard Time'=>'Asia/Jerusalem',
|
||||
'Jordan Standard Time'=>'Asia/Amman',
|
||||
'Kamchatka Standard Time'=>'Asia/Kamchatka',
|
||||
'Korea Standard Time'=>'Asia/Seoul',
|
||||
'Magadan Standard Time'=>'Asia/Magadan',
|
||||
'Mauritius Standard Time'=>'Indian/Mauritius',
|
||||
'Mexico Standard Time'=>'America/Mexico_City',
|
||||
'Mexico Standard Time 2'=>'America/Chihuahua',
|
||||
'Mid-Atlantic Standard Time'=>'Etc/GMT-2',
|
||||
'Middle East Standard Time'=>'Asia/Beirut',
|
||||
'Montevideo Standard Time'=>'America/Montevideo',
|
||||
'Morocco Standard Time'=>'Africa/Casablanca',
|
||||
'Mountain Standard Time'=>'America/Denver',
|
||||
'Mountain Standard Time (Mexico)'=>'America/Chihuahua',
|
||||
'Myanmar Standard Time'=>'Asia/Rangoon',
|
||||
'N. Central Asia Standard Time'=>'Asia/Novosibirsk',
|
||||
'Namibia Standard Time'=>'Africa/Windhoek',
|
||||
'Nepal Standard Time'=>'Asia/Katmandu',
|
||||
'New Zealand Standard Time'=>'Pacific/Auckland',
|
||||
'Newfoundland Standard Time'=>'America/St_Johns',
|
||||
'North Asia East Standard Time'=>'Asia/Irkutsk',
|
||||
'North Asia Standard Time'=>'Asia/Krasnoyarsk',
|
||||
'Pacific SA Standard Time'=>'America/Santiago',
|
||||
'Pacific Standard Time'=>'America/Los_Angeles',
|
||||
'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel',
|
||||
'Pakistan Standard Time'=>'Asia/Karachi',
|
||||
'Paraguay Standard Time'=>'America/Asuncion',
|
||||
'Romance Standard Time'=>'Europe/Paris',
|
||||
'Russian Standard Time'=>'Europe/Moscow',
|
||||
'SA Eastern Standard Time'=>'America/Cayenne',
|
||||
'SA Pacific Standard Time'=>'America/Bogota',
|
||||
'SA Western Standard Time'=>'America/La_Paz',
|
||||
'SE Asia Standard Time'=>'Asia/Bangkok',
|
||||
'Samoa Standard Time'=>'Pacific/Apia',
|
||||
'Singapore Standard Time'=>'Asia/Singapore',
|
||||
'South Africa Standard Time'=>'Africa/Johannesburg',
|
||||
'Sri Lanka Standard Time'=>'Asia/Colombo',
|
||||
'Syria Standard Time'=>'Asia/Damascus',
|
||||
'Taipei Standard Time'=>'Asia/Taipei',
|
||||
'Tasmania Standard Time'=>'Australia/Hobart',
|
||||
'Tokyo Standard Time'=>'Asia/Tokyo',
|
||||
'Tonga Standard Time'=>'Pacific/Tongatapu',
|
||||
'US Eastern Standard Time'=>'America/Indianapolis',
|
||||
'US Mountain Standard Time'=>'America/Phoenix',
|
||||
'UTC+12'=>'Etc/GMT-12',
|
||||
'UTC-02'=>'Etc/GMT+2',
|
||||
'UTC-11'=>'Etc/GMT+11',
|
||||
'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar',
|
||||
'Venezuela Standard Time'=>'America/Caracas',
|
||||
'Vladivostok Standard Time'=>'Asia/Vladivostok',
|
||||
'W. Australia Standard Time'=>'Australia/Perth',
|
||||
'W. Central Africa Standard Time'=>'Africa/Lagos',
|
||||
'W. Europe Standard Time'=>'Europe/Berlin',
|
||||
'West Asia Standard Time'=>'Asia/Tashkent',
|
||||
'West Pacific Standard Time'=>'Pacific/Port_Moresby',
|
||||
'Yakutsk Standard Time'=>'Asia/Yakutsk',
|
||||
|
||||
// Microsoft exchange timezones
|
||||
// Source:
|
||||
// http://msdn.microsoft.com/en-us/library/ms988620%28v=exchg.65%29.aspx
|
||||
//
|
||||
// Correct timezones deduced with help from:
|
||||
// http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
'Universal Coordinated Time' => 'UTC',
|
||||
'Casablanca, Monrovia' => 'Africa/Casablanca',
|
||||
'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon',
|
||||
'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London',
|
||||
'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin',
|
||||
'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague',
|
||||
'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris',
|
||||
'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris',
|
||||
'Prague, Central Europe' => 'Europe/Prague',
|
||||
'Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb' => 'Europe/Sarajevo',
|
||||
'West Central Africa' => 'Africa/Luanda', // This was a best guess
|
||||
'Athens, Istanbul, Minsk' => 'Europe/Athens',
|
||||
'Bucharest' => 'Europe/Bucharest',
|
||||
'Cairo' => 'Africa/Cairo',
|
||||
'Harare, Pretoria' => 'Africa/Harare',
|
||||
'Helsinki, Riga, Tallinn' => 'Europe/Helsinki',
|
||||
'Israel, Jerusalem Standard Time' => 'Asia/Jerusalem',
|
||||
'Baghdad' => 'Asia/Baghdad',
|
||||
'Arab, Kuwait, Riyadh' => 'Asia/Kuwait',
|
||||
'Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow',
|
||||
'East Africa, Nairobi' => 'Africa/Nairobi',
|
||||
'Tehran' => 'Asia/Tehran',
|
||||
'Abu Dhabi, Muscat' => 'Asia/Muscat', // Best guess
|
||||
'Baku, Tbilisi, Yerevan' => 'Asia/Baku',
|
||||
'Kabul' => 'Asia/Kabul',
|
||||
'Ekaterinburg' => 'Asia/Yekaterinburg',
|
||||
'Islamabad, Karachi, Tashkent' => 'Asia/Karachi',
|
||||
'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta',
|
||||
'Kathmandu, Nepal' => 'Asia/Kathmandu',
|
||||
'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty',
|
||||
'Astana, Dhaka' => 'Asia/Dhaka',
|
||||
'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo',
|
||||
'Rangoon' => 'Asia/Rangoon',
|
||||
'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok',
|
||||
'Krasnoyarsk' => 'Asia/Krasnoyarsk',
|
||||
'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai',
|
||||
'Irkutsk, Ulaan Bataar' => 'Asia/Irkutsk',
|
||||
'Kuala Lumpur, Singapore' => 'Asia/Singapore',
|
||||
'Perth, Western Australia' => 'Australia/Perth',
|
||||
'Taipei' => 'Asia/Taipei',
|
||||
'Osaka, Sapporo, Tokyo' => 'Asia/Tokyo',
|
||||
'Seoul, Korea Standard time' => 'Asia/Seoul',
|
||||
'Yakutsk' => 'Asia/Yakutsk',
|
||||
'Adelaide, Central Australia' => 'Australia/Adelaide',
|
||||
'Darwin' => 'Australia/Darwin',
|
||||
'Brisbane, East Australia' => 'Australia/Brisbane',
|
||||
'Canberra, Melbourne, Sydney, Hobart (year 2000 only)' => 'Australia/Sydney',
|
||||
'Guam, Port Moresby' => 'Pacific/Guam',
|
||||
'Hobart, Tasmania' => 'Australia/Hobart',
|
||||
'Vladivostok' => 'Asia/Vladivostok',
|
||||
'Magadan, Solomon Is., New Caledonia' => 'Asia/Magadan',
|
||||
'Auckland, Wellington' => 'Pacific/Auckland',
|
||||
'Fiji Islands, Kamchatka, Marshall Is.' => 'Pacific/Fiji',
|
||||
'Nuku\'alofa, Tonga' => 'Pacific/Tongatapu',
|
||||
'Azores' => 'Atlantic/Azores',
|
||||
'Cape Verde Is.' => 'Atlantic/Cape_Verde',
|
||||
'Mid-Atlantic' => 'America/Noronha',
|
||||
'Brasilia' => 'America/Sao_Paulo', // Best guess
|
||||
'Buenos Aires' => 'America/Argentina/Buenos_Aires',
|
||||
'Greenland' => 'America/Godthab',
|
||||
'Newfoundland' => 'America/St_Johns',
|
||||
'Atlantic Time (Canada)' => 'America/Halifax',
|
||||
'Caracas, La Paz' => 'America/Caracas',
|
||||
'Santiago' => 'America/Santiago',
|
||||
'Bogota, Lima, Quito' => 'America/Bogota',
|
||||
'Eastern Time (US & Canada)' => 'America/New_York',
|
||||
'Indiana (East)' => 'America/Indiana/Indianapolis',
|
||||
'Central America' => 'America/Guatemala',
|
||||
'Central Time (US & Canada)' => 'America/Chicago',
|
||||
'Mexico City, Tegucigalpa' => 'America/Mexico_City',
|
||||
'Saskatchewan' => 'America/Edmonton',
|
||||
'Arizona' => 'America/Phoenix',
|
||||
'Mountain Time (US & Canada)' => 'America/Denver', // Best guess
|
||||
'Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles', // Best guess
|
||||
'Alaska' => 'America/Anchorage',
|
||||
'Hawaii' => 'Pacific/Honolulu',
|
||||
'Midway Island, Samoa' => 'Pacific/Midway',
|
||||
'Eniwetok, Kwajalein, Dateline Time' => 'Pacific/Kwajalein',
|
||||
|
||||
// The following list are timezone names that could be generated by
|
||||
// Lotus / Domino
|
||||
'Dateline' => 'Etc/GMT-12',
|
||||
'Samoa' => 'Pacific/Apia',
|
||||
'Hawaiian' => 'Pacific/Honolulu',
|
||||
'Alaskan' => 'America/Anchorage',
|
||||
'Pacific' => 'America/Los_Angeles',
|
||||
'Pacific Standard Time' => 'America/Los_Angeles',
|
||||
'Mexico Standard Time 2' => 'America/Chihuahua',
|
||||
'Mountain' => 'America/Denver',
|
||||
'Mountain Standard Time' => 'America/Chihuahua',
|
||||
'US Mountain' => 'America/Phoenix',
|
||||
'Canada Central' => 'America/Edmonton',
|
||||
'Central America' => 'America/Guatemala',
|
||||
'Central' => 'America/Chicago',
|
||||
'Central Standard Time' => 'America/Mexico_City',
|
||||
'Mexico' => 'America/Mexico_City',
|
||||
'Eastern' => 'America/New_York',
|
||||
'SA Pacific' => 'America/Bogota',
|
||||
'US Eastern' => 'America/Indiana/Indianapolis',
|
||||
'Venezuela' => 'America/Caracas',
|
||||
'Atlantic' => 'America/Halifax',
|
||||
'Central Brazilian' => 'America/Manaus',
|
||||
'Pacific SA' => 'America/Santiago',
|
||||
'SA Western' => 'America/La_Paz',
|
||||
'Newfoundland' => 'America/St_Johns',
|
||||
'Argentina' => 'America/Argentina/Buenos_Aires',
|
||||
'E. South America' => 'America/Belem',
|
||||
'Greenland' => 'America/Godthab',
|
||||
'Montevideo' => 'America/Montevideo',
|
||||
'SA Eastern' => 'America/Belem',
|
||||
'Mid-Atlantic' => 'Etc/GMT-2',
|
||||
'Azores' => 'Atlantic/Azores',
|
||||
'Cape Verde' => 'Atlantic/Cape_Verde',
|
||||
'Greenwich' => 'Atlantic/Reykjavik', // No I'm serious.. Greenwich is not GMT.
|
||||
'Morocco' => 'Africa/Casablanca',
|
||||
'Central Europe' => 'Europe/Prague',
|
||||
'Central European' => 'Europe/Sarajevo',
|
||||
'Romance' => 'Europe/Paris',
|
||||
'W. Central Africa' => 'Africa/Lagos', // Best guess
|
||||
'W. Europe' => 'Europe/Amsterdam',
|
||||
'E. Europe' => 'Europe/Minsk',
|
||||
'Egypt' => 'Africa/Cairo',
|
||||
'FLE' => 'Europe/Helsinki',
|
||||
'GTB' => 'Europe/Athens',
|
||||
'Israel' => 'Asia/Jerusalem',
|
||||
'Jordan' => 'Asia/Amman',
|
||||
'Middle East' => 'Asia/Beirut',
|
||||
'Namibia' => 'Africa/Windhoek',
|
||||
'South Africa' => 'Africa/Harare',
|
||||
'Arab' => 'Asia/Kuwait',
|
||||
'Arabic' => 'Asia/Baghdad',
|
||||
'E. Africa' => 'Africa/Nairobi',
|
||||
'Georgian' => 'Asia/Tbilisi',
|
||||
'Russian' => 'Europe/Moscow',
|
||||
'Iran' => 'Asia/Tehran',
|
||||
'Arabian' => 'Asia/Muscat',
|
||||
'Armenian' => 'Asia/Yerevan',
|
||||
'Azerbijan' => 'Asia/Baku',
|
||||
'Caucasus' => 'Asia/Yerevan',
|
||||
'Mauritius' => 'Indian/Mauritius',
|
||||
'Afghanistan' => 'Asia/Kabul',
|
||||
'Ekaterinburg' => 'Asia/Yekaterinburg',
|
||||
'Pakistan' => 'Asia/Karachi',
|
||||
'West Asia' => 'Asia/Tashkent',
|
||||
'India' => 'Asia/Calcutta',
|
||||
'Sri Lanka' => 'Asia/Colombo',
|
||||
'Nepal' => 'Asia/Kathmandu',
|
||||
'Central Asia' => 'Asia/Dhaka',
|
||||
'N. Central Asia' => 'Asia/Almaty',
|
||||
'Myanmar' => 'Asia/Rangoon',
|
||||
'North Asia' => 'Asia/Krasnoyarsk',
|
||||
'SE Asia' => 'Asia/Bangkok',
|
||||
'China' => 'Asia/Shanghai',
|
||||
'North Asia East' => 'Asia/Irkutsk',
|
||||
'Singapore' => 'Asia/Singapore',
|
||||
'Taipei' => 'Asia/Taipei',
|
||||
'W. Australia' => 'Australia/Perth',
|
||||
'Korea' => 'Asia/Seoul',
|
||||
'Tokyo' => 'Asia/Tokyo',
|
||||
'Yakutsk' => 'Asia/Yakutsk',
|
||||
'AUS Central' => 'Australia/Darwin',
|
||||
'Cen. Australia' => 'Australia/Adelaide',
|
||||
'AUS Eastern' => 'Australia/Sydney',
|
||||
'E. Australia' => 'Australia/Brisbane',
|
||||
'Tasmania' => 'Australia/Hobart',
|
||||
'Vladivostok' => 'Asia/Vladivostok',
|
||||
'West Pacific' => 'Pacific/Guam',
|
||||
'Central Pacific' => 'Asia/Magadan',
|
||||
'Fiji' => 'Pacific/Fiji',
|
||||
'New Zealand' => 'Pacific/Auckland',
|
||||
'Tonga' => 'Pacific/Tongatapu',
|
||||
|
||||
// PHP 5.5.10 failed on a few timezones that were valid before. We're
|
||||
// normalizing them here.
|
||||
'CST6CDT' => 'America/Chicago',
|
||||
'Cuba' => 'America/Havana',
|
||||
'Egypt' => 'Africa/Cairo',
|
||||
'Eire' => 'Europe/Dublin',
|
||||
'EST5EDT' => 'America/New_York',
|
||||
'Factory' => 'UTC',
|
||||
'GB-Eire' => 'Europe/London',
|
||||
'GMT0' => 'UTC',
|
||||
'Greenwich' => 'UTC',
|
||||
'Hongkong' => 'Asia/Hong_Kong',
|
||||
'Iceland' => 'Atlantic/Reykjavik',
|
||||
'Iran' => 'Asia/Tehran',
|
||||
'Israel' => 'Asia/Jerusalem',
|
||||
'Jamaica' => 'America/Jamaica',
|
||||
'Japan' => 'Asia/Tokyo',
|
||||
'Kwajalein' => 'Pacific/Kwajalein',
|
||||
'Libya' => 'Africa/Tripoli',
|
||||
'MST7MDT' => 'America/Denver',
|
||||
'Navajo' => 'America/Denver',
|
||||
'NZ-CHAT' => 'Pacific/Chatham',
|
||||
'Poland' => 'Europe/Warsaw',
|
||||
'Portugal' => 'Europe/Lisbon',
|
||||
'PST8PDT' => 'America/Los_Angeles',
|
||||
'Singapore' => 'Asia/Singapore',
|
||||
'Turkey' => 'Europe/Istanbul',
|
||||
'Universal' => 'UTC',
|
||||
'W-SU' => 'Europe/Moscow',
|
||||
);
|
||||
|
||||
/**
|
||||
* List of microsoft exchange timezone ids.
|
||||
*
|
||||
* Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx
|
||||
*/
|
||||
public static $microsoftExchangeMap = array(
|
||||
0 => 'UTC',
|
||||
31 => 'Africa/Casablanca',
|
||||
|
||||
// Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo.
|
||||
// I'm not even kidding.. We handle this special case in the
|
||||
// getTimeZone method.
|
||||
2 => 'Europe/Lisbon',
|
||||
1 => 'Europe/London',
|
||||
4 => 'Europe/Berlin',
|
||||
6 => 'Europe/Prague',
|
||||
3 => 'Europe/Paris',
|
||||
69 => 'Africa/Luanda', // This was a best guess
|
||||
7 => 'Europe/Athens',
|
||||
5 => 'Europe/Bucharest',
|
||||
49 => 'Africa/Cairo',
|
||||
50 => 'Africa/Harare',
|
||||
59 => 'Europe/Helsinki',
|
||||
27 => 'Asia/Jerusalem',
|
||||
26 => 'Asia/Baghdad',
|
||||
74 => 'Asia/Kuwait',
|
||||
51 => 'Europe/Moscow',
|
||||
56 => 'Africa/Nairobi',
|
||||
25 => 'Asia/Tehran',
|
||||
24 => 'Asia/Muscat', // Best guess
|
||||
54 => 'Asia/Baku',
|
||||
48 => 'Asia/Kabul',
|
||||
58 => 'Asia/Yekaterinburg',
|
||||
47 => 'Asia/Karachi',
|
||||
23 => 'Asia/Calcutta',
|
||||
62 => 'Asia/Kathmandu',
|
||||
46 => 'Asia/Almaty',
|
||||
71 => 'Asia/Dhaka',
|
||||
66 => 'Asia/Colombo',
|
||||
61 => 'Asia/Rangoon',
|
||||
22 => 'Asia/Bangkok',
|
||||
64 => 'Asia/Krasnoyarsk',
|
||||
45 => 'Asia/Shanghai',
|
||||
63 => 'Asia/Irkutsk',
|
||||
21 => 'Asia/Singapore',
|
||||
73 => 'Australia/Perth',
|
||||
75 => 'Asia/Taipei',
|
||||
20 => 'Asia/Tokyo',
|
||||
72 => 'Asia/Seoul',
|
||||
70 => 'Asia/Yakutsk',
|
||||
19 => 'Australia/Adelaide',
|
||||
44 => 'Australia/Darwin',
|
||||
18 => 'Australia/Brisbane',
|
||||
76 => 'Australia/Sydney',
|
||||
43 => 'Pacific/Guam',
|
||||
42 => 'Australia/Hobart',
|
||||
68 => 'Asia/Vladivostok',
|
||||
41 => 'Asia/Magadan',
|
||||
17 => 'Pacific/Auckland',
|
||||
40 => 'Pacific/Fiji',
|
||||
67 => 'Pacific/Tongatapu',
|
||||
29 => 'Atlantic/Azores',
|
||||
53 => 'Atlantic/Cape_Verde',
|
||||
30 => 'America/Noronha',
|
||||
8 => 'America/Sao_Paulo', // Best guess
|
||||
32 => 'America/Argentina/Buenos_Aires',
|
||||
60 => 'America/Godthab',
|
||||
28 => 'America/St_Johns',
|
||||
9 => 'America/Halifax',
|
||||
33 => 'America/Caracas',
|
||||
65 => 'America/Santiago',
|
||||
35 => 'America/Bogota',
|
||||
10 => 'America/New_York',
|
||||
34 => 'America/Indiana/Indianapolis',
|
||||
55 => 'America/Guatemala',
|
||||
11 => 'America/Chicago',
|
||||
37 => 'America/Mexico_City',
|
||||
36 => 'America/Edmonton',
|
||||
38 => 'America/Phoenix',
|
||||
12 => 'America/Denver', // Best guess
|
||||
13 => 'America/Los_Angeles', // Best guess
|
||||
14 => 'America/Anchorage',
|
||||
15 => 'Pacific/Honolulu',
|
||||
16 => 'Pacific/Midway',
|
||||
39 => 'Pacific/Kwajalein',
|
||||
);
|
||||
|
||||
/**
|
||||
* This method will try to find out the correct timezone for an iCalendar
|
||||
* date-time value.
|
||||
*
|
||||
* You must pass the contents of the TZID parameter, as well as the full
|
||||
* calendar.
|
||||
*
|
||||
* If the lookup fails, this method will return the default PHP timezone
|
||||
* (as configured using date_default_timezone_set, or the date.timezone ini
|
||||
* setting).
|
||||
*
|
||||
* Alternatively, if $failIfUncertain is set to true, it will throw an
|
||||
* exception if we cannot accurately determine the timezone.
|
||||
*
|
||||
* @param string $tzid
|
||||
* @param Sabre\VObject\Component $vcalendar
|
||||
* @return DateTimeZone
|
||||
*/
|
||||
static public function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false) {
|
||||
|
||||
// First we will just see if the tzid is a support timezone identifier.
|
||||
//
|
||||
// The only exception is if the timezone starts with (. This is to
|
||||
// handle cases where certain microsoft products generate timezone
|
||||
// identifiers that for instance look like:
|
||||
//
|
||||
// (GMT+01.00) Sarajevo/Warsaw/Zagreb
|
||||
//
|
||||
// Since PHP 5.5.10, the first bit will be used as the timezone and
|
||||
// this method will return just GMT+01:00. This is wrong, because it
|
||||
// doesn't take DST into account.
|
||||
if ($tzid[0]!=='(') {
|
||||
try {
|
||||
return new \DateTimeZone($tzid);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Next, we check if the tzid is somewhere in our tzid map.
|
||||
if (isset(self::$map[$tzid])) {
|
||||
return new \DateTimeZone(self::$map[$tzid]);
|
||||
}
|
||||
|
||||
// Maybe the author was hyper-lazy and just included an offset. We
|
||||
// support it, but we aren't happy about it.
|
||||
//
|
||||
// Note that the path in the source will never be taken from PHP 5.5.10
|
||||
// onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it
|
||||
// already gets returned early in this function. Once we drop support
|
||||
// for versions under PHP 5.5.10, this bit can be taken out of the
|
||||
// source.
|
||||
if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
|
||||
return new \DateTimeZone('Etc/GMT' . $matches[1] . ltrim(substr($matches[2],0,2),'0'));
|
||||
}
|
||||
|
||||
if ($vcalendar) {
|
||||
|
||||
// If that didn't work, we will scan VTIMEZONE objects
|
||||
foreach($vcalendar->select('VTIMEZONE') as $vtimezone) {
|
||||
|
||||
if ((string)$vtimezone->TZID === $tzid) {
|
||||
|
||||
// Some clients add 'X-LIC-LOCATION' with the olson name.
|
||||
if (isset($vtimezone->{'X-LIC-LOCATION'})) {
|
||||
|
||||
$lic = (string)$vtimezone->{'X-LIC-LOCATION'};
|
||||
|
||||
// Libical generators may specify strings like
|
||||
// "SystemV/EST5EDT". For those we must remove the
|
||||
// SystemV part.
|
||||
if (substr($lic,0,8)==='SystemV/') {
|
||||
$lic = substr($lic,8);
|
||||
}
|
||||
|
||||
return self::getTimeZone($lic, null, $failIfUncertain);
|
||||
|
||||
}
|
||||
// Microsoft may add a magic number, which we also have an
|
||||
// answer for.
|
||||
if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
|
||||
$cdoId = (int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue();
|
||||
|
||||
// 2 can mean both Europe/Lisbon and Europe/Sarajevo.
|
||||
if ($cdoId===2 && strpos((string)$vtimezone->TZID, 'Sarajevo')!==false) {
|
||||
return new \DateTimeZone('Europe/Sarajevo');
|
||||
}
|
||||
|
||||
if (isset(self::$microsoftExchangeMap[$cdoId])) {
|
||||
return new \DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($failIfUncertain) {
|
||||
throw new \InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: ' . $tzid);
|
||||
}
|
||||
|
||||
// If we got all the way here, we default to UTC.
|
||||
return new \DateTimeZone(date_default_timezone_get());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,382 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* This utility converts vcards from one version to another.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
class VCardConverter {
|
||||
|
||||
/**
|
||||
* Converts a vCard object to a new version.
|
||||
*
|
||||
* targetVersion must be one of:
|
||||
* Document::VCARD21
|
||||
* Document::VCARD30
|
||||
* Document::VCARD40
|
||||
*
|
||||
* Currently only 3.0 and 4.0 as input and output versions.
|
||||
*
|
||||
* 2.1 has some minor support for the input version, it's incomplete at the
|
||||
* moment though.
|
||||
*
|
||||
* If input and output version are identical, a clone is returned.
|
||||
*
|
||||
* @param Component\VCard $input
|
||||
* @param int $targetVersion
|
||||
*/
|
||||
public function convert(Component\VCard $input, $targetVersion) {
|
||||
|
||||
$inputVersion = $input->getDocumentType();
|
||||
if ($inputVersion===$targetVersion) {
|
||||
return clone $input;
|
||||
}
|
||||
|
||||
if (!in_array($inputVersion, array(Document::VCARD21, Document::VCARD30, Document::VCARD40))) {
|
||||
throw new \InvalidArgumentException('Only vCard 2.1, 3.0 and 4.0 are supported for the input data');
|
||||
}
|
||||
if (!in_array($targetVersion, array(Document::VCARD30, Document::VCARD40))) {
|
||||
throw new \InvalidArgumentException('You can only use vCard 3.0 or 4.0 for the target version');
|
||||
}
|
||||
|
||||
$newVersion = $targetVersion===Document::VCARD40?'4.0':'3.0';
|
||||
|
||||
$output = new Component\VCard(array(
|
||||
'VERSION' => $newVersion,
|
||||
));
|
||||
|
||||
foreach($input->children as $property) {
|
||||
|
||||
$this->convertProperty($input, $output, $property, $targetVersion);
|
||||
|
||||
}
|
||||
|
||||
return $output;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles conversion of a single property.
|
||||
*
|
||||
* @param Component\VCard $input
|
||||
* @param Component\VCard $output
|
||||
* @param Property $property
|
||||
* @param int $targetVersion
|
||||
* @return void
|
||||
*/
|
||||
protected function convertProperty(Component\VCard $input, Component\VCard $output, Property $property, $targetVersion) {
|
||||
|
||||
// Skipping these, those are automatically added.
|
||||
if (in_array($property->name, array('VERSION', 'PRODID'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parameters = $property->parameters();
|
||||
|
||||
$valueType = null;
|
||||
if (isset($parameters['VALUE'])) {
|
||||
$valueType = $parameters['VALUE']->getValue();
|
||||
unset($parameters['VALUE']);
|
||||
}
|
||||
if (!$valueType) {
|
||||
$valueType = $property->getValueType();
|
||||
}
|
||||
|
||||
$newProperty = null;
|
||||
|
||||
if ($targetVersion===Document::VCARD30) {
|
||||
|
||||
if ($property instanceof Property\Uri && in_array($property->name, array('PHOTO','LOGO','SOUND'))) {
|
||||
|
||||
$newProperty = $this->convertUriToBinary($output, $property, $parameters);
|
||||
|
||||
} elseif ($property->name === 'KIND') {
|
||||
|
||||
switch(strtolower($property->getValue())) {
|
||||
case 'org' :
|
||||
// OS X addressbook property.
|
||||
$newProperty = $output->createProperty('X-ABSHOWAS','COMPANY');
|
||||
break;
|
||||
case 'individual' :
|
||||
// Individual is implied, so we can just skip it.
|
||||
return;
|
||||
|
||||
case 'group' :
|
||||
// OS X addressbook property
|
||||
$newProperty = $output->createProperty('X-ADDRESSBOOKSERVER-KIND','GROUP');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
} elseif ($targetVersion===Document::VCARD40) {
|
||||
|
||||
// These properties were removed in vCard 4.0
|
||||
if (in_array($property->name, array('NAME', 'MAILER', 'LABEL', 'CLASS'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($property instanceOf Property\Binary) {
|
||||
|
||||
$newProperty = $this->convertBinaryToUri($output, $property, $parameters);
|
||||
|
||||
} else {
|
||||
switch($property->name) {
|
||||
case 'X-ABSHOWAS' :
|
||||
if (strtoupper($property->getValue()) === 'COMPANY') {
|
||||
$newProperty = $output->createProperty('KIND','org');
|
||||
}
|
||||
break;
|
||||
case 'X-ADDRESSBOOKSERVER-KIND' :
|
||||
if (strtoupper($property->getValue()) === 'GROUP') {
|
||||
$newProperty = $output->createProperty('KIND','group');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (is_null($newProperty)) {
|
||||
|
||||
$newProperty = $output->createProperty(
|
||||
$property->name,
|
||||
$property->getParts(),
|
||||
array(), // no parameters yet
|
||||
$valueType
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// set property group
|
||||
$newProperty->group = $property->group;
|
||||
|
||||
if ($targetVersion===Document::VCARD40) {
|
||||
$this->convertParameters40($newProperty, $parameters);
|
||||
} else {
|
||||
$this->convertParameters30($newProperty, $parameters);
|
||||
}
|
||||
|
||||
// Lastly, we need to see if there's a need for a VALUE parameter.
|
||||
//
|
||||
// We can do that by instantating a empty property with that name, and
|
||||
// seeing if the default valueType is identical to the current one.
|
||||
$tempProperty = $output->createProperty($newProperty->name);
|
||||
if ($tempProperty->getValueType() !== $newProperty->getValueType()) {
|
||||
$newProperty['VALUE'] = $newProperty->getValueType();
|
||||
}
|
||||
|
||||
$output->add($newProperty);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BINARY property to a URI property.
|
||||
*
|
||||
* vCard 4.0 no longer supports BINARY properties.
|
||||
*
|
||||
* @param Component\VCard $output
|
||||
* @param Property\Uri $property The input property.
|
||||
* @param $parameters List of parameters that will eventually be added to
|
||||
* the new property.
|
||||
* @return Property\Uri
|
||||
*/
|
||||
protected function convertBinaryToUri(Component\VCard $output, Property\Binary $property, array &$parameters) {
|
||||
|
||||
$newProperty = $output->createProperty(
|
||||
$property->name,
|
||||
null, // no value
|
||||
array(), // no parameters yet
|
||||
'URI' // Forcing the BINARY type
|
||||
);
|
||||
|
||||
$mimeType = 'application/octet-stream';
|
||||
|
||||
// See if we can find a better mimetype.
|
||||
if (isset($parameters['TYPE'])) {
|
||||
|
||||
$newTypes = array();
|
||||
foreach($parameters['TYPE']->getParts() as $typePart) {
|
||||
if (in_array(
|
||||
strtoupper($typePart),
|
||||
array('JPEG','PNG','GIF')
|
||||
)) {
|
||||
$mimeType = 'image/' . strtolower($typePart);
|
||||
} else {
|
||||
$newTypes[] = $typePart;
|
||||
}
|
||||
}
|
||||
|
||||
// If there were any parameters we're not converting to a
|
||||
// mime-type, we need to keep them.
|
||||
if ($newTypes) {
|
||||
$parameters['TYPE']->setParts($newTypes);
|
||||
} else {
|
||||
unset($parameters['TYPE']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$newProperty->setValue('data:' . $mimeType . ';base64,' . base64_encode($property->getValue()));
|
||||
|
||||
return $newProperty;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a URI property to a BINARY property.
|
||||
*
|
||||
* In vCard 4.0 attachments are encoded as data: uri. Even though these may
|
||||
* be valid in vCard 3.0 as well, we should convert those to BINARY if
|
||||
* possible, to improve compatibility.
|
||||
*
|
||||
* @param Component\VCard $output
|
||||
* @param Property\Uri $property The input property.
|
||||
* @param $parameters List of parameters that will eventually be added to
|
||||
* the new property.
|
||||
* @return Property\Binary|null
|
||||
*/
|
||||
protected function convertUriToBinary(Component\VCard $output, Property\Uri $property, array &$parameters) {
|
||||
|
||||
$value = $property->getValue();
|
||||
|
||||
// Only converting data: uris
|
||||
if (substr($value, 0, 5)!=='data:') {
|
||||
return;
|
||||
}
|
||||
|
||||
$newProperty = $output->createProperty(
|
||||
$property->name,
|
||||
null, // no value
|
||||
array(), // no parameters yet
|
||||
'BINARY'
|
||||
);
|
||||
|
||||
$mimeType = substr($value, 5, strpos($value, ',')-5);
|
||||
if (strpos($mimeType, ';')) {
|
||||
$mimeType = substr($mimeType,0,strpos($mimeType, ';'));
|
||||
$newProperty->setValue(base64_decode(substr($value, strpos($value,',')+1)));
|
||||
} else {
|
||||
$newProperty->setValue(substr($value, strpos($value,',')+1));
|
||||
}
|
||||
unset($value);
|
||||
|
||||
$newProperty['ENCODING'] = 'b';
|
||||
switch($mimeType) {
|
||||
|
||||
case 'image/jpeg' :
|
||||
$newProperty['TYPE'] = 'JPEG';
|
||||
break;
|
||||
case 'image/png' :
|
||||
$newProperty['TYPE'] = 'PNG';
|
||||
break;
|
||||
case 'image/gif' :
|
||||
$newProperty['TYPE'] = 'GIF';
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return $newProperty;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds parameters to a new property for vCard 4.0
|
||||
*
|
||||
* @param Property $newProperty
|
||||
* @param array $parameters
|
||||
* @return void
|
||||
*/
|
||||
protected function convertParameters40(Property $newProperty, array $parameters) {
|
||||
|
||||
// Adding all parameters.
|
||||
foreach($parameters as $param) {
|
||||
|
||||
// vCard 2.1 allowed parameters with no name
|
||||
if ($param->noName) $param->noName = false;
|
||||
|
||||
switch($param->name) {
|
||||
|
||||
// We need to see if there's any TYPE=PREF, because in vCard 4
|
||||
// that's now PREF=1.
|
||||
case 'TYPE' :
|
||||
foreach($param->getParts() as $paramPart) {
|
||||
|
||||
if (strtoupper($paramPart)==='PREF') {
|
||||
$newProperty->add('PREF','1');
|
||||
} else {
|
||||
$newProperty->add($param->name, $paramPart);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
// These no longer exist in vCard 4
|
||||
case 'ENCODING' :
|
||||
case 'CHARSET' :
|
||||
break;
|
||||
|
||||
default :
|
||||
$newProperty->add($param->name, $param->getParts());
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds parameters to a new property for vCard 3.0
|
||||
*
|
||||
* @param Property $newProperty
|
||||
* @param array $parameters
|
||||
* @return void
|
||||
*/
|
||||
protected function convertParameters30(Property $newProperty, array $parameters) {
|
||||
|
||||
// Adding all parameters.
|
||||
foreach($parameters as $param) {
|
||||
|
||||
// vCard 2.1 allowed parameters with no name
|
||||
if ($param->noName) $param->noName = false;
|
||||
|
||||
switch($param->name) {
|
||||
|
||||
case 'ENCODING' :
|
||||
// This value only existed in vCard 2.1, and should be
|
||||
// removed for anything else.
|
||||
if (strtoupper($param->getValue())!=='QUOTED-PRINTABLE') {
|
||||
$newProperty->add($param->name, $param->getParts());
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* Converting PREF=1 to TYPE=PREF.
|
||||
*
|
||||
* Any other PREF numbers we'll drop.
|
||||
*/
|
||||
case 'PREF' :
|
||||
if ($param->getValue()=='1') {
|
||||
$newProperty->add('TYPE','PREF');
|
||||
}
|
||||
break;
|
||||
|
||||
default :
|
||||
$newProperty->add($param->name, $param->getParts());
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Calendar\Sabre\VObject;
|
||||
|
||||
/**
|
||||
* This class contains the version number for the VObject package
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Version {
|
||||
|
||||
/**
|
||||
* Full version number
|
||||
*/
|
||||
const VERSION = '3.1.4';
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Includes file
|
||||
*
|
||||
* This file includes the entire VObject library in one go.
|
||||
* The benefit is that an autoloader is not needed, which is often faster.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
// Begin includes
|
||||
include __DIR__ . '/Cli.php';
|
||||
include __DIR__ . '/DateTimeParser.php';
|
||||
include __DIR__ . '/ElementList.php';
|
||||
include __DIR__ . '/FreeBusyGenerator.php';
|
||||
include __DIR__ . '/Node.php';
|
||||
include __DIR__ . '/Parameter.php';
|
||||
include __DIR__ . '/ParseException.php';
|
||||
include __DIR__ . '/Parser/Parser.php';
|
||||
include __DIR__ . '/Property.php';
|
||||
include __DIR__ . '/Reader.php';
|
||||
include __DIR__ . '/RecurrenceIterator.php';
|
||||
include __DIR__ . '/Splitter/SplitterInterface.php';
|
||||
include __DIR__ . '/Splitter/VCard.php';
|
||||
include __DIR__ . '/StringUtil.php';
|
||||
include __DIR__ . '/TimeZoneUtil.php';
|
||||
include __DIR__ . '/VCardConverter.php';
|
||||
include __DIR__ . '/Version.php';
|
||||
include __DIR__ . '/Component.php';
|
||||
include __DIR__ . '/Document.php';
|
||||
include __DIR__ . '/EofException.php';
|
||||
include __DIR__ . '/Parser/Json.php';
|
||||
include __DIR__ . '/Parser/MimeDir.php';
|
||||
include __DIR__ . '/Property/Binary.php';
|
||||
include __DIR__ . '/Property/Boolean.php';
|
||||
include __DIR__ . '/Property/Float.php';
|
||||
include __DIR__ . '/Property/ICalendar/DateTime.php';
|
||||
include __DIR__ . '/Property/ICalendar/Duration.php';
|
||||
include __DIR__ . '/Property/ICalendar/Period.php';
|
||||
include __DIR__ . '/Property/ICalendar/Recur.php';
|
||||
include __DIR__ . '/Property/Integer.php';
|
||||
include __DIR__ . '/Property/Text.php';
|
||||
include __DIR__ . '/Property/Time.php';
|
||||
include __DIR__ . '/Property/Unknown.php';
|
||||
include __DIR__ . '/Property/Uri.php';
|
||||
include __DIR__ . '/Property/UtcOffset.php';
|
||||
include __DIR__ . '/Property/VCard/DateAndOrTime.php';
|
||||
include __DIR__ . '/Property/VCard/DateTime.php';
|
||||
include __DIR__ . '/Property/VCard/LanguageTag.php';
|
||||
include __DIR__ . '/Property/VCard/TimeStamp.php';
|
||||
include __DIR__ . '/Splitter/ICalendar.php';
|
||||
include __DIR__ . '/Component/VAlarm.php';
|
||||
include __DIR__ . '/Component/VCalendar.php';
|
||||
include __DIR__ . '/Component/VCard.php';
|
||||
include __DIR__ . '/Component/VEvent.php';
|
||||
include __DIR__ . '/Component/VFreeBusy.php';
|
||||
include __DIR__ . '/Component/VJournal.php';
|
||||
include __DIR__ . '/Component/VTodo.php';
|
||||
include __DIR__ . '/Property/FlatText.php';
|
||||
include __DIR__ . '/Property/ICalendar/CalAddress.php';
|
||||
include __DIR__ . '/Property/ICalendar/Date.php';
|
||||
include __DIR__ . '/Property/VCard/Date.php';
|
||||
// End includes
|
|
@ -0,0 +1,618 @@
|
|||
/*
|
||||
* FullCalendar v1.5.4 Stylesheet
|
||||
*
|
||||
* Copyright (c) 2011 Adam Shaw
|
||||
* Dual licensed under the MIT and GPL licenses, located in
|
||||
* MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
|
||||
*
|
||||
* Date: Tue Sep 4 23:38:33 2012 -0700
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
.fc {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.fc table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
html .fc,
|
||||
.fc table {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.fc td,
|
||||
.fc th {
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Header
|
||||
------------------------------------------------------------------------*/
|
||||
|
||||
.fc-header td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fc-header-left {
|
||||
width: 25%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.fc-header-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fc-header-right {
|
||||
width: 25%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.fc-header-title {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.fc-header-title h2 {
|
||||
margin-top: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fc .fc-header-space {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.fc-header .fc-button {
|
||||
margin-bottom: 1em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* buttons edges butting together */
|
||||
|
||||
.fc-header .fc-button {
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
.fc-header .fc-corner-right {
|
||||
margin-right: 1px; /* back to normal */
|
||||
}
|
||||
|
||||
.fc-header .ui-corner-right {
|
||||
margin-right: 0; /* back to normal */
|
||||
}
|
||||
|
||||
/* button layering (for border precedence) */
|
||||
|
||||
.fc-header .fc-state-hover,
|
||||
.fc-header .ui-state-hover {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.fc-header .fc-state-down {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.fc-header .fc-state-active,
|
||||
.fc-header .ui-state-active {
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Content
|
||||
------------------------------------------------------------------------*/
|
||||
|
||||
.fc-content {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.fc-view {
|
||||
width: 100%; /* needed for view switching (when view is absolute) */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Cell Styles
|
||||
------------------------------------------------------------------------*/
|
||||
|
||||
.fc-widget-header, /* <th>, usually */
|
||||
.fc-widget-content { /* <td>, usually */
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.fc-state-highlight { /* <td> today cell */ /* TODO: add .fc-today to <th> */
|
||||
background: #ffc;
|
||||
}
|
||||
|
||||
.fc-cell-overlay { /* semi-transparent rectangle while dragging */
|
||||
background: #9cf;
|
||||
opacity: .2;
|
||||
filter: alpha(opacity=20); /* for IE */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Buttons
|
||||
------------------------------------------------------------------------*/
|
||||
|
||||
.fc-button {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fc-state-default { /* non-theme */
|
||||
border-style: solid;
|
||||
border-width: 1px 0;
|
||||
}
|
||||
|
||||
.fc-button-inner {
|
||||
position: relative;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fc-state-default .fc-button-inner { /* non-theme */
|
||||
border-style: solid;
|
||||
border-width: 0 1px;
|
||||
}
|
||||
|
||||
.fc-button-content {
|
||||
position: relative;
|
||||
float: left;
|
||||
height: 1.9em;
|
||||
line-height: 1.9em;
|
||||
padding: 0 .6em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* icon (for jquery ui) */
|
||||
|
||||
.fc-button-content .fc-icon-wrap {
|
||||
position: relative;
|
||||
float: left;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.fc-button-content .ui-icon {
|
||||
position: relative;
|
||||
float: left;
|
||||
margin-top: -50%;
|
||||
*margin-top: 0;
|
||||
*top: -50%;
|
||||
}
|
||||
|
||||
/* gloss effect */
|
||||
|
||||
.fc-state-default .fc-button-effect {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.fc-state-default .fc-button-effect span {
|
||||
position: absolute;
|
||||
top: -100px;
|
||||
left: 0;
|
||||
width: 500px;
|
||||
height: 100px;
|
||||
border-width: 100px 0 0 1px;
|
||||
border-style: solid;
|
||||
border-color: #fff;
|
||||
background: #444;
|
||||
opacity: .09;
|
||||
filter: alpha(opacity=9);
|
||||
}
|
||||
|
||||
/* button states (determines colors) */
|
||||
|
||||
.fc-state-default,
|
||||
.fc-state-default .fc-button-inner {
|
||||
border-style: solid;
|
||||
border-color: #ccc #bbb #aaa;
|
||||
background: #F3F3F3;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.fc-state-hover,
|
||||
.fc-state-hover .fc-button-inner {
|
||||
border-color: #999;
|
||||
}
|
||||
|
||||
.fc-state-down,
|
||||
.fc-state-down .fc-button-inner {
|
||||
border-color: #555;
|
||||
background: #777;
|
||||
}
|
||||
|
||||
.fc-state-active,
|
||||
.fc-state-active .fc-button-inner {
|
||||
border-color: #555;
|
||||
background: #777;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.fc-state-disabled,
|
||||
.fc-state-disabled .fc-button-inner {
|
||||
color: #999;
|
||||
border-color: #ddd;
|
||||
}
|
||||
|
||||
.fc-state-disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.fc-state-disabled .fc-button-effect {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Global Event Styles
|
||||
------------------------------------------------------------------------*/
|
||||
|
||||
.fc-event {
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
font-size: .85em;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
a.fc-event,
|
||||
.fc-event-draggable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a.fc-event {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.fc-rtl .fc-event {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.fc-event-skin {
|
||||
border-color: #36c; /* default BORDER color */
|
||||
background-color: #36c; /* default BACKGROUND color */
|
||||
color: #fff; /* default TEXT color */
|
||||
}
|
||||
|
||||
.fc-event-inner {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fc-event-time,
|
||||
.fc-event-title {
|
||||
padding: 0 1px;
|
||||
}
|
||||
|
||||
.fc .ui-resizable-handle { /*** TODO: don't use ui-resizable anymore, change class ***/
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
overflow: hidden; /* hacky spaces (IE6/7) */
|
||||
font-size: 300%; /* */
|
||||
line-height: 50%; /* */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Horizontal Events
|
||||
------------------------------------------------------------------------*/
|
||||
|
||||
.fc-event-hori {
|
||||
border-width: 1px 0;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
/* resizable */
|
||||
|
||||
.fc-event-hori .ui-resizable-e {
|
||||
top: 0 !important; /* importants override pre jquery ui 1.7 styles */
|
||||
right: -3px !important;
|
||||
width: 7px !important;
|
||||
height: 100% !important;
|
||||
cursor: e-resize;
|
||||
}
|
||||
|
||||
.fc-event-hori .ui-resizable-w {
|
||||
top: 0 !important;
|
||||
left: -3px !important;
|
||||
width: 7px !important;
|
||||
height: 100% !important;
|
||||
cursor: w-resize;
|
||||
}
|
||||
|
||||
.fc-event-hori .ui-resizable-handle {
|
||||
_padding-bottom: 14px; /* IE6 had 0 height */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Fake Rounded Corners (for buttons and events)
|
||||
------------------------------------------------------------*/
|
||||
|
||||
.fc-corner-left {
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.fc-corner-left .fc-button-inner,
|
||||
.fc-corner-left .fc-event-inner {
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.fc-corner-right {
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.fc-corner-right .fc-button-inner,
|
||||
.fc-corner-right .fc-event-inner {
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
.fc-corner-top {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.fc-corner-top .fc-event-inner {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.fc-corner-bottom {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.fc-corner-bottom .fc-event-inner {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Fake Rounded Corners SPECIFICALLY FOR EVENTS
|
||||
-----------------------------------------------------------------*/
|
||||
|
||||
.fc-corner-left .fc-event-inner {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
.fc-corner-right .fc-event-inner {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.fc-corner-top .fc-event-inner {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.fc-corner-bottom .fc-event-inner {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Reusable Separate-border Table
|
||||
------------------------------------------------------------*/
|
||||
|
||||
table.fc-border-separate {
|
||||
border-collapse: separate;
|
||||
}
|
||||
|
||||
.fc-border-separate th,
|
||||
.fc-border-separate td {
|
||||
border-width: 1px 0 0 1px;
|
||||
}
|
||||
|
||||
.fc-border-separate th.fc-last,
|
||||
.fc-border-separate td.fc-last {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.fc-border-separate tr.fc-last th,
|
||||
.fc-border-separate tr.fc-last td {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.fc-border-separate tbody tr.fc-first td,
|
||||
.fc-border-separate tbody tr.fc-first th {
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Month View, Basic Week View, Basic Day View
|
||||
------------------------------------------------------------------------*/
|
||||
|
||||
.fc-grid th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fc-grid .fc-day-number {
|
||||
float: right;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.fc-grid .fc-other-month .fc-day-number {
|
||||
opacity: 0.3;
|
||||
filter: alpha(opacity=30); /* for IE */
|
||||
/* opacity with small font can sometimes look too faded
|
||||
might want to set the 'color' property instead
|
||||
making day-numbers bold also fixes the problem */
|
||||
}
|
||||
|
||||
.fc-grid .fc-day-content {
|
||||
clear: both;
|
||||
padding: 2px 2px 1px; /* distance between events and day edges */
|
||||
}
|
||||
|
||||
/* event styles */
|
||||
|
||||
.fc-grid .fc-event-time {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* right-to-left */
|
||||
|
||||
.fc-rtl .fc-grid .fc-day-number {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fc-rtl .fc-grid .fc-event-time {
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Agenda Week View, Agenda Day View
|
||||
------------------------------------------------------------------------*/
|
||||
|
||||
.fc-agenda table {
|
||||
border-collapse: separate;
|
||||
}
|
||||
|
||||
.fc-agenda-days th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fc-agenda .fc-agenda-axis {
|
||||
width: 50px;
|
||||
padding: 0 4px;
|
||||
vertical-align: middle;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.fc-agenda .fc-day-content {
|
||||
padding: 2px 2px 1px;
|
||||
}
|
||||
|
||||
/* make axis border take precedence */
|
||||
|
||||
.fc-agenda-days .fc-agenda-axis {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.fc-agenda-days .fc-col0 {
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
/* all-day area */
|
||||
|
||||
.fc-agenda-allday th {
|
||||
border-width: 0 1px;
|
||||
}
|
||||
|
||||
.fc-agenda-allday .fc-day-content {
|
||||
min-height: 34px; /* TODO: doesnt work well in quirksmode */
|
||||
_height: 34px;
|
||||
}
|
||||
|
||||
/* divider (between all-day and slots) */
|
||||
|
||||
.fc-agenda-divider-inner {
|
||||
height: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fc-widget-header .fc-agenda-divider-inner {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
/* slot rows */
|
||||
|
||||
.fc-agenda-slots th {
|
||||
border-width: 1px 1px 0;
|
||||
}
|
||||
|
||||
.fc-agenda-slots td {
|
||||
border-width: 1px 0 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.fc-agenda-slots td div {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.fc-agenda-slots tr.fc-slot0 th,
|
||||
.fc-agenda-slots tr.fc-slot0 td {
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
.fc-agenda-slots tr.fc-minor th,
|
||||
.fc-agenda-slots tr.fc-minor td {
|
||||
border-top-style: dotted;
|
||||
}
|
||||
|
||||
.fc-agenda-slots tr.fc-minor th.ui-widget-header {
|
||||
*border-top-style: solid; /* doesn't work with background in IE6/7 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Vertical Events
|
||||
------------------------------------------------------------------------*/
|
||||
|
||||
.fc-event-vert {
|
||||
border-width: 0 1px;
|
||||
}
|
||||
|
||||
.fc-event-vert .fc-event-head,
|
||||
.fc-event-vert .fc-event-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fc-event-vert .fc-event-time {
|
||||
white-space: nowrap;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.fc-event-vert .fc-event-bg { /* makes the event lighter w/ a semi-transparent overlay */
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
opacity: .3;
|
||||
filter: alpha(opacity=30);
|
||||
}
|
||||
|
||||
.fc .ui-draggable-dragging .fc-event-bg, /* TODO: something nicer like .fc-opacity */
|
||||
.fc-select-helper .fc-event-bg {
|
||||
display: none\9; /* for IE6/7/8. nested opacity filters while dragging don't work */
|
||||
}
|
||||
|
||||
/* resizable */
|
||||
|
||||
.fc-event-vert .ui-resizable-s {
|
||||
bottom: 0 !important; /* importants override pre jquery ui 1.7 styles */
|
||||
width: 100% !important;
|
||||
height: 8px !important;
|
||||
overflow: hidden !important;
|
||||
line-height: 8px !important;
|
||||
font-size: 11px !important;
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
cursor: s-resize;
|
||||
}
|
||||
|
||||
.fc-agenda .ui-resizable-resizing { /* TODO: better selector */
|
||||
_overflow: hidden;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* FullCalendar v1.5.4 Print Stylesheet
|
||||
*
|
||||
* Include this stylesheet on your page to get a more printer-friendly calendar.
|
||||
* When including this stylesheet, use the media='print' attribute of the <link> tag.
|
||||
* Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css.
|
||||
*
|
||||
* Copyright (c) 2011 Adam Shaw
|
||||
* Dual licensed under the MIT and GPL licenses, located in
|
||||
* MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
|
||||
*
|
||||
* Date: Tue Sep 4 23:38:33 2012 -0700
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* Events
|
||||
-----------------------------------------------------*/
|
||||
|
||||
.fc-event-skin {
|
||||
background: none !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
/* horizontal events */
|
||||
|
||||
.fc-event-hori {
|
||||
border-width: 0 0 1px 0 !important;
|
||||
border-bottom-style: dotted !important;
|
||||
border-bottom-color: #000 !important;
|
||||
padding: 1px 0 0 0 !important;
|
||||
}
|
||||
|
||||
.fc-event-hori .fc-event-inner {
|
||||
border-width: 0 !important;
|
||||
padding: 0 1px !important;
|
||||
}
|
||||
|
||||
/* vertical events */
|
||||
|
||||
.fc-event-vert {
|
||||
border-width: 0 0 0 1px !important;
|
||||
border-left-style: dotted !important;
|
||||
border-left-color: #000 !important;
|
||||
padding: 0 1px 0 0 !important;
|
||||
}
|
||||
|
||||
.fc-event-vert .fc-event-inner {
|
||||
border-width: 0 !important;
|
||||
padding: 1px 0 !important;
|
||||
}
|
||||
|
||||
.fc-event-bg {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.fc-event .ui-resizable-handle {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Timepicker stylesheet
|
||||
* Highly inspired from datepicker
|
||||
* FG - Nov 2010 - Web3R
|
||||
*
|
||||
* version 0.0.3 : Fixed some settings, more dynamic
|
||||
* version 0.0.4 : Removed width:100% on tables
|
||||
* version 0.1.1 : set width 0 on tables to fix an ie6 bug
|
||||
*/
|
||||
|
||||
.ui-timepicker-inline { display: inline; }
|
||||
|
||||
#ui-timepicker-div { padding: 0.2em; background-color: #fff; }
|
||||
.ui-timepicker-table { display: inline-table; width: 0; }
|
||||
.ui-timepicker-table table { margin:0.15em 0 0 0; border-collapse: collapse; }
|
||||
|
||||
.ui-timepicker-hours, .ui-timepicker-minutes { padding: 0.2em; }
|
||||
|
||||
.ui-timepicker-table .ui-timepicker-title { line-height: 1.8em; text-align: center; }
|
||||
.ui-timepicker-table td { padding: 0.1em; width: 2.2em; }
|
||||
.ui-timepicker-table th.periods { padding: 0.1em; width: 2.2em; }
|
||||
|
||||
/* span for disabled cells */
|
||||
.ui-timepicker-table td span {
|
||||
display:block;
|
||||
padding:0.2em 0.3em 0.2em 0.5em;
|
||||
width: 1.2em;
|
||||
|
||||
text-align:right;
|
||||
text-decoration:none;
|
||||
}
|
||||
/* anchors for clickable cells */
|
||||
.ui-timepicker-table td a {
|
||||
display:block;
|
||||
padding:0.2em 0.3em 0.2em 0.5em;
|
||||
width: 1.2em;
|
||||
cursor: pointer;
|
||||
text-align:right;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
|
||||
/* buttons and button pane styling */
|
||||
.ui-timepicker .ui-timepicker-buttonpane {
|
||||
background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0;
|
||||
}
|
||||
.ui-timepicker .ui-timepicker-buttonpane button { margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
|
||||
/* The close button */
|
||||
.ui-timepicker .ui-timepicker-close { float: right }
|
||||
|
||||
/* the now button */
|
||||
.ui-timepicker .ui-timepicker-now { float: left; }
|
||||
|
||||
/* the deselect button */
|
||||
.ui-timepicker .ui-timepicker-deselect { float: left; }
|
||||
|
||||
|
||||
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
|
||||
.ui-timepicker-cover {
|
||||
display: none; /*sorry for IE5*/
|
||||
display/**/: block; /*sorry for IE5*/
|
||||
position: absolute; /*must have*/
|
||||
z-index: -1; /*must have*/
|
||||
filter: mask(); /*must have*/
|
||||
top: -4px; /*must have*/
|
||||
left: -4px; /*must have*/
|
||||
width: 200px; /*must have*/
|
||||
height: 200px; /*must have*/
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
.tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; }
|
||||
.tipsy-inner { background-color: #000; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; }
|
||||
|
||||
/* Rounded corners */
|
||||
.tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
|
||||
|
||||
/* Uncomment for shadow */
|
||||
/*.tipsy-inner { box-shadow: 0 0 5px #000000; -webkit-box-shadow: 0 0 5px #000000; -moz-box-shadow: 0 0 5px #000000; }*/
|
||||
|
||||
.tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; }
|
||||
|
||||
/* Rules to colour arrows */
|
||||
.tipsy-arrow-n { border-bottom-color: #000; }
|
||||
.tipsy-arrow-s { border-top-color: #000; }
|
||||
.tipsy-arrow-e { border-left-color: #000; }
|
||||
.tipsy-arrow-w { border-right-color: #000; }
|
||||
|
||||
.tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
|
||||
.tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
|
||||
.tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
|
||||
.tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }
|
|
@ -0,0 +1,25 @@
|
|||
.tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; }
|
||||
.tipsy-inner { background-color: #f0f0f0; color: #222222; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; }
|
||||
|
||||
/* Rounded corners */
|
||||
.tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
|
||||
|
||||
/* Uncomment for shadow */
|
||||
/*.tipsy-inner { box-shadow: 0 0 5px #000000; -webkit-box-shadow: 0 0 5px #000000; -moz-box-shadow: 0 0 5px #000000; }*/
|
||||
|
||||
.tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #f0f0f0; }
|
||||
|
||||
/* Rules to colour arrows */
|
||||
.tipsy-arrow-n { border-bottom-color: #f0f0f0; }
|
||||
.tipsy-arrow-s { border-top-color: #f0f0f0; }
|
||||
.tipsy-arrow-e { border-left-color: #f0f0f0; }
|
||||
.tipsy-arrow-w { border-right-color: #f0f0f0; }
|
||||
|
||||
.tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
|
||||
.tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
|
||||
.tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
|
||||
.tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2010-2013 Jeremy Ashkenas, DocumentCloud
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,278 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.""''''''
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2011 John Resig, http://jquery.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,12 @@
|
|||
/* Czech initialisation for the timepicker plugin */
|
||||
/* Written by David Spohr (spohr.david at gmail). */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['cs'] = {
|
||||
hourText: 'Hodiny',
|
||||
minuteText: 'Minuty',
|
||||
amPmText: ['AM', 'PM'] ,
|
||||
closeButtonText: 'Zavřít',
|
||||
nowButtonText: 'Nyní',
|
||||
deselectButtonText: 'Odoznačit' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['cs']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* German initialisation for the timepicker plugin */
|
||||
/* Written by Lowie Hulzinga. */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['de'] = {
|
||||
hourText: 'Stunde',
|
||||
minuteText: 'Minuten',
|
||||
amPmText: ['AM', 'PM'] ,
|
||||
closeButtonText: 'Beenden',
|
||||
nowButtonText: 'Aktuelle Zeit',
|
||||
deselectButtonText: 'Wischen' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['de']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* Spanish initialisation for the jQuery time picker plugin. */
|
||||
/* Writen by Jandro González (agonzalezalves@gmail.com) */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['es'] = {
|
||||
hourText: 'Hora',
|
||||
minuteText: 'Minuto',
|
||||
amPmText: ['AM', 'PM'],
|
||||
closeButtonText: 'Aceptar',
|
||||
nowButtonText: 'Ahora',
|
||||
deselectButtonText: 'Deseleccionar' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['es']);
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
/* French initialisation for the jQuery time picker plugin. */
|
||||
/* Written by Bernd Plagge (bplagge@choicenet.ne.jp),
|
||||
Francois Gelinas (frank@fgelinas.com) */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['fr'] = {
|
||||
hourText: 'Heures',
|
||||
minuteText: 'Minutes',
|
||||
amPmText: ['AM', 'PM'],
|
||||
closeButtonText: 'Fermer',
|
||||
nowButtonText: 'Maintenant',
|
||||
deselectButtonText: 'Désélectionner' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['fr']);
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
/* Croatian/Bosnian initialisation for the timepicker plugin */
|
||||
/* Written by Rene Brakus (rene.brakus@infobip.com). */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['hr'] = {
|
||||
hourText: 'Sat',
|
||||
minuteText: 'Minuta',
|
||||
amPmText: ['Prijepodne', 'Poslijepodne'],
|
||||
closeButtonText: 'Zatvoriti',
|
||||
nowButtonText: 'Sada',
|
||||
deselectButtonText: 'Poništite'}
|
||||
|
||||
$.timepicker.setDefaults($.timepicker.regional['hr']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* Italian initialisation for the jQuery time picker plugin. */
|
||||
/* Written by Serge Margarita (serge.margarita@gmail.com) */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['it'] = {
|
||||
hourText: 'Ore',
|
||||
minuteText: 'Minuti',
|
||||
amPmText: ['AM', 'PM'],
|
||||
closeButtonText: 'Chiudi',
|
||||
nowButtonText: 'Adesso',
|
||||
deselectButtonText: 'Svuota' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['it']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* Japanese initialisation for the jQuery time picker plugin. */
|
||||
/* Written by Bernd Plagge (bplagge@choicenet.ne.jp). */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['ja'] = {
|
||||
hourText: '時間',
|
||||
minuteText: '分',
|
||||
amPmText: ['午前', '午後'],
|
||||
closeButtonText: '閉じる',
|
||||
nowButtonText: '現時',
|
||||
deselectButtonText: '選択解除' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['ja']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* Nederlands initialisation for the timepicker plugin */
|
||||
/* Written by Lowie Hulzinga. */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['nl'] = {
|
||||
hourText: 'Uren',
|
||||
minuteText: 'Minuten',
|
||||
amPmText: ['AM', 'PM'],
|
||||
closeButtonText: 'Sluiten',
|
||||
nowButtonText: 'Actuele tijd',
|
||||
deselectButtonText: 'Wissen' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['nl']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* Polish initialisation for the timepicker plugin */
|
||||
/* Written by Mateusz Wadolkowski (mw@pcdoctor.pl). */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['pl'] = {
|
||||
hourText: 'Godziny',
|
||||
minuteText: 'Minuty',
|
||||
amPmText: ['', ''],
|
||||
closeButtonText: 'Zamknij',
|
||||
nowButtonText: 'Teraz',
|
||||
deselectButtonText: 'Odznacz'}
|
||||
$.timepicker.setDefaults($.timepicker.regional['pl']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* Brazilan initialisation for the timepicker plugin */
|
||||
/* Written by Daniel Almeida (quantodaniel@gmail.com). */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['pt-BR'] = {
|
||||
hourText: 'Hora',
|
||||
minuteText: 'Minuto',
|
||||
amPmText: ['AM', 'PM'],
|
||||
closeButtonText: 'Fechar',
|
||||
nowButtonText: 'Agora',
|
||||
deselectButtonText: 'Limpar' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['pt-BR']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* Slovenian localization for the jQuery time picker plugin. */
|
||||
/* Written by Blaž Maležič (blaz@malezic.si) */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['sl'] = {
|
||||
hourText: 'Ure',
|
||||
minuteText: 'Minute',
|
||||
amPmText: ['AM', 'PM'],
|
||||
closeButtonText: 'Zapri',
|
||||
nowButtonText: 'Zdaj',
|
||||
deselectButtonText: 'Pobriši' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['sl']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* Swedish initialisation for the timepicker plugin */
|
||||
/* Written by Björn Westlin (bjorn.westlin@su.se). */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['sv'] = {
|
||||
hourText: 'Timme',
|
||||
minuteText: 'Minut',
|
||||
amPmText: ['AM', 'PM'] ,
|
||||
closeButtonText: 'Stäng',
|
||||
nowButtonText: 'Nu',
|
||||
deselectButtonText: 'Rensa' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['sv']);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/* Turkish initialisation for the jQuery time picker plugin. */
|
||||
/* Written by Mutlu Tevfik Koçak (mtkocak@gmail.com) */
|
||||
jQuery(function($){
|
||||
$.timepicker.regional['tr'] = {
|
||||
hourText: 'Saat',
|
||||
minuteText: 'Dakika',
|
||||
amPmText: ['AM', 'PM'],
|
||||
closeButtonText: 'Kapat',
|
||||
nowButtonText: 'Şu anda',
|
||||
deselectButtonText: 'Seçimi temizle' }
|
||||
$.timepicker.setDefaults($.timepicker.regional['tr']);
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,21 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2008 Jason Frame (jason@onehackoranother.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,258 @@
|
|||
// tipsy, facebook style tooltips for jquery
|
||||
// version 1.0.0a
|
||||
// (c) 2008-2010 jason frame [jason@onehackoranother.com]
|
||||
// released under the MIT license
|
||||
|
||||
(function($) {
|
||||
|
||||
function maybeCall(thing, ctx) {
|
||||
return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
|
||||
};
|
||||
|
||||
function isElementInDOM(ele) {
|
||||
while (ele = ele.parentNode) {
|
||||
if (ele == document) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
function Tipsy(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = options;
|
||||
this.enabled = true;
|
||||
this.fixTitle();
|
||||
};
|
||||
|
||||
Tipsy.prototype = {
|
||||
show: function() {
|
||||
var title = this.getTitle();
|
||||
if (title && this.enabled) {
|
||||
var $tip = this.tip();
|
||||
|
||||
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
|
||||
$tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
|
||||
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
|
||||
|
||||
var pos = $.extend({}, this.$element.offset(), {
|
||||
width: this.$element[0].offsetWidth,
|
||||
height: this.$element[0].offsetHeight
|
||||
});
|
||||
|
||||
var actualWidth = $tip[0].offsetWidth,
|
||||
actualHeight = $tip[0].offsetHeight,
|
||||
gravity = maybeCall(this.options.gravity, this.$element[0]);
|
||||
|
||||
var tp;
|
||||
switch (gravity.charAt(0)) {
|
||||
case 'n':
|
||||
tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
|
||||
break;
|
||||
case 's':
|
||||
tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
|
||||
break;
|
||||
case 'e':
|
||||
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
|
||||
break;
|
||||
case 'w':
|
||||
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
|
||||
break;
|
||||
}
|
||||
|
||||
if (gravity.length == 2) {
|
||||
if (gravity.charAt(1) == 'w') {
|
||||
tp.left = pos.left + pos.width / 2 - 15;
|
||||
} else {
|
||||
tp.left = pos.left + pos.width / 2 - actualWidth + 15;
|
||||
}
|
||||
}
|
||||
|
||||
$tip.css(tp).addClass('tipsy-' + gravity);
|
||||
$tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
|
||||
if (this.options.className) {
|
||||
$tip.addClass(maybeCall(this.options.className, this.$element[0]));
|
||||
}
|
||||
|
||||
if (this.options.fade) {
|
||||
$tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
|
||||
} else {
|
||||
$tip.css({visibility: 'visible', opacity: this.options.opacity});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (this.options.fade) {
|
||||
this.tip().stop().fadeOut(function() { $(this).remove(); });
|
||||
} else {
|
||||
this.tip().remove();
|
||||
}
|
||||
},
|
||||
|
||||
fixTitle: function() {
|
||||
var $e = this.$element;
|
||||
if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
|
||||
$e.attr('original-title', $e.attr('title') || '').removeAttr('title');
|
||||
}
|
||||
},
|
||||
|
||||
getTitle: function() {
|
||||
var title, $e = this.$element, o = this.options;
|
||||
this.fixTitle();
|
||||
var title, o = this.options;
|
||||
if (typeof o.title == 'string') {
|
||||
title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
|
||||
} else if (typeof o.title == 'function') {
|
||||
title = o.title.call($e[0]);
|
||||
}
|
||||
title = ('' + title).replace(/(^\s*|\s*$)/, "");
|
||||
return title || o.fallback;
|
||||
},
|
||||
|
||||
tip: function() {
|
||||
if (!this.$tip) {
|
||||
this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
|
||||
this.$tip.data('tipsy-pointee', this.$element[0]);
|
||||
}
|
||||
return this.$tip;
|
||||
},
|
||||
|
||||
validate: function() {
|
||||
if (!this.$element[0].parentNode) {
|
||||
this.hide();
|
||||
this.$element = null;
|
||||
this.options = null;
|
||||
}
|
||||
},
|
||||
|
||||
enable: function() { this.enabled = true; },
|
||||
disable: function() { this.enabled = false; },
|
||||
toggleEnabled: function() { this.enabled = !this.enabled; }
|
||||
};
|
||||
|
||||
$.fn.tipsy = function(options) {
|
||||
|
||||
if (options === true) {
|
||||
return this.data('tipsy');
|
||||
} else if (typeof options == 'string') {
|
||||
var tipsy = this.data('tipsy');
|
||||
if (tipsy) tipsy[options]();
|
||||
return this;
|
||||
}
|
||||
|
||||
options = $.extend({}, $.fn.tipsy.defaults, options);
|
||||
|
||||
function get(ele) {
|
||||
var tipsy = $.data(ele, 'tipsy');
|
||||
if (!tipsy) {
|
||||
tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
|
||||
$.data(ele, 'tipsy', tipsy);
|
||||
}
|
||||
return tipsy;
|
||||
}
|
||||
|
||||
function enter() {
|
||||
var tipsy = get(this);
|
||||
tipsy.hoverState = 'in';
|
||||
if (options.delayIn == 0) {
|
||||
tipsy.show();
|
||||
} else {
|
||||
tipsy.fixTitle();
|
||||
setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
|
||||
}
|
||||
};
|
||||
|
||||
function leave() {
|
||||
var tipsy = get(this);
|
||||
tipsy.hoverState = 'out';
|
||||
if (options.delayOut == 0) {
|
||||
tipsy.hide();
|
||||
} else {
|
||||
setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
|
||||
}
|
||||
};
|
||||
|
||||
if (!options.live) this.each(function() { get(this); });
|
||||
|
||||
if (options.trigger != 'manual') {
|
||||
var binder = options.live ? 'live' : 'bind',
|
||||
eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
|
||||
eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
|
||||
this[binder](eventIn, enter)[binder](eventOut, leave);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
$.fn.tipsy.defaults = {
|
||||
className: null,
|
||||
delayIn: 0,
|
||||
delayOut: 0,
|
||||
fade: false,
|
||||
fallback: '',
|
||||
gravity: 'n',
|
||||
html: false,
|
||||
live: false,
|
||||
offset: 0,
|
||||
opacity: 0.8,
|
||||
title: 'title',
|
||||
trigger: 'hover'
|
||||
};
|
||||
|
||||
$.fn.tipsy.revalidate = function() {
|
||||
$('.tipsy').each(function() {
|
||||
var pointee = $.data(this, 'tipsy-pointee');
|
||||
if (!pointee || !isElementInDOM(pointee)) {
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Overwrite this method to provide options on a per-element basis.
|
||||
// For example, you could store the gravity in a 'tipsy-gravity' attribute:
|
||||
// return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
|
||||
// (remember - do not modify 'options' in place!)
|
||||
$.fn.tipsy.elementOptions = function(ele, options) {
|
||||
return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
|
||||
};
|
||||
|
||||
$.fn.tipsy.autoNS = function() {
|
||||
return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
|
||||
};
|
||||
|
||||
$.fn.tipsy.autoWE = function() {
|
||||
return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
|
||||
};
|
||||
|
||||
/**
|
||||
* yields a closure of the supplied parameters, producing a function that takes
|
||||
* no arguments and is suitable for use as an autogravity function like so:
|
||||
*
|
||||
* @param margin (int) - distance from the viewable region edge that an
|
||||
* element should be before setting its tooltip's gravity to be away
|
||||
* from that edge.
|
||||
* @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer
|
||||
* if there are no viewable region edges effecting the tooltip's
|
||||
* gravity. It will try to vary from this minimally, for example,
|
||||
* if 'sw' is preferred and an element is near the right viewable
|
||||
* region edge, but not the top edge, it will set the gravity for
|
||||
* that element's tooltip to be 'se', preserving the southern
|
||||
* component.
|
||||
*/
|
||||
$.fn.tipsy.autoBounds = function(margin, prefer) {
|
||||
return function() {
|
||||
var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
|
||||
boundTop = $(document).scrollTop() + margin,
|
||||
boundLeft = $(document).scrollLeft() + margin,
|
||||
$this = $(this);
|
||||
|
||||
if ($this.offset().top < boundTop) dir.ns = 'n';
|
||||
if ($this.offset().left < boundLeft) dir.ew = 'w';
|
||||
if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e';
|
||||
if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's';
|
||||
|
||||
return dir.ns + (dir.ew ? dir.ew : '');
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
namespace OCA\Calendar\PublicApi;
|
||||
|
||||
class Calendar {
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
namespace OCA\Calendar\PublicApi;
|
||||
|
||||
class Events extends ObjectType {
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
namespace OCA\Calendar\PublicApi;
|
||||
|
||||
class Journals extends ObjectType {
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
class OC_Calendar_App {
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
class OC_Calendar_Calendar{
|
||||
/**
|
||||
* @brief Returns the list of calendars for a specific user.
|
||||
* @param string $uid User ID
|
||||
* @param boolean $active Only return calendars with this $active state, default(=false) is don't care
|
||||
* @return array
|
||||
*/
|
||||
public static function allCalendars($uid, $active=false) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the data of one calendar
|
||||
* @param integer $id
|
||||
* @return associative array
|
||||
*/
|
||||
public static function find($id) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new calendar
|
||||
* @param string $userid
|
||||
* @param string $name
|
||||
* @param string $components Default: "VEVENT,VTODO,VJOURNAL"
|
||||
* @param string $timezone Default: null
|
||||
* @param integer $order Default: 1
|
||||
* @param string $color Default: null, format: '#RRGGBB(AA)'
|
||||
* @return insertid
|
||||
*/
|
||||
public static function addCalendar($userid,$name,$components='VEVENT,VTODO,VJOURNAL',$timezone=null,$order=0,$color=null) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates default calendars
|
||||
* @param string $userid
|
||||
* @return boolean
|
||||
*/
|
||||
public static function addDefaultCalendars($userid = null) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Edits a calendar
|
||||
* @param integer $id
|
||||
* @param string $name Default: null
|
||||
* @param string $components Default: null
|
||||
* @param string $timezone Default: null
|
||||
* @param integer $order Default: null
|
||||
* @param string $color Default: null, format: '#RRGGBB(AA)'
|
||||
* @return boolean
|
||||
*
|
||||
* Values not null will be set
|
||||
*/
|
||||
public static function editCalendar($id,$name=null,$components=null,$timezone=null,$order=null,$color=null) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a calendar (in)active
|
||||
* @param integer $id
|
||||
* @param boolean $active
|
||||
* @return boolean
|
||||
*/
|
||||
public static function setCalendarActive($id,$active) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates ctag for calendar
|
||||
* @param integer $id
|
||||
* @return boolean
|
||||
*/
|
||||
public static function touchCalendar($id) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief removes a calendar
|
||||
* @param integer $id
|
||||
* @return boolean
|
||||
*/
|
||||
public static function deleteCalendar($id) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a URI for Calendar
|
||||
* @param string $name name of the calendar
|
||||
* @param array $existing existing calendar URIs
|
||||
* @return string uri
|
||||
*/
|
||||
public static function createURI($name,$existing) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief gets the userid from a principal path
|
||||
* @return string
|
||||
*/
|
||||
public static function extractUserID($principaluri) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief generates the Event Source Info for our JS
|
||||
* @param array $calendar calendar data
|
||||
* @return array
|
||||
*/
|
||||
public static function getEventSourceInfo($calendar) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief checks if a calendar name is available for a user
|
||||
* @param string $calendarname
|
||||
* @param string $userid
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isCalendarNameavailable($calendarname, $userid) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
class OC_Calendar_Object {
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
namespace OCA\Calendar\PublicApi;
|
||||
|
||||
class Objects {
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
namespace OCA\Calendar\PublicApi;
|
||||
|
||||
abstract class ObjectType {
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
namespace OCA\Calendar\PublicApi;
|
||||
|
||||
class Todos extends ObjectType {
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
namespace OCA\Calendar;
|
||||
|
||||
require_once(__DIR__ . '/../3rdparty/VObject/includes.php');
|
||||
|
||||
define('OCA\Calendar\JSON_API_VERSION', '1.0');
|
||||
define('OCA\Calendar\PHP_API_VERSION', '1.0');
|
||||
|
||||
$app = new App();
|
||||
$app->registerNavigation();
|
||||
|
||||
$app->registerCron();
|
||||
$app->registerHooks();
|
||||
$app->registerProviders();
|
||||
|
||||
Sabre\VObject\Document::$propertyMap['DateTime'] = '\OCA\Calendar\CustomSabre\Property\DateTime';
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Georg Ehrke <oc.list@georgehrke.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\App'] = 'calendar/lib/app.php';
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backend\Anniversary'] = 'calendar/backend/anniversary.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backend\Backend'] = 'calendar/backend/backend.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backend\Birthday'] = 'calendar/backend/birthday.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backend\CalDAV'] = 'calendar/backend/caldav.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backend\IBackend'] = 'calendar/backend/ibackend.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backend\Local'] = 'calendar/backend/local.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backend\LocalStorage'] = 'calendar/backend/localstorage.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backend\Sharing'] = 'calendar/backend/sharing.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backend\WebCal'] = 'calendar/backend/webcal.php';
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\Backgroundjob\Task'] = 'calendar/backgroundjob/task.php';
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\BusinessLayer\BusinessLayer'] = 'calendar/businesslayer/businesslayer.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\BusinessLayer\CalendarBusinessLayer'] = 'calendar/businesslayer/calendarbusinesslayer.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\BusinessLayer\ObjectBusinessLayer'] = 'calendar/businesslayer/objectbusinesslayer.php';
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\Controller\Controller'] = 'calendar/controller/controller.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Controller\CalendarController'] = 'calendar/controller/calendarcontroller.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Controller\ObjectController'] = 'calendar/controller/objectcontroller.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Controller\ObjectTypeController'] = 'calendar/controller/objecttypecontroller.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Controller\EventController'] = 'calendar/controller/eventcontroller.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Controller\JournalController'] = 'calendar/controller/journalcontroller.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Controller\TodoController'] = 'calendar/controller/todocontroller.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Controller\SettingsController'] = 'calendar/controller/settingscontroller.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Controller\ViewController'] = 'calendar/controller/viewcontroller.php';
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\Entity'] = 'calendar/db/entity.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\Collection'] = 'calendar/db/collection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\Mapper'] = 'calendar/db/mapper.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\Backend'] = 'calendar/db/backend.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\BackendCollection'] = 'calendar/db/backendcollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\BackendMapper'] = 'calendar/db/backendmapper.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\Calendar'] = 'calendar/db/calendar.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\CalendarCollection'] = 'calendar/db/calendarcollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\CalendarMapper'] = 'calendar/db/calendarmapper.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\Object'] = 'calendar/db/object.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\ObjectCollection'] = 'calendar/db/objectcollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\ObjectMapper'] = 'calendar/db/objectmapper.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\Timezone'] = 'calendar/db/timezone.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\TimezoneCollection'] = 'calendar/db/timezonecollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\ObjectType'] = 'calendar/db/objecttype.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Db\Permissions'] = 'calendar/db/permissions.php';
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSONResponse'] = 'calendar/http/jsonresponse.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\IResponse'] = 'calendar/http/iresponse.php';
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICS'] = 'calendar/http/ics/ics.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSCollection'] = 'calendar/http/ics/icscollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSReader'] = 'calendar/http/ics/icsreader.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSCalendar'] = 'calendar/http/ics/calendar.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSCalendarCollection'] = 'calendar/http/ics/calendarcollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSCalendarReader'] = 'calendar/http/ics/calendarreader.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSObject'] = 'calendar/http/ics/object.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSObjectCollection'] = 'calendar/http/ics/objectcollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSObjectReader'] = 'calendar/http/ics/objectreader.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSTimezone'] = 'calendar/http/ics/timezone.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSTimezoneCollection'] = 'calendar/http/ics/timzonecollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\ICS\ICSTimezoneReader'] = 'calendar/http/ics/timezonereader.php';
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSON'] = 'calendar/http/json/json.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONCollection'] = 'calendar/http/json/jsoncollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONReader'] = 'calendar/http/json/jsonreader.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONCalendar'] = 'calendar/http/json/calendar.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONCalendarCollection'] = 'calendar/http/json/calendarcollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONCalendarReader'] = 'calendar/http/json/calendarreader.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONObject'] = 'calendar/http/json/object.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONObjectCollection'] = 'calendar/http/json/objectcollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONObjectReader'] = 'calendar/http/json/objectreader.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONTimezone'] = 'calendar/http/json/timezone.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONTimezoneCollection'] = 'calendar/http/json/timezonecollection.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Http\JSON\JSONTimezoneReader'] = 'calendar/http/json/timezonereader.php';
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\SabreProperty\DateTime'] = 'calendar/sabre/property/datetime.php';
|
||||
|
||||
//caldav implementation
|
||||
/*\OC::$CLASSPATH['OCA\Calendar\Sabre'] = '';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Sabre'] = '';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Sabre'] = '';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Sabre'] = '';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Sabre'] = '';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Sabre'] = '';*/
|
||||
|
||||
\OC::$CLASSPATH['OCA\Calendar\Utility\Utility'] = 'calendar/utility/utility.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Utility\BackendUtility'] = 'calendar/utility/backend.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Utility\CalendarUtility'] = 'calendar/utility/calendar.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Utility\JSONUtility'] = 'calendar/utility/json.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Utility\ObjectUtility'] = 'calendar/utility/object.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Utility\SabreUtility'] = 'calendar/utility/sabre.php';
|
||||
\OC::$CLASSPATH['OCA\Calendar\Utility\UpdateUtility'] = 'calendar/utility/update.php';
|
|
@ -0,0 +1,392 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<database>
|
||||
<name>*dbname*</name>
|
||||
<create>true</create>
|
||||
<overwrite>false</overwrite>
|
||||
<charset>utf8</charset>
|
||||
<!-- table for cached calendars -->
|
||||
<table>
|
||||
<name>*dbprefix*clndr_calcache</name>
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>user_id</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>owner_id</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>backend</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>uri</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>displayname</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>components</name>
|
||||
<type>text</type>
|
||||
<default>VEVENT</default>
|
||||
<notnull>false</notnull>
|
||||
<length>100</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>ctag</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>timezone</name>
|
||||
<type>text</type>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>color</name>
|
||||
<type>text</type>
|
||||
<default>#000000</default>
|
||||
<notnull>false</notnull>
|
||||
<length>10</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>order</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>enabled</name>
|
||||
<type>boolean</type>
|
||||
<default>1</default>
|
||||
<notnull>true</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>cruds</name>
|
||||
<type>integer</type>
|
||||
<default>31</default>
|
||||
<notnull>true</notnull>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
<!-- table for cached objects -->
|
||||
<table>
|
||||
<name>*dbprefix*clndr_objcache</name>
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>calendarid</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>uri</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>type</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>40</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>startdate</name>
|
||||
<type>timestamp</type>
|
||||
<default>1970-01-01 00:00:00</default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>enddate</name>
|
||||
<type>timestamp</type>
|
||||
<default>1970-01-01 00:00:00</default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>timezone</name>
|
||||
<type>text</type>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>repeating</name>
|
||||
<type>boolean</type>
|
||||
<default>0</default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>lastoccurence</name>
|
||||
<type>timestamp</type>
|
||||
<default>1970-01-01 00:00:00</default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>summary</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>calendardata</name>
|
||||
<type>clob</type>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>lastmodified</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>etag</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>40</length>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
<!-- table for cached repeating events -->
|
||||
<table>
|
||||
<name>*dbprefix*clndr_repeatcache</name>
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>calendarid</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>uri</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>startdate</name>
|
||||
<type>timestamp</type>
|
||||
<default>1970-01-01 00:00:00</default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>enddate</name>
|
||||
<type>timestamp</type>
|
||||
<default>1970-01-01 00:00:00</default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
<!-- table for calendars in the database backend -->
|
||||
<table>
|
||||
<name>*dbprefix*clndr_calendars</name>
|
||||
<!--<was>*dbprefix*calendar_calendars</was>-->
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>userid</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>displayname</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>100</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>uri</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>active</name>
|
||||
<type>integer</type>
|
||||
<default>1</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>ctag</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>calendarorder</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>calendarcolor</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>10</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>timezone</name>
|
||||
<type>text</type>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>components</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>100</length>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
<!-- table for objects in the database backend -->
|
||||
<table>
|
||||
<name>*dbprefix*clndr_objects</name>
|
||||
<!--<was>*dbprefix*calendar_objects</was>-->
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>calendarid</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>objecttype</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>40</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>startdate</name>
|
||||
<type>timestamp</type>
|
||||
<default>1970-01-01 00:00:00</default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>enddate</name>
|
||||
<type>timestamp</type>
|
||||
<default>1970-01-01 00:00:00</default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>repeating</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>summary</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>calendardata</name>
|
||||
<type>clob</type>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>uri</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>lastmodified</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
</database>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0"?>
|
||||
<info>
|
||||
<id>calendar</id>
|
||||
<name>Calendar</name>
|
||||
<licence>AGPL</licence>
|
||||
<author>Georg Ehrke, Bart Visscher, Jakob Sack</author>
|
||||
<require>6.90</require>
|
||||
<shipped>true</shipped>
|
||||
<description>Calendar with CalDAV support</description>
|
||||
<default_enable/>
|
||||
<remote>
|
||||
<calendar>sabre/main.php</calendar>
|
||||
<caldav>sabre/main.php</caldav>
|
||||
</remote>
|
||||
<public>
|
||||
<calendar>sharing/public.php</calendar>
|
||||
<caldav>sharing/public.php</caldav>
|
||||
</public>
|
||||
</info>
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
class OC_Migration_Provider_Calendar extends OC_Migration_Provider{
|
||||
|
||||
// Create the xml for the user supplied
|
||||
function export( ) {
|
||||
$options = array(
|
||||
'table'=>'calendar_calendars',
|
||||
'matchcol'=>'userid',
|
||||
'matchval'=>$this->uid,
|
||||
'idcol'=>'id'
|
||||
);
|
||||
$ids = $this->content->copyRows( $options );
|
||||
|
||||
$options = array(
|
||||
'table'=>'calendar_objects',
|
||||
'matchcol'=>'calendarid',
|
||||
'matchval'=>$ids
|
||||
);
|
||||
|
||||
// Export tags
|
||||
$ids2 = $this->content->copyRows( $options );
|
||||
|
||||
// If both returned some ids then they worked
|
||||
if(is_array($ids) && is_array($ids2)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Import function for calendar
|
||||
function import( ) {
|
||||
switch( $this->appinfo->version ) {
|
||||
default:
|
||||
// All versions of the app have had the same db structure, so all can use the same import function
|
||||
$query = $this->content->prepare( 'SELECT * FROM `calendar_calendars` WHERE `userid` = ?' );
|
||||
$results = $query->execute( array( $this->olduid ) );
|
||||
$idmap = array();
|
||||
while( $row = $results->fetchRow() ) {
|
||||
// Import each calendar
|
||||
$calendarquery = OCP\DB::prepare( 'INSERT INTO `*PREFIX*calendar_calendars` (`userid`,`displayname`,`uri`,`ctag`,`calendarorder`,`calendarcolor`,`timezone`,`components`) VALUES(?,?,?,?,?,?,?,?)' );
|
||||
$calendarquery->execute(array( $this->uid, $row['displayname'], $row['uri'], $row['ctag'], $row['calendarorder'], $row['calendarcolor'], $row['timezone'], $row['components']));
|
||||
// Map the id
|
||||
$idmap[$row['id']] = OCP\DB::insertid('*PREFIX*calendar_calendars');
|
||||
// Make the calendar active
|
||||
OC_Calendar_Calendar::setCalendarActive($idmap[$row['id']], true);
|
||||
}
|
||||
// Now tags
|
||||
foreach($idmap as $oldid => $newid) {
|
||||
|
||||
$query = $this->content->prepare( 'SELECT * FROM `calendar_objects` WHERE `calendarid` = ?' );
|
||||
$results = $query->execute( array( $oldid ) );
|
||||
while( $row = $results->fetchRow() ) {
|
||||
// Import the objects
|
||||
$objectquery = OCP\DB::prepare( 'INSERT INTO `*PREFIX*calendar_objects` (`calendarid`,`objecttype`,`startdate`,`enddate`,`repeating`,`summary`,`calendardata`,`uri`,`lastmodified`) VALUES(?,?,?,?,?,?,?,?,?)' );
|
||||
$objectquery->execute(array( $newid, $row['objecttype'], $row['startdate'], $row['enddate'], $row['repeating'], $row['summary'], $row['calendardata'], $row['uri'], $row['lastmodified'] ));
|
||||
}
|
||||
}
|
||||
// All done!
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Load the provider
|
||||
new OC_Migration_Provider_Calendar( 'calendar' );
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Georg Ehrke
|
||||
* Copyright (c) 2014 Thomas Müller
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
use \OCA\Calendar;
|
||||
//set up simple routes
|
||||
$this->create('calendar.view.index', '/')->action(function($params){
|
||||
$app = new \OCA\Calendar\App($params);
|
||||
$app->dispatch('ViewController', 'index');
|
||||
});
|
||||
|
||||
$this->create('calendar.settings.getView', '/getView')->get()->action(function($params){
|
||||
$app = new \OCA\Calendar\App($params);
|
||||
$app->dispatch('SettingsController', 'getView');
|
||||
});
|
||||
|
||||
$this->create('calendar.settings.setView', '/setView/{view}')->get()->action(function($params){
|
||||
$app = new \OCA\Calendar\App($params);
|
||||
$app->dispatch('SettingsController', 'setView');
|
||||
});
|
||||
|
||||
$this->create('calendar.calendars.forceUpdate', '/v1/calendars-forceUpdate')->action(function($params){
|
||||
$app = new \OCA\Calendar\App($params);
|
||||
$app->dispatch('CalendarController', 'forceUpdate');
|
||||
});
|
||||
|
||||
//set up resources
|
||||
$routes = array(
|
||||
'resources' => array(
|
||||
'calendar' => array('url' => '/v1/calendars'),
|
||||
'object' => array('url' => '/v1/calendars/{calendarId}/objects'),
|
||||
'event' => array('url' => '/v1/calendars/{calendarId}/events'),
|
||||
'journal' => array('url' => '/v1/calendars/{calendarId}/journals'),
|
||||
'todo' => array('url' => '/v1/calendars/{calendarId}/todos'),
|
||||
)
|
||||
);
|
||||
|
||||
$a = new \OCA\Calendar\App();
|
||||
$a->registerRoutes($this, $routes);
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
use \OCA\Calendar\Db\ObjectType;
|
||||
|
||||
$installedVersion=OCP\Config::getAppValue('calendar', 'installed_version');
|
||||
|
||||
if(version_compare($installedVersion, '0.9.8', '<=')) {
|
||||
//add backends:
|
||||
$backends = array(
|
||||
array (
|
||||
'backend' => 'local',
|
||||
'classname' => '\OCA\Calendar\Backend\Local',
|
||||
'arguments' => '',
|
||||
'enabled' => true,
|
||||
),/*
|
||||
array (
|
||||
'backend' => 'WebCal',
|
||||
'classname' => '\OCA\Calendar\Backend\WebCal',
|
||||
'arguments' => '',
|
||||
'enabled' => false
|
||||
),*/
|
||||
|
||||
);
|
||||
\OCP\Config::setSystemValue('calendar_backends', $backends);
|
||||
|
||||
//fix calendars:
|
||||
$users = \OCP\User::getUsers();
|
||||
foreach($users as $userId) {
|
||||
$userstimezone = OCP\Config::getUserValue($userId, 'calendar', 'timezone', date_default_timezone_get());
|
||||
$stmt = OCP\DB::prepare('UPDATE `*PREFIX*clndr_calendars` SET `timezone`=? WHERE `userid`=?');
|
||||
$stmt->execute(array($userstimezone, $userId));
|
||||
//how to delete config values ???
|
||||
}
|
||||
|
||||
//there was no way set which calendar supports what kind of component, so we can set all calendars to support all components.
|
||||
$stmtComponents = OCP\DB::prepare('UPDATE `*PREFIX*clndr_calendars` SET `components`=?');
|
||||
$stmtComponents->execute(array((string) ObjectType::ALL));
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
0.10.7
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue