Completed
Push — developer ( e65ba3...d7bfd9 )
by Błażej
478:46 queued 446:35
created
libraries/antlr/DFA.php 4 patches
Doc Comments   +1 added lines patch added patch discarded remove patch
@@ -191,6 +191,7 @@
 block discarded – undo
191 191
 	 *  like "\1\2\3\9", convert to short[] {2,9,9,9}.  We do this to avoid
192 192
 	 *  static short[] which generates so much init code that the class won't
193 193
 	 *  compile. :(
194
+	 * @param string $encodedString
194 195
 	 */
195 196
 	public static function unpackEncodedString($encodedString) {
196 197
 		$data = array();
Please login to merge, or discard this patch.
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -42,10 +42,10 @@
 block discarded – undo
42 42
 	protected $eot;
43 43
 	protected $eof;
44 44
 	protected $min;
45
-    protected $max;
46
-    protected $accept;
47
-    protected $special;
48
-    protected $transition;
45
+	protected $max;
46
+	protected $accept;
47
+	protected $special;
48
+	protected $transition;
49 49
 
50 50
 	protected $decisionNumber;
51 51
 
Please login to merge, or discard this patch.
Spacing   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -60,16 +60,16 @@  discard block
 block discarded – undo
60 60
 	 *  an exception upon error.
61 61
 	 */
62 62
 	
63
-	public function predict($input){
64
-		if ( $this->debug ) {
65
-			echo ("Enter DFA.predict for decision ".$this->decisionNumber);
63
+	public function predict($input) {
64
+		if ($this->debug) {
65
+			echo ("Enter DFA.predict for decision " . $this->decisionNumber);
66 66
 		}
67 67
 		$mark = $input->mark(); // remember where decision started in input
68 68
 		try {
69 69
 			$ret = $this->_predict($input);
70 70
 			
71 71
 		}
72
-		catch(Exception $e) {
72
+		catch (Exception $e) {
73 73
 			$input->rewind($mark);
74 74
 			throw $e;
75 75
 		}
@@ -79,20 +79,20 @@  discard block
 block discarded – undo
79 79
 	
80 80
 	public function _predict($input) {
81 81
 		$s = 0; // we always start at s0
82
-			while ( true ) {
83
-				if ( $this->debug ) echo ("DFA ".$this->decisionNumber." state ".$s." LA(1)=".$input->LA(1)."(".$input->LA(1)."), index=".$input->index());
82
+			while (true) {
83
+				if ($this->debug) echo ("DFA " . $this->decisionNumber . " state " . $s . " LA(1)=" . $input->LA(1) . "(" . $input->LA(1) . "), index=" . $input->index());
84 84
 				$specialState = $this->special[$s];
85
-				if ( $specialState>=0 ) {
86
-					if ( $this->debug ) {
87
-						echo ("DFA ".$this->decisionNumber.
88
-							" state ".$s." is special state ".$specialState);
85
+				if ($specialState >= 0) {
86
+					if ($this->debug) {
87
+						echo ("DFA " . $this->decisionNumber .
88
+							" state " . $s . " is special state " . $specialState);
89 89
 					}
90 90
 					$s = $this->specialStateTransition($specialState, $input);
91
-					if ( $this->debug ) {
92
-						echo ("DFA ".$this->decisionNumber.
93
-							" returns from special state ".$specialState." to ".s);
91
+					if ($this->debug) {
92
+						echo ("DFA " . $this->decisionNumber .
93
+							" returns from special state " . $specialState . " to " . s);
94 94
 					}
95
-					if ( $s==-1 ) {
95
+					if ($s == -1) {
96 96
 						$this->noViableAlt($s, $input);
97 97
 						return 0;
98 98
 					}
@@ -100,50 +100,50 @@  discard block
 block discarded – undo
100 100
 					continue;
101 101
 				}
102 102
 				
103
-				if ( $this->accept[$s] >= 1 ) {
104
-					if ( $this->debug ) echo ("accept; predict "+$this->accept[$s]+" from state "+$this->s);
103
+				if ($this->accept[$s] >= 1) {
104
+					if ($this->debug) echo ("accept; predict "+$this->accept[$s] + " from state "+$this->s);
105 105
 					return $this->accept[$s];
106 106
 				}
107 107
 				// look for a normal char transition
108 108
 				$c = $input->LA(1); // -1 == \uFFFF, all tokens fit in 65000 space
109
-				if ($c>=$this->min[$s] && $c<=$this->max[$s]) {
110
-					$snext = $this->transition[$s][$c-$this->min[$s]]; // move to next state
111
-					if ( $snext < 0 ) {
109
+				if ($c >= $this->min[$s] && $c <= $this->max[$s]) {
110
+					$snext = $this->transition[$s][$c - $this->min[$s]]; // move to next state
111
+					if ($snext < 0) {
112 112
 						// was in range but not a normal transition
113 113
 						// must check EOT, which is like the else clause.
114 114
 						// eot[s]>=0 indicates that an EOT edge goes to another
115 115
 						// state.
116
-						if ( $this->eot[$s]>=0 ) {  // EOT Transition to accept state?
117
-							if ( $this->debug ) echo("EOT transition");
116
+						if ($this->eot[$s] >= 0) {  // EOT Transition to accept state?
117
+							if ($this->debug) echo("EOT transition");
118 118
 							$s = $this->eot[$s];
119 119
 							$input->consume();
120 120
 							continue;
121 121
 						}
122
-						$this->noViableAlt($s,$input);
122
+						$this->noViableAlt($s, $input);
123 123
 						return 0;
124 124
 					}
125 125
 					$s = $snext;
126 126
 					$input->consume();
127 127
 					continue;
128 128
 				}
129
-				if ( $eot[$s]>=0 ) {  // EOT Transition?
130
-					if ( $this->debug ) println("EOT transition");
129
+				if ($eot[$s] >= 0) {  // EOT Transition?
130
+					if ($this->debug) println("EOT transition");
131 131
 					$s = $this->eot[$s];
132 132
 					$input->consume();
133 133
 					continue;
134 134
 				}
135
-				if ( $c==Token::$EOF && $eof[$s]>=0 ) {  // EOF Transition to accept state?
136
-					if ( $this->debug ) echo ("accept via EOF; predict "+$this->accept[$eof[$s]]+" from "+$eof[$s]);
135
+				if ($c == Token::$EOF && $eof[$s] >= 0) {  // EOF Transition to accept state?
136
+					if ($this->debug) echo ("accept via EOF; predict "+$this->accept[$eof[$s]] + " from "+$eof[$s]);
137 137
 					return $this->accept[$eof[$s]];
138 138
 				}
139 139
 				// not in range and not EOF/EOT, must be invalid symbol
140
-				if ( $this->debug ) {
141
-					echo("min[".$s."]=".$this->min[$s]);
142
-					echo("max[".$s."]=".$this->max[$s]);
143
-					echo("eot[".$s."]=".$this->eot[$s]);
144
-					echo("eof[".$s."]=".$this->eof[$s]);
145
-					for ($p=0; $p<$this->transition[$s]->length; $p++) {
146
-						echo $this->transition[$s][$p]+" ";
140
+				if ($this->debug) {
141
+					echo("min[" . $s . "]=" . $this->min[$s]);
142
+					echo("max[" . $s . "]=" . $this->max[$s]);
143
+					echo("eot[" . $s . "]=" . $this->eot[$s]);
144
+					echo("eof[" . $s . "]=" . $this->eof[$s]);
145
+					for ($p = 0; $p < $this->transition[$s]->length; $p++) {
146
+						echo $this->transition[$s][$p] + " ";
147 147
 					}
148 148
 					echo "\n";
149 149
 				}
@@ -153,9 +153,9 @@  discard block
 block discarded – undo
153 153
 
154 154
 	}
155 155
 
156
-	public function noViableAlt($s, $input){
157
-		if ($this->recognizer->state->backtracking>0) {
158
-			$this->recognizer->state->failed=true;
156
+	public function noViableAlt($s, $input) {
157
+		if ($this->recognizer->state->backtracking > 0) {
158
+			$this->recognizer->state->failed = true;
159 159
 			return;
160 160
 		}
161 161
 		$nvae =
@@ -168,7 +168,7 @@  discard block
 block discarded – undo
168 168
 	}
169 169
 
170 170
 	/** A hook for debugging interface */
171
-	protected function error($nvae) { ; }
171
+	protected function error($nvae) {; }
172 172
 
173 173
 	public function specialStateTransition($s, $input)
174 174
 	{
@@ -187,19 +187,19 @@  discard block
 block discarded – undo
187 187
 	public static function unpackEncodedString($encodedString) {
188 188
 		$data = array();
189 189
 		$di = 0;
190
-		for ($i=0,$len=strlen($encodedString); $i<$len; $i+=2) {
190
+		for ($i = 0, $len = strlen($encodedString); $i < $len; $i += 2) {
191 191
 			$n = charAt($encodedString, $i);
192
-			$v = charAt($encodedString, $i+1);
192
+			$v = charAt($encodedString, $i + 1);
193 193
 			// add v n times to data
194
-			for ($j=1; $j<=$n; $j++) {
195
-				if($v==0xff) $v=-1;
194
+			for ($j = 1; $j <= $n; $j++) {
195
+				if ($v == 0xff) $v = -1;
196 196
 				$data[$di++] = $v;
197 197
 			}
198 198
 		}
199 199
 		return $data;
200 200
 	}
201 201
 	
202
-	public function __call($fn, $params){
202
+	public function __call($fn, $params) {
203 203
 		return call_user_func_array(array($this->recognizer, $fn), $params);
204 204
 	}
205 205
 }
Please login to merge, or discard this patch.
Braces   +19 added lines, -8 removed lines patch added patch discarded remove patch
@@ -68,8 +68,7 @@  discard block
 block discarded – undo
68 68
 		try {
69 69
 			$ret = $this->_predict($input);
70 70
 			
71
-		}
72
-		catch(Exception $e) {
71
+		} catch(Exception $e) {
73 72
 			$input->rewind($mark);
74 73
 			throw $e;
75 74
 		}
@@ -80,7 +79,9 @@  discard block
 block discarded – undo
80 79
 	public function _predict($input) {
81 80
 		$s = 0; // we always start at s0
82 81
 			while ( true ) {
83
-				if ( $this->debug ) echo ("DFA ".$this->decisionNumber." state ".$s." LA(1)=".$input->LA(1)."(".$input->LA(1)."), index=".$input->index());
82
+				if ( $this->debug ) {
83
+					echo ("DFA ".$this->decisionNumber." state ".$s." LA(1)=".$input->LA(1)."(".$input->LA(1)."), index=".$input->index());
84
+				}
84 85
 				$specialState = $this->special[$s];
85 86
 				if ( $specialState>=0 ) {
86 87
 					if ( $this->debug ) {
@@ -101,7 +102,9 @@  discard block
 block discarded – undo
101 102
 				}
102 103
 				
103 104
 				if ( $this->accept[$s] >= 1 ) {
104
-					if ( $this->debug ) echo ("accept; predict "+$this->accept[$s]+" from state "+$this->s);
105
+					if ( $this->debug ) {
106
+						echo ("accept; predict "+$this->accept[$s]+" from state "+$this->s);
107
+					}
105 108
 					return $this->accept[$s];
106 109
 				}
107 110
 				// look for a normal char transition
@@ -114,7 +117,9 @@  discard block
 block discarded – undo
114 117
 						// eot[s]>=0 indicates that an EOT edge goes to another
115 118
 						// state.
116 119
 						if ( $this->eot[$s]>=0 ) {  // EOT Transition to accept state?
117
-							if ( $this->debug ) echo("EOT transition");
120
+							if ( $this->debug ) {
121
+								echo("EOT transition");
122
+							}
118 123
 							$s = $this->eot[$s];
119 124
 							$input->consume();
120 125
 							continue;
@@ -127,13 +132,17 @@  discard block
 block discarded – undo
127 132
 					continue;
128 133
 				}
129 134
 				if ( $eot[$s]>=0 ) {  // EOT Transition?
130
-					if ( $this->debug ) println("EOT transition");
135
+					if ( $this->debug ) {
136
+						println("EOT transition");
137
+					}
131 138
 					$s = $this->eot[$s];
132 139
 					$input->consume();
133 140
 					continue;
134 141
 				}
135 142
 				if ( $c==Token::$EOF && $eof[$s]>=0 ) {  // EOF Transition to accept state?
136
-					if ( $this->debug ) echo ("accept via EOF; predict "+$this->accept[$eof[$s]]+" from "+$eof[$s]);
143
+					if ( $this->debug ) {
144
+						echo ("accept via EOF; predict "+$this->accept[$eof[$s]]+" from "+$eof[$s]);
145
+					}
137 146
 					return $this->accept[$eof[$s]];
138 147
 				}
139 148
 				// not in range and not EOF/EOT, must be invalid symbol
@@ -192,7 +201,9 @@  discard block
 block discarded – undo
192 201
 			$v = charAt($encodedString, $i+1);
193 202
 			// add v n times to data
194 203
 			for ($j=1; $j<=$n; $j++) {
195
-				if($v==0xff) $v=-1;
204
+				if($v==0xff) {
205
+					$v=-1;
206
+				}
196 207
 				$data[$di++] = $v;
197 208
 			}
198 209
 		}
Please login to merge, or discard this patch.
libraries/antlr/EarlyExitException.php 1 patch
Doc Comments   +3 added lines patch added patch discarded remove patch
@@ -31,6 +31,9 @@
 block discarded – undo
31 31
 class EarlyExitException extends RecognitionException {
32 32
 	public $decisionNumber;
33 33
 
34
+	/**
35
+	 * @param integer $decisionNumber
36
+	 */
34 37
 	public function __construct($decisionNumber, $input) {
35 38
 		parent::__construct($input);
36 39
 		$this->decisionNumber = $decisionNumber;
Please login to merge, or discard this patch.
libraries/antlr/IntStream.php 3 patches
Doc Comments   +8 added lines patch added patch discarded remove patch
@@ -31,12 +31,17 @@  discard block
 block discarded – undo
31 31
  *  or token type sequence (such as interpretation).
32 32
  */
33 33
 interface IntStream {
34
+
35
+	/**
36
+	 * @return void
37
+	 */
34 38
 	public function consume();
35 39
 
36 40
 	/** Get int at current input pointer + i ahead where i=1 is next int.
37 41
 	 *  Negative indexes are allowed.  LA(-1) is previous token (token
38 42
 	 *  just matched).  LA(-i) where i is before first token should
39 43
 	 *  yield -1, invalid char / EOF.
44
+	 * @param integer $i
40 45
 	 */
41 46
 	public function LA($i);
42 47
 
@@ -62,6 +67,7 @@  discard block
 block discarded – undo
62 67
 	 *  created after this marker argument, this routine must unroll them
63 68
 	 *  like a stack.  Assume the state the stream was in when this marker
64 69
 	 *  was created.
70
+	 * @return void
65 71
 	 */
66 72
 	public function rewind($marker=null);
67 73
 
@@ -72,6 +78,7 @@  discard block
 block discarded – undo
72 78
 	 *  This must throw away resources for all markers back to the marker
73 79
 	 *  argument.  So if you're nested 5 levels of mark(), and then release(2)
74 80
 	 *  you have to release resources for depths 2..5.
81
+	 * @return void
75 82
 	 */
76 83
 	public function release($marker);
77 84
 
@@ -94,6 +101,7 @@  discard block
 block discarded – undo
94 101
 	 *  The index is 0..n-1.  A seek to position i means that LA(1) will
95 102
 	 *  return the ith symbol.  So, seeking to 0 means LA(1) will return the
96 103
 	 *  first element in the stream. 
104
+	 * @return void
97 105
 	 */
98 106
 	public function seek($index);
99 107
 
Please login to merge, or discard this patch.
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -41,18 +41,18 @@
 block discarded – undo
41 41
 	public function LA($i);
42 42
 
43 43
 	/** Tell the stream to start buffering if it hasn't already.  Return
44
-     *  current input position, index(), or some other marker so that
44
+	 *  current input position, index(), or some other marker so that
45 45
 	 *  when passed to rewind() you get back to the same spot.
46 46
 	 *  rewind(mark()) should not affect the input cursor.  The Lexer
47 47
 	 *  track line/col info as well as input index so its markers are
48 48
 	 *  not pure input indexes.  Same for tree node streams.
49
-     */
49
+	 */
50 50
 	public function mark();
51 51
 
52 52
 	/** Return the current input symbol index 0..n where n indicates the
53
-     *  last symbol has been read.  The index is the symbol about to be
53
+	 *  last symbol has been read.  The index is the symbol about to be
54 54
 	 *  read not the most recently read symbol.
55
-     */
55
+	 */
56 56
 	public function index();
57 57
 
58 58
 	/** Reset the stream so that next call to index would return marker.
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -63,7 +63,7 @@
 block discarded – undo
63 63
 	 *  like a stack.  Assume the state the stream was in when this marker
64 64
 	 *  was created.
65 65
 	 */
66
-	public function rewind($marker=null);
66
+	public function rewind($marker = null);
67 67
 
68 68
 	/** You may want to commit to a backtrack but don't want to force the
69 69
 	 *  stream to keep bookkeeping objects around for a marker that is
Please login to merge, or discard this patch.
libraries/antlr/NoViableAltException.php 3 patches
Doc Comments   +3 added lines patch added patch discarded remove patch
@@ -32,6 +32,9 @@
 block discarded – undo
32 32
 	public $decisionNumber;
33 33
 	public $stateNumber;
34 34
 
35
+	/**
36
+	 * @param string $grammarDecisionDescription
37
+	 */
35 38
 	public function __construct($grammarDecisionDescription,
36 39
 								$decisionNumber,
37 40
 								$stateNumber,
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -44,11 +44,11 @@
 block discarded – undo
44 44
 	}
45 45
 
46 46
 	public function __toString() {
47
-		if ( $this->input instanceof CharStream ) {
48
-			return "NoViableAltException('".$this->getUnexpectedType()."'@[".$this->grammarDecisionDescription."])";
47
+		if ($this->input instanceof CharStream) {
48
+			return "NoViableAltException('" . $this->getUnexpectedType() . "'@[" . $this->grammarDecisionDescription . "])";
49 49
 		}
50 50
 		else {
51
-			return "NoViableAltException(".$this->getUnexpectedType()."@[".$this->grammarDecisionDescription."])";
51
+			return "NoViableAltException(" . $this->getUnexpectedType() . "@[" . $this->grammarDecisionDescription . "])";
52 52
 		}
53 53
 	}
54 54
 }
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -46,8 +46,7 @@
 block discarded – undo
46 46
 	public function __toString() {
47 47
 		if ( $this->input instanceof CharStream ) {
48 48
 			return "NoViableAltException('".$this->getUnexpectedType()."'@[".$this->grammarDecisionDescription."])";
49
-		}
50
-		else {
49
+		} else {
51 50
 			return "NoViableAltException(".$this->getUnexpectedType()."@[".$this->grammarDecisionDescription."])";
52 51
 		}
53 52
 	}
Please login to merge, or discard this patch.
libraries/antlr/Set.php 2 patches
Doc Comments   +3 added lines patch added patch discarded remove patch
@@ -24,6 +24,9 @@
 block discarded – undo
24 24
 			$this->store = $this->union($otherSet)->store;
25 25
 		}
26 26
 		
27
+		/**
28
+		 * @param integer $value
29
+		 */
27 30
 		public function remove($value){
28 31
 			unset($this->store[$value]);
29 32
 		}
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -1,30 +1,30 @@
 block discarded – undo
1 1
 <?php
2 2
 	//Todo: find a decent set implementation for php
3
-	class Set{
4
-		public function __construct($arr){
3
+	class Set {
4
+		public function __construct($arr) {
5 5
 			$this->store = array();
6
-			foreach($arr as $el){
6
+			foreach ($arr as $el) {
7 7
 				$this->store[$el] = $el;
8 8
 			}
9 9
 		}
10 10
 		
11
-		public function add($value){
11
+		public function add($value) {
12 12
 			$this->store[$value] = $value;
13 13
 		}
14 14
 		
15
-		public function member($value){
15
+		public function member($value) {
16 16
 			return array_key_exists($value, $this->store);
17 17
 		}
18 18
 		
19
-		public function union($otherSet){
19
+		public function union($otherSet) {
20 20
 			return new Set(array_merge($this->store, $otherSet->store));
21 21
 		}
22 22
 		
23
-		public function unionInPlace($otherSet){
23
+		public function unionInPlace($otherSet) {
24 24
 			$this->store = $this->union($otherSet)->store;
25 25
 		}
26 26
 		
27
-		public function remove($value){
27
+		public function remove($value) {
28 28
 			unset($this->store[$value]);
29 29
 		}
30 30
 	}
Please login to merge, or discard this patch.
libraries/antlr/TokenStream.php 2 patches
Doc Comments   +1 added lines patch added patch discarded remove patch
@@ -55,6 +55,7 @@
 block discarded – undo
55 55
 	 *  If the stream does not buffer all the tokens then it can just
56 56
 	 *  return "" or null;  Users should not access $ruleLabel.text in
57 57
 	 *  an action of course in that case.
58
+	 * @return null|string
58 59
 	 */
59 60
 	public function toStringBetween($start, $stop);
60 61
 	
Please login to merge, or discard this patch.
Indentation   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -29,13 +29,13 @@
 block discarded – undo
29 29
 
30 30
 /** A stream of tokens accessing tokens from a TokenSource */
31 31
 interface TokenStream extends IntStream {
32
-    /** Get Token at current input pointer + i ahead where i=1 is next Token.
32
+	/** Get Token at current input pointer + i ahead where i=1 is next Token.
33 33
 	 *  i<0 indicates tokens in the past.  So -1 is previous token and -2 is
34 34
 	 *  two tokens ago. LT(0) is undefined.  For i>=n, return Token.EOFToken.
35 35
 	 *  Return null for LT(0) and any index that results in an absolute address
36 36
 	 *  that is negative.
37 37
 	 */
38
-    public function LT($k);
38
+	public function LT($k);
39 39
 
40 40
 	/** Get a token at an absolute index i; 0..n-1.  This is really only
41 41
 	 *  needed for profiling and debugging and token stream rewriting.
Please login to merge, or discard this patch.
libraries/HTTP_Session/Session.php 1 patch
Doc Comments   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -549,7 +549,7 @@  discard block
 block discarded – undo
549 549
 	 * Returns session variable
550 550
 	 *
551 551
 	 * @param string $name    Name of a variable
552
-	 * @param mixed  $default Default value of a variable if not set
552
+	 * @param string  $default Default value of a variable if not set
553 553
 	 *
554 554
 	 * @static
555 555
 	 * @access public
@@ -758,7 +758,7 @@  discard block
 block discarded – undo
758 758
 	 *
759 759
 	 * @static
760 760
 	 * @access public
761
-	 * @return bool   The previous value of the property
761
+	 * @return string   The previous value of the property
762 762
 	 */
763 763
 	public static function setGcMaxLifetime($gcMaxLifetime = null)
764 764
 	{
@@ -781,7 +781,7 @@  discard block
 block discarded – undo
781 781
 	 *
782 782
 	 * @static
783 783
 	 * @access public
784
-	 * @return bool   The previous value of the property
784
+	 * @return string   The previous value of the property
785 785
 	 */
786 786
 	public static function setGcProbability($gcProbability = null)
787 787
 	{
Please login to merge, or discard this patch.
libraries/SabreDAV/CalDAV/Backend/AbstractBackend.php 3 patches
Doc Comments   -1 removed lines patch added patch discarded remove patch
@@ -28,7 +28,6 @@
 block discarded – undo
28 28
      *
29 29
      * Read the PropPatch documenation for more info and examples.
30 30
      *
31
-     * @param string $path
32 31
      * @param \Sabre\DAV\PropPatch $propPatch
33 32
      * @return void
34 33
      */
Please login to merge, or discard this patch.
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,8 +2,8 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\CalDAV\Backend;
4 4
 
5
-use Sabre\VObject;
6 5
 use Sabre\CalDAV;
6
+use Sabre\VObject;
7 7
 
8 8
 /**
9 9
  * Abstract Calendaring backend. Extend this class to create your own backends.
Please login to merge, or discard this patch.
Indentation   +206 added lines, -206 removed lines patch added patch discarded remove patch
@@ -16,211 +16,211 @@
 block discarded – undo
16 16
  */
17 17
 abstract class AbstractBackend implements BackendInterface {
18 18
 
19
-    /**
20
-     * Updates properties for a calendar.
21
-     *
22
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
23
-     * To do the actual updates, you must tell this object which properties
24
-     * you're going to process with the handle() method.
25
-     *
26
-     * Calling the handle method is like telling the PropPatch object "I
27
-     * promise I can handle updating this property".
28
-     *
29
-     * Read the PropPatch documenation for more info and examples.
30
-     *
31
-     * @param string $path
32
-     * @param \Sabre\DAV\PropPatch $propPatch
33
-     * @return void
34
-     */
35
-    public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) {
36
-
37
-    }
38
-
39
-    /**
40
-     * Returns a list of calendar objects.
41
-     *
42
-     * This method should work identical to getCalendarObject, but instead
43
-     * return all the calendar objects in the list as an array.
44
-     *
45
-     * If the backend supports this, it may allow for some speed-ups.
46
-     *
47
-     * @param mixed $calendarId
48
-     * @param array $uris
49
-     * @return array
50
-     */
51
-    public function getMultipleCalendarObjects($calendarId, array $uris) {
52
-
53
-        return array_map(function($uri) use ($calendarId) {
54
-            return $this->getCalendarObject($calendarId, $uri);
55
-        }, $uris);
56
-
57
-    }
58
-
59
-    /**
60
-     * Performs a calendar-query on the contents of this calendar.
61
-     *
62
-     * The calendar-query is defined in RFC4791 : CalDAV. Using the
63
-     * calendar-query it is possible for a client to request a specific set of
64
-     * object, based on contents of iCalendar properties, date-ranges and
65
-     * iCalendar component types (VTODO, VEVENT).
66
-     *
67
-     * This method should just return a list of (relative) urls that match this
68
-     * query.
69
-     *
70
-     * The list of filters are specified as an array. The exact array is
71
-     * documented by \Sabre\CalDAV\CalendarQueryParser.
72
-     *
73
-     * Note that it is extremely likely that getCalendarObject for every path
74
-     * returned from this method will be called almost immediately after. You
75
-     * may want to anticipate this to speed up these requests.
76
-     *
77
-     * This method provides a default implementation, which parses *all* the
78
-     * iCalendar objects in the specified calendar.
79
-     *
80
-     * This default may well be good enough for personal use, and calendars
81
-     * that aren't very large. But if you anticipate high usage, big calendars
82
-     * or high loads, you are strongly adviced to optimize certain paths.
83
-     *
84
-     * The best way to do so is override this method and to optimize
85
-     * specifically for 'common filters'.
86
-     *
87
-     * Requests that are extremely common are:
88
-     *   * requests for just VEVENTS
89
-     *   * requests for just VTODO
90
-     *   * requests with a time-range-filter on either VEVENT or VTODO.
91
-     *
92
-     * ..and combinations of these requests. It may not be worth it to try to
93
-     * handle every possible situation and just rely on the (relatively
94
-     * easy to use) CalendarQueryValidator to handle the rest.
95
-     *
96
-     * Note that especially time-range-filters may be difficult to parse. A
97
-     * time-range filter specified on a VEVENT must for instance also handle
98
-     * recurrence rules correctly.
99
-     * A good example of how to interprete all these filters can also simply
100
-     * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
101
-     * as possible, so it gives you a good idea on what type of stuff you need
102
-     * to think of.
103
-     *
104
-     * @param mixed $calendarId
105
-     * @param array $filters
106
-     * @return array
107
-     */
108
-    public function calendarQuery($calendarId, array $filters) {
109
-
110
-        $result = [];
111
-        $objects = $this->getCalendarObjects($calendarId);
112
-
113
-        foreach ($objects as $object) {
114
-
115
-            if ($this->validateFilterForObject($object, $filters)) {
116
-                $result[] = $object['uri'];
117
-            }
118
-
119
-        }
120
-
121
-        return $result;
122
-
123
-    }
124
-
125
-    /**
126
-     * This method validates if a filter (as passed to calendarQuery) matches
127
-     * the given object.
128
-     *
129
-     * @param array $object
130
-     * @param array $filters
131
-     * @return bool
132
-     */
133
-    protected function validateFilterForObject(array $object, array $filters) {
134
-
135
-        // Unfortunately, setting the 'calendardata' here is optional. If
136
-        // it was excluded, we actually need another call to get this as
137
-        // well.
138
-        if (!isset($object['calendardata'])) {
139
-            $object = $this->getCalendarObject($object['calendarid'], $object['uri']);
140
-        }
141
-
142
-        $vObject = VObject\Reader::read($object['calendardata']);
143
-
144
-        $validator = new CalDAV\CalendarQueryValidator();
145
-        $result = $validator->validate($vObject, $filters);
146
-
147
-        // Destroy circular references so PHP will GC the object.
148
-        $vObject->destroy();
149
-
150
-        return $result;
151
-
152
-    }
153
-
154
-    /**
155
-     * Searches through all of a users calendars and calendar objects to find
156
-     * an object with a specific UID.
157
-     *
158
-     * This method should return the path to this object, relative to the
159
-     * calendar home, so this path usually only contains two parts:
160
-     *
161
-     * calendarpath/objectpath.ics
162
-     *
163
-     * If the uid is not found, return null.
164
-     *
165
-     * This method should only consider * objects that the principal owns, so
166
-     * any calendars owned by other principals that also appear in this
167
-     * collection should be ignored.
168
-     *
169
-     * @param string $principalUri
170
-     * @param string $uid
171
-     * @return string|null
172
-     */
173
-    public function getCalendarObjectByUID($principalUri, $uid) {
174
-
175
-        // Note: this is a super slow naive implementation of this method. You
176
-        // are highly recommended to optimize it, if your backend allows it.
177
-        foreach ($this->getCalendarsForUser($principalUri) as $calendar) {
178
-
179
-            // We must ignore calendars owned by other principals.
180
-            if ($calendar['principaluri'] !== $principalUri) {
181
-                continue;
182
-            }
183
-
184
-            // Ignore calendars that are shared.
185
-            if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal'] !== $principalUri) {
186
-                continue;
187
-            }
188
-
189
-            $results = $this->calendarQuery(
190
-                $calendar['id'],
191
-                [
192
-                    'name'         => 'VCALENDAR',
193
-                    'prop-filters' => [],
194
-                    'comp-filters' => [
195
-                        [
196
-                            'name'           => 'VEVENT',
197
-                            'is-not-defined' => false,
198
-                            'time-range'     => null,
199
-                            'comp-filters'   => [],
200
-                            'prop-filters'   => [
201
-                                [
202
-                                    'name'           => 'UID',
203
-                                    'is-not-defined' => false,
204
-                                    'time-range'     => null,
205
-                                    'text-match'     => [
206
-                                        'value'            => $uid,
207
-                                        'negate-condition' => false,
208
-                                        'collation'        => 'i;octet',
209
-                                    ],
210
-                                    'param-filters' => [],
211
-                                ],
212
-                            ]
213
-                        ]
214
-                    ],
215
-                ]
216
-            );
217
-            if ($results) {
218
-                // We have a match
219
-                return $calendar['uri'] . '/' . $results[0];
220
-            }
221
-
222
-        }
223
-
224
-    }
19
+	/**
20
+	 * Updates properties for a calendar.
21
+	 *
22
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
23
+	 * To do the actual updates, you must tell this object which properties
24
+	 * you're going to process with the handle() method.
25
+	 *
26
+	 * Calling the handle method is like telling the PropPatch object "I
27
+	 * promise I can handle updating this property".
28
+	 *
29
+	 * Read the PropPatch documenation for more info and examples.
30
+	 *
31
+	 * @param string $path
32
+	 * @param \Sabre\DAV\PropPatch $propPatch
33
+	 * @return void
34
+	 */
35
+	public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) {
36
+
37
+	}
38
+
39
+	/**
40
+	 * Returns a list of calendar objects.
41
+	 *
42
+	 * This method should work identical to getCalendarObject, but instead
43
+	 * return all the calendar objects in the list as an array.
44
+	 *
45
+	 * If the backend supports this, it may allow for some speed-ups.
46
+	 *
47
+	 * @param mixed $calendarId
48
+	 * @param array $uris
49
+	 * @return array
50
+	 */
51
+	public function getMultipleCalendarObjects($calendarId, array $uris) {
52
+
53
+		return array_map(function($uri) use ($calendarId) {
54
+			return $this->getCalendarObject($calendarId, $uri);
55
+		}, $uris);
56
+
57
+	}
58
+
59
+	/**
60
+	 * Performs a calendar-query on the contents of this calendar.
61
+	 *
62
+	 * The calendar-query is defined in RFC4791 : CalDAV. Using the
63
+	 * calendar-query it is possible for a client to request a specific set of
64
+	 * object, based on contents of iCalendar properties, date-ranges and
65
+	 * iCalendar component types (VTODO, VEVENT).
66
+	 *
67
+	 * This method should just return a list of (relative) urls that match this
68
+	 * query.
69
+	 *
70
+	 * The list of filters are specified as an array. The exact array is
71
+	 * documented by \Sabre\CalDAV\CalendarQueryParser.
72
+	 *
73
+	 * Note that it is extremely likely that getCalendarObject for every path
74
+	 * returned from this method will be called almost immediately after. You
75
+	 * may want to anticipate this to speed up these requests.
76
+	 *
77
+	 * This method provides a default implementation, which parses *all* the
78
+	 * iCalendar objects in the specified calendar.
79
+	 *
80
+	 * This default may well be good enough for personal use, and calendars
81
+	 * that aren't very large. But if you anticipate high usage, big calendars
82
+	 * or high loads, you are strongly adviced to optimize certain paths.
83
+	 *
84
+	 * The best way to do so is override this method and to optimize
85
+	 * specifically for 'common filters'.
86
+	 *
87
+	 * Requests that are extremely common are:
88
+	 *   * requests for just VEVENTS
89
+	 *   * requests for just VTODO
90
+	 *   * requests with a time-range-filter on either VEVENT or VTODO.
91
+	 *
92
+	 * ..and combinations of these requests. It may not be worth it to try to
93
+	 * handle every possible situation and just rely on the (relatively
94
+	 * easy to use) CalendarQueryValidator to handle the rest.
95
+	 *
96
+	 * Note that especially time-range-filters may be difficult to parse. A
97
+	 * time-range filter specified on a VEVENT must for instance also handle
98
+	 * recurrence rules correctly.
99
+	 * A good example of how to interprete all these filters can also simply
100
+	 * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
101
+	 * as possible, so it gives you a good idea on what type of stuff you need
102
+	 * to think of.
103
+	 *
104
+	 * @param mixed $calendarId
105
+	 * @param array $filters
106
+	 * @return array
107
+	 */
108
+	public function calendarQuery($calendarId, array $filters) {
109
+
110
+		$result = [];
111
+		$objects = $this->getCalendarObjects($calendarId);
112
+
113
+		foreach ($objects as $object) {
114
+
115
+			if ($this->validateFilterForObject($object, $filters)) {
116
+				$result[] = $object['uri'];
117
+			}
118
+
119
+		}
120
+
121
+		return $result;
122
+
123
+	}
124
+
125
+	/**
126
+	 * This method validates if a filter (as passed to calendarQuery) matches
127
+	 * the given object.
128
+	 *
129
+	 * @param array $object
130
+	 * @param array $filters
131
+	 * @return bool
132
+	 */
133
+	protected function validateFilterForObject(array $object, array $filters) {
134
+
135
+		// Unfortunately, setting the 'calendardata' here is optional. If
136
+		// it was excluded, we actually need another call to get this as
137
+		// well.
138
+		if (!isset($object['calendardata'])) {
139
+			$object = $this->getCalendarObject($object['calendarid'], $object['uri']);
140
+		}
141
+
142
+		$vObject = VObject\Reader::read($object['calendardata']);
143
+
144
+		$validator = new CalDAV\CalendarQueryValidator();
145
+		$result = $validator->validate($vObject, $filters);
146
+
147
+		// Destroy circular references so PHP will GC the object.
148
+		$vObject->destroy();
149
+
150
+		return $result;
151
+
152
+	}
153
+
154
+	/**
155
+	 * Searches through all of a users calendars and calendar objects to find
156
+	 * an object with a specific UID.
157
+	 *
158
+	 * This method should return the path to this object, relative to the
159
+	 * calendar home, so this path usually only contains two parts:
160
+	 *
161
+	 * calendarpath/objectpath.ics
162
+	 *
163
+	 * If the uid is not found, return null.
164
+	 *
165
+	 * This method should only consider * objects that the principal owns, so
166
+	 * any calendars owned by other principals that also appear in this
167
+	 * collection should be ignored.
168
+	 *
169
+	 * @param string $principalUri
170
+	 * @param string $uid
171
+	 * @return string|null
172
+	 */
173
+	public function getCalendarObjectByUID($principalUri, $uid) {
174
+
175
+		// Note: this is a super slow naive implementation of this method. You
176
+		// are highly recommended to optimize it, if your backend allows it.
177
+		foreach ($this->getCalendarsForUser($principalUri) as $calendar) {
178
+
179
+			// We must ignore calendars owned by other principals.
180
+			if ($calendar['principaluri'] !== $principalUri) {
181
+				continue;
182
+			}
183
+
184
+			// Ignore calendars that are shared.
185
+			if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal'] !== $principalUri) {
186
+				continue;
187
+			}
188
+
189
+			$results = $this->calendarQuery(
190
+				$calendar['id'],
191
+				[
192
+					'name'         => 'VCALENDAR',
193
+					'prop-filters' => [],
194
+					'comp-filters' => [
195
+						[
196
+							'name'           => 'VEVENT',
197
+							'is-not-defined' => false,
198
+							'time-range'     => null,
199
+							'comp-filters'   => [],
200
+							'prop-filters'   => [
201
+								[
202
+									'name'           => 'UID',
203
+									'is-not-defined' => false,
204
+									'time-range'     => null,
205
+									'text-match'     => [
206
+										'value'            => $uid,
207
+										'negate-condition' => false,
208
+										'collation'        => 'i;octet',
209
+									],
210
+									'param-filters' => [],
211
+								],
212
+							]
213
+						]
214
+					],
215
+				]
216
+			);
217
+			if ($results) {
218
+				// We have a match
219
+				return $calendar['uri'] . '/' . $results[0];
220
+			}
221
+
222
+		}
223
+
224
+	}
225 225
 
226 226
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/CalDAV/Backend/PDO.php 5 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,10 +2,10 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\CalDAV\Backend;
4 4
 
5
-use Sabre\VObject;
6 5
 use Sabre\CalDAV;
7 6
 use Sabre\DAV;
8 7
 use Sabre\DAV\Exception\Forbidden;
8
+use Sabre\VObject;
9 9
 
10 10
 /**
11 11
  * PDO CalDAV backend
Please login to merge, or discard this patch.
Braces   +12 added lines, -4 removed lines patch added patch discarded remove patch
@@ -398,7 +398,9 @@  discard block
 block discarded – undo
398 398
         $stmt->execute([$calendarId, $objectUri]);
399 399
         $row = $stmt->fetch(\PDO::FETCH_ASSOC);
400 400
 
401
-        if (!$row) return null;
401
+        if (!$row) {
402
+        	return null;
403
+        }
402 404
 
403 405
         return [
404 406
             'id'            => $row['id'],
@@ -862,7 +864,9 @@  discard block
 block discarded – undo
862 864
         $stmt->execute([ $calendarId ]);
863 865
         $currentToken = $stmt->fetchColumn(0);
864 866
 
865
-        if (is_null($currentToken)) return null;
867
+        if (is_null($currentToken)) {
868
+        	return null;
869
+        }
866 870
 
867 871
         $result = [
868 872
             'syncToken' => $currentToken,
@@ -874,7 +878,9 @@  discard block
 block discarded – undo
874 878
         if ($syncToken) {
875 879
 
876 880
             $query = sprintf('SELECT uri, operation FROM %s WHERE synctoken >= ? && synctoken < ? && calendarid = ? ORDER BY synctoken', $this->calendarChangesTableName);
877
-            if ($limit > 0) $query .= " LIMIT " . (int)$limit;
881
+            if ($limit > 0) {
882
+            	$query .= " LIMIT " . (int)$limit;
883
+            }
878 884
 
879 885
             // Fetching all changes
880 886
             $stmt = $this->pdo->prepare($query);
@@ -1150,7 +1156,9 @@  discard block
 block discarded – undo
1150 1156
         $stmt->execute([$principalUri, $objectUri]);
1151 1157
         $row = $stmt->fetch(\PDO::FETCH_ASSOC);
1152 1158
 
1153
-        if (!$row) return null;
1159
+        if (!$row) {
1160
+        	return null;
1161
+        }
1154 1162
 
1155 1163
         return [
1156 1164
             'uri'          => $row['uri'],
Please login to merge, or discard this patch.
Indentation   +1147 added lines, -1147 removed lines patch added patch discarded remove patch
@@ -19,763 +19,763 @@  discard block
 block discarded – undo
19 19
  */
20 20
 class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, SchedulingSupport {
21 21
 
22
-    /**
23
-     * We need to specify a max date, because we need to stop *somewhere*
24
-     *
25
-     * On 32 bit system the maximum for a signed integer is 2147483647, so
26
-     * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
27
-     * in 2038-01-19 to avoid problems when the date is converted
28
-     * to a unix timestamp.
29
-     */
30
-    const MAX_DATE = '2038-01-01';
31
-
32
-    /**
33
-     * pdo
34
-     *
35
-     * @var \PDO
36
-     */
37
-    protected $pdo;
38
-
39
-    /**
40
-     * The table name that will be used for calendars
41
-     *
42
-     * @var string
43
-     */
44
-    public $calendarTableName = 'calendars';
45
-
46
-    /**
47
-     * The table name that will be used for calendar objects
48
-     *
49
-     * @var string
50
-     */
51
-    public $calendarObjectTableName = 'calendarobjects';
52
-
53
-    /**
54
-     * The table name that will be used for tracking changes in calendars.
55
-     *
56
-     * @var string
57
-     */
58
-    public $calendarChangesTableName = 'calendarchanges';
59
-
60
-    /**
61
-     * The table name that will be used inbox items.
62
-     *
63
-     * @var string
64
-     */
65
-    public $schedulingObjectTableName = 'schedulingobjects';
66
-
67
-    /**
68
-     * The table name that will be used for calendar subscriptions.
69
-     *
70
-     * @var string
71
-     */
72
-    public $calendarSubscriptionsTableName = 'calendarsubscriptions';
73
-
74
-    /**
75
-     * List of CalDAV properties, and how they map to database fieldnames
76
-     * Add your own properties by simply adding on to this array.
77
-     *
78
-     * Note that only string-based properties are supported here.
79
-     *
80
-     * @var array
81
-     */
82
-    public $propertyMap = [
83
-        '{DAV:}displayname'                                   => 'displayname',
84
-        '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
85
-        '{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => 'timezone',
86
-        '{http://apple.com/ns/ical/}calendar-order'           => 'calendarorder',
87
-        '{http://apple.com/ns/ical/}calendar-color'           => 'calendarcolor',
88
-    ];
89
-
90
-    /**
91
-     * List of subscription properties, and how they map to database fieldnames.
92
-     *
93
-     * @var array
94
-     */
95
-    public $subscriptionPropertyMap = [
96
-        '{DAV:}displayname'                                           => 'displayname',
97
-        '{http://apple.com/ns/ical/}refreshrate'                      => 'refreshrate',
98
-        '{http://apple.com/ns/ical/}calendar-order'                   => 'calendarorder',
99
-        '{http://apple.com/ns/ical/}calendar-color'                   => 'calendarcolor',
100
-        '{http://calendarserver.org/ns/}subscribed-strip-todos'       => 'striptodos',
101
-        '{http://calendarserver.org/ns/}subscribed-strip-alarms'      => 'stripalarms',
102
-        '{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments',
103
-    ];
104
-
105
-    /**
106
-     * Creates the backend
107
-     *
108
-     * @param \PDO $pdo
109
-     */
110
-    public function __construct(\PDO $pdo) {
111
-
112
-        $this->pdo = $pdo;
113
-
114
-    }
115
-
116
-    /**
117
-     * Returns a list of calendars for a principal.
118
-     *
119
-     * Every project is an array with the following keys:
120
-     *  * id, a unique id that will be used by other functions to modify the
121
-     *    calendar. This can be the same as the uri or a database key.
122
-     *  * uri. This is just the 'base uri' or 'filename' of the calendar.
123
-     *  * principaluri. The owner of the calendar. Almost always the same as
124
-     *    principalUri passed to this method.
125
-     *
126
-     * Furthermore it can contain webdav properties in clark notation. A very
127
-     * common one is '{DAV:}displayname'.
128
-     *
129
-     * Many clients also require:
130
-     * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
131
-     * For this property, you can just return an instance of
132
-     * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet.
133
-     *
134
-     * If you return {http://sabredav.org/ns}read-only and set the value to 1,
135
-     * ACL will automatically be put in read-only mode.
136
-     *
137
-     * @param string $principalUri
138
-     * @return array
139
-     */
140
-    public function getCalendarsForUser($principalUri) {
141
-
142
-        $fields = array_values($this->propertyMap);
143
-        $fields[] = 'id';
144
-        $fields[] = 'uri';
145
-        $fields[] = 'synctoken';
146
-        $fields[] = 'components';
147
-        $fields[] = 'principaluri';
148
-        $fields[] = 'transparent';
149
-
150
-        // Making fields a comma-delimited list
151
-        $fields = implode(', ', $fields);
22
+	/**
23
+	 * We need to specify a max date, because we need to stop *somewhere*
24
+	 *
25
+	 * On 32 bit system the maximum for a signed integer is 2147483647, so
26
+	 * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
27
+	 * in 2038-01-19 to avoid problems when the date is converted
28
+	 * to a unix timestamp.
29
+	 */
30
+	const MAX_DATE = '2038-01-01';
31
+
32
+	/**
33
+	 * pdo
34
+	 *
35
+	 * @var \PDO
36
+	 */
37
+	protected $pdo;
38
+
39
+	/**
40
+	 * The table name that will be used for calendars
41
+	 *
42
+	 * @var string
43
+	 */
44
+	public $calendarTableName = 'calendars';
45
+
46
+	/**
47
+	 * The table name that will be used for calendar objects
48
+	 *
49
+	 * @var string
50
+	 */
51
+	public $calendarObjectTableName = 'calendarobjects';
52
+
53
+	/**
54
+	 * The table name that will be used for tracking changes in calendars.
55
+	 *
56
+	 * @var string
57
+	 */
58
+	public $calendarChangesTableName = 'calendarchanges';
59
+
60
+	/**
61
+	 * The table name that will be used inbox items.
62
+	 *
63
+	 * @var string
64
+	 */
65
+	public $schedulingObjectTableName = 'schedulingobjects';
66
+
67
+	/**
68
+	 * The table name that will be used for calendar subscriptions.
69
+	 *
70
+	 * @var string
71
+	 */
72
+	public $calendarSubscriptionsTableName = 'calendarsubscriptions';
73
+
74
+	/**
75
+	 * List of CalDAV properties, and how they map to database fieldnames
76
+	 * Add your own properties by simply adding on to this array.
77
+	 *
78
+	 * Note that only string-based properties are supported here.
79
+	 *
80
+	 * @var array
81
+	 */
82
+	public $propertyMap = [
83
+		'{DAV:}displayname'                                   => 'displayname',
84
+		'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
85
+		'{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => 'timezone',
86
+		'{http://apple.com/ns/ical/}calendar-order'           => 'calendarorder',
87
+		'{http://apple.com/ns/ical/}calendar-color'           => 'calendarcolor',
88
+	];
89
+
90
+	/**
91
+	 * List of subscription properties, and how they map to database fieldnames.
92
+	 *
93
+	 * @var array
94
+	 */
95
+	public $subscriptionPropertyMap = [
96
+		'{DAV:}displayname'                                           => 'displayname',
97
+		'{http://apple.com/ns/ical/}refreshrate'                      => 'refreshrate',
98
+		'{http://apple.com/ns/ical/}calendar-order'                   => 'calendarorder',
99
+		'{http://apple.com/ns/ical/}calendar-color'                   => 'calendarcolor',
100
+		'{http://calendarserver.org/ns/}subscribed-strip-todos'       => 'striptodos',
101
+		'{http://calendarserver.org/ns/}subscribed-strip-alarms'      => 'stripalarms',
102
+		'{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments',
103
+	];
104
+
105
+	/**
106
+	 * Creates the backend
107
+	 *
108
+	 * @param \PDO $pdo
109
+	 */
110
+	public function __construct(\PDO $pdo) {
111
+
112
+		$this->pdo = $pdo;
113
+
114
+	}
115
+
116
+	/**
117
+	 * Returns a list of calendars for a principal.
118
+	 *
119
+	 * Every project is an array with the following keys:
120
+	 *  * id, a unique id that will be used by other functions to modify the
121
+	 *    calendar. This can be the same as the uri or a database key.
122
+	 *  * uri. This is just the 'base uri' or 'filename' of the calendar.
123
+	 *  * principaluri. The owner of the calendar. Almost always the same as
124
+	 *    principalUri passed to this method.
125
+	 *
126
+	 * Furthermore it can contain webdav properties in clark notation. A very
127
+	 * common one is '{DAV:}displayname'.
128
+	 *
129
+	 * Many clients also require:
130
+	 * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
131
+	 * For this property, you can just return an instance of
132
+	 * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet.
133
+	 *
134
+	 * If you return {http://sabredav.org/ns}read-only and set the value to 1,
135
+	 * ACL will automatically be put in read-only mode.
136
+	 *
137
+	 * @param string $principalUri
138
+	 * @return array
139
+	 */
140
+	public function getCalendarsForUser($principalUri) {
141
+
142
+		$fields = array_values($this->propertyMap);
143
+		$fields[] = 'id';
144
+		$fields[] = 'uri';
145
+		$fields[] = 'synctoken';
146
+		$fields[] = 'components';
147
+		$fields[] = 'principaluri';
148
+		$fields[] = 'transparent';
149
+
150
+		// Making fields a comma-delimited list
151
+		$fields = implode(', ', $fields);
152 152
 				$stmt = $this->pdo->prepare(
153 153
 <<<SQL
154 154
 		SELECT $fields FROM {$this->calendarTableName}
155 155
 		WHERE principaluri = ? ORDER BY calendarorder ASC
156 156
 SQL
157 157
 		);
158
-        $stmt->execute([$principalUri]);
159
-
160
-        $calendars = [];
161
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
162
-
163
-            $components = [];
164
-            if ($row['components']) {
165
-                $components = explode(',', $row['components']);
166
-            }
167
-
168
-            $calendar = [
169
-                'id'                                                                 => $row['id'],
170
-                'uri'                                                                => $row['uri'],
171
-                'principaluri'                                                       => $row['principaluri'],
172
-                '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag'                  => 'http://sabre.io/ns/sync/' . ($row['synctoken'] ? $row['synctoken'] : '0'),
173
-                '{http://sabredav.org/ns}sync-token'                                 => $row['synctoken'] ? $row['synctoken'] : '0',
174
-                '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet($components),
175
-                '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'         => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'),
176
-            ];
177
-
178
-
179
-            foreach ($this->propertyMap as $xmlName => $dbName) {
180
-                $calendar[$xmlName] = $row[$dbName];
181
-            }
182
-
183
-            $calendars[] = $calendar;
184
-
185
-        }
186
-
187
-        return $calendars;
188
-
189
-    }
190
-
191
-    /**
192
-     * Creates a new calendar for a principal.
193
-     *
194
-     * If the creation was a success, an id must be returned that can be used
195
-     * to reference this calendar in other methods, such as updateCalendar.
196
-     *
197
-     * @param string $principalUri
198
-     * @param string $calendarUri
199
-     * @param array $properties
200
-     * @return string
201
-     */
202
-    public function createCalendar($principalUri, $calendarUri, array $properties) {
203
-
204
-        $fieldNames = [
205
-            'principaluri',
206
-            'uri',
207
-            'synctoken',
208
-            'transparent',
209
-        ];
210
-        $values = [
211
-            ':principaluri' => $principalUri,
212
-            ':uri'          => $calendarUri,
213
-            ':synctoken'    => 1,
214
-            ':transparent'  => 0,
215
-        ];
216
-
217
-        // Default value
218
-        $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
219
-        $fieldNames[] = 'components';
220
-        if (!isset($properties[$sccs])) {
221
-            $values[':components'] = 'VEVENT,VTODO';
222
-        } else {
223
-            if (!($properties[$sccs] instanceof CalDAV\Xml\Property\SupportedCalendarComponentSet)) {
224
-                throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet');
225
-            }
226
-            $values[':components'] = implode(',', $properties[$sccs]->getValue());
227
-        }
228
-        $transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp';
229
-        if (isset($properties[$transp])) {
230
-            $values[':transparent'] = $properties[$transp]->getValue() === 'transparent';
231
-        }
232
-
233
-        foreach ($this->propertyMap as $xmlName => $dbName) {
234
-            if (isset($properties[$xmlName])) {
235
-
236
-                $values[':' . $dbName] = $properties[$xmlName];
237
-                $fieldNames[] = $dbName;
238
-            }
239
-        }
240
-
241
-        $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")");
242
-        $stmt->execute($values);
243
-
244
-        return $this->pdo->lastInsertId();
245
-
246
-    }
247
-
248
-    /**
249
-     * Updates properties for a calendar.
250
-     *
251
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
252
-     * To do the actual updates, you must tell this object which properties
253
-     * you're going to process with the handle() method.
254
-     *
255
-     * Calling the handle method is like telling the PropPatch object "I
256
-     * promise I can handle updating this property".
257
-     *
258
-     * Read the PropPatch documenation for more info and examples.
259
-     *
260
-     * @param string $calendarId
261
-     * @param \Sabre\DAV\PropPatch $propPatch
262
-     * @return void
263
-     */
264
-    public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) {
265
-
266
-        $supportedProperties = array_keys($this->propertyMap);
267
-        $supportedProperties[] = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp';
268
-
269
-        $propPatch->handle($supportedProperties, function($mutations) use ($calendarId) {
270
-            $newValues = [];
271
-            foreach ($mutations as $propertyName => $propertyValue) {
272
-
273
-                switch ($propertyName) {
274
-                    case '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' :
275
-                        $fieldName = 'transparent';
276
-                        $newValues[$fieldName] = $propertyValue->getValue() === 'transparent';
277
-                        break;
278
-                    default :
279
-                        $fieldName = $this->propertyMap[$propertyName];
280
-                        $newValues[$fieldName] = $propertyValue;
281
-                        break;
282
-                }
283
-
284
-            }
285
-            $valuesSql = [];
286
-            foreach ($newValues as $fieldName => $value) {
287
-                $valuesSql[] = $fieldName . ' = ?';
288
-            }
158
+		$stmt->execute([$principalUri]);
159
+
160
+		$calendars = [];
161
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
162
+
163
+			$components = [];
164
+			if ($row['components']) {
165
+				$components = explode(',', $row['components']);
166
+			}
167
+
168
+			$calendar = [
169
+				'id'                                                                 => $row['id'],
170
+				'uri'                                                                => $row['uri'],
171
+				'principaluri'                                                       => $row['principaluri'],
172
+				'{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag'                  => 'http://sabre.io/ns/sync/' . ($row['synctoken'] ? $row['synctoken'] : '0'),
173
+				'{http://sabredav.org/ns}sync-token'                                 => $row['synctoken'] ? $row['synctoken'] : '0',
174
+				'{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet($components),
175
+				'{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'         => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'),
176
+			];
177
+
178
+
179
+			foreach ($this->propertyMap as $xmlName => $dbName) {
180
+				$calendar[$xmlName] = $row[$dbName];
181
+			}
182
+
183
+			$calendars[] = $calendar;
184
+
185
+		}
186
+
187
+		return $calendars;
188
+
189
+	}
190
+
191
+	/**
192
+	 * Creates a new calendar for a principal.
193
+	 *
194
+	 * If the creation was a success, an id must be returned that can be used
195
+	 * to reference this calendar in other methods, such as updateCalendar.
196
+	 *
197
+	 * @param string $principalUri
198
+	 * @param string $calendarUri
199
+	 * @param array $properties
200
+	 * @return string
201
+	 */
202
+	public function createCalendar($principalUri, $calendarUri, array $properties) {
203
+
204
+		$fieldNames = [
205
+			'principaluri',
206
+			'uri',
207
+			'synctoken',
208
+			'transparent',
209
+		];
210
+		$values = [
211
+			':principaluri' => $principalUri,
212
+			':uri'          => $calendarUri,
213
+			':synctoken'    => 1,
214
+			':transparent'  => 0,
215
+		];
216
+
217
+		// Default value
218
+		$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
219
+		$fieldNames[] = 'components';
220
+		if (!isset($properties[$sccs])) {
221
+			$values[':components'] = 'VEVENT,VTODO';
222
+		} else {
223
+			if (!($properties[$sccs] instanceof CalDAV\Xml\Property\SupportedCalendarComponentSet)) {
224
+				throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet');
225
+			}
226
+			$values[':components'] = implode(',', $properties[$sccs]->getValue());
227
+		}
228
+		$transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp';
229
+		if (isset($properties[$transp])) {
230
+			$values[':transparent'] = $properties[$transp]->getValue() === 'transparent';
231
+		}
232
+
233
+		foreach ($this->propertyMap as $xmlName => $dbName) {
234
+			if (isset($properties[$xmlName])) {
235
+
236
+				$values[':' . $dbName] = $properties[$xmlName];
237
+				$fieldNames[] = $dbName;
238
+			}
239
+		}
240
+
241
+		$stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")");
242
+		$stmt->execute($values);
243
+
244
+		return $this->pdo->lastInsertId();
245
+
246
+	}
247
+
248
+	/**
249
+	 * Updates properties for a calendar.
250
+	 *
251
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
252
+	 * To do the actual updates, you must tell this object which properties
253
+	 * you're going to process with the handle() method.
254
+	 *
255
+	 * Calling the handle method is like telling the PropPatch object "I
256
+	 * promise I can handle updating this property".
257
+	 *
258
+	 * Read the PropPatch documenation for more info and examples.
259
+	 *
260
+	 * @param string $calendarId
261
+	 * @param \Sabre\DAV\PropPatch $propPatch
262
+	 * @return void
263
+	 */
264
+	public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) {
265
+
266
+		$supportedProperties = array_keys($this->propertyMap);
267
+		$supportedProperties[] = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp';
268
+
269
+		$propPatch->handle($supportedProperties, function($mutations) use ($calendarId) {
270
+			$newValues = [];
271
+			foreach ($mutations as $propertyName => $propertyValue) {
272
+
273
+				switch ($propertyName) {
274
+					case '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' :
275
+						$fieldName = 'transparent';
276
+						$newValues[$fieldName] = $propertyValue->getValue() === 'transparent';
277
+						break;
278
+					default :
279
+						$fieldName = $this->propertyMap[$propertyName];
280
+						$newValues[$fieldName] = $propertyValue;
281
+						break;
282
+				}
283
+
284
+			}
285
+			$valuesSql = [];
286
+			foreach ($newValues as $fieldName => $value) {
287
+				$valuesSql[] = $fieldName . ' = ?';
288
+			}
289 289
 			$query = sprintf('UPDATE %s SET %s WHERE id = ?', $this->calendarTableName, implode(', ', $valuesSql));
290
-            $stmt = $this->pdo->prepare($query);
291
-            $newValues['id'] = $calendarId;
292
-            $stmt->execute(array_values($newValues));
290
+			$stmt = $this->pdo->prepare($query);
291
+			$newValues['id'] = $calendarId;
292
+			$stmt->execute(array_values($newValues));
293 293
 
294
-            $this->addChange($calendarId, "", 2);
294
+			$this->addChange($calendarId, "", 2);
295 295
 
296
-            return true;
296
+			return true;
297 297
 
298
-        });
298
+		});
299 299
 
300
-    }
300
+	}
301 301
 
302
-    /**
303
-     * Delete a calendar and all it's objects
304
-     *
305
-     * @param string $calendarId
306
-     * @return void
307
-     */
308
-    public function deleteCalendar($calendarId) {
302
+	/**
303
+	 * Delete a calendar and all it's objects
304
+	 *
305
+	 * @param string $calendarId
306
+	 * @return void
307
+	 */
308
+	public function deleteCalendar($calendarId) {
309 309
 
310 310
 		$query = sprintf('DELETE FROM %s WHERE calendarid = ?', $this->calendarObjectTableName);
311
-        $stmt = $this->pdo->prepare($query);
312
-        $stmt->execute([$calendarId]);
311
+		$stmt = $this->pdo->prepare($query);
312
+		$stmt->execute([$calendarId]);
313 313
 
314 314
 		$query = sprintf('DELETE FROM %s WHERE id = ?', $this->calendarTableName);
315
-        $stmt = $this->pdo->prepare($query);
316
-        $stmt->execute([$calendarId]);
315
+		$stmt = $this->pdo->prepare($query);
316
+		$stmt->execute([$calendarId]);
317 317
 
318
-        $query = sprintf('DELETE FROM %s WHERE calendarid = ?', $this->calendarChangesTableName);
318
+		$query = sprintf('DELETE FROM %s WHERE calendarid = ?', $this->calendarChangesTableName);
319 319
 		$stmt = $this->pdo->prepare($query);
320
-        $stmt->execute([$calendarId]);
321
-
322
-    }
323
-
324
-    /**
325
-     * Returns all calendar objects within a calendar.
326
-     *
327
-     * Every item contains an array with the following keys:
328
-     *   * calendardata - The iCalendar-compatible calendar data
329
-     *   * uri - a unique key which will be used to construct the uri. This can
330
-     *     be any arbitrary string, but making sure it ends with '.ics' is a
331
-     *     good idea. This is only the basename, or filename, not the full
332
-     *     path.
333
-     *   * lastmodified - a timestamp of the last modification time
334
-     *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
335
-     *   '  "abcdef"')
336
-     *   * size - The size of the calendar objects, in bytes.
337
-     *   * component - optional, a string containing the type of object, such
338
-     *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
339
-     *     the Content-Type header.
340
-     *
341
-     * Note that the etag is optional, but it's highly encouraged to return for
342
-     * speed reasons.
343
-     *
344
-     * The calendardata is also optional. If it's not returned
345
-     * 'getCalendarObject' will be called later, which *is* expected to return
346
-     * calendardata.
347
-     *
348
-     * If neither etag or size are specified, the calendardata will be
349
-     * used/fetched to determine these numbers. If both are specified the
350
-     * amount of times this is needed is reduced by a great degree.
351
-     *
352
-     * @param string $calendarId
353
-     * @return array
354
-     */
355
-    public function getCalendarObjects($calendarId) {
320
+		$stmt->execute([$calendarId]);
321
+
322
+	}
323
+
324
+	/**
325
+	 * Returns all calendar objects within a calendar.
326
+	 *
327
+	 * Every item contains an array with the following keys:
328
+	 *   * calendardata - The iCalendar-compatible calendar data
329
+	 *   * uri - a unique key which will be used to construct the uri. This can
330
+	 *     be any arbitrary string, but making sure it ends with '.ics' is a
331
+	 *     good idea. This is only the basename, or filename, not the full
332
+	 *     path.
333
+	 *   * lastmodified - a timestamp of the last modification time
334
+	 *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
335
+	 *   '  "abcdef"')
336
+	 *   * size - The size of the calendar objects, in bytes.
337
+	 *   * component - optional, a string containing the type of object, such
338
+	 *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
339
+	 *     the Content-Type header.
340
+	 *
341
+	 * Note that the etag is optional, but it's highly encouraged to return for
342
+	 * speed reasons.
343
+	 *
344
+	 * The calendardata is also optional. If it's not returned
345
+	 * 'getCalendarObject' will be called later, which *is* expected to return
346
+	 * calendardata.
347
+	 *
348
+	 * If neither etag or size are specified, the calendardata will be
349
+	 * used/fetched to determine these numbers. If both are specified the
350
+	 * amount of times this is needed is reduced by a great degree.
351
+	 *
352
+	 * @param string $calendarId
353
+	 * @return array
354
+	 */
355
+	public function getCalendarObjects($calendarId) {
356 356
 		
357 357
 		$query = sprintf('SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM %s WHERE calendarid = ?', $this->calendarObjectTableName);
358
-        $stmt = $this->pdo->prepare($query);
359
-        $stmt->execute([$calendarId]);
360
-
361
-        $result = [];
362
-        foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
363
-            $result[] = [
364
-                'id'           => $row['id'],
365
-                'uri'          => $row['uri'],
366
-                'lastmodified' => $row['lastmodified'],
367
-                'etag'         => '"' . $row['etag'] . '"',
368
-                'calendarid'   => $row['calendarid'],
369
-                'size'         => (int)$row['size'],
370
-                'component'    => strtolower($row['componenttype']),
371
-            ];
372
-        }
373
-
374
-        return $result;
375
-
376
-    }
377
-
378
-    /**
379
-     * Returns information from a single calendar object, based on it's object
380
-     * uri.
381
-     *
382
-     * The object uri is only the basename, or filename and not a full path.
383
-     *
384
-     * The returned array must have the same keys as getCalendarObjects. The
385
-     * 'calendardata' object is required here though, while it's not required
386
-     * for getCalendarObjects.
387
-     *
388
-     * This method must return null if the object did not exist.
389
-     *
390
-     * @param string $calendarId
391
-     * @param string $objectUri
392
-     * @return array|null
393
-     */
394
-    public function getCalendarObject($calendarId, $objectUri) {
358
+		$stmt = $this->pdo->prepare($query);
359
+		$stmt->execute([$calendarId]);
360
+
361
+		$result = [];
362
+		foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
363
+			$result[] = [
364
+				'id'           => $row['id'],
365
+				'uri'          => $row['uri'],
366
+				'lastmodified' => $row['lastmodified'],
367
+				'etag'         => '"' . $row['etag'] . '"',
368
+				'calendarid'   => $row['calendarid'],
369
+				'size'         => (int)$row['size'],
370
+				'component'    => strtolower($row['componenttype']),
371
+			];
372
+		}
373
+
374
+		return $result;
375
+
376
+	}
377
+
378
+	/**
379
+	 * Returns information from a single calendar object, based on it's object
380
+	 * uri.
381
+	 *
382
+	 * The object uri is only the basename, or filename and not a full path.
383
+	 *
384
+	 * The returned array must have the same keys as getCalendarObjects. The
385
+	 * 'calendardata' object is required here though, while it's not required
386
+	 * for getCalendarObjects.
387
+	 *
388
+	 * This method must return null if the object did not exist.
389
+	 *
390
+	 * @param string $calendarId
391
+	 * @param string $objectUri
392
+	 * @return array|null
393
+	 */
394
+	public function getCalendarObject($calendarId, $objectUri) {
395 395
 		
396 396
 		$query = sprintf('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM %s WHERE calendarid = ? && uri = ?', $this->calendarObjectTableName);
397
-        $stmt = $this->pdo->prepare($query);
398
-        $stmt->execute([$calendarId, $objectUri]);
399
-        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
400
-
401
-        if (!$row) return null;
402
-
403
-        return [
404
-            'id'            => $row['id'],
405
-            'uri'           => $row['uri'],
406
-            'lastmodified'  => $row['lastmodified'],
407
-            'etag'          => '"' . $row['etag'] . '"',
408
-            'calendarid'    => $row['calendarid'],
409
-            'size'          => (int)$row['size'],
410
-            'calendardata'  => $row['calendardata'],
411
-            'component'     => strtolower($row['componenttype']),
412
-         ];
413
-
414
-    }
415
-
416
-    /**
417
-     * Returns a list of calendar objects.
418
-     *
419
-     * This method should work identical to getCalendarObject, but instead
420
-     * return all the calendar objects in the list as an array.
421
-     *
422
-     * If the backend supports this, it may allow for some speed-ups.
423
-     *
424
-     * @param mixed $calendarId
425
-     * @param array $uris
426
-     * @return array
427
-     */
428
-    public function getMultipleCalendarObjects($calendarId, array $uris) {
429
-
430
-        $query =sprintf('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM %s WHERE calendarid = ? && uri IN (', $this->calendarObjectTableName);
431
-        // Inserting a whole bunch of question marks
432
-        $query .= implode(',', array_fill(0, count($uris), '?'));
433
-        $query .= ')';
434
-
435
-        $stmt = $this->pdo->prepare($query);
436
-        $stmt->execute(array_merge([$calendarId], $uris));
437
-
438
-        $result = [];
439
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
440
-
441
-            $result[] = [
442
-                'id'           => $row['id'],
443
-                'uri'          => $row['uri'],
444
-                'lastmodified' => $row['lastmodified'],
445
-                'etag'         => '"' . $row['etag'] . '"',
446
-                'calendarid'   => $row['calendarid'],
447
-                'size'         => (int)$row['size'],
448
-                'calendardata' => $row['calendardata'],
449
-                'component'    => strtolower($row['componenttype']),
450
-            ];
451
-
452
-        }
453
-        return $result;
454
-
455
-    }
456
-
457
-
458
-    /**
459
-     * Creates a new calendar object.
460
-     *
461
-     * The object uri is only the basename, or filename and not a full path.
462
-     *
463
-     * It is possible return an etag from this function, which will be used in
464
-     * the response to this PUT request. Note that the ETag must be surrounded
465
-     * by double-quotes.
466
-     *
467
-     * However, you should only really return this ETag if you don't mangle the
468
-     * calendar-data. If the result of a subsequent GET to this object is not
469
-     * the exact same as this request body, you should omit the ETag.
470
-     *
471
-     * @param mixed $calendarId
472
-     * @param string $objectUri
473
-     * @param string $calendarData
474
-     * @return string|null
475
-     */
476
-    public function createCalendarObject($calendarId, $objectUri, $calendarData) {
477
-
478
-        $extraData = $this->getDenormalizedData($calendarData);
479
-
480
-        $stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarObjectTableName . ' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)');
481
-        $stmt->execute([
482
-            $calendarId,
483
-            $objectUri,
484
-            $calendarData,
485
-            time(),
486
-            $extraData['etag'],
487
-            $extraData['size'],
488
-            $extraData['componentType'],
489
-            $extraData['firstOccurence'],
490
-            $extraData['lastOccurence'],
491
-            $extraData['uid'],
492
-        ]);
493
-        $this->addChange($calendarId, $objectUri, 1);
494
-
495
-        return '"' . $extraData['etag'] . '"';
496
-
497
-    }
498
-
499
-    /**
500
-     * Updates an existing calendarobject, based on it's uri.
501
-     *
502
-     * The object uri is only the basename, or filename and not a full path.
503
-     *
504
-     * It is possible return an etag from this function, which will be used in
505
-     * the response to this PUT request. Note that the ETag must be surrounded
506
-     * by double-quotes.
507
-     *
508
-     * However, you should only really return this ETag if you don't mangle the
509
-     * calendar-data. If the result of a subsequent GET to this object is not
510
-     * the exact same as this request body, you should omit the ETag.
511
-     *
512
-     * @param mixed $calendarId
513
-     * @param string $objectUri
514
-     * @param string $calendarData
515
-     * @return string|null
516
-     */
517
-    public function updateCalendarObject($calendarId, $objectUri, $calendarData) {
518
-
519
-        $extraData = $this->getDenormalizedData($calendarData);
397
+		$stmt = $this->pdo->prepare($query);
398
+		$stmt->execute([$calendarId, $objectUri]);
399
+		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
400
+
401
+		if (!$row) return null;
402
+
403
+		return [
404
+			'id'            => $row['id'],
405
+			'uri'           => $row['uri'],
406
+			'lastmodified'  => $row['lastmodified'],
407
+			'etag'          => '"' . $row['etag'] . '"',
408
+			'calendarid'    => $row['calendarid'],
409
+			'size'          => (int)$row['size'],
410
+			'calendardata'  => $row['calendardata'],
411
+			'component'     => strtolower($row['componenttype']),
412
+		 ];
413
+
414
+	}
415
+
416
+	/**
417
+	 * Returns a list of calendar objects.
418
+	 *
419
+	 * This method should work identical to getCalendarObject, but instead
420
+	 * return all the calendar objects in the list as an array.
421
+	 *
422
+	 * If the backend supports this, it may allow for some speed-ups.
423
+	 *
424
+	 * @param mixed $calendarId
425
+	 * @param array $uris
426
+	 * @return array
427
+	 */
428
+	public function getMultipleCalendarObjects($calendarId, array $uris) {
429
+
430
+		$query =sprintf('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM %s WHERE calendarid = ? && uri IN (', $this->calendarObjectTableName);
431
+		// Inserting a whole bunch of question marks
432
+		$query .= implode(',', array_fill(0, count($uris), '?'));
433
+		$query .= ')';
434
+
435
+		$stmt = $this->pdo->prepare($query);
436
+		$stmt->execute(array_merge([$calendarId], $uris));
437
+
438
+		$result = [];
439
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
440
+
441
+			$result[] = [
442
+				'id'           => $row['id'],
443
+				'uri'          => $row['uri'],
444
+				'lastmodified' => $row['lastmodified'],
445
+				'etag'         => '"' . $row['etag'] . '"',
446
+				'calendarid'   => $row['calendarid'],
447
+				'size'         => (int)$row['size'],
448
+				'calendardata' => $row['calendardata'],
449
+				'component'    => strtolower($row['componenttype']),
450
+			];
451
+
452
+		}
453
+		return $result;
454
+
455
+	}
456
+
457
+
458
+	/**
459
+	 * Creates a new calendar object.
460
+	 *
461
+	 * The object uri is only the basename, or filename and not a full path.
462
+	 *
463
+	 * It is possible return an etag from this function, which will be used in
464
+	 * the response to this PUT request. Note that the ETag must be surrounded
465
+	 * by double-quotes.
466
+	 *
467
+	 * However, you should only really return this ETag if you don't mangle the
468
+	 * calendar-data. If the result of a subsequent GET to this object is not
469
+	 * the exact same as this request body, you should omit the ETag.
470
+	 *
471
+	 * @param mixed $calendarId
472
+	 * @param string $objectUri
473
+	 * @param string $calendarData
474
+	 * @return string|null
475
+	 */
476
+	public function createCalendarObject($calendarId, $objectUri, $calendarData) {
477
+
478
+		$extraData = $this->getDenormalizedData($calendarData);
479
+
480
+		$stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarObjectTableName . ' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)');
481
+		$stmt->execute([
482
+			$calendarId,
483
+			$objectUri,
484
+			$calendarData,
485
+			time(),
486
+			$extraData['etag'],
487
+			$extraData['size'],
488
+			$extraData['componentType'],
489
+			$extraData['firstOccurence'],
490
+			$extraData['lastOccurence'],
491
+			$extraData['uid'],
492
+		]);
493
+		$this->addChange($calendarId, $objectUri, 1);
494
+
495
+		return '"' . $extraData['etag'] . '"';
496
+
497
+	}
498
+
499
+	/**
500
+	 * Updates an existing calendarobject, based on it's uri.
501
+	 *
502
+	 * The object uri is only the basename, or filename and not a full path.
503
+	 *
504
+	 * It is possible return an etag from this function, which will be used in
505
+	 * the response to this PUT request. Note that the ETag must be surrounded
506
+	 * by double-quotes.
507
+	 *
508
+	 * However, you should only really return this ETag if you don't mangle the
509
+	 * calendar-data. If the result of a subsequent GET to this object is not
510
+	 * the exact same as this request body, you should omit the ETag.
511
+	 *
512
+	 * @param mixed $calendarId
513
+	 * @param string $objectUri
514
+	 * @param string $calendarData
515
+	 * @return string|null
516
+	 */
517
+	public function updateCalendarObject($calendarId, $objectUri, $calendarData) {
518
+
519
+		$extraData = $this->getDenormalizedData($calendarData);
520 520
 		$query = sprintf('UPDATE %s SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? && uri = ?', $this->calendarObjectTableName);
521
-        $stmt = $this->pdo->prepare($query);
522
-        $stmt->execute([$calendarData, time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'], $extraData['uid'], $calendarId, $objectUri]);
523
-
524
-        $this->addChange($calendarId, $objectUri, 2);
525
-
526
-        return '"' . $extraData['etag'] . '"';
527
-
528
-    }
529
-
530
-    /**
531
-     * Parses some information from calendar objects, used for optimized
532
-     * calendar-queries.
533
-     *
534
-     * Returns an array with the following keys:
535
-     *   * etag - An md5 checksum of the object without the quotes.
536
-     *   * size - Size of the object in bytes
537
-     *   * componentType - VEVENT, VTODO or VJOURNAL
538
-     *   * firstOccurence
539
-     *   * lastOccurence
540
-     *   * uid - value of the UID property
541
-     *
542
-     * @param string $calendarData
543
-     * @return array
544
-     */
545
-    protected function getDenormalizedData($calendarData) {
546
-
547
-        $vObject = VObject\Reader::read($calendarData);
548
-        $componentType = null;
549
-        $component = null;
550
-        $firstOccurence = null;
551
-        $lastOccurence = null;
552
-        $uid = null;
553
-        foreach ($vObject->getComponents() as $component) {
554
-            if ($component->name !== 'VTIMEZONE') {
555
-                $componentType = $component->name;
556
-                $uid = (string)$component->UID;
557
-                break;
558
-            }
559
-        }
560
-        if (!$componentType) {
561
-            throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
562
-        }
563
-        if ($componentType === 'VEVENT') {
564
-            $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
565
-            // Finding the last occurence is a bit harder
566
-            if (!isset($component->RRULE)) {
567
-                if (isset($component->DTEND)) {
568
-                    $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
569
-                } elseif (isset($component->DURATION)) {
570
-                    $endDate = clone $component->DTSTART->getDateTime();
571
-                    $endDate = $endDate->add(VObject\DateTimeParser::parse($component->DURATION->getValue()));
572
-                    $lastOccurence = $endDate->getTimeStamp();
573
-                } elseif (!$component->DTSTART->hasTime()) {
574
-                    $endDate = clone $component->DTSTART->getDateTime();
575
-                    $endDate = $endDate->modify('+1 day');
576
-                    $lastOccurence = $endDate->getTimeStamp();
577
-                } else {
578
-                    $lastOccurence = $firstOccurence;
579
-                }
580
-            } else {
581
-                $it = new VObject\Recur\EventIterator($vObject, (string)$component->UID);
582
-                $maxDate = new \DateTime(self::MAX_DATE);
583
-                if ($it->isInfinite()) {
584
-                    $lastOccurence = $maxDate->getTimeStamp();
585
-                } else {
586
-                    $end = $it->getDtEnd();
587
-                    while ($it->valid() && $end < $maxDate) {
588
-                        $end = $it->getDtEnd();
589
-                        $it->next();
590
-
591
-                    }
592
-                    $lastOccurence = $end->getTimeStamp();
593
-                }
594
-
595
-            }
596
-        }
597
-
598
-        // Destroy circular references to PHP will GC the object.
599
-        $vObject->destroy();
600
-
601
-        return [
602
-            'etag'           => md5($calendarData),
603
-            'size'           => strlen($calendarData),
604
-            'componentType'  => $componentType,
605
-            'firstOccurence' => $firstOccurence,
606
-            'lastOccurence'  => $lastOccurence,
607
-            'uid'            => $uid,
608
-        ];
609
-
610
-    }
611
-
612
-    /**
613
-     * Deletes an existing calendar object.
614
-     *
615
-     * The object uri is only the basename, or filename and not a full path.
616
-     *
617
-     * @param string $calendarId
618
-     * @param string $objectUri
619
-     * @return void
620
-     */
621
-    public function deleteCalendarObject($calendarId, $objectUri) {
521
+		$stmt = $this->pdo->prepare($query);
522
+		$stmt->execute([$calendarData, time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'], $extraData['uid'], $calendarId, $objectUri]);
523
+
524
+		$this->addChange($calendarId, $objectUri, 2);
525
+
526
+		return '"' . $extraData['etag'] . '"';
527
+
528
+	}
529
+
530
+	/**
531
+	 * Parses some information from calendar objects, used for optimized
532
+	 * calendar-queries.
533
+	 *
534
+	 * Returns an array with the following keys:
535
+	 *   * etag - An md5 checksum of the object without the quotes.
536
+	 *   * size - Size of the object in bytes
537
+	 *   * componentType - VEVENT, VTODO or VJOURNAL
538
+	 *   * firstOccurence
539
+	 *   * lastOccurence
540
+	 *   * uid - value of the UID property
541
+	 *
542
+	 * @param string $calendarData
543
+	 * @return array
544
+	 */
545
+	protected function getDenormalizedData($calendarData) {
546
+
547
+		$vObject = VObject\Reader::read($calendarData);
548
+		$componentType = null;
549
+		$component = null;
550
+		$firstOccurence = null;
551
+		$lastOccurence = null;
552
+		$uid = null;
553
+		foreach ($vObject->getComponents() as $component) {
554
+			if ($component->name !== 'VTIMEZONE') {
555
+				$componentType = $component->name;
556
+				$uid = (string)$component->UID;
557
+				break;
558
+			}
559
+		}
560
+		if (!$componentType) {
561
+			throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
562
+		}
563
+		if ($componentType === 'VEVENT') {
564
+			$firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
565
+			// Finding the last occurence is a bit harder
566
+			if (!isset($component->RRULE)) {
567
+				if (isset($component->DTEND)) {
568
+					$lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
569
+				} elseif (isset($component->DURATION)) {
570
+					$endDate = clone $component->DTSTART->getDateTime();
571
+					$endDate = $endDate->add(VObject\DateTimeParser::parse($component->DURATION->getValue()));
572
+					$lastOccurence = $endDate->getTimeStamp();
573
+				} elseif (!$component->DTSTART->hasTime()) {
574
+					$endDate = clone $component->DTSTART->getDateTime();
575
+					$endDate = $endDate->modify('+1 day');
576
+					$lastOccurence = $endDate->getTimeStamp();
577
+				} else {
578
+					$lastOccurence = $firstOccurence;
579
+				}
580
+			} else {
581
+				$it = new VObject\Recur\EventIterator($vObject, (string)$component->UID);
582
+				$maxDate = new \DateTime(self::MAX_DATE);
583
+				if ($it->isInfinite()) {
584
+					$lastOccurence = $maxDate->getTimeStamp();
585
+				} else {
586
+					$end = $it->getDtEnd();
587
+					while ($it->valid() && $end < $maxDate) {
588
+						$end = $it->getDtEnd();
589
+						$it->next();
590
+
591
+					}
592
+					$lastOccurence = $end->getTimeStamp();
593
+				}
594
+
595
+			}
596
+		}
597
+
598
+		// Destroy circular references to PHP will GC the object.
599
+		$vObject->destroy();
600
+
601
+		return [
602
+			'etag'           => md5($calendarData),
603
+			'size'           => strlen($calendarData),
604
+			'componentType'  => $componentType,
605
+			'firstOccurence' => $firstOccurence,
606
+			'lastOccurence'  => $lastOccurence,
607
+			'uid'            => $uid,
608
+		];
609
+
610
+	}
611
+
612
+	/**
613
+	 * Deletes an existing calendar object.
614
+	 *
615
+	 * The object uri is only the basename, or filename and not a full path.
616
+	 *
617
+	 * @param string $calendarId
618
+	 * @param string $objectUri
619
+	 * @return void
620
+	 */
621
+	public function deleteCalendarObject($calendarId, $objectUri) {
622 622
 
623 623
 		$query = sprintf('DELETE FROM %s WHERE calendarid = ? && uri = ?', $this->calendarObjectTableName);
624
-        $stmt = $this->pdo->prepare($query);
625
-        $stmt->execute([$calendarId, $objectUri]);
626
-
627
-        $this->addChange($calendarId, $objectUri, 3);
628
-
629
-    }
630
-
631
-    /**
632
-     * Performs a calendar-query on the contents of this calendar.
633
-     *
634
-     * The calendar-query is defined in RFC4791 : CalDAV. Using the
635
-     * calendar-query it is possible for a client to request a specific set of
636
-     * object, based on contents of iCalendar properties, date-ranges and
637
-     * iCalendar component types (VTODO, VEVENT).
638
-     *
639
-     * This method should just return a list of (relative) urls that match this
640
-     * query.
641
-     *
642
-     * The list of filters are specified as an array. The exact array is
643
-     * documented by \Sabre\CalDAV\CalendarQueryParser.
644
-     *
645
-     * Note that it is extremely likely that getCalendarObject for every path
646
-     * returned from this method will be called almost immediately after. You
647
-     * may want to anticipate this to speed up these requests.
648
-     *
649
-     * This method provides a default implementation, which parses *all* the
650
-     * iCalendar objects in the specified calendar.
651
-     *
652
-     * This default may well be good enough for personal use, and calendars
653
-     * that aren't very large. But if you anticipate high usage, big calendars
654
-     * or high loads, you are strongly adviced to optimize certain paths.
655
-     *
656
-     * The best way to do so is override this method and to optimize
657
-     * specifically for 'common filters'.
658
-     *
659
-     * Requests that are extremely common are:
660
-     *   * requests for just VEVENTS
661
-     *   * requests for just VTODO
662
-     *   * requests with a time-range-filter on a VEVENT.
663
-     *
664
-     * ..and combinations of these requests. It may not be worth it to try to
665
-     * handle every possible situation and just rely on the (relatively
666
-     * easy to use) CalendarQueryValidator to handle the rest.
667
-     *
668
-     * Note that especially time-range-filters may be difficult to parse. A
669
-     * time-range filter specified on a VEVENT must for instance also handle
670
-     * recurrence rules correctly.
671
-     * A good example of how to interprete all these filters can also simply
672
-     * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
673
-     * as possible, so it gives you a good idea on what type of stuff you need
674
-     * to think of.
675
-     *
676
-     * This specific implementation (for the PDO) backend optimizes filters on
677
-     * specific components, and VEVENT time-ranges.
678
-     *
679
-     * @param string $calendarId
680
-     * @param array $filters
681
-     * @return array
682
-     */
683
-    public function calendarQuery($calendarId, array $filters) {
684
-
685
-        $componentType = null;
686
-        $requirePostFilter = true;
687
-        $timeRange = null;
688
-
689
-        // if no filters were specified, we don't need to filter after a query
690
-        if (!$filters['prop-filters'] && !$filters['comp-filters']) {
691
-            $requirePostFilter = false;
692
-        }
693
-
694
-        // Figuring out if there's a component filter
695
-        if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
696
-            $componentType = $filters['comp-filters'][0]['name'];
697
-
698
-            // Checking if we need post-filters
699
-            if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) {
700
-                $requirePostFilter = false;
701
-            }
702
-            // There was a time-range filter
703
-            if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) {
704
-                $timeRange = $filters['comp-filters'][0]['time-range'];
705
-
706
-                // If start time OR the end time is not specified, we can do a
707
-                // 100% accurate mysql query.
708
-                if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) {
709
-                    $requirePostFilter = false;
710
-                }
711
-            }
712
-
713
-        }
714
-
715
-        if ($requirePostFilter) {
716
-            $query = sprintf("SELECT uri, calendardata FROM %s WHERE calendarid = :calendarid", $this->calendarObjectTableName);
717
-        } else {
718
-            $query = sprintf("SELECT uri FROM %s WHERE calendarid = :calendarid", $this->calendarObjectTableName);
719
-        }
720
-
721
-        $values = [
722
-            'calendarid' => $calendarId,
723
-        ];
724
-
725
-        if ($componentType) {
726
-            $query .= " && componenttype = :componenttype";
727
-            $values['componenttype'] = $componentType;
728
-        }
729
-
730
-        if ($timeRange && $timeRange['start']) {
731
-            $query .= " && lastoccurence > :startdate";
732
-            $values['startdate'] = $timeRange['start']->getTimeStamp();
733
-        }
734
-        if ($timeRange && $timeRange['end']) {
735
-            $query .= " && firstoccurence < :enddate";
736
-            $values['enddate'] = $timeRange['end']->getTimeStamp();
737
-        }
738
-
739
-        $stmt = $this->pdo->prepare($query);
740
-        $stmt->execute($values);
741
-
742
-        $result = [];
743
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
744
-            if ($requirePostFilter) {
745
-                if (!$this->validateFilterForObject($row, $filters)) {
746
-                    continue;
747
-                }
748
-            }
749
-            $result[] = $row['uri'];
750
-
751
-        }
752
-
753
-        return $result;
754
-
755
-    }
756
-
757
-    /**
758
-     * Searches through all of a users calendars and calendar objects to find
759
-     * an object with a specific UID.
760
-     *
761
-     * This method should return the path to this object, relative to the
762
-     * calendar home, so this path usually only contains two parts:
763
-     *
764
-     * calendarpath/objectpath.ics
765
-     *
766
-     * If the uid is not found, return null.
767
-     *
768
-     * This method should only consider * objects that the principal owns, so
769
-     * any calendars owned by other principals that also appear in this
770
-     * collection should be ignored.
771
-     *
772
-     * @param string $principalUri
773
-     * @param string $uid
774
-     * @return string|null
775
-     */
776
-    public function getCalendarObjectByUID($principalUri, $uid) {
777
-
778
-        $query = <<<SQL
624
+		$stmt = $this->pdo->prepare($query);
625
+		$stmt->execute([$calendarId, $objectUri]);
626
+
627
+		$this->addChange($calendarId, $objectUri, 3);
628
+
629
+	}
630
+
631
+	/**
632
+	 * Performs a calendar-query on the contents of this calendar.
633
+	 *
634
+	 * The calendar-query is defined in RFC4791 : CalDAV. Using the
635
+	 * calendar-query it is possible for a client to request a specific set of
636
+	 * object, based on contents of iCalendar properties, date-ranges and
637
+	 * iCalendar component types (VTODO, VEVENT).
638
+	 *
639
+	 * This method should just return a list of (relative) urls that match this
640
+	 * query.
641
+	 *
642
+	 * The list of filters are specified as an array. The exact array is
643
+	 * documented by \Sabre\CalDAV\CalendarQueryParser.
644
+	 *
645
+	 * Note that it is extremely likely that getCalendarObject for every path
646
+	 * returned from this method will be called almost immediately after. You
647
+	 * may want to anticipate this to speed up these requests.
648
+	 *
649
+	 * This method provides a default implementation, which parses *all* the
650
+	 * iCalendar objects in the specified calendar.
651
+	 *
652
+	 * This default may well be good enough for personal use, and calendars
653
+	 * that aren't very large. But if you anticipate high usage, big calendars
654
+	 * or high loads, you are strongly adviced to optimize certain paths.
655
+	 *
656
+	 * The best way to do so is override this method and to optimize
657
+	 * specifically for 'common filters'.
658
+	 *
659
+	 * Requests that are extremely common are:
660
+	 *   * requests for just VEVENTS
661
+	 *   * requests for just VTODO
662
+	 *   * requests with a time-range-filter on a VEVENT.
663
+	 *
664
+	 * ..and combinations of these requests. It may not be worth it to try to
665
+	 * handle every possible situation and just rely on the (relatively
666
+	 * easy to use) CalendarQueryValidator to handle the rest.
667
+	 *
668
+	 * Note that especially time-range-filters may be difficult to parse. A
669
+	 * time-range filter specified on a VEVENT must for instance also handle
670
+	 * recurrence rules correctly.
671
+	 * A good example of how to interprete all these filters can also simply
672
+	 * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
673
+	 * as possible, so it gives you a good idea on what type of stuff you need
674
+	 * to think of.
675
+	 *
676
+	 * This specific implementation (for the PDO) backend optimizes filters on
677
+	 * specific components, and VEVENT time-ranges.
678
+	 *
679
+	 * @param string $calendarId
680
+	 * @param array $filters
681
+	 * @return array
682
+	 */
683
+	public function calendarQuery($calendarId, array $filters) {
684
+
685
+		$componentType = null;
686
+		$requirePostFilter = true;
687
+		$timeRange = null;
688
+
689
+		// if no filters were specified, we don't need to filter after a query
690
+		if (!$filters['prop-filters'] && !$filters['comp-filters']) {
691
+			$requirePostFilter = false;
692
+		}
693
+
694
+		// Figuring out if there's a component filter
695
+		if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
696
+			$componentType = $filters['comp-filters'][0]['name'];
697
+
698
+			// Checking if we need post-filters
699
+			if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) {
700
+				$requirePostFilter = false;
701
+			}
702
+			// There was a time-range filter
703
+			if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) {
704
+				$timeRange = $filters['comp-filters'][0]['time-range'];
705
+
706
+				// If start time OR the end time is not specified, we can do a
707
+				// 100% accurate mysql query.
708
+				if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) {
709
+					$requirePostFilter = false;
710
+				}
711
+			}
712
+
713
+		}
714
+
715
+		if ($requirePostFilter) {
716
+			$query = sprintf("SELECT uri, calendardata FROM %s WHERE calendarid = :calendarid", $this->calendarObjectTableName);
717
+		} else {
718
+			$query = sprintf("SELECT uri FROM %s WHERE calendarid = :calendarid", $this->calendarObjectTableName);
719
+		}
720
+
721
+		$values = [
722
+			'calendarid' => $calendarId,
723
+		];
724
+
725
+		if ($componentType) {
726
+			$query .= " && componenttype = :componenttype";
727
+			$values['componenttype'] = $componentType;
728
+		}
729
+
730
+		if ($timeRange && $timeRange['start']) {
731
+			$query .= " && lastoccurence > :startdate";
732
+			$values['startdate'] = $timeRange['start']->getTimeStamp();
733
+		}
734
+		if ($timeRange && $timeRange['end']) {
735
+			$query .= " && firstoccurence < :enddate";
736
+			$values['enddate'] = $timeRange['end']->getTimeStamp();
737
+		}
738
+
739
+		$stmt = $this->pdo->prepare($query);
740
+		$stmt->execute($values);
741
+
742
+		$result = [];
743
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
744
+			if ($requirePostFilter) {
745
+				if (!$this->validateFilterForObject($row, $filters)) {
746
+					continue;
747
+				}
748
+			}
749
+			$result[] = $row['uri'];
750
+
751
+		}
752
+
753
+		return $result;
754
+
755
+	}
756
+
757
+	/**
758
+	 * Searches through all of a users calendars and calendar objects to find
759
+	 * an object with a specific UID.
760
+	 *
761
+	 * This method should return the path to this object, relative to the
762
+	 * calendar home, so this path usually only contains two parts:
763
+	 *
764
+	 * calendarpath/objectpath.ics
765
+	 *
766
+	 * If the uid is not found, return null.
767
+	 *
768
+	 * This method should only consider * objects that the principal owns, so
769
+	 * any calendars owned by other principals that also appear in this
770
+	 * collection should be ignored.
771
+	 *
772
+	 * @param string $principalUri
773
+	 * @param string $uid
774
+	 * @return string|null
775
+	 */
776
+	public function getCalendarObjectByUID($principalUri, $uid) {
777
+
778
+		$query = <<<SQL
779 779
 SELECT
780 780
     calendars.uri AS calendaruri, calendarobjects.uri as objecturi
781 781
 FROM
@@ -789,439 +789,439 @@  discard block
 block discarded – undo
789 789
     calendarobjects.uid = ?
790 790
 SQL;
791 791
 
792
-        $stmt = $this->pdo->prepare($query);
793
-        $stmt->execute([$principalUri, $uid]);
794
-
795
-        if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
796
-            return $row['calendaruri'] . '/' . $row['objecturi'];
797
-        }
798
-
799
-    }
800
-
801
-    /**
802
-     * The getChanges method returns all the changes that have happened, since
803
-     * the specified syncToken in the specified calendar.
804
-     *
805
-     * This function should return an array, such as the following:
806
-     *
807
-     * [
808
-     *   'syncToken' => 'The current synctoken',
809
-     *   'added'   => [
810
-     *      'new.txt',
811
-     *   ],
812
-     *   'modified'   => [
813
-     *      'modified.txt',
814
-     *   ],
815
-     *   'deleted' => [
816
-     *      'foo.php.bak',
817
-     *      'old.txt'
818
-     *   ]
819
-     * ];
820
-     *
821
-     * The returned syncToken property should reflect the *current* syncToken
822
-     * of the calendar, as reported in the {http://sabredav.org/ns}sync-token
823
-     * property this is needed here too, to ensure the operation is atomic.
824
-     *
825
-     * If the $syncToken argument is specified as null, this is an initial
826
-     * sync, and all members should be reported.
827
-     *
828
-     * The modified property is an array of nodenames that have changed since
829
-     * the last token.
830
-     *
831
-     * The deleted property is an array with nodenames, that have been deleted
832
-     * from collection.
833
-     *
834
-     * The $syncLevel argument is basically the 'depth' of the report. If it's
835
-     * 1, you only have to report changes that happened only directly in
836
-     * immediate descendants. If it's 2, it should also include changes from
837
-     * the nodes below the child collections. (grandchildren)
838
-     *
839
-     * The $limit argument allows a client to specify how many results should
840
-     * be returned at most. If the limit is not specified, it should be treated
841
-     * as infinite.
842
-     *
843
-     * If the limit (infinite or not) is higher than you're willing to return,
844
-     * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
845
-     *
846
-     * If the syncToken is expired (due to data cleanup) or unknown, you must
847
-     * return null.
848
-     *
849
-     * The limit is 'suggestive'. You are free to ignore it.
850
-     *
851
-     * @param string $calendarId
852
-     * @param string $syncToken
853
-     * @param int $syncLevel
854
-     * @param int $limit
855
-     * @return array
856
-     */
857
-    public function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) {
858
-
859
-        // Current synctoken
792
+		$stmt = $this->pdo->prepare($query);
793
+		$stmt->execute([$principalUri, $uid]);
794
+
795
+		if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
796
+			return $row['calendaruri'] . '/' . $row['objecturi'];
797
+		}
798
+
799
+	}
800
+
801
+	/**
802
+	 * The getChanges method returns all the changes that have happened, since
803
+	 * the specified syncToken in the specified calendar.
804
+	 *
805
+	 * This function should return an array, such as the following:
806
+	 *
807
+	 * [
808
+	 *   'syncToken' => 'The current synctoken',
809
+	 *   'added'   => [
810
+	 *      'new.txt',
811
+	 *   ],
812
+	 *   'modified'   => [
813
+	 *      'modified.txt',
814
+	 *   ],
815
+	 *   'deleted' => [
816
+	 *      'foo.php.bak',
817
+	 *      'old.txt'
818
+	 *   ]
819
+	 * ];
820
+	 *
821
+	 * The returned syncToken property should reflect the *current* syncToken
822
+	 * of the calendar, as reported in the {http://sabredav.org/ns}sync-token
823
+	 * property this is needed here too, to ensure the operation is atomic.
824
+	 *
825
+	 * If the $syncToken argument is specified as null, this is an initial
826
+	 * sync, and all members should be reported.
827
+	 *
828
+	 * The modified property is an array of nodenames that have changed since
829
+	 * the last token.
830
+	 *
831
+	 * The deleted property is an array with nodenames, that have been deleted
832
+	 * from collection.
833
+	 *
834
+	 * The $syncLevel argument is basically the 'depth' of the report. If it's
835
+	 * 1, you only have to report changes that happened only directly in
836
+	 * immediate descendants. If it's 2, it should also include changes from
837
+	 * the nodes below the child collections. (grandchildren)
838
+	 *
839
+	 * The $limit argument allows a client to specify how many results should
840
+	 * be returned at most. If the limit is not specified, it should be treated
841
+	 * as infinite.
842
+	 *
843
+	 * If the limit (infinite or not) is higher than you're willing to return,
844
+	 * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
845
+	 *
846
+	 * If the syncToken is expired (due to data cleanup) or unknown, you must
847
+	 * return null.
848
+	 *
849
+	 * The limit is 'suggestive'. You are free to ignore it.
850
+	 *
851
+	 * @param string $calendarId
852
+	 * @param string $syncToken
853
+	 * @param int $syncLevel
854
+	 * @param int $limit
855
+	 * @return array
856
+	 */
857
+	public function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) {
858
+
859
+		// Current synctoken
860 860
 		$query = sprintf('SELECT synctoken FROM %s WHERE id = ?', $this->calendarTableName);
861
-        $stmt = $this->pdo->prepare($query);
862
-        $stmt->execute([ $calendarId ]);
863
-        $currentToken = $stmt->fetchColumn(0);
864
-
865
-        if (is_null($currentToken)) return null;
866
-
867
-        $result = [
868
-            'syncToken' => $currentToken,
869
-            'added'     => [],
870
-            'modified'  => [],
871
-            'deleted'   => [],
872
-        ];
873
-
874
-        if ($syncToken) {
875
-
876
-            $query = sprintf('SELECT uri, operation FROM %s WHERE synctoken >= ? && synctoken < ? && calendarid = ? ORDER BY synctoken', $this->calendarChangesTableName);
877
-            if ($limit > 0) $query .= " LIMIT " . (int)$limit;
878
-
879
-            // Fetching all changes
880
-            $stmt = $this->pdo->prepare($query);
881
-            $stmt->execute([$syncToken, $currentToken, $calendarId]);
882
-
883
-            $changes = [];
884
-
885
-            // This loop ensures that any duplicates are overwritten, only the
886
-            // last change on a node is relevant.
887
-            while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
888
-
889
-                $changes[$row['uri']] = $row['operation'];
890
-
891
-            }
892
-
893
-            foreach ($changes as $uri => $operation) {
894
-
895
-                switch ($operation) {
896
-                    case 1 :
897
-                        $result['added'][] = $uri;
898
-                        break;
899
-                    case 2 :
900
-                        $result['modified'][] = $uri;
901
-                        break;
902
-                    case 3 :
903
-                        $result['deleted'][] = $uri;
904
-                        break;
905
-                }
906
-
907
-            }
908
-        } else {
909
-            // No synctoken supplied, this is the initial sync.
910
-            $query = sprintf('SELECT uri FROM %s WHERE calendarid = ?', $this->calendarObjectTableName);
911
-            $stmt = $this->pdo->prepare($query);
912
-            $stmt->execute([$calendarId]);
913
-
914
-            $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
915
-        }
916
-        return $result;
917
-
918
-    }
919
-
920
-    /**
921
-     * Adds a change record to the calendarchanges table.
922
-     *
923
-     * @param mixed $calendarId
924
-     * @param string $objectUri
925
-     * @param int $operation 1 = add, 2 = modify, 3 = delete.
926
-     * @return void
927
-     */
928
-    protected function addChange($calendarId, $objectUri, $operation) {
929
-
930
-        $stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarChangesTableName . ' (uri, synctoken, calendarid, operation) SELECT ?, synctoken, ?, ? FROM ' . $this->calendarTableName . ' WHERE id = ?');
931
-        $stmt->execute([
932
-            $objectUri,
933
-            $calendarId,
934
-            $operation,
935
-            $calendarId
936
-        ]);
861
+		$stmt = $this->pdo->prepare($query);
862
+		$stmt->execute([ $calendarId ]);
863
+		$currentToken = $stmt->fetchColumn(0);
864
+
865
+		if (is_null($currentToken)) return null;
866
+
867
+		$result = [
868
+			'syncToken' => $currentToken,
869
+			'added'     => [],
870
+			'modified'  => [],
871
+			'deleted'   => [],
872
+		];
873
+
874
+		if ($syncToken) {
875
+
876
+			$query = sprintf('SELECT uri, operation FROM %s WHERE synctoken >= ? && synctoken < ? && calendarid = ? ORDER BY synctoken', $this->calendarChangesTableName);
877
+			if ($limit > 0) $query .= " LIMIT " . (int)$limit;
878
+
879
+			// Fetching all changes
880
+			$stmt = $this->pdo->prepare($query);
881
+			$stmt->execute([$syncToken, $currentToken, $calendarId]);
882
+
883
+			$changes = [];
884
+
885
+			// This loop ensures that any duplicates are overwritten, only the
886
+			// last change on a node is relevant.
887
+			while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
888
+
889
+				$changes[$row['uri']] = $row['operation'];
890
+
891
+			}
892
+
893
+			foreach ($changes as $uri => $operation) {
894
+
895
+				switch ($operation) {
896
+					case 1 :
897
+						$result['added'][] = $uri;
898
+						break;
899
+					case 2 :
900
+						$result['modified'][] = $uri;
901
+						break;
902
+					case 3 :
903
+						$result['deleted'][] = $uri;
904
+						break;
905
+				}
906
+
907
+			}
908
+		} else {
909
+			// No synctoken supplied, this is the initial sync.
910
+			$query = sprintf('SELECT uri FROM %s WHERE calendarid = ?', $this->calendarObjectTableName);
911
+			$stmt = $this->pdo->prepare($query);
912
+			$stmt->execute([$calendarId]);
913
+
914
+			$result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
915
+		}
916
+		return $result;
917
+
918
+	}
919
+
920
+	/**
921
+	 * Adds a change record to the calendarchanges table.
922
+	 *
923
+	 * @param mixed $calendarId
924
+	 * @param string $objectUri
925
+	 * @param int $operation 1 = add, 2 = modify, 3 = delete.
926
+	 * @return void
927
+	 */
928
+	protected function addChange($calendarId, $objectUri, $operation) {
929
+
930
+		$stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarChangesTableName . ' (uri, synctoken, calendarid, operation) SELECT ?, synctoken, ?, ? FROM ' . $this->calendarTableName . ' WHERE id = ?');
931
+		$stmt->execute([
932
+			$objectUri,
933
+			$calendarId,
934
+			$operation,
935
+			$calendarId
936
+		]);
937 937
 		$query = sprintf('UPDATE %s SET synctoken = synctoken + 1 WHERE id = ?', $this->calendarTableName);
938
-        $stmt = $this->pdo->prepare($query);
939
-        $stmt->execute([
940
-            $calendarId
941
-        ]);
942
-
943
-    }
944
-
945
-    /**
946
-     * Returns a list of subscriptions for a principal.
947
-     *
948
-     * Every subscription is an array with the following keys:
949
-     *  * id, a unique id that will be used by other functions to modify the
950
-     *    subscription. This can be the same as the uri or a database key.
951
-     *  * uri. This is just the 'base uri' or 'filename' of the subscription.
952
-     *  * principaluri. The owner of the subscription. Almost always the same as
953
-     *    principalUri passed to this method.
954
-     *  * source. Url to the actual feed
955
-     *
956
-     * Furthermore, all the subscription info must be returned too:
957
-     *
958
-     * 1. {DAV:}displayname
959
-     * 2. {http://apple.com/ns/ical/}refreshrate
960
-     * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
961
-     *    should not be stripped).
962
-     * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
963
-     *    should not be stripped).
964
-     * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
965
-     *    attachments should not be stripped).
966
-     * 7. {http://apple.com/ns/ical/}calendar-color
967
-     * 8. {http://apple.com/ns/ical/}calendar-order
968
-     * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
969
-     *    (should just be an instance of
970
-     *    Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
971
-     *    default components).
972
-     *
973
-     * @param string $principalUri
974
-     * @return array
975
-     */
976
-    public function getSubscriptionsForUser($principalUri) {
977
-
978
-        $fields = array_values($this->subscriptionPropertyMap);
979
-        $fields[] = 'id';
980
-        $fields[] = 'uri';
981
-        $fields[] = 'source';
982
-        $fields[] = 'principaluri';
983
-        $fields[] = 'lastmodified';
984
-
985
-        // Making fields a comma-delimited list
986
-        $fields = implode(', ', $fields);
938
+		$stmt = $this->pdo->prepare($query);
939
+		$stmt->execute([
940
+			$calendarId
941
+		]);
942
+
943
+	}
944
+
945
+	/**
946
+	 * Returns a list of subscriptions for a principal.
947
+	 *
948
+	 * Every subscription is an array with the following keys:
949
+	 *  * id, a unique id that will be used by other functions to modify the
950
+	 *    subscription. This can be the same as the uri or a database key.
951
+	 *  * uri. This is just the 'base uri' or 'filename' of the subscription.
952
+	 *  * principaluri. The owner of the subscription. Almost always the same as
953
+	 *    principalUri passed to this method.
954
+	 *  * source. Url to the actual feed
955
+	 *
956
+	 * Furthermore, all the subscription info must be returned too:
957
+	 *
958
+	 * 1. {DAV:}displayname
959
+	 * 2. {http://apple.com/ns/ical/}refreshrate
960
+	 * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
961
+	 *    should not be stripped).
962
+	 * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
963
+	 *    should not be stripped).
964
+	 * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
965
+	 *    attachments should not be stripped).
966
+	 * 7. {http://apple.com/ns/ical/}calendar-color
967
+	 * 8. {http://apple.com/ns/ical/}calendar-order
968
+	 * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
969
+	 *    (should just be an instance of
970
+	 *    Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
971
+	 *    default components).
972
+	 *
973
+	 * @param string $principalUri
974
+	 * @return array
975
+	 */
976
+	public function getSubscriptionsForUser($principalUri) {
977
+
978
+		$fields = array_values($this->subscriptionPropertyMap);
979
+		$fields[] = 'id';
980
+		$fields[] = 'uri';
981
+		$fields[] = 'source';
982
+		$fields[] = 'principaluri';
983
+		$fields[] = 'lastmodified';
984
+
985
+		// Making fields a comma-delimited list
986
+		$fields = implode(', ', $fields);
987 987
 		$query = sprintf('SELECT %s FROM %s WHERE principaluri = ? ORDER BY calendarorder ASC', $fields, $this->calendarSubscriptionsTableName);
988
-        $stmt = $this->pdo->prepare($query);
989
-        $stmt->execute([$principalUri]);
990
-
991
-        $subscriptions = [];
992
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
993
-
994
-            $subscription = [
995
-                'id'           => $row['id'],
996
-                'uri'          => $row['uri'],
997
-                'principaluri' => $row['principaluri'],
998
-                'source'       => $row['source'],
999
-                'lastmodified' => $row['lastmodified'],
1000
-
1001
-                '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VEVENT']),
1002
-            ];
1003
-
1004
-            foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
1005
-                if (!is_null($row[$dbName])) {
1006
-                    $subscription[$xmlName] = $row[$dbName];
1007
-                }
1008
-            }
1009
-
1010
-            $subscriptions[] = $subscription;
1011
-
1012
-        }
1013
-
1014
-        return $subscriptions;
1015
-
1016
-    }
1017
-
1018
-    /**
1019
-     * Creates a new subscription for a principal.
1020
-     *
1021
-     * If the creation was a success, an id must be returned that can be used to reference
1022
-     * this subscription in other methods, such as updateSubscription.
1023
-     *
1024
-     * @param string $principalUri
1025
-     * @param string $uri
1026
-     * @param array $properties
1027
-     * @return mixed
1028
-     */
1029
-    public function createSubscription($principalUri, $uri, array $properties) {
1030
-
1031
-        $fieldNames = [
1032
-            'principaluri',
1033
-            'uri',
1034
-            'source',
1035
-            'lastmodified',
1036
-        ];
1037
-
1038
-        if (!isset($properties['{http://calendarserver.org/ns/}source'])) {
1039
-            throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions');
1040
-        }
1041
-
1042
-        $values = [
1043
-            ':principaluri' => $principalUri,
1044
-            ':uri'          => $uri,
1045
-            ':source'       => $properties['{http://calendarserver.org/ns/}source']->getHref(),
1046
-            ':lastmodified' => time(),
1047
-        ];
1048
-
1049
-        foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
1050
-            if (isset($properties[$xmlName])) {
1051
-
1052
-                $values[':' . $dbName] = $properties[$xmlName];
1053
-                $fieldNames[] = $dbName;
1054
-            }
1055
-        }
1056
-
1057
-        $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarSubscriptionsTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")");
1058
-        $stmt->execute($values);
1059
-
1060
-        return $this->pdo->lastInsertId();
1061
-
1062
-    }
1063
-
1064
-    /**
1065
-     * Updates a subscription
1066
-     *
1067
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
1068
-     * To do the actual updates, you must tell this object which properties
1069
-     * you're going to process with the handle() method.
1070
-     *
1071
-     * Calling the handle method is like telling the PropPatch object "I
1072
-     * promise I can handle updating this property".
1073
-     *
1074
-     * Read the PropPatch documenation for more info and examples.
1075
-     *
1076
-     * @param mixed $subscriptionId
1077
-     * @param \Sabre\DAV\PropPatch $propPatch
1078
-     * @return void
1079
-     */
1080
-    public function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) {
1081
-
1082
-        $supportedProperties = array_keys($this->subscriptionPropertyMap);
1083
-        $supportedProperties[] = '{http://calendarserver.org/ns/}source';
1084
-
1085
-        $propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) {
1086
-
1087
-            $newValues = [];
1088
-
1089
-            foreach ($mutations as $propertyName => $propertyValue) {
1090
-
1091
-                if ($propertyName === '{http://calendarserver.org/ns/}source') {
1092
-                    $newValues['source'] = $propertyValue->getHref();
1093
-                } else {
1094
-                    $fieldName = $this->subscriptionPropertyMap[$propertyName];
1095
-                    $newValues[$fieldName] = $propertyValue;
1096
-                }
1097
-
1098
-            }
1099
-
1100
-            // Now we're generating the sql query.
1101
-            $valuesSql = [];
1102
-            foreach ($newValues as $fieldName => $value) {
1103
-                $valuesSql[] = $fieldName . ' = ?';
1104
-            }
988
+		$stmt = $this->pdo->prepare($query);
989
+		$stmt->execute([$principalUri]);
990
+
991
+		$subscriptions = [];
992
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
993
+
994
+			$subscription = [
995
+				'id'           => $row['id'],
996
+				'uri'          => $row['uri'],
997
+				'principaluri' => $row['principaluri'],
998
+				'source'       => $row['source'],
999
+				'lastmodified' => $row['lastmodified'],
1000
+
1001
+				'{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VEVENT']),
1002
+			];
1003
+
1004
+			foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
1005
+				if (!is_null($row[$dbName])) {
1006
+					$subscription[$xmlName] = $row[$dbName];
1007
+				}
1008
+			}
1009
+
1010
+			$subscriptions[] = $subscription;
1011
+
1012
+		}
1013
+
1014
+		return $subscriptions;
1015
+
1016
+	}
1017
+
1018
+	/**
1019
+	 * Creates a new subscription for a principal.
1020
+	 *
1021
+	 * If the creation was a success, an id must be returned that can be used to reference
1022
+	 * this subscription in other methods, such as updateSubscription.
1023
+	 *
1024
+	 * @param string $principalUri
1025
+	 * @param string $uri
1026
+	 * @param array $properties
1027
+	 * @return mixed
1028
+	 */
1029
+	public function createSubscription($principalUri, $uri, array $properties) {
1030
+
1031
+		$fieldNames = [
1032
+			'principaluri',
1033
+			'uri',
1034
+			'source',
1035
+			'lastmodified',
1036
+		];
1037
+
1038
+		if (!isset($properties['{http://calendarserver.org/ns/}source'])) {
1039
+			throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions');
1040
+		}
1041
+
1042
+		$values = [
1043
+			':principaluri' => $principalUri,
1044
+			':uri'          => $uri,
1045
+			':source'       => $properties['{http://calendarserver.org/ns/}source']->getHref(),
1046
+			':lastmodified' => time(),
1047
+		];
1048
+
1049
+		foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
1050
+			if (isset($properties[$xmlName])) {
1051
+
1052
+				$values[':' . $dbName] = $properties[$xmlName];
1053
+				$fieldNames[] = $dbName;
1054
+			}
1055
+		}
1056
+
1057
+		$stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarSubscriptionsTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")");
1058
+		$stmt->execute($values);
1059
+
1060
+		return $this->pdo->lastInsertId();
1061
+
1062
+	}
1063
+
1064
+	/**
1065
+	 * Updates a subscription
1066
+	 *
1067
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
1068
+	 * To do the actual updates, you must tell this object which properties
1069
+	 * you're going to process with the handle() method.
1070
+	 *
1071
+	 * Calling the handle method is like telling the PropPatch object "I
1072
+	 * promise I can handle updating this property".
1073
+	 *
1074
+	 * Read the PropPatch documenation for more info and examples.
1075
+	 *
1076
+	 * @param mixed $subscriptionId
1077
+	 * @param \Sabre\DAV\PropPatch $propPatch
1078
+	 * @return void
1079
+	 */
1080
+	public function updateSubscription($subscriptionId, DAV\PropPatch $propPatch) {
1081
+
1082
+		$supportedProperties = array_keys($this->subscriptionPropertyMap);
1083
+		$supportedProperties[] = '{http://calendarserver.org/ns/}source';
1084
+
1085
+		$propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) {
1086
+
1087
+			$newValues = [];
1088
+
1089
+			foreach ($mutations as $propertyName => $propertyValue) {
1090
+
1091
+				if ($propertyName === '{http://calendarserver.org/ns/}source') {
1092
+					$newValues['source'] = $propertyValue->getHref();
1093
+				} else {
1094
+					$fieldName = $this->subscriptionPropertyMap[$propertyName];
1095
+					$newValues[$fieldName] = $propertyValue;
1096
+				}
1097
+
1098
+			}
1099
+
1100
+			// Now we're generating the sql query.
1101
+			$valuesSql = [];
1102
+			foreach ($newValues as $fieldName => $value) {
1103
+				$valuesSql[] = $fieldName . ' = ?';
1104
+			}
1105 1105
 			$query = sprintf('UPDATE %s SET  %s , lastmodified = ? WHERE id = ?', $this->calendarSubscriptionsTableName, implode(', ', $valuesSql));
1106
-            $stmt = $this->pdo->prepare($query);
1107
-            $newValues['lastmodified'] = time();
1108
-            $newValues['id'] = $subscriptionId;
1109
-            $stmt->execute(array_values($newValues));
1106
+			$stmt = $this->pdo->prepare($query);
1107
+			$newValues['lastmodified'] = time();
1108
+			$newValues['id'] = $subscriptionId;
1109
+			$stmt->execute(array_values($newValues));
1110 1110
 
1111
-            return true;
1111
+			return true;
1112 1112
 
1113
-        });
1113
+		});
1114 1114
 
