@@ -70,7 +70,7 @@ |
||
70 | 70 | * the next element. |
71 | 71 | * |
72 | 72 | * @param Reader $reader |
73 | - * @return mixed |
|
73 | + * @return AddressBookMultiGetReport |
|
74 | 74 | */ |
75 | 75 | static function xmlDeserialize(Reader $reader) { |
76 | 76 |
@@ -21,93 +21,93 @@ |
||
21 | 21 | */ |
22 | 22 | class AddressBookMultiGetReport implements XmlDeserializable { |
23 | 23 | |
24 | - /** |
|
25 | - * An array with requested properties. |
|
26 | - * |
|
27 | - * @var array |
|
28 | - */ |
|
29 | - public $properties; |
|
30 | - |
|
31 | - /** |
|
32 | - * This is an array with the urls that are being requested. |
|
33 | - * |
|
34 | - * @var array |
|
35 | - */ |
|
36 | - public $hrefs; |
|
37 | - |
|
38 | - /** |
|
39 | - * The mimetype of the content that should be returend. Usually |
|
40 | - * text/vcard. |
|
41 | - * |
|
42 | - * @var string |
|
43 | - */ |
|
44 | - public $contentType = null; |
|
45 | - |
|
46 | - /** |
|
47 | - * The version of vcard data that should be returned. Usually 3.0, |
|
48 | - * referring to vCard 3.0. |
|
49 | - * |
|
50 | - * @var string |
|
51 | - */ |
|
52 | - public $version = null; |
|
53 | - |
|
54 | - /** |
|
55 | - * The deserialize method is called during xml parsing. |
|
56 | - * |
|
57 | - * This method is called statictly, this is because in theory this method |
|
58 | - * may be used as a type of constructor, or factory method. |
|
59 | - * |
|
60 | - * Often you want to return an instance of the current class, but you are |
|
61 | - * free to return other data as well. |
|
62 | - * |
|
63 | - * You are responsible for advancing the reader to the next element. Not |
|
64 | - * doing anything will result in a never-ending loop. |
|
65 | - * |
|
66 | - * If you just want to skip parsing for this element altogether, you can |
|
67 | - * just call $reader->next(); |
|
68 | - * |
|
69 | - * $reader->parseInnerTree() will parse the entire sub-tree, and advance to |
|
70 | - * the next element. |
|
71 | - * |
|
72 | - * @param Reader $reader |
|
73 | - * @return mixed |
|
74 | - */ |
|
75 | - static function xmlDeserialize(Reader $reader) { |
|
76 | - |
|
77 | - $elems = $reader->parseInnerTree([ |
|
78 | - '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', |
|
79 | - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', |
|
80 | - ]); |
|
81 | - |
|
82 | - $newProps = [ |
|
83 | - 'hrefs' => [], |
|
84 | - 'properties' => [] |
|
85 | - ]; |
|
86 | - |
|
87 | - foreach ($elems as $elem) { |
|
88 | - |
|
89 | - switch ($elem['name']) { |
|
90 | - |
|
91 | - case '{DAV:}prop' : |
|
92 | - $newProps['properties'] = array_keys($elem['value']); |
|
93 | - if (isset($elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'])) { |
|
94 | - $newProps += $elem['value']['{' . Plugin::NS_CARDDAV . '}address-data']; |
|
95 | - } |
|
96 | - break; |
|
97 | - case '{DAV:}href' : |
|
98 | - $newProps['hrefs'][] = Uri\resolve($reader->contextUri, $elem['value']); |
|
99 | - break; |
|
100 | - |
|
101 | - } |
|
102 | - |
|
103 | - } |
|
104 | - |
|
105 | - $obj = new self(); |
|
106 | - foreach ($newProps as $key => $value) { |
|
107 | - $obj->$key = $value; |
|
108 | - } |
|
109 | - return $obj; |
|
110 | - |
|
111 | - } |
|
24 | + /** |
|
25 | + * An array with requested properties. |
|
26 | + * |
|
27 | + * @var array |
|
28 | + */ |
|
29 | + public $properties; |
|
30 | + |
|
31 | + /** |
|
32 | + * This is an array with the urls that are being requested. |
|
33 | + * |
|
34 | + * @var array |
|
35 | + */ |
|
36 | + public $hrefs; |
|
37 | + |
|
38 | + /** |
|
39 | + * The mimetype of the content that should be returend. Usually |
|
40 | + * text/vcard. |
|
41 | + * |
|
42 | + * @var string |
|
43 | + */ |
|
44 | + public $contentType = null; |
|
45 | + |
|
46 | + /** |
|
47 | + * The version of vcard data that should be returned. Usually 3.0, |
|
48 | + * referring to vCard 3.0. |
|
49 | + * |
|
50 | + * @var string |
|
51 | + */ |
|
52 | + public $version = null; |
|
53 | + |
|
54 | + /** |
|
55 | + * The deserialize method is called during xml parsing. |
|
56 | + * |
|
57 | + * This method is called statictly, this is because in theory this method |
|
58 | + * may be used as a type of constructor, or factory method. |
|
59 | + * |
|
60 | + * Often you want to return an instance of the current class, but you are |
|
61 | + * free to return other data as well. |
|
62 | + * |
|
63 | + * You are responsible for advancing the reader to the next element. Not |
|
64 | + * doing anything will result in a never-ending loop. |
|
65 | + * |
|
66 | + * If you just want to skip parsing for this element altogether, you can |
|
67 | + * just call $reader->next(); |
|
68 | + * |
|
69 | + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to |
|
70 | + * the next element. |
|
71 | + * |
|
72 | + * @param Reader $reader |
|
73 | + * @return mixed |
|
74 | + */ |
|
75 | + static function xmlDeserialize(Reader $reader) { |
|
76 | + |
|
77 | + $elems = $reader->parseInnerTree([ |
|
78 | + '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', |
|
79 | + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', |
|
80 | + ]); |
|
81 | + |
|
82 | + $newProps = [ |
|
83 | + 'hrefs' => [], |
|
84 | + 'properties' => [] |
|
85 | + ]; |
|
86 | + |
|
87 | + foreach ($elems as $elem) { |
|
88 | + |
|
89 | + switch ($elem['name']) { |
|
90 | + |
|
91 | + case '{DAV:}prop' : |
|
92 | + $newProps['properties'] = array_keys($elem['value']); |
|
93 | + if (isset($elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'])) { |
|
94 | + $newProps += $elem['value']['{' . Plugin::NS_CARDDAV . '}address-data']; |
|
95 | + } |
|
96 | + break; |
|
97 | + case '{DAV:}href' : |
|
98 | + $newProps['hrefs'][] = Uri\resolve($reader->contextUri, $elem['value']); |
|
99 | + break; |
|
100 | + |
|
101 | + } |
|
102 | + |
|
103 | + } |
|
104 | + |
|
105 | + $obj = new self(); |
|
106 | + foreach ($newProps as $key => $value) { |
|
107 | + $obj->$key = $value; |
|
108 | + } |
|
109 | + return $obj; |
|
110 | + |
|
111 | + } |
|
112 | 112 | |
113 | 113 | } |
@@ -108,7 +108,7 @@ |
||
108 | 108 | * the next element. |
109 | 109 | * |
110 | 110 | * @param Reader $reader |
111 | - * @return mixed |
|
111 | + * @return AddressBookQueryReport |
|
112 | 112 | */ |
113 | 113 | static function xmlDeserialize(Reader $reader) { |
114 | 114 |
@@ -2,10 +2,10 @@ |
||
2 | 2 | |
3 | 3 | namespace Sabre\CardDAV\Xml\Request; |
4 | 4 | |
5 | +use Sabre\CardDAV\Plugin; |
|
6 | +use Sabre\DAV\Exception\BadRequest; |
|
5 | 7 | use Sabre\Xml\Reader; |
6 | 8 | use Sabre\Xml\XmlDeserializable; |
7 | -use Sabre\DAV\Exception\BadRequest; |
|
8 | -use Sabre\CardDAV\Plugin; |
|
9 | 9 | |
10 | 10 | /** |
11 | 11 | * AddressBookQueryReport request parser. |
@@ -21,172 +21,172 @@ |
||
21 | 21 | */ |
22 | 22 | class AddressBookQueryReport implements XmlDeserializable { |
23 | 23 | |
24 | - /** |
|
25 | - * An array with requested properties. |
|
26 | - * |
|
27 | - * @var array |
|
28 | - */ |
|
29 | - public $properties; |
|
30 | - |
|
31 | - /** |
|
32 | - * List of property/component filters. |
|
33 | - * |
|
34 | - * This is an array with filters. Every item is a property filter. Every |
|
35 | - * property filter has the following keys: |
|
36 | - * * name - name of the component to filter on |
|
37 | - * * test - anyof or allof |
|
38 | - * * is-not-defined - Test for non-existence |
|
39 | - * * param-filters - A list of parameter filters on the property |
|
40 | - * * text-matches - A list of text values the filter needs to match |
|
41 | - * |
|
42 | - * Each param-filter has the following keys: |
|
43 | - * * name - name of the parameter |
|
44 | - * * is-not-defined - Test for non-existence |
|
45 | - * * text-match - Match the parameter value |
|
46 | - * |
|
47 | - * Each text-match in property filters, and the single text-match in |
|
48 | - * param-filters have the following keys: |
|
49 | - * |
|
50 | - * * value - value to match |
|
51 | - * * match-type - contains, starts-with, ends-with, equals |
|
52 | - * * negate-condition - Do the opposite match |
|
53 | - * * collation - Usually i;unicode-casemap |
|
54 | - * |
|
55 | - * @var array |
|
56 | - */ |
|
57 | - public $filters; |
|
58 | - |
|
59 | - /** |
|
60 | - * The number of results the client wants |
|
61 | - * |
|
62 | - * null means it wasn't specified, which in most cases means 'all results'. |
|
63 | - * |
|
64 | - * @var int|null |
|
65 | - */ |
|
66 | - public $limit; |
|
67 | - |
|
68 | - /** |
|
69 | - * Either 'anyof' or 'allof' |
|
70 | - * |
|
71 | - * @var string |
|
72 | - */ |
|
73 | - public $test; |
|
74 | - |
|
75 | - /** |
|
76 | - * The mimetype of the content that should be returend. Usually |
|
77 | - * text/vcard. |
|
78 | - * |
|
79 | - * @var string |
|
80 | - */ |
|
81 | - public $contentType = null; |
|
82 | - |
|
83 | - /** |
|
84 | - * The version of vcard data that should be returned. Usually 3.0, |
|
85 | - * referring to vCard 3.0. |
|
86 | - * |
|
87 | - * @var string |
|
88 | - */ |
|
89 | - public $version = null; |
|
90 | - |
|
91 | - |
|
92 | - /** |
|
93 | - * The deserialize method is called during xml parsing. |
|
94 | - * |
|
95 | - * This method is called statictly, this is because in theory this method |
|
96 | - * may be used as a type of constructor, or factory method. |
|
97 | - * |
|
98 | - * Often you want to return an instance of the current class, but you are |
|
99 | - * free to return other data as well. |
|
100 | - * |
|
101 | - * You are responsible for advancing the reader to the next element. Not |
|
102 | - * doing anything will result in a never-ending loop. |
|
103 | - * |
|
104 | - * If you just want to skip parsing for this element altogether, you can |
|
105 | - * just call $reader->next(); |
|
106 | - * |
|
107 | - * $reader->parseInnerTree() will parse the entire sub-tree, and advance to |
|
108 | - * the next element. |
|
109 | - * |
|
110 | - * @param Reader $reader |
|
111 | - * @return mixed |
|
112 | - */ |
|
113 | - static function xmlDeserialize(Reader $reader) { |
|
114 | - |
|
115 | - $elems = (array)$reader->parseInnerTree([ |
|
116 | - '{urn:ietf:params:xml:ns:carddav}prop-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter', |
|
117 | - '{urn:ietf:params:xml:ns:carddav}param-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter', |
|
118 | - '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', |
|
119 | - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', |
|
120 | - ]); |
|
121 | - |
|
122 | - $newProps = [ |
|
123 | - 'filters' => null, |
|
124 | - 'properties' => [], |
|
125 | - 'test' => 'anyof', |
|
126 | - 'limit' => null, |
|
127 | - ]; |
|
128 | - |
|
129 | - if (!is_array($elems)) $elems = []; |
|
130 | - |
|
131 | - foreach ($elems as $elem) { |
|
132 | - |
|
133 | - switch ($elem['name']) { |
|
134 | - |
|
135 | - case '{DAV:}prop' : |
|
136 | - $newProps['properties'] = array_keys($elem['value']); |
|
137 | - if (isset($elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'])) { |
|
138 | - $newProps += $elem['value']['{' . Plugin::NS_CARDDAV . '}address-data']; |
|
139 | - } |
|
140 | - break; |
|
141 | - case '{' . Plugin::NS_CARDDAV . '}filter' : |
|
142 | - |
|
143 | - if (!is_null($newProps['filters'])) { |
|
144 | - throw new BadRequest('You can only include 1 {' . Plugin::NS_CARDDAV . '}filter element'); |
|
145 | - } |
|
146 | - if (isset($elem['attributes']['test'])) { |
|
147 | - $newProps['test'] = $elem['attributes']['test']; |
|
148 | - if ($newProps['test'] !== 'allof' && $newProps['test'] !== 'anyof') { |
|
149 | - throw new BadRequest('The "test" attribute must be one of "allof" or "anyof"'); |
|
150 | - } |
|
151 | - } |
|
152 | - |
|
153 | - $newProps['filters'] = []; |
|
154 | - foreach ((array)$elem['value'] as $subElem) { |
|
155 | - if ($subElem['name'] === '{' . Plugin::NS_CARDDAV . '}prop-filter') { |
|
156 | - $newProps['filters'][] = $subElem['value']; |
|
157 | - } |
|
158 | - } |
|
159 | - break; |
|
160 | - case '{' . Plugin::NS_CARDDAV . '}limit' : |
|
161 | - foreach ($elem['value'] as $child) { |
|
162 | - if ($child['name'] === '{' . Plugin::NS_CARDDAV . '}nresults') { |
|
163 | - $newProps['limit'] = (int)$child['value']; |
|
164 | - } |
|
165 | - } |
|
166 | - break; |
|
167 | - |
|
168 | - } |
|
169 | - |
|
170 | - } |
|
171 | - |
|
172 | - if (is_null($newProps['filters'])) { |
|
173 | - /* |
|
24 | + /** |
|
25 | + * An array with requested properties. |
|
26 | + * |
|
27 | + * @var array |
|
28 | + */ |
|
29 | + public $properties; |
|
30 | + |
|
31 | + /** |
|
32 | + * List of property/component filters. |
|
33 | + * |
|
34 | + * This is an array with filters. Every item is a property filter. Every |
|
35 | + * property filter has the following keys: |
|
36 | + * * name - name of the component to filter on |
|
37 | + * * test - anyof or allof |
|
38 | + * * is-not-defined - Test for non-existence |
|
39 | + * * param-filters - A list of parameter filters on the property |
|
40 | + * * text-matches - A list of text values the filter needs to match |
|
41 | + * |
|
42 | + * Each param-filter has the following keys: |
|
43 | + * * name - name of the parameter |
|
44 | + * * is-not-defined - Test for non-existence |
|
45 | + * * text-match - Match the parameter value |
|
46 | + * |
|
47 | + * Each text-match in property filters, and the single text-match in |
|
48 | + * param-filters have the following keys: |
|
49 | + * |
|
50 | + * * value - value to match |
|
51 | + * * match-type - contains, starts-with, ends-with, equals |
|
52 | + * * negate-condition - Do the opposite match |
|
53 | + * * collation - Usually i;unicode-casemap |
|
54 | + * |
|
55 | + * @var array |
|
56 | + */ |
|
57 | + public $filters; |
|
58 | + |
|
59 | + /** |
|
60 | + * The number of results the client wants |
|
61 | + * |
|
62 | + * null means it wasn't specified, which in most cases means 'all results'. |
|
63 | + * |
|
64 | + * @var int|null |
|
65 | + */ |
|
66 | + public $limit; |
|
67 | + |
|
68 | + /** |
|
69 | + * Either 'anyof' or 'allof' |
|
70 | + * |
|
71 | + * @var string |
|
72 | + */ |
|
73 | + public $test; |
|
74 | + |
|
75 | + /** |
|
76 | + * The mimetype of the content that should be returend. Usually |
|
77 | + * text/vcard. |
|
78 | + * |
|
79 | + * @var string |
|
80 | + */ |
|
81 | + public $contentType = null; |
|
82 | + |
|
83 | + /** |
|
84 | + * The version of vcard data that should be returned. Usually 3.0, |
|
85 | + * referring to vCard 3.0. |
|
86 | + * |
|
87 | + * @var string |
|
88 | + */ |
|
89 | + public $version = null; |
|
90 | + |
|
91 | + |
|
92 | + /** |
|
93 | + * The deserialize method is called during xml parsing. |
|
94 | + * |
|
95 | + * This method is called statictly, this is because in theory this method |
|
96 | + * may be used as a type of constructor, or factory method. |
|
97 | + * |
|
98 | + * Often you want to return an instance of the current class, but you are |
|
99 | + * free to return other data as well. |
|
100 | + * |
|
101 | + * You are responsible for advancing the reader to the next element. Not |
|
102 | + * doing anything will result in a never-ending loop. |
|
103 | + * |
|
104 | + * If you just want to skip parsing for this element altogether, you can |
|
105 | + * just call $reader->next(); |
|
106 | + * |
|
107 | + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to |
|
108 | + * the next element. |
|
109 | + * |
|
110 | + * @param Reader $reader |
|
111 | + * @return mixed |
|
112 | + */ |
|
113 | + static function xmlDeserialize(Reader $reader) { |
|
114 | + |
|
115 | + $elems = (array)$reader->parseInnerTree([ |
|
116 | + '{urn:ietf:params:xml:ns:carddav}prop-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter', |
|
117 | + '{urn:ietf:params:xml:ns:carddav}param-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter', |
|
118 | + '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', |
|
119 | + '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', |
|
120 | + ]); |
|
121 | + |
|
122 | + $newProps = [ |
|
123 | + 'filters' => null, |
|
124 | + 'properties' => [], |
|
125 | + 'test' => 'anyof', |
|
126 | + 'limit' => null, |
|
127 | + ]; |
|
128 | + |
|
129 | + if (!is_array($elems)) $elems = []; |
|
130 | + |
|
131 | + foreach ($elems as $elem) { |
|
132 | + |
|
133 | + switch ($elem['name']) { |
|
134 | + |
|
135 | + case '{DAV:}prop' : |
|
136 | + $newProps['properties'] = array_keys($elem['value']); |
|
137 | + if (isset($elem['value']['{' . Plugin::NS_CARDDAV . '}address-data'])) { |
|
138 | + $newProps += $elem['value']['{' . Plugin::NS_CARDDAV . '}address-data']; |
|
139 | + } |
|
140 | + break; |
|
141 | + case '{' . Plugin::NS_CARDDAV . '}filter' : |
|
142 | + |
|
143 | + if (!is_null($newProps['filters'])) { |
|
144 | + throw new BadRequest('You can only include 1 {' . Plugin::NS_CARDDAV . '}filter element'); |
|
145 | + } |
|
146 | + if (isset($elem['attributes']['test'])) { |
|
147 | + $newProps['test'] = $elem['attributes']['test']; |
|
148 | + if ($newProps['test'] !== 'allof' && $newProps['test'] !== 'anyof') { |
|
149 | + throw new BadRequest('The "test" attribute must be one of "allof" or "anyof"'); |
|
150 | + } |
|
151 | + } |
|
152 | + |
|
153 | + $newProps['filters'] = []; |
|
154 | + foreach ((array)$elem['value'] as $subElem) { |
|
155 | + if ($subElem['name'] === '{' . Plugin::NS_CARDDAV . '}prop-filter') { |
|
156 | + $newProps['filters'][] = $subElem['value']; |
|
157 | + } |
|
158 | + } |
|
159 | + break; |
|
160 | + case '{' . Plugin::NS_CARDDAV . '}limit' : |
|
161 | + foreach ($elem['value'] as $child) { |
|
162 | + if ($child['name'] === '{' . Plugin::NS_CARDDAV . '}nresults') { |
|
163 | + $newProps['limit'] = (int)$child['value']; |
|
164 | + } |
|
165 | + } |
|
166 | + break; |
|
167 | + |
|
168 | + } |
|
169 | + |
|
170 | + } |
|
171 | + |
|
172 | + if (is_null($newProps['filters'])) { |
|
173 | + /* |
|
174 | 174 | * We are supposed to throw this error, but KDE sometimes does not |
175 | 175 | * include the filter element, and we need to treat it as if no |
176 | 176 | * filters are supplied |
177 | 177 | */ |
178 | - //throw new BadRequest('The {' . Plugin::NS_CARDDAV . '}filter element is required for this request'); |
|
179 | - $newProps['filters'] = []; |
|
178 | + //throw new BadRequest('The {' . Plugin::NS_CARDDAV . '}filter element is required for this request'); |
|
179 | + $newProps['filters'] = []; |
|
180 | 180 | |
181 | - } |
|
181 | + } |
|
182 | 182 | |
183 | - $obj = new self(); |
|
184 | - foreach ($newProps as $key => $value) { |
|
185 | - $obj->$key = $value; |
|
186 | - } |
|
183 | + $obj = new self(); |
|
184 | + foreach ($newProps as $key => $value) { |
|
185 | + $obj->$key = $value; |
|
186 | + } |
|
187 | 187 | |
188 | - return $obj; |
|
188 | + return $obj; |
|
189 | 189 | |
190 | - } |
|
190 | + } |
|
191 | 191 | |
192 | 192 | } |
@@ -112,7 +112,7 @@ discard block |
||
112 | 112 | */ |
113 | 113 | static function xmlDeserialize(Reader $reader) { |
114 | 114 | |
115 | - $elems = (array)$reader->parseInnerTree([ |
|
115 | + $elems = (array) $reader->parseInnerTree([ |
|
116 | 116 | '{urn:ietf:params:xml:ns:carddav}prop-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter', |
117 | 117 | '{urn:ietf:params:xml:ns:carddav}param-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter', |
118 | 118 | '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', |
@@ -151,7 +151,7 @@ discard block |
||
151 | 151 | } |
152 | 152 | |
153 | 153 | $newProps['filters'] = []; |
154 | - foreach ((array)$elem['value'] as $subElem) { |
|
154 | + foreach ((array) $elem['value'] as $subElem) { |
|
155 | 155 | if ($subElem['name'] === '{' . Plugin::NS_CARDDAV . '}prop-filter') { |
156 | 156 | $newProps['filters'][] = $subElem['value']; |
157 | 157 | } |
@@ -160,7 +160,7 @@ discard block |
||
160 | 160 | case '{' . Plugin::NS_CARDDAV . '}limit' : |
161 | 161 | foreach ($elem['value'] as $child) { |
162 | 162 | if ($child['name'] === '{' . Plugin::NS_CARDDAV . '}nresults') { |
163 | - $newProps['limit'] = (int)$child['value']; |
|
163 | + $newProps['limit'] = (int) $child['value']; |
|
164 | 164 | } |
165 | 165 | } |
166 | 166 | break; |
@@ -126,7 +126,9 @@ |
||
126 | 126 | 'limit' => null, |
127 | 127 | ]; |
128 | 128 | |
129 | - if (!is_array($elems)) $elems = []; |
|
129 | + if (!is_array($elems)) { |
|
130 | + $elems = []; |
|
131 | + } |
|
130 | 132 | |
131 | 133 | foreach ($elems as $elem) { |
132 | 134 |
@@ -341,6 +341,10 @@ |
||
341 | 341 | return $file; |
342 | 342 | } |
343 | 343 | |
344 | + /** |
|
345 | + * @param string $class |
|
346 | + * @param string $ext |
|
347 | + */ |
|
344 | 348 | private function findFileWithExtension($class, $ext) |
345 | 349 | { |
346 | 350 | // PSR-4 lookup |
@@ -42,364 +42,364 @@ discard block |
||
42 | 42 | */ |
43 | 43 | class ClassLoader |
44 | 44 | { |
45 | - // PSR-4 |
|
46 | - private $prefixLengthsPsr4 = array(); |
|
47 | - private $prefixDirsPsr4 = array(); |
|
48 | - private $fallbackDirsPsr4 = array(); |
|
49 | - |
|
50 | - // PSR-0 |
|
51 | - private $prefixesPsr0 = array(); |
|
52 | - private $fallbackDirsPsr0 = array(); |
|
53 | - |
|
54 | - private $useIncludePath = false; |
|
55 | - private $classMap = array(); |
|
56 | - |
|
57 | - private $classMapAuthoritative = false; |
|
58 | - |
|
59 | - public function getPrefixes() |
|
60 | - { |
|
61 | - if (!empty($this->prefixesPsr0)) { |
|
62 | - return call_user_func_array('array_merge', $this->prefixesPsr0); |
|
63 | - } |
|
64 | - |
|
65 | - return array(); |
|
66 | - } |
|
67 | - |
|
68 | - public function getPrefixesPsr4() |
|
69 | - { |
|
70 | - return $this->prefixDirsPsr4; |
|
71 | - } |
|
72 | - |
|
73 | - public function getFallbackDirs() |
|
74 | - { |
|
75 | - return $this->fallbackDirsPsr0; |
|
76 | - } |
|
77 | - |
|
78 | - public function getFallbackDirsPsr4() |
|
79 | - { |
|
80 | - return $this->fallbackDirsPsr4; |
|
81 | - } |
|
82 | - |
|
83 | - public function getClassMap() |
|
84 | - { |
|
85 | - return $this->classMap; |
|
86 | - } |
|
87 | - |
|
88 | - /** |
|
89 | - * @param array $classMap Class to filename map |
|
90 | - */ |
|
91 | - public function addClassMap(array $classMap) |
|
92 | - { |
|
93 | - if ($this->classMap) { |
|
94 | - $this->classMap = array_merge($this->classMap, $classMap); |
|
95 | - } else { |
|
96 | - $this->classMap = $classMap; |
|
97 | - } |
|
98 | - } |
|
99 | - |
|
100 | - /** |
|
101 | - * Registers a set of PSR-0 directories for a given prefix, either |
|
102 | - * appending or prepending to the ones previously set for this prefix. |
|
103 | - * |
|
104 | - * @param string $prefix The prefix |
|
105 | - * @param array|string $paths The PSR-0 root directories |
|
106 | - * @param bool $prepend Whether to prepend the directories |
|
107 | - */ |
|
108 | - public function add($prefix, $paths, $prepend = false) |
|
109 | - { |
|
110 | - if (!$prefix) { |
|
111 | - if ($prepend) { |
|
112 | - $this->fallbackDirsPsr0 = array_merge( |
|
113 | - (array) $paths, |
|
114 | - $this->fallbackDirsPsr0 |
|
115 | - ); |
|
116 | - } else { |
|
117 | - $this->fallbackDirsPsr0 = array_merge( |
|
118 | - $this->fallbackDirsPsr0, |
|
119 | - (array) $paths |
|
120 | - ); |
|
121 | - } |
|
122 | - |
|
123 | - return; |
|
124 | - } |
|
125 | - |
|
126 | - $first = $prefix[0]; |
|
127 | - if (!isset($this->prefixesPsr0[$first][$prefix])) { |
|
128 | - $this->prefixesPsr0[$first][$prefix] = (array) $paths; |
|
129 | - |
|
130 | - return; |
|
131 | - } |
|
132 | - if ($prepend) { |
|
133 | - $this->prefixesPsr0[$first][$prefix] = array_merge( |
|
134 | - (array) $paths, |
|
135 | - $this->prefixesPsr0[$first][$prefix] |
|
136 | - ); |
|
137 | - } else { |
|
138 | - $this->prefixesPsr0[$first][$prefix] = array_merge( |
|
139 | - $this->prefixesPsr0[$first][$prefix], |
|
140 | - (array) $paths |
|
141 | - ); |
|
142 | - } |
|
143 | - } |
|
144 | - |
|
145 | - /** |
|
146 | - * Registers a set of PSR-4 directories for a given namespace, either |
|
147 | - * appending or prepending to the ones previously set for this namespace. |
|
148 | - * |
|
149 | - * @param string $prefix The prefix/namespace, with trailing '\\' |
|
150 | - * @param array|string $paths The PSR-4 base directories |
|
151 | - * @param bool $prepend Whether to prepend the directories |
|
152 | - * |
|
153 | - * @throws \InvalidArgumentException |
|
154 | - */ |
|
155 | - public function addPsr4($prefix, $paths, $prepend = false) |
|
156 | - { |
|
157 | - if (!$prefix) { |
|
158 | - // Register directories for the root namespace. |
|
159 | - if ($prepend) { |
|
160 | - $this->fallbackDirsPsr4 = array_merge( |
|
161 | - (array) $paths, |
|
162 | - $this->fallbackDirsPsr4 |
|
163 | - ); |
|
164 | - } else { |
|
165 | - $this->fallbackDirsPsr4 = array_merge( |
|
166 | - $this->fallbackDirsPsr4, |
|
167 | - (array) $paths |
|
168 | - ); |
|
169 | - } |
|
170 | - } elseif (!isset($this->prefixDirsPsr4[$prefix])) { |
|
171 | - // Register directories for a new namespace. |
|
172 | - $length = strlen($prefix); |
|
173 | - if ('\\' !== $prefix[$length - 1]) { |
|
174 | - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
|
175 | - } |
|
176 | - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
|
177 | - $this->prefixDirsPsr4[$prefix] = (array) $paths; |
|
178 | - } elseif ($prepend) { |
|
179 | - // Prepend directories for an already registered namespace. |
|
180 | - $this->prefixDirsPsr4[$prefix] = array_merge( |
|
181 | - (array) $paths, |
|
182 | - $this->prefixDirsPsr4[$prefix] |
|
183 | - ); |
|
184 | - } else { |
|
185 | - // Append directories for an already registered namespace. |
|
186 | - $this->prefixDirsPsr4[$prefix] = array_merge( |
|
187 | - $this->prefixDirsPsr4[$prefix], |
|
188 | - (array) $paths |
|
189 | - ); |
|
190 | - } |
|
191 | - } |
|
192 | - |
|
193 | - /** |
|
194 | - * Registers a set of PSR-0 directories for a given prefix, |
|
195 | - * replacing any others previously set for this prefix. |
|
196 | - * |
|
197 | - * @param string $prefix The prefix |
|
198 | - * @param array|string $paths The PSR-0 base directories |
|
199 | - */ |
|
200 | - public function set($prefix, $paths) |
|
201 | - { |
|
202 | - if (!$prefix) { |
|
203 | - $this->fallbackDirsPsr0 = (array) $paths; |
|
204 | - } else { |
|
205 | - $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; |
|
206 | - } |
|
207 | - } |
|
208 | - |
|
209 | - /** |
|
210 | - * Registers a set of PSR-4 directories for a given namespace, |
|
211 | - * replacing any others previously set for this namespace. |
|
212 | - * |
|
213 | - * @param string $prefix The prefix/namespace, with trailing '\\' |
|
214 | - * @param array|string $paths The PSR-4 base directories |
|
215 | - * |
|
216 | - * @throws \InvalidArgumentException |
|
217 | - */ |
|
218 | - public function setPsr4($prefix, $paths) |
|
219 | - { |
|
220 | - if (!$prefix) { |
|
221 | - $this->fallbackDirsPsr4 = (array) $paths; |
|
222 | - } else { |
|
223 | - $length = strlen($prefix); |
|
224 | - if ('\\' !== $prefix[$length - 1]) { |
|
225 | - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
|
226 | - } |
|
227 | - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
|
228 | - $this->prefixDirsPsr4[$prefix] = (array) $paths; |
|
229 | - } |
|
230 | - } |
|
231 | - |
|
232 | - /** |
|
233 | - * Turns on searching the include path for class files. |
|
234 | - * |
|
235 | - * @param bool $useIncludePath |
|
236 | - */ |
|
237 | - public function setUseIncludePath($useIncludePath) |
|
238 | - { |
|
239 | - $this->useIncludePath = $useIncludePath; |
|
240 | - } |
|
241 | - |
|
242 | - /** |
|
243 | - * Can be used to check if the autoloader uses the include path to check |
|
244 | - * for classes. |
|
245 | - * |
|
246 | - * @return bool |
|
247 | - */ |
|
248 | - public function getUseIncludePath() |
|
249 | - { |
|
250 | - return $this->useIncludePath; |
|
251 | - } |
|
252 | - |
|
253 | - /** |
|
254 | - * Turns off searching the prefix and fallback directories for classes |
|
255 | - * that have not been registered with the class map. |
|
256 | - * |
|
257 | - * @param bool $classMapAuthoritative |
|
258 | - */ |
|
259 | - public function setClassMapAuthoritative($classMapAuthoritative) |
|
260 | - { |
|
261 | - $this->classMapAuthoritative = $classMapAuthoritative; |
|
262 | - } |
|
263 | - |
|
264 | - /** |
|
265 | - * Should class lookup fail if not found in the current class map? |
|
266 | - * |
|
267 | - * @return bool |
|
268 | - */ |
|
269 | - public function isClassMapAuthoritative() |
|
270 | - { |
|
271 | - return $this->classMapAuthoritative; |
|
272 | - } |
|
273 | - |
|
274 | - /** |
|
275 | - * Registers this instance as an autoloader. |
|
276 | - * |
|
277 | - * @param bool $prepend Whether to prepend the autoloader or not |
|
278 | - */ |
|
279 | - public function register($prepend = false) |
|
280 | - { |
|
281 | - spl_autoload_register(array($this, 'loadClass'), true, $prepend); |
|
282 | - } |
|
283 | - |
|
284 | - /** |
|
285 | - * Unregisters this instance as an autoloader. |
|
286 | - */ |
|
287 | - public function unregister() |
|
288 | - { |
|
289 | - spl_autoload_unregister(array($this, 'loadClass')); |
|
290 | - } |
|
291 | - |
|
292 | - /** |
|
293 | - * Loads the given class or interface. |
|
294 | - * |
|
295 | - * @param string $class The name of the class |
|
296 | - * @return bool|null True if loaded, null otherwise |
|
297 | - */ |
|
298 | - public function loadClass($class) |
|
299 | - { |
|
300 | - if ($file = $this->findFile($class)) { |
|
301 | - includeFile($file); |
|
302 | - |
|
303 | - return true; |
|
304 | - } |
|
305 | - } |
|
306 | - |
|
307 | - /** |
|
308 | - * Finds the path to the file where the class is defined. |
|
309 | - * |
|
310 | - * @param string $class The name of the class |
|
311 | - * |
|
312 | - * @return string|false The path if found, false otherwise |
|
313 | - */ |
|
314 | - public function findFile($class) |
|
315 | - { |
|
316 | - // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 |
|
317 | - if ('\\' == $class[0]) { |
|
318 | - $class = substr($class, 1); |
|
319 | - } |
|
320 | - |
|
321 | - // class map lookup |
|
322 | - if (isset($this->classMap[$class])) { |
|
323 | - return $this->classMap[$class]; |
|
324 | - } |
|
325 | - if ($this->classMapAuthoritative) { |
|
326 | - return false; |
|
327 | - } |
|
328 | - |
|
329 | - $file = $this->findFileWithExtension($class, '.php'); |
|
330 | - |
|
331 | - // Search for Hack files if we are running on HHVM |
|
332 | - if ($file === null && defined('HHVM_VERSION')) { |
|
333 | - $file = $this->findFileWithExtension($class, '.hh'); |
|
334 | - } |
|
335 | - |
|
336 | - if ($file === null) { |
|
337 | - // Remember that this class does not exist. |
|
338 | - return $this->classMap[$class] = false; |
|
339 | - } |
|
340 | - |
|
341 | - return $file; |
|
342 | - } |
|
343 | - |
|
344 | - private function findFileWithExtension($class, $ext) |
|
345 | - { |
|
346 | - // PSR-4 lookup |
|
347 | - $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; |
|
348 | - |
|
349 | - $first = $class[0]; |
|
350 | - if (isset($this->prefixLengthsPsr4[$first])) { |
|
351 | - foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { |
|
352 | - if (0 === strpos($class, $prefix)) { |
|
353 | - foreach ($this->prefixDirsPsr4[$prefix] as $dir) { |
|
354 | - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { |
|
355 | - return $file; |
|
356 | - } |
|
357 | - } |
|
358 | - } |
|
359 | - } |
|
360 | - } |
|
361 | - |
|
362 | - // PSR-4 fallback dirs |
|
363 | - foreach ($this->fallbackDirsPsr4 as $dir) { |
|
364 | - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { |
|
365 | - return $file; |
|
366 | - } |
|
367 | - } |
|
368 | - |
|
369 | - // PSR-0 lookup |
|
370 | - if (false !== $pos = strrpos($class, '\\')) { |
|
371 | - // namespaced class name |
|
372 | - $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) |
|
373 | - . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); |
|
374 | - } else { |
|
375 | - // PEAR-like class name |
|
376 | - $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; |
|
377 | - } |
|
378 | - |
|
379 | - if (isset($this->prefixesPsr0[$first])) { |
|
380 | - foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { |
|
381 | - if (0 === strpos($class, $prefix)) { |
|
382 | - foreach ($dirs as $dir) { |
|
383 | - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { |
|
384 | - return $file; |
|
385 | - } |
|
386 | - } |
|
387 | - } |
|
388 | - } |
|
389 | - } |
|
390 | - |
|
391 | - // PSR-0 fallback dirs |
|
392 | - foreach ($this->fallbackDirsPsr0 as $dir) { |
|
393 | - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { |
|
394 | - return $file; |
|
395 | - } |
|
396 | - } |
|
397 | - |
|
398 | - // PSR-0 include paths. |
|
399 | - if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { |
|
400 | - return $file; |
|
401 | - } |
|
402 | - } |
|
45 | + // PSR-4 |
|
46 | + private $prefixLengthsPsr4 = array(); |
|
47 | + private $prefixDirsPsr4 = array(); |
|
48 | + private $fallbackDirsPsr4 = array(); |
|
49 | + |
|
50 | + // PSR-0 |
|
51 | + private $prefixesPsr0 = array(); |
|
52 | + private $fallbackDirsPsr0 = array(); |
|
53 | + |
|
54 | + private $useIncludePath = false; |
|
55 | + private $classMap = array(); |
|
56 | + |
|
57 | + private $classMapAuthoritative = false; |
|
58 | + |
|
59 | + public function getPrefixes() |
|
60 | + { |
|
61 | + if (!empty($this->prefixesPsr0)) { |
|
62 | + return call_user_func_array('array_merge', $this->prefixesPsr0); |
|
63 | + } |
|
64 | + |
|
65 | + return array(); |
|
66 | + } |
|
67 | + |
|
68 | + public function getPrefixesPsr4() |
|
69 | + { |
|
70 | + return $this->prefixDirsPsr4; |
|
71 | + } |
|
72 | + |
|
73 | + public function getFallbackDirs() |
|
74 | + { |
|
75 | + return $this->fallbackDirsPsr0; |
|
76 | + } |
|
77 | + |
|
78 | + public function getFallbackDirsPsr4() |
|
79 | + { |
|
80 | + return $this->fallbackDirsPsr4; |
|
81 | + } |
|
82 | + |
|
83 | + public function getClassMap() |
|
84 | + { |
|
85 | + return $this->classMap; |
|
86 | + } |
|
87 | + |
|
88 | + /** |
|
89 | + * @param array $classMap Class to filename map |
|
90 | + */ |
|
91 | + public function addClassMap(array $classMap) |
|
92 | + { |
|
93 | + if ($this->classMap) { |
|
94 | + $this->classMap = array_merge($this->classMap, $classMap); |
|
95 | + } else { |
|
96 | + $this->classMap = $classMap; |
|
97 | + } |
|
98 | + } |
|
99 | + |
|
100 | + /** |
|
101 | + * Registers a set of PSR-0 directories for a given prefix, either |
|
102 | + * appending or prepending to the ones previously set for this prefix. |
|
103 | + * |
|
104 | + * @param string $prefix The prefix |
|
105 | + * @param array|string $paths The PSR-0 root directories |
|
106 | + * @param bool $prepend Whether to prepend the directories |
|
107 | + */ |
|
108 | + public function add($prefix, $paths, $prepend = false) |
|
109 | + { |
|
110 | + if (!$prefix) { |
|
111 | + if ($prepend) { |
|
112 | + $this->fallbackDirsPsr0 = array_merge( |
|
113 | + (array) $paths, |
|
114 | + $this->fallbackDirsPsr0 |
|
115 | + ); |
|
116 | + } else { |
|
117 | + $this->fallbackDirsPsr0 = array_merge( |
|
118 | + $this->fallbackDirsPsr0, |
|
119 | + (array) $paths |
|
120 | + ); |
|
121 | + } |
|
122 | + |
|
123 | + return; |
|
124 | + } |
|
125 | + |
|
126 | + $first = $prefix[0]; |
|
127 | + if (!isset($this->prefixesPsr0[$first][$prefix])) { |
|
128 | + $this->prefixesPsr0[$first][$prefix] = (array) $paths; |
|
129 | + |
|
130 | + return; |
|
131 | + } |
|
132 | + if ($prepend) { |
|
133 | + $this->prefixesPsr0[$first][$prefix] = array_merge( |
|
134 | + (array) $paths, |
|
135 | + $this->prefixesPsr0[$first][$prefix] |
|
136 | + ); |
|
137 | + } else { |
|
138 | + $this->prefixesPsr0[$first][$prefix] = array_merge( |
|
139 | + $this->prefixesPsr0[$first][$prefix], |
|
140 | + (array) $paths |
|
141 | + ); |
|
142 | + } |
|
143 | + } |
|
144 | + |
|
145 | + /** |
|
146 | + * Registers a set of PSR-4 directories for a given namespace, either |
|
147 | + * appending or prepending to the ones previously set for this namespace. |
|
148 | + * |
|
149 | + * @param string $prefix The prefix/namespace, with trailing '\\' |
|
150 | + * @param array|string $paths The PSR-4 base directories |
|
151 | + * @param bool $prepend Whether to prepend the directories |
|
152 | + * |
|
153 | + * @throws \InvalidArgumentException |
|
154 | + */ |
|
155 | + public function addPsr4($prefix, $paths, $prepend = false) |
|
156 | + { |
|
157 | + if (!$prefix) { |
|
158 | + // Register directories for the root namespace. |
|
159 | + if ($prepend) { |
|
160 | + $this->fallbackDirsPsr4 = array_merge( |
|
161 | + (array) $paths, |
|
162 | + $this->fallbackDirsPsr4 |
|
163 | + ); |
|
164 | + } else { |
|
165 | + $this->fallbackDirsPsr4 = array_merge( |
|
166 | + $this->fallbackDirsPsr4, |
|
167 | + (array) $paths |
|
168 | + ); |
|
169 | + } |
|
170 | + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { |
|
171 | + // Register directories for a new namespace. |
|
172 | + $length = strlen($prefix); |
|
173 | + if ('\\' !== $prefix[$length - 1]) { |
|
174 | + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
|
175 | + } |
|
176 | + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
|
177 | + $this->prefixDirsPsr4[$prefix] = (array) $paths; |
|
178 | + } elseif ($prepend) { |
|
179 | + // Prepend directories for an already registered namespace. |
|
180 | + $this->prefixDirsPsr4[$prefix] = array_merge( |
|
181 | + (array) $paths, |
|
182 | + $this->prefixDirsPsr4[$prefix] |
|
183 | + ); |
|
184 | + } else { |
|
185 | + // Append directories for an already registered namespace. |
|
186 | + $this->prefixDirsPsr4[$prefix] = array_merge( |
|
187 | + $this->prefixDirsPsr4[$prefix], |
|
188 | + (array) $paths |
|
189 | + ); |
|
190 | + } |
|
191 | + } |
|
192 | + |
|
193 | + /** |
|
194 | + * Registers a set of PSR-0 directories for a given prefix, |
|
195 | + * replacing any others previously set for this prefix. |
|
196 | + * |
|
197 | + * @param string $prefix The prefix |
|
198 | + * @param array|string $paths The PSR-0 base directories |
|
199 | + */ |
|
200 | + public function set($prefix, $paths) |
|
201 | + { |
|
202 | + if (!$prefix) { |
|
203 | + $this->fallbackDirsPsr0 = (array) $paths; |
|
204 | + } else { |
|
205 | + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; |
|
206 | + } |
|
207 | + } |
|
208 | + |
|
209 | + /** |
|
210 | + * Registers a set of PSR-4 directories for a given namespace, |
|
211 | + * replacing any others previously set for this namespace. |
|
212 | + * |
|
213 | + * @param string $prefix The prefix/namespace, with trailing '\\' |
|
214 | + * @param array|string $paths The PSR-4 base directories |
|
215 | + * |
|
216 | + * @throws \InvalidArgumentException |
|
217 | + */ |
|
218 | + public function setPsr4($prefix, $paths) |
|
219 | + { |
|
220 | + if (!$prefix) { |
|
221 | + $this->fallbackDirsPsr4 = (array) $paths; |
|
222 | + } else { |
|
223 | + $length = strlen($prefix); |
|
224 | + if ('\\' !== $prefix[$length - 1]) { |
|
225 | + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
|
226 | + } |
|
227 | + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
|
228 | + $this->prefixDirsPsr4[$prefix] = (array) $paths; |
|
229 | + } |
|
230 | + } |
|
231 | + |
|
232 | + /** |
|
233 | + * Turns on searching the include path for class files. |
|
234 | + * |
|
235 | + * @param bool $useIncludePath |
|
236 | + */ |
|
237 | + public function setUseIncludePath($useIncludePath) |
|
238 | + { |
|
239 | + $this->useIncludePath = $useIncludePath; |
|
240 | + } |
|
241 | + |
|
242 | + /** |
|
243 | + * Can be used to check if the autoloader uses the include path to check |
|
244 | + * for classes. |
|
245 | + * |
|
246 | + * @return bool |
|
247 | + */ |
|
248 | + public function getUseIncludePath() |
|
249 | + { |
|
250 | + return $this->useIncludePath; |
|
251 | + } |
|
252 | + |
|
253 | + /** |
|
254 | + * Turns off searching the prefix and fallback directories for classes |
|
255 | + * that have not been registered with the class map. |
|
256 | + * |
|
257 | + * @param bool $classMapAuthoritative |
|
258 | + */ |
|
259 | + public function setClassMapAuthoritative($classMapAuthoritative) |
|
260 | + { |
|
261 | + $this->classMapAuthoritative = $classMapAuthoritative; |
|
262 | + } |
|
263 | + |
|
264 | + /** |
|
265 | + * Should class lookup fail if not found in the current class map? |
|
266 | + * |
|
267 | + * @return bool |
|
268 | + */ |
|
269 | + public function isClassMapAuthoritative() |
|
270 | + { |
|
271 | + return $this->classMapAuthoritative; |
|
272 | + } |
|
273 | + |
|
274 | + /** |
|
275 | + * Registers this instance as an autoloader. |
|
276 | + * |
|
277 | + * @param bool $prepend Whether to prepend the autoloader or not |
|
278 | + */ |
|
279 | + public function register($prepend = false) |
|
280 | + { |
|
281 | + spl_autoload_register(array($this, 'loadClass'), true, $prepend); |
|
282 | + } |
|
283 | + |
|
284 | + /** |
|
285 | + * Unregisters this instance as an autoloader. |
|
286 | + */ |
|
287 | + public function unregister() |
|
288 | + { |
|
289 | + spl_autoload_unregister(array($this, 'loadClass')); |
|
290 | + } |
|
291 | + |
|
292 | + /** |
|
293 | + * Loads the given class or interface. |
|
294 | + * |
|
295 | + * @param string $class The name of the class |
|
296 | + * @return bool|null True if loaded, null otherwise |
|
297 | + */ |
|
298 | + public function loadClass($class) |
|
299 | + { |
|
300 | + if ($file = $this->findFile($class)) { |
|
301 | + includeFile($file); |
|
302 | + |
|
303 | + return true; |
|
304 | + } |
|
305 | + } |
|
306 | + |
|
307 | + /** |
|
308 | + * Finds the path to the file where the class is defined. |
|
309 | + * |
|
310 | + * @param string $class The name of the class |
|
311 | + * |
|
312 | + * @return string|false The path if found, false otherwise |
|
313 | + */ |
|
314 | + public function findFile($class) |
|
315 | + { |
|
316 | + // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 |
|
317 | + if ('\\' == $class[0]) { |
|
318 | + $class = substr($class, 1); |
|
319 | + } |
|
320 | + |
|
321 | + // class map lookup |
|
322 | + if (isset($this->classMap[$class])) { |
|
323 | + return $this->classMap[$class]; |
|
324 | + } |
|
325 | + if ($this->classMapAuthoritative) { |
|
326 | + return false; |
|
327 | + } |
|
328 | + |
|
329 | + $file = $this->findFileWithExtension($class, '.php'); |
|
330 | + |
|
331 | + // Search for Hack files if we are running on HHVM |
|
332 | + if ($file === null && defined('HHVM_VERSION')) { |
|
333 | + $file = $this->findFileWithExtension($class, '.hh'); |
|
334 | + } |
|
335 | + |
|
336 | + if ($file === null) { |
|
337 | + // Remember that this class does not exist. |
|
338 | + return $this->classMap[$class] = false; |
|
339 | + } |
|
340 | + |
|
341 | + return $file; |
|
342 | + } |
|
343 | + |
|
344 | + private function findFileWithExtension($class, $ext) |
|
345 | + { |
|
346 | + // PSR-4 lookup |
|
347 | + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; |
|
348 | + |
|
349 | + $first = $class[0]; |
|
350 | + if (isset($this->prefixLengthsPsr4[$first])) { |
|
351 | + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { |
|
352 | + if (0 === strpos($class, $prefix)) { |
|
353 | + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { |
|
354 | + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { |
|
355 | + return $file; |
|
356 | + } |
|
357 | + } |
|
358 | + } |
|
359 | + } |
|
360 | + } |
|
361 | + |
|
362 | + // PSR-4 fallback dirs |
|
363 | + foreach ($this->fallbackDirsPsr4 as $dir) { |
|
364 | + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { |
|
365 | + return $file; |
|
366 | + } |
|
367 | + } |
|
368 | + |
|
369 | + // PSR-0 lookup |
|
370 | + if (false !== $pos = strrpos($class, '\\')) { |
|
371 | + // namespaced class name |
|
372 | + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) |
|
373 | + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); |
|
374 | + } else { |
|
375 | + // PEAR-like class name |
|
376 | + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; |
|
377 | + } |
|
378 | + |
|
379 | + if (isset($this->prefixesPsr0[$first])) { |
|
380 | + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { |
|
381 | + if (0 === strpos($class, $prefix)) { |
|
382 | + foreach ($dirs as $dir) { |
|
383 | + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { |
|
384 | + return $file; |
|
385 | + } |
|
386 | + } |
|
387 | + } |
|
388 | + } |
|
389 | + } |
|
390 | + |
|
391 | + // PSR-0 fallback dirs |
|
392 | + foreach ($this->fallbackDirsPsr0 as $dir) { |
|
393 | + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { |
|
394 | + return $file; |
|
395 | + } |
|
396 | + } |
|
397 | + |
|
398 | + // PSR-0 include paths. |
|
399 | + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { |
|
400 | + return $file; |
|
401 | + } |
|
402 | + } |
|
403 | 403 | } |
404 | 404 | |
405 | 405 | /** |
@@ -409,5 +409,5 @@ discard block |
||
409 | 409 | */ |
410 | 410 | function includeFile($file) |
411 | 411 | { |
412 | - include $file; |
|
412 | + include $file; |
|
413 | 413 | } |
@@ -2,8 +2,8 @@ |
||
2 | 2 | |
3 | 3 | namespace Sabre\DAV\Auth\Backend; |
4 | 4 | |
5 | -use Sabre\HTTP; |
|
6 | 5 | use Sabre\DAV; |
6 | +use Sabre\HTTP; |
|
7 | 7 | use Sabre\HTTP\RequestInterface; |
8 | 8 | use Sabre\HTTP\ResponseInterface; |
9 | 9 |
@@ -20,143 +20,143 @@ |
||
20 | 20 | */ |
21 | 21 | abstract class AbstractDigest implements BackendInterface { |
22 | 22 | |
23 | - /** |
|
24 | - * Authentication Realm. |
|
25 | - * |
|
26 | - * The realm is often displayed by browser clients when showing the |
|
27 | - * authentication dialog. |
|
28 | - * |
|
29 | - * @var string |
|
30 | - */ |
|
31 | - protected $realm = 'SabreDAV'; |
|
32 | - |
|
33 | - /** |
|
34 | - * This is the prefix that will be used to generate principal urls. |
|
35 | - * |
|
36 | - * @var string |
|
37 | - */ |
|
38 | - protected $principalPrefix = 'principals/'; |
|
39 | - |
|
40 | - /** |
|
41 | - * Sets the authentication realm for this backend. |
|
42 | - * |
|
43 | - * Be aware that for Digest authentication, the realm influences the digest |
|
44 | - * hash. Choose the realm wisely, because if you change it later, all the |
|
45 | - * existing hashes will break and nobody can authenticate. |
|
46 | - * |
|
47 | - * @param string $realm |
|
48 | - * @return void |
|
49 | - */ |
|
50 | - public function setRealm($realm) { |
|
51 | - |
|
52 | - $this->realm = $realm; |
|
53 | - |
|
54 | - } |
|
55 | - |
|
56 | - /** |
|
57 | - * Returns a users digest hash based on the username and realm. |
|
58 | - * |
|
59 | - * If the user was not known, null must be returned. |
|
60 | - * |
|
61 | - * @param string $realm |
|
62 | - * @param string $username |
|
63 | - * @return string|null |
|
64 | - */ |
|
65 | - abstract function getDigestHash($realm, $username); |
|
66 | - |
|
67 | - /** |
|
68 | - * When this method is called, the backend must check if authentication was |
|
69 | - * successful. |
|
70 | - * |
|
71 | - * The returned value must be one of the following |
|
72 | - * |
|
73 | - * [true, "principals/username"] |
|
74 | - * [false, "reason for failure"] |
|
75 | - * |
|
76 | - * If authentication was successful, it's expected that the authentication |
|
77 | - * backend returns a so-called principal url. |
|
78 | - * |
|
79 | - * Examples of a principal url: |
|
80 | - * |
|
81 | - * principals/admin |
|
82 | - * principals/user1 |
|
83 | - * principals/users/joe |
|
84 | - * principals/uid/123457 |
|
85 | - * |
|
86 | - * If you don't use WebDAV ACL (RFC3744) we recommend that you simply |
|
87 | - * return a string such as: |
|
88 | - * |
|
89 | - * principals/users/[username] |
|
90 | - * |
|
91 | - * @param RequestInterface $request |
|
92 | - * @param ResponseInterface $response |
|
93 | - * @return array |
|
94 | - */ |
|
95 | - public function check(RequestInterface $request, ResponseInterface $response) { |
|
96 | - |
|
97 | - $digest = new HTTP\Auth\Digest( |
|
98 | - $this->realm, |
|
99 | - $request, |
|
100 | - $response |
|
101 | - ); |
|
102 | - $digest->init(); |
|
103 | - |
|
104 | - $username = $digest->getUsername(); |
|
105 | - |
|
106 | - // No username was given |
|
107 | - if (!$username) { |
|
108 | - return [false, "No 'Authorization: Digest' header found. Either the client didn't send one, or the server is misconfigured"]; |
|
109 | - } |
|
110 | - |
|
111 | - $hash = $this->getDigestHash($this->realm, $username); |
|
112 | - // If this was false, the user account didn't exist |
|
113 | - if ($hash === false || is_null($hash)) { |
|
114 | - return [false, "Username or password was incorrect"]; |
|
115 | - } |
|
116 | - if (!is_string($hash)) { |
|
117 | - throw new DAV\Exception('The returned value from getDigestHash must be a string or null'); |
|
118 | - } |
|
119 | - |
|
120 | - // If this was false, the password or part of the hash was incorrect. |
|
121 | - if (!$digest->validateA1($hash)) { |
|
122 | - return [false, "Username or password was incorrect"]; |
|
123 | - } |
|
124 | - |
|
125 | - return [true, $this->principalPrefix . $username]; |
|
126 | - |
|
127 | - } |
|
128 | - |
|
129 | - /** |
|
130 | - * This method is called when a user could not be authenticated, and |
|
131 | - * authentication was required for the current request. |
|
132 | - * |
|
133 | - * This gives you the opportunity to set authentication headers. The 401 |
|
134 | - * status code will already be set. |
|
135 | - * |
|
136 | - * In this case of Basic Auth, this would for example mean that the |
|
137 | - * following header needs to be set: |
|
138 | - * |
|
139 | - * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV'); |
|
140 | - * |
|
141 | - * Keep in mind that in the case of multiple authentication backends, other |
|
142 | - * WWW-Authenticate headers may already have been set, and you'll want to |
|
143 | - * append your own WWW-Authenticate header instead of overwriting the |
|
144 | - * existing one. |
|
145 | - * |
|
146 | - * @param RequestInterface $request |
|
147 | - * @param ResponseInterface $response |
|
148 | - * @return void |
|
149 | - */ |
|
150 | - public function challenge(RequestInterface $request, ResponseInterface $response) { |
|
151 | - |
|
152 | - $auth = new HTTP\Auth\Digest( |
|
153 | - $this->realm, |
|
154 | - $request, |
|
155 | - $response |
|
156 | - ); |
|
157 | - $auth->init(); |
|
158 | - $auth->requireLogin(); |
|
159 | - |
|
160 | - } |
|
23 | + /** |
|
24 | + * Authentication Realm. |
|
25 | + * |
|
26 | + * The realm is often displayed by browser clients when showing the |
|
27 | + * authentication dialog. |
|
28 | + * |
|
29 | + * @var string |
|
30 | + */ |
|
31 | + protected $realm = 'SabreDAV'; |
|
32 | + |
|
33 | + /** |
|
34 | + * This is the prefix that will be used to generate principal urls. |
|
35 | + * |
|
36 | + * @var string |
|
37 | + */ |
|
38 | + protected $principalPrefix = 'principals/'; |
|
39 | + |
|
40 | + /** |
|
41 | + * Sets the authentication realm for this backend. |
|
42 | + * |
|
43 | + * Be aware that for Digest authentication, the realm influences the digest |
|
44 | + * hash. Choose the realm wisely, because if you change it later, all the |
|
45 | + * existing hashes will break and nobody can authenticate. |
|
46 | + * |
|
47 | + * @param string $realm |
|
48 | + * @return void |
|
49 | + */ |
|
50 | + public function setRealm($realm) { |
|
51 | + |
|
52 | + $this->realm = $realm; |
|
53 | + |
|
54 | + } |
|
55 | + |
|
56 | + /** |
|
57 | + * Returns a users digest hash based on the username and realm. |
|
58 | + * |
|
59 | + * If the user was not known, null must be returned. |
|
60 | + * |
|
61 | + * @param string $realm |
|
62 | + * @param string $username |
|
63 | + * @return string|null |
|
64 | + */ |
|
65 | + abstract function getDigestHash($realm, $username); |
|
66 | + |
|
67 | + /** |
|
68 | + * When this method is called, the backend must check if authentication was |
|
69 | + * successful. |
|
70 | + * |
|
71 | + * The returned value must be one of the following |
|
72 | + * |
|
73 | + * [true, "principals/username"] |
|
74 | + * [false, "reason for failure"] |
|
75 | + * |
|
76 | + * If authentication was successful, it's expected that the authentication |
|
77 | + * backend returns a so-called principal url. |
|
78 | + * |
|
79 | + * Examples of a principal url: |
|
80 | + * |
|
81 | + * principals/admin |
|
82 | + * principals/user1 |
|
83 | + * principals/users/joe |
|
84 | + * principals/uid/123457 |
|
85 | + * |
|
86 | + * If you don't use WebDAV ACL (RFC3744) we recommend that you simply |
|
87 | + * return a string such as: |
|
88 | + * |
|
89 | + * principals/users/[username] |
|
90 | + * |
|
91 | + * @param RequestInterface $request |
|
92 | + * @param ResponseInterface $response |
|
93 | + * @return array |
|
94 | + */ |
|
95 | + public function check(RequestInterface $request, ResponseInterface $response) { |
|
96 | + |
|
97 | + $digest = new HTTP\Auth\Digest( |
|
98 | + $this->realm, |
|
99 | + $request, |
|
100 | + $response |
|
101 | + ); |
|
102 | + $digest->init(); |
|
103 | + |
|
104 | + $username = $digest->getUsername(); |
|
105 | + |
|
106 | + // No username was given |
|
107 | + if (!$username) { |
|
108 | + return [false, "No 'Authorization: Digest' header found. Either the client didn't send one, or the server is misconfigured"]; |
|
109 | + } |
|
110 | + |
|
111 | + $hash = $this->getDigestHash($this->realm, $username); |
|
112 | + // If this was false, the user account didn't exist |
|
113 | + if ($hash === false || is_null($hash)) { |
|
114 | + return [false, "Username or password was incorrect"]; |
|
115 | + } |
|
116 | + if (!is_string($hash)) { |
|
117 | + throw new DAV\Exception('The returned value from getDigestHash must be a string or null'); |
|
118 | + } |
|
119 | + |
|
120 | + // If this was false, the password or part of the hash was incorrect. |
|
121 | + if (!$digest->validateA1($hash)) { |
|
122 | + return [false, "Username or password was incorrect"]; |
|
123 | + } |
|
124 | + |
|
125 | + return [true, $this->principalPrefix . $username]; |
|
126 | + |
|
127 | + } |
|
128 | + |
|
129 | + /** |
|
130 | + * This method is called when a user could not be authenticated, and |
|
131 | + * authentication was required for the current request. |
|
132 | + * |
|
133 | + * This gives you the opportunity to set authentication headers. The 401 |
|
134 | + * status code will already be set. |
|
135 | + * |
|
136 | + * In this case of Basic Auth, this would for example mean that the |
|
137 | + * following header needs to be set: |
|
138 | + * |
|
139 | + * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV'); |
|
140 | + * |
|
141 | + * Keep in mind that in the case of multiple authentication backends, other |
|
142 | + * WWW-Authenticate headers may already have been set, and you'll want to |
|
143 | + * append your own WWW-Authenticate header instead of overwriting the |
|
144 | + * existing one. |
|
145 | + * |
|
146 | + * @param RequestInterface $request |
|
147 | + * @param ResponseInterface $response |
|
148 | + * @return void |
|
149 | + */ |
|
150 | + public function challenge(RequestInterface $request, ResponseInterface $response) { |
|
151 | + |
|
152 | + $auth = new HTTP\Auth\Digest( |
|
153 | + $this->realm, |
|
154 | + $request, |
|
155 | + $response |
|
156 | + ); |
|
157 | + $auth->init(); |
|
158 | + $auth->requireLogin(); |
|
159 | + |
|
160 | + } |
|
161 | 161 | |
162 | 162 | } |
@@ -2,12 +2,12 @@ |
||
2 | 2 | |
3 | 3 | namespace Sabre\DAV\Auth; |
4 | 4 | |
5 | -use Sabre\HTTP\RequestInterface; |
|
6 | -use Sabre\HTTP\ResponseInterface; |
|
7 | -use Sabre\HTTP\URLUtil; |
|
8 | 5 | use Sabre\DAV\Exception\NotAuthenticated; |
9 | 6 | use Sabre\DAV\Server; |
10 | 7 | use Sabre\DAV\ServerPlugin; |
8 | +use Sabre\HTTP\RequestInterface; |
|
9 | +use Sabre\HTTP\ResponseInterface; |
|
10 | +use Sabre\HTTP\URLUtil; |
|
11 | 11 | |
12 | 12 | /** |
13 | 13 | * This plugin provides Authentication for a WebDAV server. |
@@ -25,189 +25,189 @@ |
||
25 | 25 | */ |
26 | 26 | class Plugin extends ServerPlugin { |
27 | 27 | |
28 | - /** |
|
29 | - * authentication backends |
|
30 | - */ |
|
31 | - protected $backends; |
|
32 | - |
|
33 | - /** |
|
34 | - * The currently logged in principal. Will be `null` if nobody is currently |
|
35 | - * logged in. |
|
36 | - * |
|
37 | - * @var string|null |
|
38 | - */ |
|
39 | - protected $currentPrincipal; |
|
40 | - |
|
41 | - /** |
|
42 | - * Creates the authentication plugin |
|
43 | - * |
|
44 | - * @param Backend\BackendInterface $authBackend |
|
45 | - */ |
|
46 | - public function __construct(Backend\BackendInterface $authBackend = null) { |
|
47 | - |
|
48 | - if (!is_null($authBackend)) { |
|
49 | - $this->addBackend($authBackend); |
|
50 | - } |
|
51 | - |
|
52 | - } |
|
53 | - |
|
54 | - /** |
|
55 | - * Adds an authentication backend to the plugin. |
|
56 | - * |
|
57 | - * @param Backend\BackendInterface $authBackend |
|
58 | - * @return void |
|
59 | - */ |
|
60 | - public function addBackend(Backend\BackendInterface $authBackend) { |
|
61 | - |
|
62 | - $this->backends[] = $authBackend; |
|
63 | - |
|
64 | - } |
|
65 | - |
|
66 | - /** |
|
67 | - * Initializes the plugin. This function is automatically called by the server |
|
68 | - * |
|
69 | - * @param Server $server |
|
70 | - * @return void |
|
71 | - */ |
|
72 | - public function initialize(Server $server) { |
|
73 | - |
|
74 | - $server->on('beforeMethod', [$this, 'beforeMethod'], 10); |
|
75 | - |
|
76 | - } |
|
77 | - |
|
78 | - /** |
|
79 | - * Returns a plugin name. |
|
80 | - * |
|
81 | - * Using this name other plugins will be able to access other plugins |
|
82 | - * using DAV\Server::getPlugin |
|
83 | - * |
|
84 | - * @return string |
|
85 | - */ |
|
86 | - public function getPluginName() { |
|
87 | - |
|
88 | - return 'auth'; |
|
89 | - |
|
90 | - } |
|
91 | - |
|
92 | - /** |
|
93 | - * Returns the currently logged-in principal. |
|
94 | - * |
|
95 | - * This will return a string such as: |
|
96 | - * |
|
97 | - * principals/username |
|
98 | - * principals/users/username |
|
99 | - * |
|
100 | - * This method will return null if nobody is logged in. |
|
101 | - * |
|
102 | - * @return string|null |
|
103 | - */ |
|
104 | - public function getCurrentPrincipal() { |
|
105 | - |
|
106 | - return $this->currentPrincipal; |
|
107 | - |
|
108 | - } |
|
109 | - |
|
110 | - /** |
|
111 | - * Returns the current username. |
|
112 | - * |
|
113 | - * This method is deprecated and is only kept for backwards compatibility |
|
114 | - * purposes. Please switch to getCurrentPrincipal(). |
|
115 | - * |
|
116 | - * @deprecated Will be removed in a future version! |
|
117 | - * @return string|null |
|
118 | - */ |
|
119 | - public function getCurrentUser() { |
|
120 | - |
|
121 | - // We just do a 'basename' on the principal to give back a sane value |
|
122 | - // here. |
|
123 | - list(, $userName) = URLUtil::splitPath( |
|
124 | - $this->getCurrentPrincipal() |
|
125 | - ); |
|
126 | - |
|
127 | - return $userName; |
|
128 | - |
|
129 | - } |
|
130 | - |
|
131 | - /** |
|
132 | - * This method is called before any HTTP method and forces users to be authenticated |
|
133 | - * |
|
134 | - * @param RequestInterface $request |
|
135 | - * @param ResponseInterface $response |
|
136 | - * @return bool |
|
137 | - */ |
|
138 | - public function beforeMethod(RequestInterface $request, ResponseInterface $response) { |
|
139 | - |
|
140 | - if ($this->currentPrincipal) { |
|
141 | - |
|
142 | - // We already have authentication information. This means that the |
|
143 | - // event has already fired earlier, and is now likely fired for a |
|
144 | - // sub-request. |
|
145 | - // |
|
146 | - // We don't want to authenticate users twice, so we simply don't do |
|
147 | - // anything here. See Issue #700 for additional reasoning. |
|
148 | - // |
|
149 | - // This is not a perfect solution, but will be fixed once the |
|
150 | - // "currently authenticated principal" is information that's not |
|
151 | - // not associated with the plugin, but rather per-request. |
|
152 | - // |
|
153 | - // See issue #580 for more information about that. |
|
154 | - return; |
|
155 | - |
|
156 | - } |
|
157 | - if (!$this->backends) { |
|
158 | - throw new \Sabre\DAV\Exception('No authentication backends were configured on this server.'); |
|
159 | - } |
|
160 | - $reasons = []; |
|
161 | - foreach ($this->backends as $backend) { |
|
162 | - |
|
163 | - $result = $backend->check( |
|
164 | - $request, |
|
165 | - $response |
|
166 | - ); |
|
167 | - |
|
168 | - if (!is_array($result) || count($result) !== 2 || !is_bool($result[0]) || !is_string($result[1])) { |
|
169 | - throw new \Sabre\DAV\Exception('The authentication backend did not return a correct value from the check() method.'); |
|
170 | - } |
|
171 | - |
|
172 | - if ($result[0]) { |
|
173 | - $this->currentPrincipal = $result[1]; |
|
174 | - // Exit early |
|
175 | - return; |
|
176 | - } |
|
177 | - $reasons[] = $result[1]; |
|
178 | - |
|
179 | - } |
|
180 | - |
|
181 | - // If we got here, it means that no authentication backend was |
|
182 | - // successful in authenticating the user. |
|
183 | - $this->currentPrincipal = null; |
|
184 | - |
|
185 | - foreach ($this->backends as $backend) { |
|
186 | - $backend->challenge($request, $response); |
|
187 | - } |
|
188 | - throw new NotAuthenticated(implode(', ', $reasons)); |
|
189 | - |
|
190 | - } |
|
191 | - |
|
192 | - /** |
|
193 | - * Returns a bunch of meta-data about the plugin. |
|
194 | - * |
|
195 | - * Providing this information is optional, and is mainly displayed by the |
|
196 | - * Browser plugin. |
|
197 | - * |
|
198 | - * The description key in the returned array may contain html and will not |
|
199 | - * be sanitized. |
|
200 | - * |
|
201 | - * @return array |
|
202 | - */ |
|
203 | - public function getPluginInfo() { |
|
204 | - |
|
205 | - return [ |
|
206 | - 'name' => $this->getPluginName(), |
|
207 | - 'description' => 'Generic authentication plugin', |
|
208 | - 'link' => 'http://sabre.io/dav/authentication/', |
|
209 | - ]; |
|
210 | - |
|
211 | - } |
|
28 | + /** |
|
29 | + * authentication backends |
|
30 | + */ |
|
31 | + protected $backends; |
|
32 | + |
|
33 | + /** |
|
34 | + * The currently logged in principal. Will be `null` if nobody is currently |
|
35 | + * logged in. |
|
36 | + * |
|
37 | + * @var string|null |
|
38 | + */ |
|
39 | + protected $currentPrincipal; |
|
40 | + |
|
41 | + /** |
|
42 | + * Creates the authentication plugin |
|
43 | + * |
|
44 | + * @param Backend\BackendInterface $authBackend |
|
45 | + */ |
|
46 | + public function __construct(Backend\BackendInterface $authBackend = null) { |
|
47 | + |
|
48 | + if (!is_null($authBackend)) { |
|
49 | + $this->addBackend($authBackend); |
|
50 | + } |
|
51 | + |
|
52 | + } |
|
53 | + |
|
54 | + /** |
|
55 | + * Adds an authentication backend to the plugin. |
|
56 | + * |
|
57 | + * @param Backend\BackendInterface $authBackend |
|
58 | + * @return void |
|
59 | + */ |
|
60 | + public function addBackend(Backend\BackendInterface $authBackend) { |
|
61 | + |
|
62 | + $this->backends[] = $authBackend; |
|
63 | + |
|
64 | + } |
|
65 | + |
|
66 | + /** |
|
67 | + * Initializes the plugin. This function is automatically called by the server |
|
68 | + * |
|
69 | + * @param Server $server |
|
70 | + * @return void |
|
71 | + */ |
|
72 | + public function initialize(Server $server) { |
|
73 | + |
|
74 | + $server->on('beforeMethod', [$this, 'beforeMethod'], 10); |
|
75 | + |
|
76 | + } |
|
77 | + |
|
78 | + /** |
|
79 | + * Returns a plugin name. |
|
80 | + * |
|
81 | + * Using this name other plugins will be able to access other plugins |
|
82 | + * using DAV\Server::getPlugin |
|
83 | + * |
|
84 | + * @return string |
|
85 | + */ |
|
86 | + public function getPluginName() { |
|
87 | + |
|
88 | + return 'auth'; |
|
89 | + |
|
90 | + } |
|
91 | + |
|
92 | + /** |
|
93 | + * Returns the currently logged-in principal. |
|
94 | + * |
|
95 | + * This will return a string such as: |
|
96 | + * |
|
97 | + * principals/username |
|
98 | + * principals/users/username |
|
99 | + * |
|
100 | + * This method will return null if nobody is logged in. |
|
101 | + * |
|
102 | + * @return string|null |
|
103 | + */ |
|
104 | + public function getCurrentPrincipal() { |
|
105 | + |
|
106 | + return $this->currentPrincipal; |
|
107 | + |
|
108 | + } |
|
109 | + |
|
110 | + /** |
|
111 | + * Returns the current username. |
|
112 | + * |
|
113 | + * This method is deprecated and is only kept for backwards compatibility |
|
114 | + * purposes. Please switch to getCurrentPrincipal(). |
|
115 | + * |
|
116 | + * @deprecated Will be removed in a future version! |
|
117 | + * @return string|null |
|
118 | + */ |
|
119 | + public function getCurrentUser() { |
|
120 | + |
|
121 | + // We just do a 'basename' on the principal to give back a sane value |
|
122 | + // here. |
|
123 | + list(, $userName) = URLUtil::splitPath( |
|
124 | + $this->getCurrentPrincipal() |
|
125 | + ); |
|
126 | + |
|
127 | + return $userName; |
|
128 | + |
|
129 | + } |
|
130 | + |
|
131 | + /** |
|
132 | + * This method is called before any HTTP method and forces users to be authenticated |
|
133 | + * |
|
134 | + * @param RequestInterface $request |
|
135 | + * @param ResponseInterface $response |
|
136 | + * @return bool |
|
137 | + */ |
|
138 | + public function beforeMethod(RequestInterface $request, ResponseInterface $response) { |
|
139 | + |
|
140 | + if ($this->currentPrincipal) { |
|
141 | + |
|
142 | + // We already have authentication information. This means that the |
|
143 | + // event has already fired earlier, and is now likely fired for a |
|
144 | + // sub-request. |
|
145 | + // |
|
146 | + // We don't want to authenticate users twice, so we simply don't do |
|
147 | + // anything here. See Issue #700 for additional reasoning. |
|
148 | + // |
|
149 | + // This is not a perfect solution, but will be fixed once the |
|
150 | + // "currently authenticated principal" is information that's not |
|
151 | + // not associated with the plugin, but rather per-request. |
|
152 | + // |
|
153 | + // See issue #580 for more information about that. |
|
154 | + return; |
|
155 | + |
|
156 | + } |
|
157 | + if (!$this->backends) { |
|
158 | + throw new \Sabre\DAV\Exception('No authentication backends were configured on this server.'); |
|
159 | + } |
|
160 | + $reasons = []; |
|
161 | + foreach ($this->backends as $backend) { |
|
162 | + |
|
163 | + $result = $backend->check( |
|
164 | + $request, |
|
165 | + $response |
|
166 | + ); |
|
167 | + |
|
168 | + if (!is_array($result) || count($result) !== 2 || !is_bool($result[0]) || !is_string($result[1])) { |
|
169 | + throw new \Sabre\DAV\Exception('The authentication backend did not return a correct value from the check() method.'); |
|
170 | + } |
|
171 | + |
|
172 | + if ($result[0]) { |
|
173 | + $this->currentPrincipal = $result[1]; |
|
174 | + // Exit early |
|
175 | + return; |
|
176 | + } |
|
177 | + $reasons[] = $result[1]; |
|
178 | + |
|
179 | + } |
|
180 | + |
|
181 | + // If we got here, it means that no authentication backend was |
|
182 | + // successful in authenticating the user. |
|
183 | + $this->currentPrincipal = null; |
|
184 | + |
|
185 | + foreach ($this->backends as $backend) { |
|
186 | + $backend->challenge($request, $response); |
|
187 | + } |
|
188 | + throw new NotAuthenticated(implode(', ', $reasons)); |
|
189 | + |
|
190 | + } |
|
191 | + |
|
192 | + /** |
|
193 | + * Returns a bunch of meta-data about the plugin. |
|
194 | + * |
|
195 | + * Providing this information is optional, and is mainly displayed by the |
|
196 | + * Browser plugin. |
|
197 | + * |
|
198 | + * The description key in the returned array may contain html and will not |
|
199 | + * be sanitized. |
|
200 | + * |
|
201 | + * @return array |
|
202 | + */ |
|
203 | + public function getPluginInfo() { |
|
204 | + |
|
205 | + return [ |
|
206 | + 'name' => $this->getPluginName(), |
|
207 | + 'description' => 'Generic authentication plugin', |
|
208 | + 'link' => 'http://sabre.io/dav/authentication/', |
|
209 | + ]; |
|
210 | + |
|
211 | + } |
|
212 | 212 | |
213 | 213 | } |
@@ -133,7 +133,7 @@ |
||
133 | 133 | * |
134 | 134 | * @param RequestInterface $request |
135 | 135 | * @param ResponseInterface $response |
136 | - * @return bool |
|
136 | + * @return boolean|null |
|
137 | 137 | */ |
138 | 138 | public function beforeMethod(RequestInterface $request, ResponseInterface $response) { |
139 | 139 |
@@ -2,10 +2,9 @@ |
||
2 | 2 | |
3 | 3 | namespace Sabre\DAV\Browser; |
4 | 4 | |
5 | -use Sabre\HTTP\URLUtil; |
|
6 | 5 | use Sabre\DAV; |
7 | 6 | use Sabre\DAV\PropFind; |
8 | -use Sabre\DAV\Inode; |
|
7 | +use Sabre\HTTP\URLUtil; |
|
9 | 8 | |
10 | 9 | /** |
11 | 10 | * GuessContentType plugin |
@@ -24,78 +24,78 @@ |
||
24 | 24 | */ |
25 | 25 | class GuessContentType extends DAV\ServerPlugin { |
26 | 26 | |
27 | - /** |
|
28 | - * List of recognized file extensions |
|
29 | - * |
|
30 | - * Feel free to add more |
|
31 | - * |
|
32 | - * @var array |
|
33 | - */ |
|
34 | - public $extensionMap = [ |
|
35 | - |
|
36 | - // images |
|
37 | - 'jpg' => 'image/jpeg', |
|
38 | - 'gif' => 'image/gif', |
|
39 | - 'png' => 'image/png', |
|
40 | - |
|
41 | - // groupware |
|
42 | - 'ics' => 'text/calendar', |
|
43 | - 'vcf' => 'text/vcard', |
|
44 | - |
|
45 | - // text |
|
46 | - 'txt' => 'text/plain', |
|
47 | - |
|
48 | - ]; |
|
49 | - |
|
50 | - /** |
|
51 | - * Initializes the plugin |
|
52 | - * |
|
53 | - * @param DAV\Server $server |
|
54 | - * @return void |
|
55 | - */ |
|
56 | - public function initialize(DAV\Server $server) { |
|
57 | - |
|
58 | - // Using a relatively low priority (200) to allow other extensions |
|
59 | - // to set the content-type first. |
|
60 | - $server->on('propFind', [$this, 'propFind'], 200); |
|
61 | - |
|
62 | - } |
|
63 | - |
|
64 | - /** |
|
65 | - * Our PROPFIND handler |
|
66 | - * |
|
67 | - * Here we set a contenttype, if the node didn't already have one. |
|
68 | - * |
|
69 | - * @param PropFind $propFind |
|
70 | - * @param INode $node |
|
71 | - * @return void |
|
72 | - */ |
|
73 | - public function propFind(PropFind $propFind, INode $node) { |
|
74 | - |
|
75 | - $propFind->handle('{DAV:}getcontenttype', function() use ($propFind) { |
|
76 | - |
|
77 | - list(, $fileName) = URLUtil::splitPath($propFind->getPath()); |
|
78 | - return $this->getContentType($fileName); |
|
79 | - |
|
80 | - }); |
|
81 | - |
|
82 | - } |
|
83 | - |
|
84 | - /** |
|
85 | - * Simple method to return the contenttype |
|
86 | - * |
|
87 | - * @param string $fileName |
|
88 | - * @return string |
|
89 | - */ |
|
90 | - protected function getContentType($fileName) { |
|
91 | - |
|
92 | - // Just grabbing the extension |
|
93 | - $extension = strtolower(substr($fileName, strrpos($fileName, '.') + 1)); |
|
94 | - if (isset($this->extensionMap[$extension])) { |
|
95 | - return $this->extensionMap[$extension]; |
|
96 | - } |
|
97 | - return 'application/octet-stream'; |
|
98 | - |
|
99 | - } |
|
27 | + /** |
|
28 | + * List of recognized file extensions |
|
29 | + * |
|
30 | + * Feel free to add more |
|
31 | + * |
|
32 | + * @var array |
|
33 | + */ |
|
34 | + public $extensionMap = [ |
|
35 | + |
|
36 | + // images |
|
37 | + 'jpg' => 'image/jpeg', |
|
38 | + 'gif' => 'image/gif', |
|
39 | + 'png' => 'image/png', |
|
40 | + |
|
41 | + // groupware |
|
42 | + 'ics' => 'text/calendar', |
|
43 | + 'vcf' => 'text/vcard', |
|
44 | + |
|
45 | + // text |
|
46 | + 'txt' => 'text/plain', |
|
47 | + |
|
48 | + ]; |
|
49 | + |
|
50 | + /** |
|
51 | + * Initializes the plugin |
|
52 | + * |
|
53 | + * @param DAV\Server $server |
|
54 | + * @return void |
|
55 | + */ |
|
56 | + public function initialize(DAV\Server $server) { |
|
57 | + |
|
58 | + // Using a relatively low priority (200) to allow other extensions |
|
59 | + // to set the content-type first. |
|
60 | + $server->on('propFind', [$this, 'propFind'], 200); |
|
61 | + |
|
62 | + } |
|
63 | + |
|
64 | + /** |
|
65 | + * Our PROPFIND handler |
|
66 | + * |
|
67 | + * Here we set a contenttype, if the node didn't already have one. |
|
68 | + * |
|
69 | + * @param PropFind $propFind |
|
70 | + * @param INode $node |
|
71 | + * @return void |
|
72 | + */ |
|
73 | + public function propFind(PropFind $propFind, INode $node) { |
|
74 | + |
|
75 | + $propFind->handle('{DAV:}getcontenttype', function() use ($propFind) { |
|
76 | + |
|
77 | + list(, $fileName) = URLUtil::splitPath($propFind->getPath()); |
|
78 | + return $this->getContentType($fileName); |
|
79 | + |
|
80 | + }); |
|
81 | + |
|
82 | + } |
|
83 | + |
|
84 | + /** |
|
85 | + * Simple method to return the contenttype |
|
86 | + * |
|
87 | + * @param string $fileName |
|
88 | + * @return string |
|
89 | + */ |
|
90 | + protected function getContentType($fileName) { |
|
91 | + |
|
92 | + // Just grabbing the extension |
|
93 | + $extension = strtolower(substr($fileName, strrpos($fileName, '.') + 1)); |
|
94 | + if (isset($this->extensionMap[$extension])) { |
|
95 | + return $this->extensionMap[$extension]; |
|
96 | + } |
|
97 | + return 'application/octet-stream'; |
|
98 | + |
|
99 | + } |
|
100 | 100 | |
101 | 101 | } |
@@ -67,7 +67,7 @@ |
||
67 | 67 | * Here we set a contenttype, if the node didn't already have one. |
68 | 68 | * |
69 | 69 | * @param PropFind $propFind |
70 | - * @param INode $node |
|
70 | + * @param DAV\INode $node |
|
71 | 71 | * @return void |
72 | 72 | */ |
73 | 73 | public function propFind(PropFind $propFind, INode $node) { |
@@ -4,9 +4,9 @@ |
||
4 | 4 | |
5 | 5 | use Sabre\DAV; |
6 | 6 | use Sabre\DAV\MkCol; |
7 | -use Sabre\HTTP\URLUtil; |
|
8 | 7 | use Sabre\HTTP\RequestInterface; |
9 | 8 | use Sabre\HTTP\ResponseInterface; |
9 | +use Sabre\HTTP\URLUtil; |
|
10 | 10 | |
11 | 11 | /** |
12 | 12 | * Browser Plugin |
@@ -317,7 +317,7 @@ discard block |
||
317 | 317 | |
318 | 318 | $buttonActions = ''; |
319 | 319 | if ($subProps['subNode'] instanceof DAV\IFile) { |
320 | - $buttonActions = '<a href="' . $this->escapeHTML($subProps['fullPath']) . '?sabreAction=info"><span class="oi" data-glyph="info"></span></a>'; |
|
320 | + $buttonActions = '<a href="' . $this->escapeHTML($subProps['fullPath']) . '?sabreAction=info"><span class="oi" data-glyph="info"></span></a>'; |
|
321 | 321 | } |
322 | 322 | $this->server->emit('browserButtonActions', [$subProps['fullPath'], $subProps['subNode'], &$buttonActions]); |
323 | 323 | |
@@ -450,7 +450,7 @@ discard block |
||
450 | 450 | HTML; |
451 | 451 | |
452 | 452 | // If the path is empty, there's no parent. |
453 | - if ($path) { |
|
453 | + if ($path) { |
|
454 | 454 | list($parentUri) = URLUtil::splitPath($path); |
455 | 455 | $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($parentUri); |
456 | 456 | $html .= '<a href="' . $fullPath . '" class="btn">⇤ Go to parent</a>'; |
@@ -23,413 +23,413 @@ discard block |
||
23 | 23 | */ |
24 | 24 | class Plugin extends DAV\ServerPlugin { |
25 | 25 | |
26 | - /** |
|
27 | - * reference to server class |
|
28 | - * |
|
29 | - * @var Sabre\DAV\Server |
|
30 | - */ |
|
31 | - protected $server; |
|
32 | - |
|
33 | - /** |
|
34 | - * enablePost turns on the 'actions' panel, which allows people to create |
|
35 | - * folders and upload files straight from a browser. |
|
36 | - * |
|
37 | - * @var bool |
|
38 | - */ |
|
39 | - protected $enablePost = true; |
|
40 | - |
|
41 | - /** |
|
42 | - * A list of properties that are usually not interesting. This can cut down |
|
43 | - * the browser output a bit by removing the properties that most people |
|
44 | - * will likely not want to see. |
|
45 | - * |
|
46 | - * @var array |
|
47 | - */ |
|
48 | - public $uninterestingProperties = [ |
|
49 | - '{DAV:}supportedlock', |
|
50 | - '{DAV:}acl-restrictions', |
|
51 | - '{DAV:}supported-privilege-set', |
|
52 | - '{DAV:}supported-method-set', |
|
53 | - ]; |
|
54 | - |
|
55 | - /** |
|
56 | - * Creates the object. |
|
57 | - * |
|
58 | - * By default it will allow file creation and uploads. |
|
59 | - * Specify the first argument as false to disable this |
|
60 | - * |
|
61 | - * @param bool $enablePost |
|
62 | - */ |
|
63 | - public function __construct($enablePost = true) { |
|
64 | - |
|
65 | - $this->enablePost = $enablePost; |
|
66 | - |
|
67 | - } |
|
68 | - |
|
69 | - /** |
|
70 | - * Initializes the plugin and subscribes to events |
|
71 | - * |
|
72 | - * @param DAV\Server $server |
|
73 | - * @return void |
|
74 | - */ |
|
75 | - public function initialize(DAV\Server $server) { |
|
76 | - |
|
77 | - $this->server = $server; |
|
78 | - $this->server->on('method:GET', [$this, 'httpGetEarly'], 90); |
|
79 | - $this->server->on('method:GET', [$this, 'httpGet'], 200); |
|
80 | - $this->server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel'], 200); |
|
81 | - if ($this->enablePost) $this->server->on('method:POST', [$this, 'httpPOST']); |
|
82 | - } |
|
83 | - |
|
84 | - /** |
|
85 | - * This method intercepts GET requests that have ?sabreAction=info |
|
86 | - * appended to the URL |
|
87 | - * |
|
88 | - * @param RequestInterface $request |
|
89 | - * @param ResponseInterface $response |
|
90 | - * @return bool |
|
91 | - */ |
|
92 | - public function httpGetEarly(RequestInterface $request, ResponseInterface $response) { |
|
93 | - |
|
94 | - $params = $request->getQueryParameters(); |
|
95 | - if (isset($params['sabreAction']) && $params['sabreAction'] === 'info') { |
|
96 | - return $this->httpGet($request, $response); |
|
97 | - } |
|
98 | - |
|
99 | - } |
|
100 | - |
|
101 | - /** |
|
102 | - * This method intercepts GET requests to collections and returns the html |
|
103 | - * |
|
104 | - * @param RequestInterface $request |
|
105 | - * @param ResponseInterface $response |
|
106 | - * @return bool |
|
107 | - */ |
|
108 | - public function httpGet(RequestInterface $request, ResponseInterface $response) { |
|
109 | - |
|
110 | - // We're not using straight-up $_GET, because we want everything to be |
|
111 | - // unit testable. |
|
112 | - $getVars = $request->getQueryParameters(); |
|
113 | - |
|
114 | - // CSP headers |
|
115 | - $this->server->httpResponse->setHeader('Content-Security-Policy', "img-src 'self'; style-src 'self';"); |
|
116 | - |
|
117 | - $sabreAction = isset($getVars['sabreAction']) ? $getVars['sabreAction'] : null; |
|
118 | - |
|
119 | - switch ($sabreAction) { |
|
120 | - |
|
121 | - case 'asset' : |
|
122 | - // Asset handling, such as images |
|
123 | - $this->serveAsset(isset($getVars['assetName']) ? $getVars['assetName'] : null); |
|
124 | - return false; |
|
125 | - default : |
|
126 | - case 'info' : |
|
127 | - try { |
|
128 | - $this->server->tree->getNodeForPath($request->getPath()); |
|
129 | - } catch (DAV\Exception\NotFound $e) { |
|
130 | - // We're simply stopping when the file isn't found to not interfere |
|
131 | - // with other plugins. |
|
132 | - return; |
|
133 | - } |
|
134 | - |
|
135 | - $response->setStatus(200); |
|
136 | - $response->setHeader('Content-Type', 'text/html; charset=utf-8'); |
|
137 | - |
|
138 | - $response->setBody( |
|
139 | - $this->generateDirectoryIndex($request->getPath()) |
|
140 | - ); |
|
141 | - |
|
142 | - return false; |
|
143 | - |
|
144 | - case 'plugins' : |
|
145 | - $response->setStatus(200); |
|
146 | - $response->setHeader('Content-Type', 'text/html; charset=utf-8'); |
|
147 | - |
|
148 | - $response->setBody( |
|
149 | - $this->generatePluginListing() |
|
150 | - ); |
|
151 | - |
|
152 | - return false; |
|
153 | - |
|
154 | - } |
|
155 | - |
|
156 | - } |
|
157 | - |
|
158 | - /** |
|
159 | - * Handles POST requests for tree operations. |
|
160 | - * |
|
161 | - * @param RequestInterface $request |
|
162 | - * @param ResponseInterface $response |
|
163 | - * @return bool |
|
164 | - */ |
|
165 | - public function httpPOST(RequestInterface $request, ResponseInterface $response) { |
|
166 | - |
|
167 | - $contentType = $request->getHeader('Content-Type'); |
|
168 | - list($contentType) = explode(';', $contentType); |
|
169 | - if ($contentType !== 'application/x-www-form-urlencoded' && |
|
170 | - $contentType !== 'multipart/form-data') { |
|
171 | - return; |
|
172 | - } |
|
173 | - $postVars = $request->getPostData(); |
|
174 | - |
|
175 | - if (!isset($postVars['sabreAction'])) |
|
176 | - return; |
|
177 | - |
|
178 | - $uri = $request->getPath(); |
|
179 | - |
|
180 | - if ($this->server->emit('onBrowserPostAction', [$uri, $postVars['sabreAction'], $postVars])) { |
|
181 | - |
|
182 | - switch ($postVars['sabreAction']) { |
|
183 | - |
|
184 | - case 'mkcol' : |
|
185 | - if (isset($postVars['name']) && trim($postVars['name'])) { |
|
186 | - // Using basename() because we won't allow slashes |
|
187 | - list(, $folderName) = URLUtil::splitPath(trim($postVars['name'])); |
|
188 | - |
|
189 | - if (isset($postVars['resourceType'])) { |
|
190 | - $resourceType = explode(',', $postVars['resourceType']); |
|
191 | - } else { |
|
192 | - $resourceType = ['{DAV:}collection']; |
|
193 | - } |
|
194 | - |
|
195 | - $properties = []; |
|
196 | - foreach ($postVars as $varName => $varValue) { |
|
197 | - // Any _POST variable in clark notation is treated |
|
198 | - // like a property. |
|
199 | - if ($varName[0] === '{') { |
|
200 | - // PHP will convert any dots to underscores. |
|
201 | - // This leaves us with no way to differentiate |
|
202 | - // the two. |
|
203 | - // Therefore we replace the string *DOT* with a |
|
204 | - // real dot. * is not allowed in uris so we |
|
205 | - // should be good. |
|
206 | - $varName = str_replace('*DOT*', '.', $varName); |
|
207 | - $properties[$varName] = $varValue; |
|
208 | - } |
|
209 | - } |
|
210 | - |
|
211 | - $mkCol = new MkCol( |
|
212 | - $resourceType, |
|
213 | - $properties |
|
214 | - ); |
|
215 | - $this->server->createCollection($uri . '/' . $folderName, $mkCol); |
|
216 | - } |
|
217 | - break; |
|
218 | - |
|
219 | - // @codeCoverageIgnoreStart |
|
220 | - case 'put' : |
|
221 | - |
|
222 | - if ($_FILES) $file = current($_FILES); |
|
223 | - else break; |
|
224 | - |
|
225 | - list(, $newName) = URLUtil::splitPath(trim($file['name'])); |
|
226 | - if (isset($postVars['name']) && trim($postVars['name'])) |
|
227 | - $newName = trim($postVars['name']); |
|
228 | - |
|
229 | - // Making sure we only have a 'basename' component |
|
230 | - list(, $newName) = URLUtil::splitPath($newName); |
|
231 | - |
|
232 | - if (is_uploaded_file($file['tmp_name'])) { |
|
233 | - $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'], 'r')); |
|
234 | - } |
|
235 | - break; |
|
236 | - // @codeCoverageIgnoreEnd |
|
237 | - |
|
238 | - } |
|
239 | - |
|
240 | - } |
|
241 | - $response->setHeader('Location', $request->getUrl()); |
|
242 | - $response->setStatus(302); |
|
243 | - return false; |
|
244 | - |
|
245 | - } |
|
246 | - |
|
247 | - /** |
|
248 | - * Escapes a string for html. |
|
249 | - * |
|
250 | - * @param string $value |
|
251 | - * @return string |
|
252 | - */ |
|
253 | - public function escapeHTML($value) { |
|
254 | - |
|
255 | - return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); |
|
256 | - |
|
257 | - } |
|
258 | - |
|
259 | - /** |
|
260 | - * Generates the html directory index for a given url |
|
261 | - * |
|
262 | - * @param string $path |
|
263 | - * @return string |
|
264 | - */ |
|
265 | - public function generateDirectoryIndex($path) { |
|
266 | - |
|
267 | - $html = $this->generateHeader($path ? $path : '/', $path); |
|
268 | - |
|
269 | - $node = $this->server->tree->getNodeForPath($path); |
|
270 | - if ($node instanceof DAV\ICollection) { |
|
271 | - |
|
272 | - $html .= "<section><h1>Nodes</h1>\n"; |
|
273 | - $html .= "<table class=\"nodeTable\">"; |
|
274 | - |
|
275 | - $subNodes = $this->server->getPropertiesForChildren($path, [ |
|
276 | - '{DAV:}displayname', |
|
277 | - '{DAV:}resourcetype', |
|
278 | - '{DAV:}getcontenttype', |
|
279 | - '{DAV:}getcontentlength', |
|
280 | - '{DAV:}getlastmodified', |
|
281 | - ]); |
|
282 | - |
|
283 | - foreach ($subNodes as $subPath => $subProps) { |
|
284 | - |
|
285 | - $subNode = $this->server->tree->getNodeForPath($subPath); |
|
286 | - $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($subPath); |
|
287 | - list(, $displayPath) = URLUtil::splitPath($subPath); |
|
288 | - |
|
289 | - $subNodes[$subPath]['subNode'] = $subNode; |
|
290 | - $subNodes[$subPath]['fullPath'] = $fullPath; |
|
291 | - $subNodes[$subPath]['displayPath'] = $displayPath; |
|
292 | - } |
|
293 | - uasort($subNodes, [$this, 'compareNodes']); |
|
294 | - |
|
295 | - foreach ($subNodes as $subProps) { |
|
296 | - $type = [ |
|
297 | - 'string' => 'Unknown', |
|
298 | - 'icon' => 'cog', |
|
299 | - ]; |
|
300 | - if (isset($subProps['{DAV:}resourcetype'])) { |
|
301 | - $type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']); |
|
302 | - } |
|
303 | - |
|
304 | - $html .= '<tr>'; |
|
305 | - $html .= '<td class="nameColumn"><a href="' . $this->escapeHTML($subProps['fullPath']) . '"><span class="oi" data-glyph="' . $this->escapeHTML($type['icon']) . '"></span> ' . $this->escapeHTML($subProps['displayPath']) . '</a></td>'; |
|
306 | - $html .= '<td class="typeColumn">' . $this->escapeHTML($type['string']) . '</td>'; |
|
307 | - $html .= '<td>'; |
|
308 | - if (isset($subProps['{DAV:}getcontentlength'])) { |
|
309 | - $html .= $this->escapeHTML($subProps['{DAV:}getcontentlength'] . ' bytes'); |
|
310 | - } |
|
311 | - $html .= '</td><td>'; |
|
312 | - if (isset($subProps['{DAV:}getlastmodified'])) { |
|
313 | - $lastMod = $subProps['{DAV:}getlastmodified']->getTime(); |
|
314 | - $html .= $this->escapeHTML($lastMod->format('F j, Y, g:i a')); |
|
315 | - } |
|
316 | - $html .= '</td>'; |
|
317 | - |
|
318 | - $buttonActions = ''; |
|
319 | - if ($subProps['subNode'] instanceof DAV\IFile) { |
|
320 | - $buttonActions = '<a href="' . $this->escapeHTML($subProps['fullPath']) . '?sabreAction=info"><span class="oi" data-glyph="info"></span></a>'; |
|
321 | - } |
|
322 | - $this->server->emit('browserButtonActions', [$subProps['fullPath'], $subProps['subNode'], &$buttonActions]); |
|
323 | - |
|
324 | - $html .= '<td>' . $buttonActions . '</td>'; |
|
325 | - $html .= '</tr>'; |
|
326 | - } |
|
327 | - |
|
328 | - $html .= '</table>'; |
|
329 | - |
|
330 | - } |
|
331 | - |
|
332 | - $html .= "</section>"; |
|
333 | - $html .= "<section><h1>Properties</h1>"; |
|
334 | - $html .= "<table class=\"propTable\">"; |
|
335 | - |
|
336 | - // Allprops request |
|
337 | - $propFind = new PropFindAll($path); |
|
338 | - $properties = $this->server->getPropertiesByNode($propFind, $node); |
|
339 | - |
|
340 | - $properties = $propFind->getResultForMultiStatus()[200]; |
|
341 | - |
|
342 | - foreach ($properties as $propName => $propValue) { |
|
343 | - if (!in_array($propName, $this->uninterestingProperties)) { |
|
344 | - $html .= $this->drawPropertyRow($propName, $propValue); |
|
345 | - } |
|
346 | - |
|
347 | - } |
|
348 | - |
|
349 | - |
|
350 | - $html .= "</table>"; |
|
351 | - $html .= "</section>"; |
|
352 | - |
|
353 | - /* Start of generating actions */ |
|
354 | - |
|
355 | - $output = ''; |
|
356 | - if ($this->enablePost) { |
|
357 | - $this->server->emit('onHTMLActionsPanel', [$node, &$output]); |
|
358 | - } |
|
359 | - |
|
360 | - if ($output) { |
|
361 | - |
|
362 | - $html .= "<section><h1>Actions</h1>"; |
|
363 | - $html .= "<div class=\"actions\">\n"; |
|
364 | - $html .= $output; |
|
365 | - $html .= "</div>\n"; |
|
366 | - $html .= "</section>\n"; |
|
367 | - } |
|
368 | - |
|
369 | - $html .= $this->generateFooter(); |
|
370 | - |
|
371 | - $this->server->httpResponse->setHeader('Content-Security-Policy', "img-src 'self'; style-src 'self';"); |
|
372 | - |
|
373 | - return $html; |
|
374 | - |
|
375 | - } |
|
376 | - |
|
377 | - /** |
|
378 | - * Generates the 'plugins' page. |
|
379 | - * |
|
380 | - * @return string |
|
381 | - */ |
|
382 | - public function generatePluginListing() { |
|
383 | - |
|
384 | - $html = $this->generateHeader('Plugins'); |
|
385 | - |
|
386 | - $html .= "<section><h1>Plugins</h1>"; |
|
387 | - $html .= "<table class=\"propTable\">"; |
|
388 | - foreach ($this->server->getPlugins() as $plugin) { |
|
389 | - $info = $plugin->getPluginInfo(); |
|
390 | - $html .= '<tr><th>' . $info['name'] . '</th>'; |
|
391 | - $html .= '<td>' . $info['description'] . '</td>'; |
|
392 | - $html .= '<td>'; |
|
393 | - if (isset($info['link']) && $info['link']) { |
|
394 | - $html .= '<a href="' . $this->escapeHTML($info['link']) . '"><span class="oi" data-glyph="book"></span></a>'; |
|
395 | - } |
|
396 | - $html .= '</td></tr>'; |
|
397 | - } |
|
398 | - $html .= "</table>"; |
|
399 | - $html .= "</section>"; |
|
400 | - |
|
401 | - /* Start of generating actions */ |
|
402 | - |
|
403 | - $html .= $this->generateFooter(); |
|
404 | - |
|
405 | - return $html; |
|
406 | - |
|
407 | - } |
|
408 | - |
|
409 | - /** |
|
410 | - * Generates the first block of HTML, including the <head> tag and page |
|
411 | - * header. |
|
412 | - * |
|
413 | - * Returns footer. |
|
414 | - * |
|
415 | - * @param string $title |
|
416 | - * @param string $path |
|
417 | - * @return void |
|
418 | - */ |
|
419 | - public function generateHeader($title, $path = null) { |
|
26 | + /** |
|
27 | + * reference to server class |
|
28 | + * |
|
29 | + * @var Sabre\DAV\Server |
|
30 | + */ |
|
31 | + protected $server; |
|
32 | + |
|
33 | + /** |
|
34 | + * enablePost turns on the 'actions' panel, which allows people to create |
|
35 | + * folders and upload files straight from a browser. |
|
36 | + * |
|
37 | + * @var bool |
|
38 | + */ |
|
39 | + protected $enablePost = true; |
|
40 | + |
|
41 | + /** |
|
42 | + * A list of properties that are usually not interesting. This can cut down |
|
43 | + * the browser output a bit by removing the properties that most people |
|
44 | + * will likely not want to see. |
|
45 | + * |
|
46 | + * @var array |
|
47 | + */ |
|
48 | + public $uninterestingProperties = [ |
|
49 | + '{DAV:}supportedlock', |
|
50 | + '{DAV:}acl-restrictions', |
|
51 | + '{DAV:}supported-privilege-set', |
|
52 | + '{DAV:}supported-method-set', |
|
53 | + ]; |
|
54 | + |
|
55 | + /** |
|
56 | + * Creates the object. |
|
57 | + * |
|
58 | + * By default it will allow file creation and uploads. |
|
59 | + * Specify the first argument as false to disable this |
|
60 | + * |
|
61 | + * @param bool $enablePost |
|
62 | + */ |
|
63 | + public function __construct($enablePost = true) { |
|
64 | + |
|
65 | + $this->enablePost = $enablePost; |
|
66 | + |
|
67 | + } |
|
68 | + |
|
69 | + /** |
|
70 | + * Initializes the plugin and subscribes to events |
|
71 | + * |
|
72 | + * @param DAV\Server $server |
|
73 | + * @return void |
|
74 | + */ |
|
75 | + public function initialize(DAV\Server $server) { |
|
76 | + |
|
77 | + $this->server = $server; |
|
78 | + $this->server->on('method:GET', [$this, 'httpGetEarly'], 90); |
|
79 | + $this->server->on('method:GET', [$this, 'httpGet'], 200); |
|
80 | + $this->server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel'], 200); |
|
81 | + if ($this->enablePost) $this->server->on('method:POST', [$this, 'httpPOST']); |
|
82 | + } |
|
83 | + |
|
84 | + /** |
|
85 | + * This method intercepts GET requests that have ?sabreAction=info |
|
86 | + * appended to the URL |
|
87 | + * |
|
88 | + * @param RequestInterface $request |
|
89 | + * @param ResponseInterface $response |
|
90 | + * @return bool |
|
91 | + */ |
|
92 | + public function httpGetEarly(RequestInterface $request, ResponseInterface $response) { |
|
93 | + |
|
94 | + $params = $request->getQueryParameters(); |
|
95 | + if (isset($params['sabreAction']) && $params['sabreAction'] === 'info') { |
|
96 | + return $this->httpGet($request, $response); |
|
97 | + } |
|
98 | + |
|
99 | + } |
|
100 | + |
|
101 | + /** |
|
102 | + * This method intercepts GET requests to collections and returns the html |
|
103 | + * |
|
104 | + * @param RequestInterface $request |
|
105 | + * @param ResponseInterface $response |
|
106 | + * @return bool |
|
107 | + */ |
|
108 | + public function httpGet(RequestInterface $request, ResponseInterface $response) { |
|
109 | + |
|
110 | + // We're not using straight-up $_GET, because we want everything to be |
|
111 | + // unit testable. |
|
112 | + $getVars = $request->getQueryParameters(); |
|
113 | + |
|
114 | + // CSP headers |
|
115 | + $this->server->httpResponse->setHeader('Content-Security-Policy', "img-src 'self'; style-src 'self';"); |
|
116 | + |
|
117 | + $sabreAction = isset($getVars['sabreAction']) ? $getVars['sabreAction'] : null; |
|
118 | + |
|
119 | + switch ($sabreAction) { |
|
120 | + |
|
121 | + case 'asset' : |
|
122 | + // Asset handling, such as images |
|
123 | + $this->serveAsset(isset($getVars['assetName']) ? $getVars['assetName'] : null); |
|
124 | + return false; |
|
125 | + default : |
|
126 | + case 'info' : |
|
127 | + try { |
|
128 | + $this->server->tree->getNodeForPath($request->getPath()); |
|
129 | + } catch (DAV\Exception\NotFound $e) { |
|
130 | + // We're simply stopping when the file isn't found to not interfere |
|
131 | + // with other plugins. |
|
132 | + return; |
|
133 | + } |
|
134 | + |
|
135 | + $response->setStatus(200); |
|
136 | + $response->setHeader('Content-Type', 'text/html; charset=utf-8'); |
|
137 | + |
|
138 | + $response->setBody( |
|
139 | + $this->generateDirectoryIndex($request->getPath()) |
|
140 | + ); |
|
141 | + |
|
142 | + return false; |
|
143 | + |
|
144 | + case 'plugins' : |
|
145 | + $response->setStatus(200); |
|
146 | + $response->setHeader('Content-Type', 'text/html; charset=utf-8'); |
|
147 | + |
|
148 | + $response->setBody( |
|
149 | + $this->generatePluginListing() |
|
150 | + ); |
|
151 | + |
|
152 | + return false; |
|
153 | + |
|
154 | + } |
|
155 | + |
|
156 | + } |
|
157 | + |
|
158 | + /** |
|
159 | + * Handles POST requests for tree operations. |
|
160 | + * |
|
161 | + * @param RequestInterface $request |
|
162 | + * @param ResponseInterface $response |
|
163 | + * @return bool |
|
164 | + */ |
|
165 | + public function httpPOST(RequestInterface $request, ResponseInterface $response) { |
|
166 | + |
|
167 | + $contentType = $request->getHeader('Content-Type'); |
|
168 | + list($contentType) = explode(';', $contentType); |
|
169 | + if ($contentType !== 'application/x-www-form-urlencoded' && |
|
170 | + $contentType !== 'multipart/form-data') { |
|
171 | + return; |
|
172 | + } |
|
173 | + $postVars = $request->getPostData(); |
|
174 | + |
|
175 | + if (!isset($postVars['sabreAction'])) |
|
176 | + return; |
|
177 | + |
|
178 | + $uri = $request->getPath(); |
|
179 | + |
|
180 | + if ($this->server->emit('onBrowserPostAction', [$uri, $postVars['sabreAction'], $postVars])) { |
|
181 | + |
|
182 | + switch ($postVars['sabreAction']) { |
|
183 | + |
|
184 | + case 'mkcol' : |
|
185 | + if (isset($postVars['name']) && trim($postVars['name'])) { |
|
186 | + // Using basename() because we won't allow slashes |
|
187 | + list(, $folderName) = URLUtil::splitPath(trim($postVars['name'])); |
|
188 | + |
|
189 | + if (isset($postVars['resourceType'])) { |
|
190 | + $resourceType = explode(',', $postVars['resourceType']); |
|
191 | + } else { |
|
192 | + $resourceType = ['{DAV:}collection']; |
|
193 | + } |
|
194 | + |
|
195 | + $properties = []; |
|
196 | + foreach ($postVars as $varName => $varValue) { |
|
197 | + // Any _POST variable in clark notation is treated |
|
198 | + // like a property. |
|
199 | + if ($varName[0] === '{') { |
|
200 | + // PHP will convert any dots to underscores. |
|
201 | + // This leaves us with no way to differentiate |
|
202 | + // the two. |
|
203 | + // Therefore we replace the string *DOT* with a |
|
204 | + // real dot. * is not allowed in uris so we |
|
205 | + // should be good. |
|
206 | + $varName = str_replace('*DOT*', '.', $varName); |
|
207 | + $properties[$varName] = $varValue; |
|
208 | + } |
|
209 | + } |
|
210 | + |
|
211 | + $mkCol = new MkCol( |
|
212 | + $resourceType, |
|
213 | + $properties |
|
214 | + ); |
|
215 | + $this->server->createCollection($uri . '/' . $folderName, $mkCol); |
|
216 | + } |
|
217 | + break; |
|
218 | + |
|
219 | + // @codeCoverageIgnoreStart |
|
220 | + case 'put' : |
|
221 | + |
|
222 | + if ($_FILES) $file = current($_FILES); |
|
223 | + else break; |
|
224 | + |
|
225 | + list(, $newName) = URLUtil::splitPath(trim($file['name'])); |
|
226 | + if (isset($postVars['name']) && trim($postVars['name'])) |
|
227 | + $newName = trim($postVars['name']); |
|
228 | + |
|
229 | + // Making sure we only have a 'basename' component |
|
230 | + list(, $newName) = URLUtil::splitPath($newName); |
|
231 | + |
|
232 | + if (is_uploaded_file($file['tmp_name'])) { |
|
233 | + $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'], 'r')); |
|
234 | + } |
|
235 | + break; |
|
236 | + // @codeCoverageIgnoreEnd |
|
237 | + |
|
238 | + } |
|
239 | + |
|
240 | + } |
|
241 | + $response->setHeader('Location', $request->getUrl()); |
|
242 | + $response->setStatus(302); |
|
243 | + return false; |
|
244 | + |
|
245 | + } |
|
246 | + |
|
247 | + /** |
|
248 | + * Escapes a string for html. |
|
249 | + * |
|
250 | + * @param string $value |
|
251 | + * @return string |
|
252 | + */ |
|
253 | + public function escapeHTML($value) { |
|
254 | + |
|
255 | + return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); |
|
256 | + |
|
257 | + } |
|
258 | + |
|
259 | + /** |
|
260 | + * Generates the html directory index for a given url |
|
261 | + * |
|
262 | + * @param string $path |
|
263 | + * @return string |
|
264 | + */ |
|
265 | + public function generateDirectoryIndex($path) { |
|
266 | + |
|
267 | + $html = $this->generateHeader($path ? $path : '/', $path); |
|
268 | + |
|
269 | + $node = $this->server->tree->getNodeForPath($path); |
|
270 | + if ($node instanceof DAV\ICollection) { |
|
271 | + |
|
272 | + $html .= "<section><h1>Nodes</h1>\n"; |
|
273 | + $html .= "<table class=\"nodeTable\">"; |
|
274 | + |
|
275 | + $subNodes = $this->server->getPropertiesForChildren($path, [ |
|
276 | + '{DAV:}displayname', |
|
277 | + '{DAV:}resourcetype', |
|
278 | + '{DAV:}getcontenttype', |
|
279 | + '{DAV:}getcontentlength', |
|
280 | + '{DAV:}getlastmodified', |
|
281 | + ]); |
|
282 | + |
|
283 | + foreach ($subNodes as $subPath => $subProps) { |
|
284 | + |
|
285 | + $subNode = $this->server->tree->getNodeForPath($subPath); |
|
286 | + $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($subPath); |
|
287 | + list(, $displayPath) = URLUtil::splitPath($subPath); |
|
288 | + |
|
289 | + $subNodes[$subPath]['subNode'] = $subNode; |
|
290 | + $subNodes[$subPath]['fullPath'] = $fullPath; |
|
291 | + $subNodes[$subPath]['displayPath'] = $displayPath; |
|
292 | + } |
|
293 | + uasort($subNodes, [$this, 'compareNodes']); |
|
294 | + |
|
295 | + foreach ($subNodes as $subProps) { |
|
296 | + $type = [ |
|
297 | + 'string' => 'Unknown', |
|
298 | + 'icon' => 'cog', |
|
299 | + ]; |
|
300 | + if (isset($subProps['{DAV:}resourcetype'])) { |
|
301 | + $type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']); |
|
302 | + } |
|
303 | + |
|
304 | + $html .= '<tr>'; |
|
305 | + $html .= '<td class="nameColumn"><a href="' . $this->escapeHTML($subProps['fullPath']) . '"><span class="oi" data-glyph="' . $this->escapeHTML($type['icon']) . '"></span> ' . $this->escapeHTML($subProps['displayPath']) . '</a></td>'; |
|
306 | + $html .= '<td class="typeColumn">' . $this->escapeHTML($type['string']) . '</td>'; |
|
307 | + $html .= '<td>'; |
|
308 | + if (isset($subProps['{DAV:}getcontentlength'])) { |
|
309 | + $html .= $this->escapeHTML($subProps['{DAV:}getcontentlength'] . ' bytes'); |
|
310 | + } |
|
311 | + $html .= '</td><td>'; |
|
312 | + if (isset($subProps['{DAV:}getlastmodified'])) { |
|
313 | + $lastMod = $subProps['{DAV:}getlastmodified']->getTime(); |
|
314 | + $html .= $this->escapeHTML($lastMod->format('F j, Y, g:i a')); |
|
315 | + } |
|
316 | + $html .= '</td>'; |
|
317 | + |
|
318 | + $buttonActions = ''; |
|
319 | + if ($subProps['subNode'] instanceof DAV\IFile) { |
|
320 | + $buttonActions = '<a href="' . $this->escapeHTML($subProps['fullPath']) . '?sabreAction=info"><span class="oi" data-glyph="info"></span></a>'; |
|
321 | + } |
|
322 | + $this->server->emit('browserButtonActions', [$subProps['fullPath'], $subProps['subNode'], &$buttonActions]); |
|
323 | + |
|
324 | + $html .= '<td>' . $buttonActions . '</td>'; |
|
325 | + $html .= '</tr>'; |
|
326 | + } |
|
327 | + |
|
328 | + $html .= '</table>'; |
|
329 | + |
|
330 | + } |
|
331 | + |
|
332 | + $html .= "</section>"; |
|
333 | + $html .= "<section><h1>Properties</h1>"; |
|
334 | + $html .= "<table class=\"propTable\">"; |
|
335 | + |
|
336 | + // Allprops request |
|
337 | + $propFind = new PropFindAll($path); |
|
338 | + $properties = $this->server->getPropertiesByNode($propFind, $node); |
|
339 | + |
|
340 | + $properties = $propFind->getResultForMultiStatus()[200]; |
|
341 | + |
|
342 | + foreach ($properties as $propName => $propValue) { |
|
343 | + if (!in_array($propName, $this->uninterestingProperties)) { |
|
344 | + $html .= $this->drawPropertyRow($propName, $propValue); |
|
345 | + } |
|
346 | + |
|
347 | + } |
|
348 | + |
|
349 | + |
|
350 | + $html .= "</table>"; |
|
351 | + $html .= "</section>"; |
|
352 | + |
|
353 | + /* Start of generating actions */ |
|
354 | + |
|
355 | + $output = ''; |
|
356 | + if ($this->enablePost) { |
|
357 | + $this->server->emit('onHTMLActionsPanel', [$node, &$output]); |
|
358 | + } |
|
359 | + |
|
360 | + if ($output) { |
|
361 | + |
|
362 | + $html .= "<section><h1>Actions</h1>"; |
|
363 | + $html .= "<div class=\"actions\">\n"; |
|
364 | + $html .= $output; |
|
365 | + $html .= "</div>\n"; |
|
366 | + $html .= "</section>\n"; |
|
367 | + } |
|
368 | + |
|
369 | + $html .= $this->generateFooter(); |
|
370 | + |
|
371 | + $this->server->httpResponse->setHeader('Content-Security-Policy', "img-src 'self'; style-src 'self';"); |
|
372 | + |
|
373 | + return $html; |
|
374 | + |
|
375 | + } |
|
376 | + |
|
377 | + /** |
|
378 | + * Generates the 'plugins' page. |
|
379 | + * |
|
380 | + * @return string |
|
381 | + */ |
|
382 | + public function generatePluginListing() { |
|
383 | + |
|
384 | + $html = $this->generateHeader('Plugins'); |
|
385 | + |
|
386 | + $html .= "<section><h1>Plugins</h1>"; |
|
387 | + $html .= "<table class=\"propTable\">"; |
|
388 | + foreach ($this->server->getPlugins() as $plugin) { |
|
389 | + $info = $plugin->getPluginInfo(); |
|
390 | + $html .= '<tr><th>' . $info['name'] . '</th>'; |
|
391 | + $html .= '<td>' . $info['description'] . '</td>'; |
|
392 | + $html .= '<td>'; |
|
393 | + if (isset($info['link']) && $info['link']) { |
|
394 | + $html .= '<a href="' . $this->escapeHTML($info['link']) . '"><span class="oi" data-glyph="book"></span></a>'; |
|
395 | + } |
|
396 | + $html .= '</td></tr>'; |
|
397 | + } |
|
398 | + $html .= "</table>"; |
|
399 | + $html .= "</section>"; |
|
400 | + |
|
401 | + /* Start of generating actions */ |
|
402 | + |
|
403 | + $html .= $this->generateFooter(); |
|
404 | + |
|
405 | + return $html; |
|
406 | + |
|
407 | + } |
|
408 | + |
|
409 | + /** |
|
410 | + * Generates the first block of HTML, including the <head> tag and page |
|
411 | + * header. |
|
412 | + * |
|
413 | + * Returns footer. |
|
414 | + * |
|
415 | + * @param string $title |
|
416 | + * @param string $path |
|
417 | + * @return void |
|
418 | + */ |
|
419 | + public function generateHeader($title, $path = null) { |
|
420 | 420 | |
421 | - $version = DAV\Version::VERSION; |
|
421 | + $version = DAV\Version::VERSION; |
|
422 | 422 | |
423 | - $vars = [ |
|
424 | - 'title' => $this->escapeHTML($title), |
|
425 | - 'favicon' => $this->escapeHTML($this->getAssetUrl('favicon.ico')), |
|
426 | - 'style' => $this->escapeHTML($this->getAssetUrl('sabredav.css')), |
|
427 | - 'iconstyle' => $this->escapeHTML($this->getAssetUrl('openiconic/open-iconic.css')), |
|
428 | - 'logo' => $this->escapeHTML($this->getAssetUrl('sabredav.png')), |
|
429 | - 'baseUrl' => $this->server->getBaseUri(), |
|
430 | - ]; |
|
423 | + $vars = [ |
|
424 | + 'title' => $this->escapeHTML($title), |
|
425 | + 'favicon' => $this->escapeHTML($this->getAssetUrl('favicon.ico')), |
|
426 | + 'style' => $this->escapeHTML($this->getAssetUrl('sabredav.css')), |
|
427 | + 'iconstyle' => $this->escapeHTML($this->getAssetUrl('openiconic/open-iconic.css')), |
|
428 | + 'logo' => $this->escapeHTML($this->getAssetUrl('sabredav.png')), |
|
429 | + 'baseUrl' => $this->server->getBaseUri(), |
|
430 | + ]; |
|
431 | 431 | |
432 | - $html = <<<HTML |
|
432 | + $html = <<<HTML |
|
433 | 433 | <!DOCTYPE html> |
434 | 434 | <html> |
435 | 435 | <head> |
@@ -449,64 +449,64 @@ discard block |
||
449 | 449 | <nav> |
450 | 450 | HTML; |
451 | 451 | |
452 | - // If the path is empty, there's no parent. |
|
453 | - if ($path) { |
|
454 | - list($parentUri) = URLUtil::splitPath($path); |
|
455 | - $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($parentUri); |
|
456 | - $html .= '<a href="' . $fullPath . '" class="btn">⇤ Go to parent</a>'; |
|
457 | - } else { |
|
458 | - $html .= '<span class="btn disabled">⇤ Go to parent</span>'; |
|
459 | - } |
|
452 | + // If the path is empty, there's no parent. |
|
453 | + if ($path) { |
|
454 | + list($parentUri) = URLUtil::splitPath($path); |
|
455 | + $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($parentUri); |
|
456 | + $html .= '<a href="' . $fullPath . '" class="btn">⇤ Go to parent</a>'; |
|
457 | + } else { |
|
458 | + $html .= '<span class="btn disabled">⇤ Go to parent</span>'; |
|
459 | + } |
|
460 | 460 | |
461 | - $html .= ' <a href="?sabreAction=plugins" class="btn"><span class="oi" data-glyph="puzzle-piece"></span> Plugins</a>'; |
|
461 | + $html .= ' <a href="?sabreAction=plugins" class="btn"><span class="oi" data-glyph="puzzle-piece"></span> Plugins</a>'; |
|
462 | 462 | |
463 | - $html .= "</nav>"; |
|
463 | + $html .= "</nav>"; |
|
464 | 464 | |
465 | - return $html; |
|
465 | + return $html; |
|
466 | 466 | |
467 | - } |
|
467 | + } |
|
468 | 468 | |
469 | - /** |
|
470 | - * Generates the page footer. |
|
471 | - * |
|
472 | - * Returns html. |
|
473 | - * |
|
474 | - * @return string |
|
475 | - */ |
|
476 | - public function generateFooter() { |
|
469 | + /** |
|
470 | + * Generates the page footer. |
|
471 | + * |
|
472 | + * Returns html. |
|
473 | + * |
|
474 | + * @return string |
|
475 | + */ |
|
476 | + public function generateFooter() { |
|
477 | 477 | |
478 | - $version = DAV\Version::VERSION; |
|
479 | - return <<<HTML |
|
478 | + $version = DAV\Version::VERSION; |
|
479 | + return <<<HTML |
|
480 | 480 | <footer>Generated by SabreDAV $version (c)2007-2015 <a href="http://sabre.io/">http://sabre.io/</a></footer> |
481 | 481 | </body> |
482 | 482 | </html> |
483 | 483 | HTML; |
484 | 484 | |
485 | - } |
|
486 | - |
|
487 | - /** |
|
488 | - * This method is used to generate the 'actions panel' output for |
|
489 | - * collections. |
|
490 | - * |
|
491 | - * This specifically generates the interfaces for creating new files, and |
|
492 | - * creating new directories. |
|
493 | - * |
|
494 | - * @param DAV\INode $node |
|
495 | - * @param mixed $output |
|
496 | - * @return void |
|
497 | - */ |
|
498 | - public function htmlActionsPanel(DAV\INode $node, &$output) { |
|
499 | - |
|
500 | - if (!$node instanceof DAV\ICollection) |
|
501 | - return; |
|
502 | - |
|
503 | - // We also know fairly certain that if an object is a non-extended |
|
504 | - // SimpleCollection, we won't need to show the panel either. |
|
505 | - if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') |
|
506 | - return; |
|
507 | - |
|
508 | - ob_start(); |
|
509 | - echo '<form method="post" action=""> |
|
485 | + } |
|
486 | + |
|
487 | + /** |
|
488 | + * This method is used to generate the 'actions panel' output for |
|
489 | + * collections. |
|
490 | + * |
|
491 | + * This specifically generates the interfaces for creating new files, and |
|
492 | + * creating new directories. |
|
493 | + * |
|
494 | + * @param DAV\INode $node |
|
495 | + * @param mixed $output |
|
496 | + * @return void |
|
497 | + */ |
|
498 | + public function htmlActionsPanel(DAV\INode $node, &$output) { |
|
499 | + |
|
500 | + if (!$node instanceof DAV\ICollection) |
|
501 | + return; |
|
502 | + |
|
503 | + // We also know fairly certain that if an object is a non-extended |
|
504 | + // SimpleCollection, we won't need to show the panel either. |
|
505 | + if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') |
|
506 | + return; |
|
507 | + |
|
508 | + ob_start(); |
|
509 | + echo '<form method="post" action=""> |
|
510 | 510 | <h3>Create new folder</h3> |
511 | 511 | <input type="hidden" name="sabreAction" value="mkcol" /> |
512 | 512 | <label>Name:</label> <input type="text" name="name" /><br /> |
@@ -521,277 +521,277 @@ discard block |
||
521 | 521 | </form> |
522 | 522 | '; |
523 | 523 | |
524 | - $output .= ob_get_clean(); |
|
525 | - |
|
526 | - } |
|
527 | - |
|
528 | - /** |
|
529 | - * This method takes a path/name of an asset and turns it into url |
|
530 | - * suiteable for http access. |
|
531 | - * |
|
532 | - * @param string $assetName |
|
533 | - * @return string |
|
534 | - */ |
|
535 | - protected function getAssetUrl($assetName) { |
|
536 | - |
|
537 | - return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); |
|
538 | - |
|
539 | - } |
|
540 | - |
|
541 | - /** |
|
542 | - * This method returns a local pathname to an asset. |
|
543 | - * |
|
544 | - * @param string $assetName |
|
545 | - * @return string |
|
546 | - * @throws DAV\Exception\NotFound |
|
547 | - */ |
|
548 | - protected function getLocalAssetPath($assetName) { |
|
549 | - |
|
550 | - $assetDir = __DIR__ . '/assets/'; |
|
551 | - $path = $assetDir . $assetName; |
|
552 | - |
|
553 | - // Making sure people aren't trying to escape from the base path. |
|
554 | - $path = str_replace('\\', '/', $path); |
|
555 | - if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') { |
|
556 | - throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected'); |
|
557 | - } |
|
558 | - if (strpos(realpath($path), realpath($assetDir)) === 0 && file_exists($path)) { |
|
559 | - return $path; |
|
560 | - } |
|
561 | - throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected'); |
|
562 | - } |
|
563 | - |
|
564 | - /** |
|
565 | - * This method reads an asset from disk and generates a full http response. |
|
566 | - * |
|
567 | - * @param string $assetName |
|
568 | - * @return void |
|
569 | - */ |
|
570 | - protected function serveAsset($assetName) { |
|
571 | - |
|
572 | - $assetPath = $this->getLocalAssetPath($assetName); |
|
573 | - |
|
574 | - // Rudimentary mime type detection |
|
575 | - $mime = 'application/octet-stream'; |
|
576 | - $map = [ |
|
577 | - 'ico' => 'image/vnd.microsoft.icon', |
|
578 | - 'png' => 'image/png', |
|
579 | - 'css' => 'text/css', |
|
580 | - ]; |
|
581 | - |
|
582 | - $ext = substr($assetName, strrpos($assetName, '.') + 1); |
|
583 | - if (isset($map[$ext])) { |
|
584 | - $mime = $map[$ext]; |
|
585 | - } |
|
586 | - |
|
587 | - $this->server->httpResponse->setHeader('Content-Type', $mime); |
|
588 | - $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath)); |
|
589 | - $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600'); |
|
590 | - $this->server->httpResponse->setStatus(200); |
|
591 | - $this->server->httpResponse->setBody(fopen($assetPath, 'r')); |
|
592 | - |
|
593 | - } |
|
594 | - |
|
595 | - /** |
|
596 | - * Sort helper function: compares two directory entries based on type and |
|
597 | - * display name. Collections sort above other types. |
|
598 | - * |
|
599 | - * @param array $a |
|
600 | - * @param array $b |
|
601 | - * @return int |
|
602 | - */ |
|
603 | - protected function compareNodes($a, $b) { |
|
604 | - |
|
605 | - $typeA = (isset($a['{DAV:}resourcetype'])) |
|
606 | - ? (in_array('{DAV:}collection', $a['{DAV:}resourcetype']->getValue())) |
|
607 | - : false; |
|
608 | - |
|
609 | - $typeB = (isset($b['{DAV:}resourcetype'])) |
|
610 | - ? (in_array('{DAV:}collection', $b['{DAV:}resourcetype']->getValue())) |
|
611 | - : false; |
|
612 | - |
|
613 | - // If same type, sort alphabetically by filename: |
|
614 | - if ($typeA === $typeB) { |
|
615 | - return strnatcasecmp($a['displayPath'], $b['displayPath']); |
|
616 | - } |
|
617 | - return (($typeA < $typeB) ? 1 : -1); |
|
618 | - |
|
619 | - } |
|
620 | - |
|
621 | - /** |
|
622 | - * Maps a resource type to a human-readable string and icon. |
|
623 | - * |
|
624 | - * @param array $resourceTypes |
|
625 | - * @param INode $node |
|
626 | - * @return array |
|
627 | - */ |
|
628 | - private function mapResourceType(array $resourceTypes, $node) { |
|
629 | - |
|
630 | - if (!$resourceTypes) { |
|
631 | - if ($node instanceof DAV\IFile) { |
|
632 | - return [ |
|
633 | - 'string' => 'File', |
|
634 | - 'icon' => 'file', |
|
635 | - ]; |
|
636 | - } else { |
|
637 | - return [ |
|
638 | - 'string' => 'Unknown', |
|
639 | - 'icon' => 'cog', |
|
640 | - ]; |
|
641 | - } |
|
642 | - } |
|
643 | - |
|
644 | - $types = [ |
|
645 | - '{http://calendarserver.org/ns/}calendar-proxy-write' => [ |
|
646 | - 'string' => 'Proxy-Write', |
|
647 | - 'icon' => 'people', |
|
648 | - ], |
|
649 | - '{http://calendarserver.org/ns/}calendar-proxy-read' => [ |
|
650 | - 'string' => 'Proxy-Read', |
|
651 | - 'icon' => 'people', |
|
652 | - ], |
|
653 | - '{urn:ietf:params:xml:ns:caldav}schedule-outbox' => [ |
|
654 | - 'string' => 'Outbox', |
|
655 | - 'icon' => 'inbox', |
|
656 | - ], |
|
657 | - '{urn:ietf:params:xml:ns:caldav}schedule-inbox' => [ |
|
658 | - 'string' => 'Inbox', |
|
659 | - 'icon' => 'inbox', |
|
660 | - ], |
|
661 | - '{urn:ietf:params:xml:ns:caldav}calendar' => [ |
|
662 | - 'string' => 'Calendar', |
|
663 | - 'icon' => 'calendar', |
|
664 | - ], |
|
665 | - '{http://calendarserver.org/ns/}shared-owner' => [ |
|
666 | - 'string' => 'Shared', |
|
667 | - 'icon' => 'calendar', |
|
668 | - ], |
|
669 | - '{http://calendarserver.org/ns/}subscribed' => [ |
|
670 | - 'string' => 'Subscription', |
|
671 | - 'icon' => 'calendar', |
|
672 | - ], |
|
673 | - '{urn:ietf:params:xml:ns:carddav}directory' => [ |
|
674 | - 'string' => 'Directory', |
|
675 | - 'icon' => 'globe', |
|
676 | - ], |
|
677 | - '{urn:ietf:params:xml:ns:carddav}addressbook' => [ |
|
678 | - 'string' => 'Address book', |
|
679 | - 'icon' => 'book', |
|
680 | - ], |
|
681 | - '{DAV:}principal' => [ |
|
682 | - 'string' => 'Principal', |
|
683 | - 'icon' => 'person', |
|
684 | - ], |
|
685 | - '{DAV:}collection' => [ |
|
686 | - 'string' => 'Collection', |
|
687 | - 'icon' => 'folder', |
|
688 | - ], |
|
689 | - ]; |
|
690 | - |
|
691 | - $info = [ |
|
692 | - 'string' => [], |
|
693 | - 'icon' => 'cog', |
|
694 | - ]; |
|
695 | - foreach ($resourceTypes as $k => $resourceType) { |
|
696 | - if (isset($types[$resourceType])) { |
|
697 | - $info['string'][] = $types[$resourceType]['string']; |
|
698 | - } else { |
|
699 | - $info['string'][] = $resourceType; |
|
700 | - } |
|
701 | - } |
|
702 | - foreach ($types as $key => $resourceInfo) { |
|
703 | - if (in_array($key, $resourceTypes)) { |
|
704 | - $info['icon'] = $resourceInfo['icon']; |
|
705 | - break; |
|
706 | - } |
|
707 | - } |
|
708 | - $info['string'] = implode(', ', $info['string']); |
|
709 | - |
|
710 | - return $info; |
|
711 | - |
|
712 | - } |
|
713 | - |
|
714 | - /** |
|
715 | - * Draws a table row for a property |
|
716 | - * |
|
717 | - * @param string $name |
|
718 | - * @param mixed $value |
|
719 | - * @return string |
|
720 | - */ |
|
721 | - private function drawPropertyRow($name, $value) { |
|
722 | - |
|
723 | - $html = new HtmlOutputHelper( |
|
724 | - $this->server->getBaseUri(), |
|
725 | - $this->server->xml->namespaceMap |
|
726 | - ); |
|
727 | - |
|
728 | - return "<tr><th>" . $html->xmlName($name) . "</th><td>" . $this->drawPropertyValue($html, $value) . "</td></tr>"; |
|
729 | - |
|
730 | - } |
|
731 | - |
|
732 | - /** |
|
733 | - * Draws a table row for a property |
|
734 | - * |
|
735 | - * @param HtmlOutputHelper $html |
|
736 | - * @param mixed $value |
|
737 | - * @return string |
|
738 | - */ |
|
739 | - private function drawPropertyValue($html, $value) { |
|
740 | - |
|
741 | - if (is_scalar($value)) { |
|
742 | - return $html->h($value); |
|
743 | - } elseif ($value instanceof HtmlOutput) { |
|
744 | - return $value->toHtml($html); |
|
745 | - } elseif ($value instanceof \Sabre\Xml\XmlSerializable) { |
|
746 | - |
|
747 | - // There's no default html output for this property, we're going |
|
748 | - // to output the actual xml serialization instead. |
|
749 | - $xml = $this->server->xml->write('{DAV:}root', $value, $this->server->getBaseUri()); |
|
750 | - // removing first and last line, as they contain our root |
|
751 | - // element. |
|
752 | - $xml = explode("\n", $xml); |
|
753 | - $xml = array_slice($xml, 2, -2); |
|
754 | - return "<pre>" . $html->h(implode("\n", $xml)) . "</pre>"; |
|
755 | - |
|
756 | - } else { |
|
757 | - return "<em>unknown</em>"; |
|
758 | - } |
|
759 | - |
|
760 | - } |
|
761 | - |
|
762 | - /** |
|
763 | - * Returns a plugin name. |
|
764 | - * |
|
765 | - * Using this name other plugins will be able to access other plugins; |
|
766 | - * using \Sabre\DAV\Server::getPlugin |
|
767 | - * |
|
768 | - * @return string |
|
769 | - */ |
|
770 | - public function getPluginName() { |
|
771 | - |
|
772 | - return 'browser'; |
|
773 | - |
|
774 | - } |
|
775 | - |
|
776 | - /** |
|
777 | - * Returns a bunch of meta-data about the plugin. |
|
778 | - * |
|
779 | - * Providing this information is optional, and is mainly displayed by the |
|
780 | - * Browser plugin. |
|
781 | - * |
|
782 | - * The description key in the returned array may contain html and will not |
|
783 | - * be sanitized. |
|
784 | - * |
|
785 | - * @return array |
|
786 | - */ |
|
787 | - public function getPluginInfo() { |
|
788 | - |
|
789 | - return [ |
|
790 | - 'name' => $this->getPluginName(), |
|
791 | - 'description' => 'Generates HTML indexes and debug information for your sabre/dav server', |
|
792 | - 'link' => 'http://sabre.io/dav/browser-plugin/', |
|
793 | - ]; |
|
794 | - |
|
795 | - } |
|
524 | + $output .= ob_get_clean(); |
|
525 | + |
|
526 | + } |
|
527 | + |
|
528 | + /** |
|
529 | + * This method takes a path/name of an asset and turns it into url |
|
530 | + * suiteable for http access. |
|
531 | + * |
|
532 | + * @param string $assetName |
|
533 | + * @return string |
|
534 | + */ |
|
535 | + protected function getAssetUrl($assetName) { |
|
536 | + |
|
537 | + return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); |
|
538 | + |
|
539 | + } |
|
540 | + |
|
541 | + /** |
|
542 | + * This method returns a local pathname to an asset. |
|
543 | + * |
|
544 | + * @param string $assetName |
|
545 | + * @return string |
|
546 | + * @throws DAV\Exception\NotFound |
|
547 | + */ |
|
548 | + protected function getLocalAssetPath($assetName) { |
|
549 | + |
|
550 | + $assetDir = __DIR__ . '/assets/'; |
|
551 | + $path = $assetDir . $assetName; |
|
552 | + |
|
553 | + // Making sure people aren't trying to escape from the base path. |
|
554 | + $path = str_replace('\\', '/', $path); |
|
555 | + if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') { |
|
556 | + throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected'); |
|
557 | + } |
|
558 | + if (strpos(realpath($path), realpath($assetDir)) === 0 && file_exists($path)) { |
|
559 | + return $path; |
|
560 | + } |
|
561 | + throw new DAV\Exception\NotFound('Path does not exist, or escaping from the base path was detected'); |
|
562 | + } |
|
563 | + |
|
564 | + /** |
|
565 | + * This method reads an asset from disk and generates a full http response. |
|
566 | + * |
|
567 | + * @param string $assetName |
|
568 | + * @return void |
|
569 | + */ |
|
570 | + protected function serveAsset($assetName) { |
|
571 | + |
|
572 | + $assetPath = $this->getLocalAssetPath($assetName); |
|
573 | + |
|
574 | + // Rudimentary mime type detection |
|
575 | + $mime = 'application/octet-stream'; |
|
576 | + $map = [ |
|
577 | + 'ico' => 'image/vnd.microsoft.icon', |
|
578 | + 'png' => 'image/png', |
|
579 | + 'css' => 'text/css', |
|
580 | + ]; |
|
581 | + |
|
582 | + $ext = substr($assetName, strrpos($assetName, '.') + 1); |
|
583 | + if (isset($map[$ext])) { |
|
584 | + $mime = $map[$ext]; |
|
585 | + } |
|
586 | + |
|
587 | + $this->server->httpResponse->setHeader('Content-Type', $mime); |
|
588 | + $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath)); |
|
589 | + $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600'); |
|
590 | + $this->server->httpResponse->setStatus(200); |
|
591 | + $this->server->httpResponse->setBody(fopen($assetPath, 'r')); |
|
592 | + |
|
593 | + } |
|
594 | + |
|
595 | + /** |
|
596 | + * Sort helper function: compares two directory entries based on type and |
|
597 | + * display name. Collections sort above other types. |
|
598 | + * |
|
599 | + * @param array $a |
|
600 | + * @param array $b |
|
601 | + * @return int |
|
602 | + */ |
|
603 | + protected function compareNodes($a, $b) { |
|
604 | + |
|
605 | + $typeA = (isset($a['{DAV:}resourcetype'])) |
|
606 | + ? (in_array('{DAV:}collection', $a['{DAV:}resourcetype']->getValue())) |
|
607 | + : false; |
|
608 | + |
|
609 | + $typeB = (isset($b['{DAV:}resourcetype'])) |
|
610 | + ? (in_array('{DAV:}collection', $b['{DAV:}resourcetype']->getValue())) |
|
611 | + : false; |
|
612 | + |
|
613 | + // If same type, sort alphabetically by filename: |
|
614 | + if ($typeA === $typeB) { |
|
615 | + return strnatcasecmp($a['displayPath'], $b['displayPath']); |
|
616 | + } |
|
617 | + return (($typeA < $typeB) ? 1 : -1); |
|
618 | + |
|
619 | + } |
|
620 | + |
|
621 | + /** |
|
622 | + * Maps a resource type to a human-readable string and icon. |
|
623 | + * |
|
624 | + * @param array $resourceTypes |
|
625 | + * @param INode $node |
|
626 | + * @return array |
|
627 | + */ |
|
628 | + private function mapResourceType(array $resourceTypes, $node) { |
|
629 | + |
|
630 | + if (!$resourceTypes) { |
|
631 | + if ($node instanceof DAV\IFile) { |
|
632 | + return [ |
|
633 | + 'string' => 'File', |
|
634 | + 'icon' => 'file', |
|
635 | + ]; |
|
636 | + } else { |
|
637 | + return [ |
|
638 | + 'string' => 'Unknown', |
|
639 | + 'icon' => 'cog', |
|
640 | + ]; |
|
641 | + } |
|
642 | + } |
|
643 | + |
|
644 | + $types = [ |
|
645 | + '{http://calendarserver.org/ns/}calendar-proxy-write' => [ |
|
646 | + 'string' => 'Proxy-Write', |
|
647 | + 'icon' => 'people', |
|
648 | + ], |
|
649 | + '{http://calendarserver.org/ns/}calendar-proxy-read' => [ |
|
650 | + 'string' => 'Proxy-Read', |
|
651 | + 'icon' => 'people', |
|
652 | + ], |
|
653 | + '{urn:ietf:params:xml:ns:caldav}schedule-outbox' => [ |
|
654 | + 'string' => 'Outbox', |
|
655 | + 'icon' => 'inbox', |
|
656 | + ], |
|
657 | + '{urn:ietf:params:xml:ns:caldav}schedule-inbox' => [ |
|
658 | + 'string' => 'Inbox', |
|
659 | + 'icon' => 'inbox', |
|
660 | + ], |
|
661 | + '{urn:ietf:params:xml:ns:caldav}calendar' => [ |
|
662 | + 'string' => 'Calendar', |
|
663 | + 'icon' => 'calendar', |
|
664 | + ], |
|
665 | + '{http://calendarserver.org/ns/}shared-owner' => [ |
|
666 | + 'string' => 'Shared', |
|
667 | + 'icon' => 'calendar', |
|
668 | + ], |
|
669 | + '{http://calendarserver.org/ns/}subscribed' => [ |
|
670 | + 'string' => 'Subscription', |
|
671 | + 'icon' => 'calendar', |
|
672 | + ], |
|
673 | + '{urn:ietf:params:xml:ns:carddav}directory' => [ |
|
674 | + 'string' => 'Directory', |
|
675 | + 'icon' => 'globe', |
|
676 | + ], |
|
677 | + '{urn:ietf:params:xml:ns:carddav}addressbook' => [ |
|
678 | + 'string' => 'Address book', |
|
679 | + 'icon' => 'book', |
|
680 | + ], |
|
681 | + '{DAV:}principal' => [ |
|
682 | + 'string' => 'Principal', |
|
683 | + 'icon' => 'person', |
|
684 | + ], |
|
685 | + '{DAV:}collection' => [ |
|
686 | + 'string' => 'Collection', |
|
687 | + 'icon' => 'folder', |
|
688 | + ], |
|
689 | + ]; |
|
690 | + |
|
691 | + $info = [ |
|
692 | + 'string' => [], |
|
693 | + 'icon' => 'cog', |
|
694 | + ]; |
|
695 | + foreach ($resourceTypes as $k => $resourceType) { |
|
696 | + if (isset($types[$resourceType])) { |
|
697 | + $info['string'][] = $types[$resourceType]['string']; |
|
698 | + } else { |
|
699 | + $info['string'][] = $resourceType; |
|
700 | + } |
|
701 | + } |
|
702 | + foreach ($types as $key => $resourceInfo) { |
|
703 | + if (in_array($key, $resourceTypes)) { |
|
704 | + $info['icon'] = $resourceInfo['icon']; |
|
705 | + break; |
|
706 | + } |
|
707 | + } |
|
708 | + $info['string'] = implode(', ', $info['string']); |
|
709 | + |
|
710 | + return $info; |
|
711 | + |
|
712 | + } |
|
713 | + |
|
714 | + /** |
|
715 | + * Draws a table row for a property |
|
716 | + * |
|
717 | + * @param string $name |
|
718 | + * @param mixed $value |
|
719 | + * @return string |
|
720 | + */ |
|
721 | + private function drawPropertyRow($name, $value) { |
|
722 | + |
|
723 | + $html = new HtmlOutputHelper( |
|
724 | + $this->server->getBaseUri(), |
|
725 | + $this->server->xml->namespaceMap |
|
726 | + ); |
|
727 | + |
|
728 | + return "<tr><th>" . $html->xmlName($name) . "</th><td>" . $this->drawPropertyValue($html, $value) . "</td></tr>"; |
|
729 | + |
|
730 | + } |
|
731 | + |
|
732 | + /** |
|
733 | + * Draws a table row for a property |
|
734 | + * |
|
735 | + * @param HtmlOutputHelper $html |
|
736 | + * @param mixed $value |
|
737 | + * @return string |
|
738 | + */ |
|
739 | + private function drawPropertyValue($html, $value) { |
|
740 | + |
|
741 | + if (is_scalar($value)) { |
|
742 | + return $html->h($value); |
|
743 | + } elseif ($value instanceof HtmlOutput) { |
|
744 | + return $value->toHtml($html); |
|
745 | + } elseif ($value instanceof \Sabre\Xml\XmlSerializable) { |
|
746 | + |
|
747 | + // There's no default html output for this property, we're going |
|
748 | + // to output the actual xml serialization instead. |
|
749 | + $xml = $this->server->xml->write('{DAV:}root', $value, $this->server->getBaseUri()); |
|
750 | + // removing first and last line, as they contain our root |
|
751 | + // element. |
|
752 | + $xml = explode("\n", $xml); |
|
753 | + $xml = array_slice($xml, 2, -2); |
|
754 | + return "<pre>" . $html->h(implode("\n", $xml)) . "</pre>"; |
|
755 | + |
|
756 | + } else { |
|
757 | + return "<em>unknown</em>"; |
|
758 | + } |
|
759 | + |
|
760 | + } |
|
761 | + |
|
762 | + /** |
|
763 | + * Returns a plugin name. |
|
764 | + * |
|
765 | + * Using this name other plugins will be able to access other plugins; |
|
766 | + * using \Sabre\DAV\Server::getPlugin |
|
767 | + * |
|
768 | + * @return string |
|
769 | + */ |
|
770 | + public function getPluginName() { |
|
771 | + |
|
772 | + return 'browser'; |
|
773 | + |
|
774 | + } |
|
775 | + |
|
776 | + /** |
|
777 | + * Returns a bunch of meta-data about the plugin. |
|
778 | + * |
|
779 | + * Providing this information is optional, and is mainly displayed by the |
|
780 | + * Browser plugin. |
|
781 | + * |
|
782 | + * The description key in the returned array may contain html and will not |
|
783 | + * be sanitized. |
|
784 | + * |
|
785 | + * @return array |
|
786 | + */ |
|
787 | + public function getPluginInfo() { |
|
788 | + |
|
789 | + return [ |
|
790 | + 'name' => $this->getPluginName(), |
|
791 | + 'description' => 'Generates HTML indexes and debug information for your sabre/dav server', |
|
792 | + 'link' => 'http://sabre.io/dav/browser-plugin/', |
|
793 | + ]; |
|
794 | + |
|
795 | + } |
|
796 | 796 | |
797 | 797 | } |
@@ -87,7 +87,7 @@ discard block |
||
87 | 87 | * |
88 | 88 | * @param RequestInterface $request |
89 | 89 | * @param ResponseInterface $response |
90 | - * @return bool |
|
90 | + * @return boolean|null |
|
91 | 91 | */ |
92 | 92 | public function httpGetEarly(RequestInterface $request, ResponseInterface $response) { |
93 | 93 | |
@@ -160,7 +160,7 @@ discard block |
||
160 | 160 | * |
161 | 161 | * @param RequestInterface $request |
162 | 162 | * @param ResponseInterface $response |
163 | - * @return bool |
|
163 | + * @return null|false |
|
164 | 164 | */ |
165 | 165 | public function httpPOST(RequestInterface $request, ResponseInterface $response) { |
166 | 166 | |
@@ -414,7 +414,7 @@ discard block |
||
414 | 414 | * |
415 | 415 | * @param string $title |
416 | 416 | * @param string $path |
417 | - * @return void |
|
417 | + * @return string |
|
418 | 418 | */ |
419 | 419 | public function generateHeader($title, $path = null) { |
420 | 420 |
@@ -78,7 +78,9 @@ discard block |
||
78 | 78 | $this->server->on('method:GET', [$this, 'httpGetEarly'], 90); |
79 | 79 | $this->server->on('method:GET', [$this, 'httpGet'], 200); |
80 | 80 | $this->server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel'], 200); |
81 | - if ($this->enablePost) $this->server->on('method:POST', [$this, 'httpPOST']); |
|
81 | + if ($this->enablePost) { |
|
82 | + $this->server->on('method:POST', [$this, 'httpPOST']); |
|
83 | + } |
|
82 | 84 | } |
83 | 85 | |
84 | 86 | /** |
@@ -172,8 +174,9 @@ discard block |
||
172 | 174 | } |
173 | 175 | $postVars = $request->getPostData(); |
174 | 176 | |
175 | - if (!isset($postVars['sabreAction'])) |
|
176 | - return; |
|
177 | + if (!isset($postVars['sabreAction'])) { |
|
178 | + return; |
|
179 | + } |
|
177 | 180 | |
178 | 181 | $uri = $request->getPath(); |
179 | 182 | |
@@ -219,12 +222,16 @@ discard block |
||
219 | 222 | // @codeCoverageIgnoreStart |
220 | 223 | case 'put' : |
221 | 224 | |
222 | - if ($_FILES) $file = current($_FILES); |
|
223 | - else break; |
|
225 | + if ($_FILES) { |
|
226 | + $file = current($_FILES); |
|
227 | + } else { |
|
228 | + break; |
|
229 | + } |
|
224 | 230 | |
225 | 231 | list(, $newName) = URLUtil::splitPath(trim($file['name'])); |
226 | - if (isset($postVars['name']) && trim($postVars['name'])) |
|
227 | - $newName = trim($postVars['name']); |
|
232 | + if (isset($postVars['name']) && trim($postVars['name'])) { |
|
233 | + $newName = trim($postVars['name']); |
|
234 | + } |
|
228 | 235 | |
229 | 236 | // Making sure we only have a 'basename' component |
230 | 237 | list(, $newName) = URLUtil::splitPath($newName); |
@@ -497,13 +504,15 @@ discard block |
||
497 | 504 | */ |
498 | 505 | public function htmlActionsPanel(DAV\INode $node, &$output) { |
499 | 506 | |
500 | - if (!$node instanceof DAV\ICollection) |
|
501 | - return; |
|
507 | + if (!$node instanceof DAV\ICollection) { |
|
508 | + return; |
|
509 | + } |
|
502 | 510 | |
503 | 511 | // We also know fairly certain that if an object is a non-extended |
504 | 512 | // SimpleCollection, we won't need to show the panel either. |
505 | - if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') |
|
506 | - return; |
|
513 | + if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') { |
|
514 | + return; |
|
515 | + } |
|
507 | 516 | |
508 | 517 | ob_start(); |
509 | 518 | echo '<form method="post" action=""> |
@@ -19,7 +19,7 @@ |
||
19 | 19 | /** |
20 | 20 | * This method allows the exception to include additional information into the WebDAV error response |
21 | 21 | * |
22 | - * @param DAV\Server $server |
|
22 | + * @param \Sabre\DAV\Server $server |
|
23 | 23 | * @param \DOMElement $errorNode |
24 | 24 | * @return void |
25 | 25 | */ |
@@ -16,18 +16,18 @@ |
||
16 | 16 | */ |
17 | 17 | class InvalidResourceType extends Forbidden { |
18 | 18 | |
19 | - /** |
|
20 | - * This method allows the exception to include additional information into the WebDAV error response |
|
21 | - * |
|
22 | - * @param DAV\Server $server |
|
23 | - * @param \DOMElement $errorNode |
|
24 | - * @return void |
|
25 | - */ |
|
26 | - public function serialize(\Sabre\DAV\Server $server, \DOMElement $errorNode) { |
|
19 | + /** |
|
20 | + * This method allows the exception to include additional information into the WebDAV error response |
|
21 | + * |
|
22 | + * @param DAV\Server $server |
|
23 | + * @param \DOMElement $errorNode |
|
24 | + * @return void |
|
25 | + */ |
|
26 | + public function serialize(\Sabre\DAV\Server $server, \DOMElement $errorNode) { |
|
27 | 27 | |
28 | - $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:valid-resourcetype'); |
|
29 | - $errorNode->appendChild($error); |
|
28 | + $error = $errorNode->ownerDocument->createElementNS('DAV:', 'd:valid-resourcetype'); |
|
29 | + $errorNode->appendChild($error); |
|
30 | 30 | |
31 | - } |
|
31 | + } |
|
32 | 32 | |
33 | 33 | } |
@@ -2,11 +2,11 @@ |
||
2 | 2 | |
3 | 3 | namespace Sabre\DAV\PropertyStorage; |
4 | 4 | |
5 | +use Sabre\DAV\INode; |
|
6 | +use Sabre\DAV\PropFind; |
|
7 | +use Sabre\DAV\PropPatch; |
|
5 | 8 | use Sabre\DAV\Server; |
6 | 9 | use Sabre\DAV\ServerPlugin; |
7 | -use Sabre\DAV\PropPatch; |
|
8 | -use Sabre\DAV\PropFind; |
|
9 | -use Sabre\DAV\INode; |
|
10 | 10 | |
11 | 11 | /** |
12 | 12 | * PropertyStorage Plugin. |
@@ -25,156 +25,156 @@ |
||
25 | 25 | */ |
26 | 26 | class Plugin extends ServerPlugin { |
27 | 27 | |
28 | - /** |
|
29 | - * If you only want this plugin to store properties for a limited set of |
|
30 | - * paths, you can use a pathFilter to do this. |
|
31 | - * |
|
32 | - * The pathFilter should be a callable. The callable retrieves a path as |
|
33 | - * its argument, and should return true or false wether it allows |
|
34 | - * properties to be stored. |
|
35 | - * |
|
36 | - * @var callable |
|
37 | - */ |
|
38 | - public $pathFilter; |
|
39 | - |
|
40 | - /** |
|
41 | - * Creates the plugin |
|
42 | - * |
|
43 | - * @param Backend\BackendInterface $backend |
|
44 | - */ |
|
45 | - public function __construct(Backend\BackendInterface $backend) { |
|
46 | - |
|
47 | - $this->backend = $backend; |
|
48 | - |
|
49 | - } |
|
50 | - |
|
51 | - /** |
|
52 | - * This initializes the plugin. |
|
53 | - * |
|
54 | - * This function is called by Sabre\DAV\Server, after |
|
55 | - * addPlugin is called. |
|
56 | - * |
|
57 | - * This method should set up the required event subscriptions. |
|
58 | - * |
|
59 | - * @param Server $server |
|
60 | - * @return void |
|
61 | - */ |
|
62 | - public function initialize(Server $server) { |
|
63 | - |
|
64 | - $server->on('propFind', [$this, 'propFind'], 130); |
|
65 | - $server->on('propPatch', [$this, 'propPatch'], 300); |
|
66 | - $server->on('afterMove', [$this, 'afterMove']); |
|
67 | - $server->on('afterUnbind', [$this, 'afterUnbind']); |
|
68 | - |
|
69 | - } |
|
70 | - |
|
71 | - /** |
|
72 | - * Called during PROPFIND operations. |
|
73 | - * |
|
74 | - * If there's any requested properties that don't have a value yet, this |
|
75 | - * plugin will look in the property storage backend to find them. |
|
76 | - * |
|
77 | - * @param PropFind $propFind |
|
78 | - * @param INode $node |
|
79 | - * @return void |
|
80 | - */ |
|
81 | - public function propFind(PropFind $propFind, INode $node) { |
|
82 | - |
|
83 | - $path = $propFind->getPath(); |
|
84 | - $pathFilter = $this->pathFilter; |
|
85 | - if ($pathFilter && !$pathFilter($path)) return; |
|
86 | - $this->backend->propFind($propFind->getPath(), $propFind); |
|
87 | - |
|
88 | - } |
|
89 | - |
|
90 | - /** |
|
91 | - * Called during PROPPATCH operations |
|
92 | - * |
|
93 | - * If there's any updated properties that haven't been stored, the |
|
94 | - * propertystorage backend can handle it. |
|
95 | - * |
|
96 | - * @param string $path |
|
97 | - * @param PropPatch $propPatch |
|
98 | - * @return void |
|
99 | - */ |
|
100 | - public function propPatch($path, PropPatch $propPatch) { |
|
101 | - |
|
102 | - $pathFilter = $this->pathFilter; |
|
103 | - if ($pathFilter && !$pathFilter($path)) return; |
|
104 | - $this->backend->propPatch($path, $propPatch); |
|
105 | - |
|
106 | - } |
|
107 | - |
|
108 | - /** |
|
109 | - * Called after a node is deleted. |
|
110 | - * |
|
111 | - * This allows the backend to clean up any properties still in the |
|
112 | - * database. |
|
113 | - * |
|
114 | - * @param string $path |
|
115 | - * @return void |
|
116 | - */ |
|
117 | - public function afterUnbind($path) { |
|
118 | - |
|
119 | - $pathFilter = $this->pathFilter; |
|
120 | - if ($pathFilter && !$pathFilter($path)) return; |
|
121 | - $this->backend->delete($path); |
|
122 | - |
|
123 | - } |
|
124 | - |
|
125 | - /** |
|
126 | - * Called after a node is moved. |
|
127 | - * |
|
128 | - * This allows the backend to move all the associated properties. |
|
129 | - * |
|
130 | - * @param string $source |
|
131 | - * @param string $destination |
|
132 | - * @return void |
|
133 | - */ |
|
134 | - public function afterMove($source, $destination) { |
|
135 | - |
|
136 | - $pathFilter = $this->pathFilter; |
|
137 | - if ($pathFilter && !$pathFilter($source)) return; |
|
138 | - // If the destination is filtered, afterUnbind will handle cleaning up |
|
139 | - // the properties. |
|
140 | - if ($pathFilter && !$pathFilter($destination)) return; |
|
141 | - |
|
142 | - $this->backend->move($source, $destination); |
|
143 | - |
|
144 | - } |
|
145 | - |
|
146 | - /** |
|
147 | - * Returns a plugin name. |
|
148 | - * |
|
149 | - * Using this name other plugins will be able to access other plugins |
|
150 | - * using \Sabre\DAV\Server::getPlugin |
|
151 | - * |
|
152 | - * @return string |
|
153 | - */ |
|
154 | - public function getPluginName() { |
|
155 | - |
|
156 | - return 'property-storage'; |
|
157 | - |
|
158 | - } |
|
159 | - |
|
160 | - /** |
|
161 | - * Returns a bunch of meta-data about the plugin. |
|
162 | - * |
|
163 | - * Providing this information is optional, and is mainly displayed by the |
|
164 | - * Browser plugin. |
|
165 | - * |
|
166 | - * The description key in the returned array may contain html and will not |
|
167 | - * be sanitized. |
|
168 | - * |
|
169 | - * @return array |
|
170 | - */ |
|
171 | - public function getPluginInfo() { |
|
172 | - |
|
173 | - return [ |
|
174 | - 'name' => $this->getPluginName(), |
|
175 | - 'description' => 'This plugin allows any arbitrary WebDAV property to be set on any resource.', |
|
176 | - 'link' => 'http://sabre.io/dav/property-storage/', |
|
177 | - ]; |
|
178 | - |
|
179 | - } |
|
28 | + /** |
|
29 | + * If you only want this plugin to store properties for a limited set of |
|
30 | + * paths, you can use a pathFilter to do this. |
|
31 | + * |
|
32 | + * The pathFilter should be a callable. The callable retrieves a path as |
|
33 | + * its argument, and should return true or false wether it allows |
|
34 | + * properties to be stored. |
|
35 | + * |
|
36 | + * @var callable |
|
37 | + */ |
|
38 | + public $pathFilter; |
|
39 | + |
|
40 | + /** |
|
41 | + * Creates the plugin |
|
42 | + * |
|
43 | + * @param Backend\BackendInterface $backend |
|
44 | + */ |
|
45 | + public function __construct(Backend\BackendInterface $backend) { |
|
46 | + |
|
47 | + $this->backend = $backend; |
|
48 | + |
|
49 | + } |
|
50 | + |
|
51 | + /** |
|
52 | + * This initializes the plugin. |
|
53 | + * |
|
54 | + * This function is called by Sabre\DAV\Server, after |
|
55 | + * addPlugin is called. |
|
56 | + * |
|
57 | + * This method should set up the required event subscriptions. |
|
58 | + * |
|
59 | + * @param Server $server |
|
60 | + * @return void |
|
61 | + */ |
|
62 | + public function initialize(Server $server) { |
|
63 | + |
|
64 | + $server->on('propFind', [$this, 'propFind'], 130); |
|
65 | + $server->on('propPatch', [$this, 'propPatch'], 300); |
|
66 | + $server->on('afterMove', [$this, 'afterMove']); |
|
67 | + $server->on('afterUnbind', [$this, 'afterUnbind']); |
|
68 | + |
|
69 | + } |
|
70 | + |
|
71 | + /** |
|
72 | + * Called during PROPFIND operations. |
|
73 | + * |
|
74 | + * If there's any requested properties that don't have a value yet, this |
|
75 | + * plugin will look in the property storage backend to find them. |
|
76 | + * |
|
77 | + * @param PropFind $propFind |
|
78 | + * @param INode $node |
|
79 | + * @return void |
|
80 | + */ |
|
81 | + public function propFind(PropFind $propFind, INode $node) { |
|
82 | + |
|
83 | + $path = $propFind->getPath(); |
|
84 | + $pathFilter = $this->pathFilter; |
|
85 | + if ($pathFilter && !$pathFilter($path)) return; |
|
86 | + $this->backend->propFind($propFind->getPath(), $propFind); |
|
87 | + |
|
88 | + } |
|
89 | + |
|
90 | + /** |
|
91 | + * Called during PROPPATCH operations |
|
92 | + * |
|
93 | + * If there's any updated properties that haven't been stored, the |
|
94 | + * propertystorage backend can handle it. |
|
95 | + * |
|
96 | + * @param string $path |
|
97 | + * @param PropPatch $propPatch |
|
98 | + * @return void |
|
99 | + */ |
|
100 | + public function propPatch($path, PropPatch $propPatch) { |
|
101 | + |
|
102 | + $pathFilter = $this->pathFilter; |
|
103 | + if ($pathFilter && !$pathFilter($path)) return; |
|
104 | + $this->backend->propPatch($path, $propPatch); |
|
105 | + |
|
106 | + } |
|
107 | + |
|
108 | + /** |
|
109 | + * Called after a node is deleted. |
|
110 | + * |
|
111 | + * This allows the backend to clean up any properties still in the |
|
112 | + * database. |
|
113 | + * |
|
114 | + * @param string $path |
|
115 | + * @return void |
|
116 | + */ |
|
117 | + public function afterUnbind($path) { |
|
118 | + |
|
119 | + $pathFilter = $this->pathFilter; |
|
120 | + if ($pathFilter && !$pathFilter($path)) return; |
|
121 | + $this->backend->delete($path); |
|
122 | + |
|
123 | + } |
|
124 | + |
|
125 | + /** |
|
126 | + * Called after a node is moved. |
|
127 | + * |
|
128 | + * This allows the backend to move all the associated properties. |
|
129 | + * |
|
130 | + * @param string $source |
|
131 | + * @param string $destination |
|
132 | + * @return void |
|
133 | + */ |
|
134 | + public function afterMove($source, $destination) { |
|
135 | + |
|
136 | + $pathFilter = $this->pathFilter; |
|
137 | + if ($pathFilter && !$pathFilter($source)) return; |
|
138 | + // If the destination is filtered, afterUnbind will handle cleaning up |
|
139 | + // the properties. |
|
140 | + if ($pathFilter && !$pathFilter($destination)) return; |
|
141 | + |
|
142 | + $this->backend->move($source, $destination); |
|
143 | + |
|
144 | + } |
|
145 | + |
|
146 | + /** |
|
147 | + * Returns a plugin name. |
|
148 | + * |
|
149 | + * Using this name other plugins will be able to access other plugins |
|
150 | + * using \Sabre\DAV\Server::getPlugin |
|
151 | + * |
|
152 | + * @return string |
|
153 | + */ |
|
154 | + public function getPluginName() { |
|
155 | + |
|
156 | + return 'property-storage'; |
|
157 | + |
|
158 | + } |
|
159 | + |
|
160 | + /** |
|
161 | + * Returns a bunch of meta-data about the plugin. |
|
162 | + * |
|
163 | + * Providing this information is optional, and is mainly displayed by the |
|
164 | + * Browser plugin. |
|
165 | + * |
|
166 | + * The description key in the returned array may contain html and will not |
|
167 | + * be sanitized. |
|
168 | + * |
|
169 | + * @return array |
|
170 | + */ |
|
171 | + public function getPluginInfo() { |
|
172 | + |
|
173 | + return [ |
|
174 | + 'name' => $this->getPluginName(), |
|
175 | + 'description' => 'This plugin allows any arbitrary WebDAV property to be set on any resource.', |
|
176 | + 'link' => 'http://sabre.io/dav/property-storage/', |
|
177 | + ]; |
|
178 | + |
|
179 | + } |
|
180 | 180 | } |
@@ -61,9 +61,9 @@ |
||
61 | 61 | */ |
62 | 62 | public function initialize(Server $server) { |
63 | 63 | |
64 | - $server->on('propFind', [$this, 'propFind'], 130); |
|
65 | - $server->on('propPatch', [$this, 'propPatch'], 300); |
|
66 | - $server->on('afterMove', [$this, 'afterMove']); |
|
64 | + $server->on('propFind', [$this, 'propFind'], 130); |
|
65 | + $server->on('propPatch', [$this, 'propPatch'], 300); |
|
66 | + $server->on('afterMove', [$this, 'afterMove']); |
|
67 | 67 | $server->on('afterUnbind', [$this, 'afterUnbind']); |
68 | 68 | |
69 | 69 | } |
@@ -82,7 +82,9 @@ discard block |
||
82 | 82 | |
83 | 83 | $path = $propFind->getPath(); |
84 | 84 | $pathFilter = $this->pathFilter; |
85 | - if ($pathFilter && !$pathFilter($path)) return; |
|
85 | + if ($pathFilter && !$pathFilter($path)) { |
|
86 | + return; |
|
87 | + } |
|
86 | 88 | $this->backend->propFind($propFind->getPath(), $propFind); |
87 | 89 | |
88 | 90 | } |
@@ -100,7 +102,9 @@ discard block |
||
100 | 102 | public function propPatch($path, PropPatch $propPatch) { |
101 | 103 | |
102 | 104 | $pathFilter = $this->pathFilter; |
103 | - if ($pathFilter && !$pathFilter($path)) return; |
|
105 | + if ($pathFilter && !$pathFilter($path)) { |
|
106 | + return; |
|
107 | + } |
|
104 | 108 | $this->backend->propPatch($path, $propPatch); |
105 | 109 | |
106 | 110 | } |
@@ -117,7 +121,9 @@ discard block |
||
117 | 121 | public function afterUnbind($path) { |
118 | 122 | |
119 | 123 | $pathFilter = $this->pathFilter; |
120 | - if ($pathFilter && !$pathFilter($path)) return; |
|
124 | + if ($pathFilter && !$pathFilter($path)) { |
|
125 | + return; |
|
126 | + } |
|
121 | 127 | $this->backend->delete($path); |
122 | 128 | |
123 | 129 | } |
@@ -134,10 +140,14 @@ discard block |
||
134 | 140 | public function afterMove($source, $destination) { |
135 | 141 | |
136 | 142 | $pathFilter = $this->pathFilter; |
137 | - if ($pathFilter && !$pathFilter($source)) return; |
|
143 | + if ($pathFilter && !$pathFilter($source)) { |
|
144 | + return; |
|
145 | + } |
|
138 | 146 | // If the destination is filtered, afterUnbind will handle cleaning up |
139 | 147 | // the properties. |
140 | - if ($pathFilter && !$pathFilter($destination)) return; |
|
148 | + if ($pathFilter && !$pathFilter($destination)) { |
|
149 | + return; |
|
150 | + } |
|
141 | 151 | |
142 | 152 | $this->backend->move($source, $destination); |
143 | 153 |