first commit
This commit is contained in:
		
							
								
								
									
										865
									
								
								libs/Plugin.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										865
									
								
								libs/Plugin.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,865 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Aerex\Taskwarrior; | ||||
|  | ||||
| use Sabre\DAV\Exception\BadRequest; | ||||
| use Sabre\VObject; | ||||
| use Sabre\HTTP\RequestInterface; | ||||
| use Sabre\HTTP\ResponseInterface; | ||||
| use Sabre\Xml\ParseException; | ||||
| use Aerex\Taskwarrior\TaskwarriorVCalendarManger; | ||||
| use DavidBadura\Taskwarrior\TaskManager; | ||||
| use DavidBadura\Taskwarrior\Task; | ||||
| use DavidBadura\Taskwarrior\Recurring; | ||||
| use DavidBadura\Taskwarrior\Annotation; | ||||
|  | ||||
| /** | ||||
|  * The plugin to interact with Baikal and Taskwarrior | ||||
|  * | ||||
|  */ | ||||
| class Taskwarrior extends ServerPlugin { | ||||
|  | ||||
|     /** | ||||
|      * Reference to server object. | ||||
|      * | ||||
|      * @var Server | ||||
|      */ | ||||
|     protected $server; | ||||
|     private $TWCalManager; | ||||
|  | ||||
|     /** | ||||
|      * Sets up the plugin | ||||
|      * | ||||
|      * @param Server $server | ||||
|      * @return void | ||||
|      */ | ||||
|     function initialize(Server $server) { | ||||
|  | ||||
|         $this->server = $server; | ||||
|         $this->$TWCalManager = new TaskwarriorCalendarEvent(); | ||||
|         $server->on('method:GET',       [$this, 'httpGet']); | ||||
|         $server->on('method:OPTIONS',   [$this, 'httpOptions']); | ||||
|         $server->on('method:HEAD',      [$this, 'httpHead']); | ||||
|         $server->on('method:DELETE',    [$this, 'httpDelete']); | ||||
|         $server->on('method:PROPFIND',  [$this, 'httpPropFind']); | ||||
|         $server->on('method:PROPPATCH', [$this, 'httpPropPatch']); | ||||
|         $server->on('method:PUT',       [$this, 'httpPut']); | ||||
|         $server->on('method:MKCOL',     [$this, 'httpMkcol']); | ||||
|         $server->on('method:MOVE',      [$this, 'httpMove']); | ||||
|         $server->on('method:COPY',      [$this, 'httpCopy']); | ||||
|         $server->on('method:REPORT',    [$this, 'httpReport']); | ||||
|  | ||||
|         $server->on('propPatch',        [$this, 'propPatchProtectedPropertyCheck'], 90); | ||||
|         $server->on('propPatch',        [$this, 'propPatchNodeUpdate'], 200); | ||||
|         $server->on('propFind',         [$this, 'propFind']); | ||||
|         $server->on('propFind',         [$this, 'propFindNode'], 120); | ||||
|         $server->on('propFind',         [$this, 'propFindLate'], 200); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a plugin name. | ||||
|      * | ||||
|      * Using this name other plugins will be able to access other plugins | ||||
|      * using DAV\Server::getPlugin | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     function getPluginName() { | ||||
|  | ||||
|         return 'taskwarrior'; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method will parse a calendar object and create an new task in taskwarrior | ||||
|      * | ||||
|      * @param VCalendar $vCal parsed calendar object | ||||
|      */ | ||||
|     function processCalendarEventForTaskwarrior(VCalendar $vCal){ | ||||
|       try { | ||||
|         $TWCalManager->buildCalendarEvent($VCal->VEVENT); | ||||
|         $TWCalManager->buildToDoEvent($VCal->VTODO); | ||||
|       } catch(Exception $e){ | ||||
|           throw new BadRequest($e->getMessage(), null, $e); | ||||
|       } | ||||
|     } | ||||
|     /** | ||||
|      * * This method is triggered whenever there was a calendar object gets | ||||
|      * * created or updated. | ||||
|      * * | ||||
|      * * @param RequestInterface $request HTTP request | ||||
|      * * @param ResponseInterface $response HTTP Response | ||||
|      * * @param VCalendar $vCal Parsed iCalendar object | ||||
|      * * @param mixed $calendarPath Path to calendar collection | ||||
|      * * @param mixed $modified The iCalendar object has been touched. | ||||
|      * * @param mixed $isNew Whether this was a new item or we're updating one | ||||
|      * * @return void | ||||
|      *                                                        */ | ||||
|     function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) { | ||||
|       $calendarNode = $this->server->tree->getNodeForPath($calendarPath); | ||||
|       $addresses = $this->getAddressesForPrincipal( | ||||
|         $calendarNode->getOwner() | ||||
|       ); | ||||
|       if ($isNew) { | ||||
|         try { | ||||
|           processCalendarEventForTaskwarrior($vCal); | ||||
|         } catch(Exception\BadRequest $e){ | ||||
|           $response->setStatus(200); | ||||
|           $response->setBody(''); | ||||
|           $response->setHeader('Content-Type', 'text/plain'); | ||||
|           $response->setHeader('X-Sabre-Real-Status', $e->getHTTPCode()); | ||||
|         } | ||||
|       } | ||||
|     }     | ||||
|     /** | ||||
|      * This is the default implementation for the GET method. | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return bool | ||||
|      */ | ||||
|     function httpGet(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $path = $request->getPath(); | ||||
|       $node = $this->server->tree->getNodeForPath($path); | ||||
|  | ||||
|       if (!$node instanceof IFile) return; | ||||
|  | ||||
|       $body = $node->get(); | ||||
|  | ||||
|       echo($body); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * HTTP OPTIONS | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return bool | ||||
|      */ | ||||
|     function httpOptions(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $methods = $this->server->getAllowedMethods($request->getPath()); | ||||
|       echo('httpOptions ' . $response); | ||||
|  | ||||
|       $response->setHeader('Allow', strtoupper(implode(', ', $methods))); | ||||
|       $features = ['1', '3', 'extended-mkcol']; | ||||
|  | ||||
|       foreach ($this->server->getPlugins() as $plugin) { | ||||
|         $features = array_merge($features, $plugin->getFeatures()); | ||||
|       } | ||||
|  | ||||
|       $response->setHeader('DAV', implode(', ', $features)); | ||||
|       $response->setHeader('MS-Author-Via', 'DAV'); | ||||
|       $response->setHeader('Accept-Ranges', 'bytes'); | ||||
|       $response->setHeader('Content-Length', '0'); | ||||
|       $response->setStatus(200); | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * HTTP HEAD | ||||
|      * | ||||
|      * This method is normally used to take a peak at a url, and only get the | ||||
|      * HTTP response headers, without the body. This is used by clients to | ||||
|      * determine if a remote file was changed, so they can use a local cached | ||||
|      * version, instead of downloading it again | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return bool | ||||
|      */ | ||||
|     function httpHead(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       // This is implemented by changing the HEAD request to a GET request, | ||||
|       // and dropping the response body. | ||||
|       $subRequest = clone $request; | ||||
|       $subRequest->setMethod('GET'); | ||||
|  | ||||
|       try { | ||||
|         $this->server->invokeMethod($subRequest, $response, false); | ||||
|         $response->setBody(''); | ||||
|       } catch (Exception\NotImplemented $e) { | ||||
|         // Some clients may do HEAD requests on collections, however, GET | ||||
|         // requests and HEAD requests _may_ not be defined on a collection, | ||||
|         // which would trigger a 501. | ||||
|         // This breaks some clients though, so we're transforming these | ||||
|         // 501s into 200s. | ||||
|         $response->setStatus(200); | ||||
|         $response->setBody(''); | ||||
|         $response->setHeader('Content-Type', 'text/plain'); | ||||
|         $response->setHeader('X-Sabre-Real-Status', $e->getHTTPCode()); | ||||
|       } | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * HTTP Delete | ||||
|      * | ||||
|      * The HTTP delete method, deletes a given uri | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return void | ||||
|      */ | ||||
|     function httpDelete(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $path = $request->getPath(); | ||||
|       echo('httpDelete.path ' . $path); | ||||
|  | ||||
|       if (!$this->server->emit('beforeUnbind', [$path])) return false; | ||||
|       $this->server->tree->delete($path); | ||||
|       $this->server->emit('afterUnbind', [$path]); | ||||
|  | ||||
|       $response->setStatus(204); | ||||
|       $response->setHeader('Content-Length', '0'); | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * WebDAV PROPFIND | ||||
|      * | ||||
|      * This WebDAV method requests information about an uri resource, or a list of resources | ||||
|      * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value | ||||
|      * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory) | ||||
|      * | ||||
|      * The request body contains an XML data structure that has a list of properties the client understands | ||||
|      * The response body is also an xml document, containing information about every uri resource and the requested properties | ||||
|      * | ||||
|      * It has to return a HTTP 207 Multi-status status code | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return void | ||||
|      */ | ||||
|     function httpPropFind(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $path = $request->getPath(); | ||||
|  | ||||
|       $requestBody = $request->getBodyAsString(); | ||||
|       if (strlen($requestBody)) { | ||||
|         try { | ||||
|           $propFindXml = $this->server->xml->expect('{DAV:}propfind', $requestBody); | ||||
|         } catch (ParseException $e) { | ||||
|           throw new BadRequest($e->getMessage(), null, $e); | ||||
|         } | ||||
|       } else { | ||||
|         $propFindXml = new Xml\Request\PropFind(); | ||||
|         $propFindXml->allProp = true; | ||||
|         $propFindXml->properties = []; | ||||
|       } | ||||
|  | ||||
|       $depth = $this->server->getHTTPDepth(1); | ||||
|       // The only two options for the depth of a propfind is 0 or 1 - as long as depth infinity is not enabled | ||||
|       if (!$this->server->enablePropfindDepthInfinity && $depth != 0) $depth = 1; | ||||
|  | ||||
|       $newProperties = $this->server->getPropertiesForPath($path, $propFindXml->properties, $depth); | ||||
|  | ||||
|       // This is a multi-status response | ||||
|       $response->setStatus(207); | ||||
|       $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); | ||||
|       $response->setHeader('Vary', 'Brief,Prefer'); | ||||
|  | ||||
|       // Normally this header is only needed for OPTIONS responses, however.. | ||||
|       // iCal seems to also depend on these being set for PROPFIND. Since | ||||
|       // this is not harmful, we'll add it. | ||||
|       $features = ['1', '3', 'extended-mkcol']; | ||||
|       foreach ($this->server->getPlugins() as $plugin) { | ||||
|         $features = array_merge($features, $plugin->getFeatures()); | ||||
|       } | ||||
|       $response->setHeader('DAV', implode(', ', $features)); | ||||
|  | ||||
|       $prefer = $this->server->getHTTPPrefer(); | ||||
|       $minimal = $prefer['return'] === 'minimal'; | ||||
|  | ||||
|       $data = $this->server->generateMultiStatus($newProperties, $minimal); | ||||
|       echo('httppropfind ' . $data); | ||||
|  | ||||
|       $response->setBody($data); | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * WebDAV PROPPATCH | ||||
|      * | ||||
|      * This method is called to update properties on a Node. The request is an XML body with all the mutations. | ||||
|      * In this XML body it is specified which properties should be set/updated and/or deleted | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return bool | ||||
|      */ | ||||
|     function httpPropPatch(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $path = $request->getPath(); | ||||
|  | ||||
|       try { | ||||
|         $propPatch = $this->server->xml->expect('{DAV:}propertyupdate', $request->getBody()); | ||||
|       } catch (ParseException $e) { | ||||
|         throw new BadRequest($e->getMessage(), null, $e); | ||||
|       } | ||||
|       $newProperties = $propPatch->properties; | ||||
|  | ||||
|       $result = $this->server->updateProperties($path, $newProperties); | ||||
|  | ||||
|       $prefer = $this->server->getHTTPPrefer(); | ||||
|       $response->setHeader('Vary', 'Brief,Prefer'); | ||||
|  | ||||
|       if ($prefer['return'] === 'minimal') { | ||||
|  | ||||
|         // If return-minimal is specified, we only have to check if the | ||||
|         // request was succesful, and don't need to return the | ||||
|         // multi-status. | ||||
|         $ok = true; | ||||
|         foreach ($result as $prop => $code) { | ||||
|           if ((int)$code > 299) { | ||||
|             $ok = false; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         if ($ok) { | ||||
|  | ||||
|           $response->setStatus(204); | ||||
|           return false; | ||||
|  | ||||
|         } | ||||
|  | ||||
|       } | ||||
|  | ||||
|       $response->setStatus(207); | ||||
|       $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); | ||||
|  | ||||
|  | ||||
|       // Reorganizing the result for generateMultiStatus | ||||
|       $multiStatus = []; | ||||
|       foreach ($result as $propertyName => $code) { | ||||
|         if (isset($multiStatus[$code])) { | ||||
|           $multiStatus[$code][$propertyName] = null; | ||||
|         } else { | ||||
|           $multiStatus[$code] = [$propertyName => null]; | ||||
|         } | ||||
|       } | ||||
|       $multiStatus['href'] = $path; | ||||
|  | ||||
|       $response->setBody( | ||||
|         $this->server->generateMultiStatus([$multiStatus]) | ||||
|       ); | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * HTTP PUT method | ||||
|      * | ||||
|      * This HTTP method updates a file, or creates a new one. | ||||
|      * | ||||
|      * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return bool | ||||
|      */ | ||||
|     function httpPut(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $body = $request->getBodyAsStream(); | ||||
|       $path = $request->getPath(); | ||||
|  | ||||
|       // Intercepting Content-Range | ||||
|       if ($request->getHeader('Content-Range')) { | ||||
|             /* | ||||
|                An origin server that allows PUT on a given target resource MUST send | ||||
|                a 400 (Bad Request) response to a PUT request that contains a | ||||
|                Content-Range header field. | ||||
|  | ||||
|                Reference: http://tools.ietf.org/html/rfc7231#section-4.3.4 | ||||
|              */ | ||||
|         throw new Exception\BadRequest('Content-Range on PUT requests are forbidden.'); | ||||
|       } | ||||
|  | ||||
|       // Intercepting the Finder problem | ||||
|       if (($expected = $request->getHeader('X-Expected-Entity-Length')) && $expected > 0) { | ||||
|  | ||||
|             /* | ||||
|             Many webservers will not cooperate well with Finder PUT requests, | ||||
|             because it uses 'Chunked' transfer encoding for the request body. | ||||
|  | ||||
|             The symptom of this problem is that Finder sends files to the | ||||
|             server, but they arrive as 0-length files in PHP. | ||||
|  | ||||
|             If we don't do anything, the user might think they are uploading | ||||
|             files successfully, but they end up empty on the server. Instead, | ||||
|             we throw back an error if we detect this. | ||||
|  | ||||
|             The reason Finder uses Chunked, is because it thinks the files | ||||
|             might change as it's being uploaded, and therefore the | ||||
|             Content-Length can vary. | ||||
|  | ||||
|             Instead it sends the X-Expected-Entity-Length header with the size | ||||
|             of the file at the very start of the request. If this header is set, | ||||
|             but we don't get a request body we will fail the request to | ||||
|             protect the end-user. | ||||
|              */ | ||||
|  | ||||
|         // Only reading first byte | ||||
|         $firstByte = fread($body, 1); | ||||
|         if (strlen($firstByte) !== 1) { | ||||
|           throw new Exception\Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.'); | ||||
|         } | ||||
|  | ||||
|         // The body needs to stay intact, so we copy everything to a | ||||
|         // temporary stream. | ||||
|  | ||||
|         $newBody = fopen('php://temp', 'r+'); | ||||
|         fwrite($newBody, $firstByte); | ||||
|         stream_copy_to_stream($body, $newBody); | ||||
|         rewind($newBody); | ||||
|  | ||||
|         $body = $newBody; | ||||
|  | ||||
|       } | ||||
|  | ||||
|       if ($this->server->tree->nodeExists($path)) { | ||||
|  | ||||
|         $node = $this->server->tree->getNodeForPath($path); | ||||
|  | ||||
|         // If the node is a collection, we'll deny it | ||||
|         if (!($node instanceof IFile)) throw new Exception\Conflict('PUT is not allowed on non-files.'); | ||||
|  | ||||
|         if (!$this->server->updateFile($path, $body, $etag)) { | ||||
|           return false; | ||||
|         } | ||||
|  | ||||
|         $response->setHeader('Content-Length', '0'); | ||||
|         if ($etag) $response->setHeader('ETag', $etag); | ||||
|         $response->setStatus(204); | ||||
|  | ||||
|       } else { | ||||
|  | ||||
|         $etag = null; | ||||
|         // If we got here, the resource didn't exist yet. | ||||
|         if (!$this->server->createFile($path, $body, $etag)) { | ||||
|           // For one reason or another the file was not created. | ||||
|           return false; | ||||
|         } | ||||
|  | ||||
|         $response->setHeader('Content-Length', '0'); | ||||
|         if ($etag) $response->setHeader('ETag', $etag); | ||||
|         $response->setStatus(201); | ||||
|  | ||||
|       } | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * WebDAV MKCOL | ||||
|      * | ||||
|      * The MKCOL method is used to create a new collection (directory) on the server | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return bool | ||||
|      */ | ||||
|     function httpMkcol(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $requestBody = $request->getBodyAsString(); | ||||
|       $path = $request->getPath(); | ||||
|  | ||||
|       if ($requestBody) { | ||||
|  | ||||
|         $contentType = $request->getHeader('Content-Type'); | ||||
|         if (strpos($contentType, 'application/xml') !== 0 && strpos($contentType, 'text/xml') !== 0) { | ||||
|  | ||||
|           // We must throw 415 for unsupported mkcol bodies | ||||
|           throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type'); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|           $mkcol = $this->server->xml->expect('{DAV:}mkcol', $requestBody); | ||||
|         } catch (\Sabre\Xml\ParseException $e) { | ||||
|           throw new Exception\BadRequest($e->getMessage(), null, $e); | ||||
|         } | ||||
|  | ||||
|         $properties = $mkcol->getProperties(); | ||||
|  | ||||
|         if (!isset($properties['{DAV:}resourcetype'])) | ||||
|           throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property'); | ||||
|  | ||||
|         $resourceType = $properties['{DAV:}resourcetype']->getValue(); | ||||
|         unset($properties['{DAV:}resourcetype']); | ||||
|  | ||||
|       } else { | ||||
|  | ||||
|         $properties = []; | ||||
|         $resourceType = ['{DAV:}collection']; | ||||
|  | ||||
|       } | ||||
|  | ||||
|       $mkcol = new MkCol($resourceType, $properties); | ||||
|  | ||||
|       $result = $this->server->createCollection($path, $mkcol); | ||||
|  | ||||
|       if (is_array($result)) { | ||||
|         $response->setStatus(207); | ||||
|         $response->setHeader('Content-Type', 'application/xml; charset=utf-8'); | ||||
|  | ||||
|         $response->setBody( | ||||
|           $this->server->generateMultiStatus([$result]) | ||||
|         ); | ||||
|  | ||||
|       } else { | ||||
|         $response->setHeader('Content-Length', '0'); | ||||
|         $response->setStatus(201); | ||||
|       } | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * WebDAV HTTP MOVE method | ||||
|      * | ||||
|      * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return bool | ||||
|      */ | ||||
|     function httpMove(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $path = $request->getPath(); | ||||
|  | ||||
|       $moveInfo = $this->server->getCopyAndMoveInfo($request); | ||||
|  | ||||
|       if ($moveInfo['destinationExists']) { | ||||
|  | ||||
|         if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) return false; | ||||
|  | ||||
|       } | ||||
|       if (!$this->server->emit('beforeUnbind', [$path])) return false; | ||||
|       if (!$this->server->emit('beforeBind', [$moveInfo['destination']])) return false; | ||||
|       if (!$this->server->emit('beforeMove', [$path, $moveInfo['destination']])) return false; | ||||
|  | ||||
|       if ($moveInfo['destinationExists']) { | ||||
|  | ||||
|         $this->server->tree->delete($moveInfo['destination']); | ||||
|         $this->server->emit('afterUnbind', [$moveInfo['destination']]); | ||||
|  | ||||
|       } | ||||
|  | ||||
|       $this->server->tree->move($path, $moveInfo['destination']); | ||||
|  | ||||
|       // Its important afterMove is called before afterUnbind, because it | ||||
|       // allows systems to transfer data from one path to another. | ||||
|       // PropertyStorage uses this. If afterUnbind was first, it would clean | ||||
|       // up all the properties before it has a chance. | ||||
|       $this->server->emit('afterMove', [$path, $moveInfo['destination']]); | ||||
|       $this->server->emit('afterUnbind', [$path]); | ||||
|       $this->server->emit('afterBind', [$moveInfo['destination']]); | ||||
|  | ||||
|       // If a resource was overwritten we should send a 204, otherwise a 201 | ||||
|       $response->setHeader('Content-Length', '0'); | ||||
|       $response->setStatus($moveInfo['destinationExists'] ? 204 : 201); | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * WebDAV HTTP COPY method | ||||
|      * | ||||
|      * This method copies one uri to a different uri, and works much like the MOVE request | ||||
|      * A lot of the actual request processing is done in getCopyMoveInfo | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return bool | ||||
|      */ | ||||
|     function httpCopy(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $path = $request->getPath(); | ||||
|  | ||||
|       $copyInfo = $this->server->getCopyAndMoveInfo($request); | ||||
|  | ||||
|       if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) return false; | ||||
|       if ($copyInfo['destinationExists']) { | ||||
|         if (!$this->server->emit('beforeUnbind', [$copyInfo['destination']])) return false; | ||||
|         $this->server->tree->delete($copyInfo['destination']); | ||||
|       } | ||||
|  | ||||
|       $this->server->tree->copy($path, $copyInfo['destination']); | ||||
|       $this->server->emit('afterBind', [$copyInfo['destination']]); | ||||
|  | ||||
|       // If a resource was overwritten we should send a 204, otherwise a 201 | ||||
|       $response->setHeader('Content-Length', '0'); | ||||
|       $response->setStatus($copyInfo['destinationExists'] ? 204 : 201); | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * HTTP REPORT method implementation | ||||
|      * | ||||
|      * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) | ||||
|      * It's used in a lot of extensions, so it made sense to implement it into the core. | ||||
|      * | ||||
|      * @param RequestInterface $request | ||||
|      * @param ResponseInterface $response | ||||
|      * @return bool | ||||
|      */ | ||||
|     function httpReport(RequestInterface $request, ResponseInterface $response) { | ||||
|  | ||||
|       $path = $request->getPath(); | ||||
|  | ||||
|       $result = $this->server->xml->parse( | ||||
|         $request->getBody(), | ||||
|         $request->getUrl(), | ||||
|         $rootElementName | ||||
|       ); | ||||
|  | ||||
|       if ($this->server->emit('report', [$rootElementName, $result, $path])) { | ||||
|  | ||||
|         // If emit returned true, it means the report was not supported | ||||
|         throw new Exception\ReportNotSupported(); | ||||
|  | ||||
|       } | ||||
|  | ||||
|       // Sending back false will interupt the event chain and tell the server | ||||
|       // we've handled this method. | ||||
|       return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method is called during property updates. | ||||
|      * | ||||
|      * Here we check if a user attempted to update a protected property and | ||||
|      * ensure that the process fails if this is the case. | ||||
|      * | ||||
|      * @param string $path | ||||
|      * @param PropPatch $propPatch | ||||
|      * @return void | ||||
|      */ | ||||
|     function propPatchProtectedPropertyCheck($path, PropPatch $propPatch) { | ||||
|  | ||||
|       // Comparing the mutation list to the list of propetected properties. | ||||
|       $mutations = $propPatch->getMutations(); | ||||
|  | ||||
|       $protected = array_intersect( | ||||
|         $this->server->protectedProperties, | ||||
|         array_keys($mutations) | ||||
|       ); | ||||
|  | ||||
|       if ($protected) { | ||||
|         $propPatch->setResultCode($protected, 403); | ||||
|       } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method is called during property updates. | ||||
|      * | ||||
|      * Here we check if a node implements IProperties and let the node handle | ||||
|      * updating of (some) properties. | ||||
|      * | ||||
|      * @param string $path | ||||
|      * @param PropPatch $propPatch | ||||
|      * @return void | ||||
|      */ | ||||
|     function propPatchNodeUpdate($path, PropPatch $propPatch) { | ||||
|  | ||||
|       // This should trigger a 404 if the node doesn't exist. | ||||
|       $node = $this->server->tree->getNodeForPath($path); | ||||
|  | ||||
|       if ($node instanceof IProperties) { | ||||
|         $node->propPatch($propPatch); | ||||
|       } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method is called when properties are retrieved. | ||||
|      * | ||||
|      * Here we add all the default properties. | ||||
|      * | ||||
|      * @param PropFind $propFind | ||||
|      * @param INode $node | ||||
|      * @return void | ||||
|      */ | ||||
|     function propFind(PropFind $propFind, INode $node) { | ||||
|  | ||||
|       $propFind->handle('{DAV:}getlastmodified', function() use ($node) { | ||||
|         $lm = $node->getLastModified(); | ||||
|         if ($lm) { | ||||
|           return new Xml\Property\GetLastModified($lm); | ||||
|         } | ||||
|       }); | ||||
|  | ||||
|       if ($node instanceof IFile) { | ||||
|         $propFind->handle('{DAV:}getcontentlength', [$node, 'getSize']); | ||||
|         $propFind->handle('{DAV:}getetag', [$node, 'getETag']); | ||||
|         $propFind->handle('{DAV:}getcontenttype', [$node, 'getContentType']); | ||||
|       } | ||||
|  | ||||
|       if ($node instanceof IQuota) { | ||||
|         $quotaInfo = null; | ||||
|         $propFind->handle('{DAV:}quota-used-bytes', function() use (&$quotaInfo, $node) { | ||||
|           $quotaInfo = $node->getQuotaInfo(); | ||||
|           return $quotaInfo[0]; | ||||
|         }); | ||||
|         $propFind->handle('{DAV:}quota-available-bytes', function() use (&$quotaInfo, $node) { | ||||
|           if (!$quotaInfo) { | ||||
|             $quotaInfo = $node->getQuotaInfo(); | ||||
|           } | ||||
|           return $quotaInfo[1]; | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|       $propFind->handle('{DAV:}supported-report-set', function() use ($propFind) { | ||||
|         $reports = []; | ||||
|         foreach ($this->server->getPlugins() as $plugin) { | ||||
|           $reports = array_merge($reports, $plugin->getSupportedReportSet($propFind->getPath())); | ||||
|         } | ||||
|         return new Xml\Property\SupportedReportSet($reports); | ||||
|       }); | ||||
|       $propFind->handle('{DAV:}resourcetype', function() use ($node) { | ||||
|         return new Xml\Property\ResourceType($this->server->getResourceTypeForNode($node)); | ||||
|       }); | ||||
|       $propFind->handle('{DAV:}supported-method-set', function() use ($propFind) { | ||||
|         return new Xml\Property\SupportedMethodSet( | ||||
|           $this->server->getAllowedMethods($propFind->getPath()) | ||||
|         ); | ||||
|       }); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Fetches properties for a node. | ||||
|      * | ||||
|      * This event is called a bit later, so plugins have a chance first to | ||||
|      * populate the result. | ||||
|      * | ||||
|      * @param PropFind $propFind | ||||
|      * @param INode $node | ||||
|      * @return void | ||||
|      */ | ||||
|     function propFindNode(PropFind $propFind, INode $node) { | ||||
|  | ||||
|       if ($node instanceof IProperties && $propertyNames = $propFind->get404Properties()) { | ||||
|  | ||||
|         $nodeProperties = $node->getProperties($propertyNames); | ||||
|         foreach ($propertyNames as $propertyName) { | ||||
|           if (array_key_exists($propertyName, $nodeProperties)) { | ||||
|             $propFind->set($propertyName, $nodeProperties[$propertyName], 200); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|       } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method is called when properties are retrieved. | ||||
|      * | ||||
|      * This specific handler is called very late in the process, because we | ||||
|      * want other systems to first have a chance to handle the properties. | ||||
|      * | ||||
|      * @param PropFind $propFind | ||||
|      * @param INode $node | ||||
|      * @return void | ||||
|      */ | ||||
|     function propFindLate(PropFind $propFind, INode $node) { | ||||
|  | ||||
|       $propFind->handle('{http://calendarserver.org/ns/}getctag', function() use ($propFind) { | ||||
|  | ||||
|         // If we already have a sync-token from the current propFind | ||||
|         // request, we can re-use that. | ||||
|         $val = $propFind->get('{http://sabredav.org/ns}sync-token'); | ||||
|         if ($val) return $val; | ||||
|  | ||||
|         $val = $propFind->get('{DAV:}sync-token'); | ||||
|         if ($val && is_scalar($val)) { | ||||
|           return $val; | ||||
|         } | ||||
|         if ($val && $val instanceof Xml\Property\Href) { | ||||
|           return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX)); | ||||
|         } | ||||
|  | ||||
|         // If we got here, the earlier two properties may simply not have | ||||
|         // been part of the earlier request. We're going to fetch them. | ||||
|         $result = $this->server->getProperties($propFind->getPath(), [ | ||||
|           '{http://sabredav.org/ns}sync-token', | ||||
|           '{DAV:}sync-token', | ||||
|         ]); | ||||
|  | ||||
|         if (isset($result['{http://sabredav.org/ns}sync-token'])) { | ||||
|           return $result['{http://sabredav.org/ns}sync-token']; | ||||
|         } | ||||
|         if (isset($result['{DAV:}sync-token'])) { | ||||
|           $val = $result['{DAV:}sync-token']; | ||||
|           if (is_scalar($val)) { | ||||
|             return $val; | ||||
|           } elseif ($val instanceof Xml\Property\Href) { | ||||
|             return substr($val->getHref(), strlen(Sync\Plugin::SYNCTOKEN_PREFIX)); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|       }); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a bunch of meta-data about the plugin. | ||||
|      * | ||||
|      * Providing this information is optional, and is mainly displayed by the | ||||
|      * Browser plugin. | ||||
|      * | ||||
|      * The description key in the returned array may contain html and will not | ||||
|      * be sanitized. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     function getPluginInfo() { | ||||
|  | ||||
|       return [ | ||||
|         'name'        => $this->getPluginName(), | ||||
|         'description' => 'The Core plugin provides syncronization between tasks and iCAL events', | ||||
|         'link'        => null, | ||||
|       ]; | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										86
									
								
								libs/TaskwarriorCalendarEvent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								libs/TaskwarriorCalendarEvent.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Aerex\Taskwarrior; | ||||
|  | ||||
| use Aerex\Taskwarrior\Config; | ||||
| use DavidBadura\Taskwarrior\Taskwarrior; | ||||
| use Sabre\VObject\Component\VCalendar; | ||||
|  | ||||
| class TaskwarriorCalendarEvent { | ||||
|  | ||||
|   /** | ||||
|    * @var Config | ||||
|    */ | ||||
|   private $config; | ||||
|  | ||||
|   /** | ||||
|    * @var string | ||||
|    *  | ||||
|    */ | ||||
|     private $taskrc; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|  | ||||
|      private $taskDataDir; | ||||
|    | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|  | ||||
|      private $taskBinFile; | ||||
|  | ||||
|  | ||||
|      /** | ||||
|       * @var Taskwarrior | ||||
|       */ | ||||
|  | ||||
|       private $taskwarrior; | ||||
|  | ||||
|  | ||||
|   public function __construct(){ | ||||
|     $this->config = new Config(); | ||||
|  | ||||
|     if($this->config.isNotValidConfiguration()){ | ||||
|       $invalidConfigurationString = $this->config.invalidConfigurations(); | ||||
|  | ||||
|       $invalidConfigurationMessage = sprintf('The following configurations are invalid %s and' . | ||||
|        ' the default configurations will be used', $invalidConfigurationString); | ||||
|       echo($invalidConfigurationMessage); | ||||
|  | ||||
|       $this->config.setDefaults(); | ||||
|     } | ||||
|  | ||||
|     $this->taskrc = $this->config->getTaskRC(); | ||||
|     $this->taskDataDir = $this->config->getTaskDataDir(); | ||||
|     $this->taskBinFile = $this->config->getTaskBinFile(); | ||||
|  | ||||
|     $this->taskwarrior = $taskwarrior($this->taskrc,$this->taskDataDir, [], $this->taskBinFile); | ||||
|  | ||||
|   } | ||||
|  | ||||
|  | ||||
|      public function buildCalendarEvent(VCalendar $vEvent){ | ||||
|  | ||||
|       return; | ||||
|      } | ||||
|  | ||||
|      public function buildToDoEvent(VCalendar $vToDo){ | ||||
|  | ||||
|        return; | ||||
|      } | ||||
|  | ||||
|    | ||||
|  | ||||
|    | ||||
|  | ||||
|  | ||||
|    | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| ?> | ||||
		Reference in New Issue
	
	Block a user