1115
-    }
1115
+	}
1116 1116
 
1117
-    /**
1118
-     * Deletes a subscription
1119
-     *
1120
-     * @param mixed $subscriptionId
1121
-     * @return void
1122
-     */
1123
-    public function deleteSubscription($subscriptionId) {
1117
+	/**
1118
+	 * Deletes a subscription
1119
+	 *
1120
+	 * @param mixed $subscriptionId
1121
+	 * @return void
1122
+	 */
1123
+	public function deleteSubscription($subscriptionId) {
1124 1124
 		$query = sprintf('DELETE FROM %s WHERE id = ?', $this->calendarSubscriptionsTableName);
1125
-        $stmt = $this->pdo->prepare($query);
1126
-        $stmt->execute([$subscriptionId]);
1127
-
1128
-    }
1129
-
1130
-    /**
1131
-     * Returns a single scheduling object.
1132
-     *
1133
-     * The returned array should contain the following elements:
1134
-     *   * uri - A unique basename for the object. This will be used to
1135
-     *           construct a full uri.
1136
-     *   * calendardata - The iCalendar object
1137
-     *   * lastmodified - The last modification date. Can be an int for a unix
1138
-     *                    timestamp, or a PHP DateTime object.
1139
-     *   * etag - A unique token that must change if the object changed.
1140
-     *   * size - The size of the object, in bytes.
1141
-     *
1142
-     * @param string $principalUri
1143
-     * @param string $objectUri
1144
-     * @return array
1145
-     */
1146
-    public function getSchedulingObject($principalUri, $objectUri) {
1125
+		$stmt = $this->pdo->prepare($query);
1126
+		$stmt->execute([$subscriptionId]);
1127
+
1128
+	}
1129
+
1130
+	/**
1131
+	 * Returns a single scheduling object.
1132
+	 *
1133
+	 * The returned array should contain the following elements:
1134
+	 *   * uri - A unique basename for the object. This will be used to
1135
+	 *           construct a full uri.
1136
+	 *   * calendardata - The iCalendar object
1137
+	 *   * lastmodified - The last modification date. Can be an int for a unix
1138
+	 *                    timestamp, or a PHP DateTime object.
1139
+	 *   * etag - A unique token that must change if the object changed.
1140
+	 *   * size - The size of the object, in bytes.
1141
+	 *
1142
+	 * @param string $principalUri
1143
+	 * @param string $objectUri
1144
+	 * @return array
1145
+	 */
1146
+	public function getSchedulingObject($principalUri, $objectUri) {
1147 1147
 		
1148 1148
 		$query = sprintf('SELECT uri, calendardata, lastmodified, etag, size FROM %s WHERE principaluri = ? && uri = ?', $this->schedulingObjectTableName);
1149
-        $stmt = $this->pdo->prepare($query);
1150
-        $stmt->execute([$principalUri, $objectUri]);
1151
-        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
1152
-
1153
-        if (!$row) return null;
1154
-
1155
-        return [
1156
-            'uri'          => $row['uri'],
1157
-            'calendardata' => $row['calendardata'],
1158
-            'lastmodified' => $row['lastmodified'],
1159
-            'etag'         => '"' . $row['etag'] . '"',
1160
-            'size'         => (int)$row['size'],
1161
-         ];
1162
-
1163
-    }
1164
-
1165
-    /**
1166
-     * Returns all scheduling objects for the inbox collection.
1167
-     *
1168
-     * These objects should be returned as an array. Every item in the array
1169
-     * should follow the same structure as returned from getSchedulingObject.
1170
-     *
1171
-     * The main difference is that 'calendardata' is optional.
1172
-     *
1173
-     * @param string $principalUri
1174
-     * @return array
1175
-     */
1176
-    public function getSchedulingObjects($principalUri) {
1149
+		$stmt = $this->pdo->prepare($query);
1150
+		$stmt->execute([$principalUri, $objectUri]);
1151
+		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
1152
+
1153
+		if (!$row) return null;
1154
+
1155
+		return [
1156
+			'uri'          => $row['uri'],
1157
+			'calendardata' => $row['calendardata'],
1158
+			'lastmodified' => $row['lastmodified'],
1159
+			'etag'         => '"' . $row['etag'] . '"',
1160
+			'size'         => (int)$row['size'],
1161
+		 ];
1162
+
1163
+	}
1164
+
1165
+	/**
1166
+	 * Returns all scheduling objects for the inbox collection.
1167
+	 *
1168
+	 * These objects should be returned as an array. Every item in the array
1169
+	 * should follow the same structure as returned from getSchedulingObject.
1170
+	 *
1171
+	 * The main difference is that 'calendardata' is optional.
1172
+	 *
1173
+	 * @param string $principalUri
1174
+	 * @return array
1175
+	 */
1176
+	public function getSchedulingObjects($principalUri) {
1177 1177
 
1178 1178
 		$query = sprintf('SELECT id, calendardata, uri, lastmodified, etag, size FROM %s WHERE principaluri = ?', $this->schedulingObjectTableName);
1179
-        $stmt = $this->pdo->prepare($query);
1180
-        $stmt->execute([$principalUri]);
1181
-
1182
-        $result = [];
1183
-        foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1184
-            $result[] = [
1185
-                'calendardata' => $row['calendardata'],
1186
-                'uri'          => $row['uri'],
1187
-                'lastmodified' => $row['lastmodified'],
1188
-                'etag'         => '"' . $row['etag'] . '"',
1189
-                'size'         => (int)$row['size'],
1190
-            ];
1191
-        }
1192
-
1193
-        return $result;
1194
-
1195
-    }
1196
-
1197
-    /**
1198
-     * Deletes a scheduling object
1199
-     *
1200
-     * @param string $principalUri
1201
-     * @param string $objectUri
1202
-     * @return void
1203
-     */
1204
-    public function deleteSchedulingObject($principalUri, $objectUri) {
1179
+		$stmt = $this->pdo->prepare($query);
1180
+		$stmt->execute([$principalUri]);
1181
+
1182
+		$result = [];
1183
+		foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1184
+			$result[] = [
1185
+				'calendardata' => $row['calendardata'],
1186
+				'uri'          => $row['uri'],
1187
+				'lastmodified' => $row['lastmodified'],
1188
+				'etag'         => '"' . $row['etag'] . '"',
1189
+				'size'         => (int)$row['size'],
1190
+			];
1191
+		}
1192
+
1193
+		return $result;
1194
+
1195
+	}
1196
+
1197
+	/**
1198
+	 * Deletes a scheduling object
1199
+	 *
1200
+	 * @param string $principalUri
1201
+	 * @param string $objectUri
1202
+	 * @return void
1203
+	 */
1204
+	public function deleteSchedulingObject($principalUri, $objectUri) {
1205 1205
 
1206 1206
 		$query = sprintf('DELETE FROM %s WHERE principaluri = ? && uri = ?', $this->schedulingObjectTableName);	
1207
-        $stmt = $this->pdo->prepare($query);
1208
-        $stmt->execute([$principalUri, $objectUri]);
1207
+		$stmt = $this->pdo->prepare($query);
1208
+		$stmt->execute([$principalUri, $objectUri]);
1209 1209
 
1210
-    }
1210
+	}
1211 1211
 
1212
-    /**
1213
-     * Creates a new scheduling object. This should land in a users' inbox.
1214
-     *
1215
-     * @param string $principalUri
1216
-     * @param string $objectUri
1217
-     * @param string $objectData
1218
-     * @return void
1219
-     */
1220
-    public function createSchedulingObject($principalUri, $objectUri, $objectData) {
1212
+	/**
1213
+	 * Creates a new scheduling object. This should land in a users' inbox.
1214
+	 *
1215
+	 * @param string $principalUri
1216
+	 * @param string $objectUri
1217
+	 * @param string $objectData
1218
+	 * @return void
1219
+	 */
1220
+	public function createSchedulingObject($principalUri, $objectUri, $objectData) {
1221 1221
 
1222
-        $stmt = $this->pdo->prepare('INSERT INTO ' . $this->schedulingObjectTableName . ' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)');
1223
-        $stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData) ]);
1222
+		$stmt = $this->pdo->prepare('INSERT INTO ' . $this->schedulingObjectTableName . ' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)');
1223
+		$stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData) ]);
1224 1224
 
1225
-    }
1225
+	}
1226 1226
 
1227 1227
 }
Please login to merge, or discard this patch.
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -1024,7 +1024,7 @@
 block discarded – undo
1024 1024
      * @param string $principalUri
1025 1025
      * @param string $uri
1026 1026
      * @param array $properties
1027
-     * @return mixed
1027
+     * @return string
1028 1028
      */
1029 1029
     public function createSubscription($principalUri, $uri, array $properties) {
1030 1030
 
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -366,7 +366,7 @@  discard block
 block discarded – undo
366 366
                 'lastmodified' => $row['lastmodified'],
367 367
                 'etag'         => '"' . $row['etag'] . '"',
368 368
                 'calendarid'   => $row['calendarid'],
369
-                'size'         => (int)$row['size'],
369
+                'size'         => (int) $row['size'],
370 370
                 'component'    => strtolower($row['componenttype']),
371 371
             ];
372 372
         }
@@ -406,7 +406,7 @@  discard block
 block discarded – undo
406 406
             'lastmodified'  => $row['lastmodified'],
407 407
             'etag'          => '"' . $row['etag'] . '"',
408 408
             'calendarid'    => $row['calendarid'],
409
-            'size'          => (int)$row['size'],
409
+            'size'          => (int) $row['size'],
410 410
             'calendardata'  => $row['calendardata'],
411 411
             'component'     => strtolower($row['componenttype']),
412 412
          ];
@@ -427,7 +427,7 @@  discard block
 block discarded – undo
427 427
      */
428 428
     public function getMultipleCalendarObjects($calendarId, array $uris) {
429 429
 
430
-        $query =sprintf('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM %s WHERE calendarid = ? && uri IN (', $this->calendarObjectTableName);
430
+        $query = sprintf('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM %s WHERE calendarid = ? && uri IN (', $this->calendarObjectTableName);
431 431
         // Inserting a whole bunch of question marks
432 432
         $query .= implode(',', array_fill(0, count($uris), '?'));
433 433
         $query .= ')';
@@ -444,7 +444,7 @@  discard block
 block discarded – undo
444 444
                 'lastmodified' => $row['lastmodified'],
445 445
                 'etag'         => '"' . $row['etag'] . '"',
446 446
                 'calendarid'   => $row['calendarid'],
447
-                'size'         => (int)$row['size'],
447
+                'size'         => (int) $row['size'],
448 448
                 'calendardata' => $row['calendardata'],
449 449
                 'component'    => strtolower($row['componenttype']),
450 450
             ];
@@ -553,7 +553,7 @@  discard block
 block discarded – undo
553 553
         foreach ($vObject->getComponents() as $component) {
554 554
             if ($component->name !== 'VTIMEZONE') {
555 555
                 $componentType = $component->name;
556
-                $uid = (string)$component->UID;
556
+                $uid = (string) $component->UID;
557 557
                 break;
558 558
             }
559 559
         }
@@ -578,7 +578,7 @@  discard block
 block discarded – undo
578 578
                     $lastOccurence = $firstOccurence;
579 579
                 }
580 580
             } else {
581
-                $it = new VObject\Recur\EventIterator($vObject, (string)$component->UID);
581
+                $it = new VObject\Recur\EventIterator($vObject, (string) $component->UID);
582 582
                 $maxDate = new \DateTime(self::MAX_DATE);
583 583
                 if ($it->isInfinite()) {
584 584
                     $lastOccurence = $maxDate->getTimeStamp();
@@ -859,7 +859,7 @@  discard block
 block discarded – undo
859 859
         // Current synctoken
860 860
 		$query = sprintf('SELECT synctoken FROM %s WHERE id = ?', $this->calendarTableName);
861 861
         $stmt = $this->pdo->prepare($query);
862
-        $stmt->execute([ $calendarId ]);
862
+        $stmt->execute([$calendarId]);
863 863
         $currentToken = $stmt->fetchColumn(0);
864 864
 
865 865
         if (is_null($currentToken)) return null;
@@ -874,7 +874,7 @@  discard block
 block discarded – undo
874 874
         if ($syncToken) {
875 875
 
876 876
             $query = sprintf('SELECT uri, operation FROM %s WHERE synctoken >= ? && synctoken < ? && calendarid = ? ORDER BY synctoken', $this->calendarChangesTableName);
877
-            if ($limit > 0) $query .= " LIMIT " . (int)$limit;
877
+            if ($limit > 0) $query .= " LIMIT " . (int) $limit;
878 878
 
879 879
             // Fetching all changes
880 880
             $stmt = $this->pdo->prepare($query);
@@ -1157,7 +1157,7 @@  discard block
 block discarded – undo
1157 1157
             'calendardata' => $row['calendardata'],
1158 1158
             'lastmodified' => $row['lastmodified'],
1159 1159
             'etag'         => '"' . $row['etag'] . '"',
1160
-            'size'         => (int)$row['size'],
1160
+            'size'         => (int) $row['size'],
1161 1161
          ];
1162 1162
 
1163 1163
     }
@@ -1186,7 +1186,7 @@  discard block
 block discarded – undo
1186 1186
                 'uri'          => $row['uri'],
1187 1187
                 'lastmodified' => $row['lastmodified'],
1188 1188
                 'etag'         => '"' . $row['etag'] . '"',
1189
-                'size'         => (int)$row['size'],
1189
+                'size'         => (int) $row['size'],
1190 1190
             ];
1191 1191
         }
1192 1192
 
@@ -1220,7 +1220,7 @@  discard block
 block discarded – undo
1220 1220
     public function createSchedulingObject($principalUri, $objectUri, $objectData) {
1221 1221
 
1222 1222
         $stmt = $this->pdo->prepare('INSERT INTO ' . $this->schedulingObjectTableName . ' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)');
1223
-        $stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData) ]);
1223
+        $stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData)]);
1224 1224
 
1225 1225
     }
1226 1226
 
Please login to merge, or discard this patch.