From 46d23633a9dccd6aa9e99134f815864bd74c1639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimund=20Schl=C3=BC=C3=9Fler?= Date: Sun, 19 Jul 2015 14:05:29 +0200 Subject: [PATCH] CleanupBackend --- appinfo/app.php | 6 +- appinfo/application.php | 61 +++- appinfo/routes.php | 4 +- appinfo/update.php | 20 ++ controller/collectionscontroller.php | 41 ++- controller/helper.php | 322 --------------------- controller/listscontroller.php | 41 ++- controller/pagecontroller.php | 22 +- controller/searchcontroller.php | 93 ------ controller/searchprovider.php | 51 ++++ controller/settingscontroller.php | 41 ++- controller/taskscontroller.php | 55 ++-- db/tasks.php | 42 +++ db/tasksmapper.php | 43 +++ service/collectionsservice.php | 44 ++- service/commentsservice.php | 122 ++++++++ service/helper.php | 319 ++++++++++++++++++++ service/listsservice.php | 116 ++++---- service/reminderservice.php | 228 +++++++++++++++ service/settingsservice.php | 23 +- service/taskparser.php | 169 +++++++++++ service/tasksservice.php | 417 ++++++++------------------- 22 files changed, 1404 insertions(+), 876 deletions(-) delete mode 100644 controller/helper.php delete mode 100644 controller/searchcontroller.php create mode 100644 controller/searchprovider.php create mode 100644 db/tasks.php create mode 100644 db/tasksmapper.php create mode 100644 service/commentsservice.php create mode 100644 service/helper.php create mode 100644 service/reminderservice.php create mode 100644 service/taskparser.php diff --git a/appinfo/app.php b/appinfo/app.php index b568ab0e..2ba8fe03 100644 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -1,10 +1,9 @@ . * */ + namespace OCA\Tasks\AppInfo; if(\OCP\App::isEnabled('calendar')) { @@ -40,4 +40,4 @@ if(\OCP\App::isEnabled('calendar')) { } else { $msg = 'Can not enable the Tasks app because the Calendar App is disabled.'; \OCP\Util::writeLog('tasks', $msg, \OCP\Util::ERROR); -} \ No newline at end of file +} diff --git a/appinfo/application.php b/appinfo/application.php index 8b3e8eb6..5471eaa1 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -3,7 +3,7 @@ * ownCloud - Tasks * * @author Raimund Schlüßler -* @copyright 2014 Raimund Schlüßler raimund.schluessler@googlemail.com +* @copyright 2015 Raimund Schlüßler raimund.schluessler@googlemail.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -29,9 +29,15 @@ use \OCA\Tasks\Controller\ListsController; use \OCA\Tasks\Controller\SettingsController; use \OCA\Tasks\Controller\TasksController; use \OCA\Tasks\Service\TasksService; +use \OCA\Tasks\Service\SearchService; use \OCA\Tasks\Service\ListsService; use \OCA\Tasks\Service\CollectionsService; use \OCA\Tasks\Service\SettingsService; +use \OCA\Tasks\Service\Helper; +use \OCA\Tasks\Service\TaskParser; +use \OCA\Tasks\Service\ReminderService; +use \OCA\Tasks\Service\CommentsService; +use \OCA\Tasks\Db\TasksMapper; class Application extends App { @@ -80,7 +86,9 @@ class Application extends App { return new TasksController( $c->query('AppName'), $c->query('Request'), - $c->query('TasksService') + $c->query('TasksService'), + $c->query('ReminderService'), + $c->query('CommentsService') ); }); @@ -90,7 +98,10 @@ class Application extends App { */ $container->registerService('TasksService', function($c) { return new TasksService( - $c->query('UserId') + $c->query('UserId'), + $c->query('TasksMapper'), + $c->query('Helper'), + $c->query('TaskParser') ); }); @@ -117,6 +128,39 @@ class Application extends App { ); }); + $container->registerService('SearchService', function($c) { + return new SearchService( + $c->query('TasksMapper'), + $c->query('Helper'), + $c->query('TaskParser') + ); + }); + + $container->registerService('TaskParser', function($c) { + return new TaskParser( + $c->query('ReminderService'), + $c->query('Helper') + ); + }); + + $container->registerService('ReminderService', function($c) { + return new ReminderService( + $c->query('Helper') + ); + }); + + $container->registerService('CommentsService', function($c) { + return new CommentsService( + $c->query('UserId'), + $c->query('Helper') + ); + }); + + $container->registerService('Helper', function() { + return new Helper( + ); + }); + /** * Core */ @@ -132,7 +176,16 @@ class Application extends App { return $c->query('ServerContainer')->getConfig(); }); - \OC::$server->getSearch()->registerProvider('OCA\Tasks\Controller\SearchController', array('apps' => array('tasks'))); + \OC::$server->getSearch()->registerProvider('OCA\Tasks\Controller\SearchProvider', array('apps' => array('tasks'))); + + /** + * Database Layer + */ + $container->registerService('TasksMapper', function($c) { + return new TasksMapper( + $c->query('ServerContainer')->getDb() + ); + }); } diff --git a/appinfo/routes.php b/appinfo/routes.php index 89cb6bbd..fe3a6dcc 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -3,7 +3,7 @@ * ownCloud - Tasks * * @author Raimund Schlüßler -* @copyright 2014 Raimund Schlüßler raimund.schluessler@googlemail.com +* @copyright 2015 Raimund Schlüßler raimund.schluessler@googlemail.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -63,4 +63,4 @@ $application->registerRoutes($this, array('routes' => array( // settings array('name' => 'settings#get', 'url' => '/settings', 'verb' => 'GET'), array('name' => 'settings#set', 'url' => '/settings/{type}/{setting}/{value}', 'verb' => 'POST'), -))); \ No newline at end of file +))); diff --git a/appinfo/update.php b/appinfo/update.php index 803a0550..d07fca0d 100644 --- a/appinfo/update.php +++ b/appinfo/update.php @@ -1,4 +1,24 @@ . +* +*/ $installedVersionTasks=OCP\Config::getAppValue('tasks', 'installed_version'); $installedVersionTasksEnhanced=OCP\Config::getAppValue('tasks_enhanced', 'installed_version'); diff --git a/controller/collectionscontroller.php b/controller/collectionscontroller.php index 5da78711..43946a96 100644 --- a/controller/collectionscontroller.php +++ b/controller/collectionscontroller.php @@ -1,25 +1,24 @@ . -* -*/ + * ownCloud - Tasks + * + * @author Raimund Schlüßler + * @copyright 2015 Raimund Schlüßler raimund.schluessler@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ namespace OCA\Tasks\Controller; @@ -55,4 +54,4 @@ class CollectionsController extends Controller { return $this->collectionsService->setVisibility($collectionID, $visibility); }); } -} \ No newline at end of file +} diff --git a/controller/helper.php b/controller/helper.php deleted file mode 100644 index d5b7c1a8..00000000 --- a/controller/helper.php +++ /dev/null @@ -1,322 +0,0 @@ -. - * - */ - -namespace OCA\Tasks\Controller; - -use Sabre\VObject; -// use OCA\Tasks\App; - -Class Helper { - - public static function parseVTODO($task) { - $object = \Sabre\VObject\Reader::read($task['calendardata']); - if(!$object) { - return false; - } - $sharedAccessClassPermissions = \OC_Calendar_Object::getAccessClassPermissions($object); - if(\OC_Calendar_Object::getowner($task['id']) !== \OC::$server->getUserSession()->getUser()->getUID()){ - if (!($sharedAccessClassPermissions & \OCP\Constants::PERMISSION_READ)) { - return false; - } - } - $object = \OC_Calendar_Object::cleanByAccessClass($task['id'], $object); - - $vtodo = $object->VTODO; - return $vtodo; - } - - public static function arrayForJSON($id, $vtodo, $user_timezone, $calendarId){ - $task = array( 'id' => $id ); - $task['calendarid'] = $calendarId; - $task['type'] = 'task'; - $task['name'] = (string) $vtodo->SUMMARY; - $task['created'] = (string) $vtodo->CREATED; - $task['note'] = (string) $vtodo->DESCRIPTION; - $task['location'] = (string) $vtodo->LOCATION; - $categories = $vtodo->CATEGORIES; - if ($categories){ - $task['categories'] = $categories->getParts(); - } - $start = $vtodo->DTSTART; - if ($start) { - try { - $start = $start->getDateTime(); - $start->setTimezone(new \DateTimeZone($user_timezone)); - $task['start'] = $start->format('Ymd\THis'); - } catch(\Exception $e) { - $task['start'] = null; - \OCP\Util::writeLog('tasks', $e->getMessage(), \OCP\Util::ERROR); - } - } else { - $task['start'] = null; - } - $due = $vtodo->DUE; - if ($due) { - try { - $due = $due->getDateTime(); - $due->setTimezone(new \DateTimeZone($user_timezone)); - $task['due'] = $due->format('Ymd\THis'); - } catch(\Exception $e) { - $task['due'] = null; - \OCP\Util::writeLog('tasks', $e->getMessage(), \OCP\Util::ERROR); - } - } else { - $task['due'] = null; - } - $reminder = $vtodo->VALARM; - if($reminder) { - try { - - if ($reminder->TRIGGER['VALUE']){ - $reminderType = $reminder->TRIGGER['VALUE']->getValue(); - } else { - throw new \Exception('Reminder type not specified.'); - } - if ($reminder->ACTION) { - $reminderAction = $reminder->ACTION->getValue(); - } else { - throw new \Exception('Reminder action not specified.'); - } - $reminderDate = null; - $reminderDuration = null; - - - if($reminderType == 'DATE-TIME'){ - $reminderDate = $reminder->TRIGGER->getDateTime(); - $reminderDate->setTimezone(new \DateTimeZone($user_timezone)); - $reminderDate = $reminderDate->format('Ymd\THis'); - } elseif ($reminderType == 'DURATION' && ($start || $due)) { - - $parsed = VObject\DateTimeParser::parseDuration($reminder->TRIGGER,true); - // Calculate the reminder date from duration and start date - $related = null; - if(is_object($reminder->TRIGGER['RELATED'])){ - $related = $reminder->TRIGGER['RELATED']->getValue(); - if($related == 'END' && $due){ - $reminderDate = $due->modify($parsed)->format('Ymd\THis'); - } else { - throw new \Exception('Reminder duration related to not available date.'); - } - } elseif ($start) { - $reminderDate = $start->modify($parsed)->format('Ymd\THis'); - } else{ - throw new \Exception('Reminder duration related to not available date.'); - } - preg_match('/^(?P\+|-)?P((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$/', $reminder->TRIGGER, $matches); - $invert = false; - if ($matches['plusminus']==='-') { - $invert = true; - } - - $parts = array( - 'week', - 'day', - 'hour', - 'minute', - 'second', - ); - - $reminderDuration = array( - 'token' => null - ); - foreach($parts as $part) { - $matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0; - $reminderDuration[$part] = $matches[$part]; - if($matches[$part] && !$reminderDuration['token']){ - $reminderDuration['token'] = $part; - } - } - if($reminderDuration['token'] == null){ - $reminderDuration['token'] = $parts[0]; - } - - $reminderDuration['params'] = array( - 'id' => (int)$invert.(int)($related == 'END'), - 'related'=> $related?$related:'START', - 'invert'=> $invert - ); - - } else { - $reminderDate = null; - $reminderDuration = null; - } - - $task['reminder'] = array( - 'type' => $reminderType, - 'action' => $reminderAction, - 'date' => $reminderDate, - 'duration' => $reminderDuration - ); - - } catch(\Exception $e) { - $task['reminder'] = null; - \OCP\Util::writeLog('tasks', $e->getMessage(), \OCP\Util::ERROR); - } - } else { - $task['reminder'] = null; - } - $priority = $vtodo->PRIORITY; - if(isset($priority)){ - $priority = (10 - $priority->getValue()) % 10; - $task['priority'] = (string) $priority; - if($priority > 5){ - $task['starred'] = true; - } - } else { - $task['priority'] = '0'; - $task['starred'] = false; - } - $completed = $vtodo->COMPLETED; - if ($completed) { - try { - $completed = $completed->getDateTime(); - $completed->setTimezone(new \DateTimeZone($user_timezone)); - $task['completed_date'] = $completed->format('Ymd\THis'); - $task['completed'] = true; - } catch(\Exception $e) { - $task['completed'] = false; - \OCP\Util::writeLog('tasks', $e->getMessage(), \OCP\Util::ERROR); - } - } else { - $task['completed'] = false; - } - - - $percentComplete = $vtodo->{'PERCENT-COMPLETE'}; - if($percentComplete){ - $task['complete'] = $percentComplete->getValue(); - } else { - $task['complete'] = '0'; - } - - - $comments = $vtodo->COMMENT; - if($comments){ - $comments_parsed = array(); - foreach($comments as $com) { - // parse time - $time = $com['X-OC-DATE-TIME']; - if ($time) { - $time = new \DateTime($time); - $time->setTimezone(new \DateTimeZone($user_timezone)); - $time = $time->format('Ymd\THis'); - } - // parse comment ID - $comID = $com['X-OC-ID']; - if ($comID) { - $comID = $com['X-OC-ID']->getValue(); - } - // parse user ID - $userID = $com['X-OC-USERID']; - if ($userID) { - $userID = (string) $com['X-OC-USERID']->getValue(); - } - $user = \OC::$server->getUserManager()->get($userID); - $userName = $userID; - if ($user){ - $userName = $user->getDisplayName(); - } - $comments_parsed[] = array( - 'id' => $comID, - 'userID' => $userID, - 'name' => $userName, - 'comment' => $com->getValue(), - 'time' => $time - ); - } - $task['comments'] = $comments_parsed; - } - return $task; - } - - - public static function createVCalendarFromRequest($request){ - $vcalendar = new \Sabre\VObject\Component\VCalendar(); - $vcalendar->PRODID = 'ownCloud Calendar'; - $vcalendar->VERSION = '2.0'; - - $vtodo = $vcalendar->createComponent('VTODO'); - $vcalendar->add($vtodo); - - $vtodo->CREATED = new \DateTime('now', new \DateTimeZone('UTC')); - - $vtodo->UID = \Sabre\VObject\UUIDUtil::getUUID(); - return self::updateVCalendarFromRequest($request, $vcalendar); - } - - public static function updateVCalendarFromRequest($request, $vcalendar){ - $vtodo = $vcalendar->VTODO; - - $lastModified = $vtodo->{'LAST-MODIFIED'}; - if(is_null($lastModified)) { - $lastModified = $vtodo->add('LAST-MODIFIED'); - } - $lastModified->setValue(new \DateTime('now', new \DateTimeZone('UTC'))); - $vtodo->DTSTAMP = new \DateTime('now', new \DateTimeZone('UTC')); - $vtodo->SUMMARY = $request['summary']; - - if($request['location']){ - $vtodo->LOCATION = $request['location']; - } - if ($request['description']){ - $vtodo->DESCRIPTION = $request['description']; - } - if($request["categories"]){ - $vtodo->CATEGORIES = $request["categories"]; - } - if($request['priority']) { - $vtodo->PRIORITY = 5; // prio: medium - } else { - $vtodo->PRIORITY = 0; // prio: undefined - } - $percentComplete = $vtodo->{'PERCENT-COMPLETE'}; - if (is_null($percentComplete)) { - $percentComplete = $vtodo->add('PERCENT-COMPLETE'); - } - if (isset($request['complete'])) { - $percentComplete->setValue($request['complete']); - } else { - $percentComplete->setValue('0'); - } - - $due = $request['due']; - if ($due) { - $timezone = \OC_Calendar_App::getTimezone(); - $timezone = new \DateTimeZone($timezone); - $due = new \DateTime($due, $timezone); - $vtodo->DUE = $due; - } else { - unset($vtodo->DUE); - } - $start = $request['start']; - if ($start) { - $timezone = \OC_Calendar_App::getTimezone(); - $timezone = new \DateTimeZone($timezone); - $start = new \DateTime($start, $timezone); - $vtodo->DTSTART = $start; - } else { - unset($vtodo->DTSTART); - } - - return $vcalendar; - } -} diff --git a/controller/listscontroller.php b/controller/listscontroller.php index d3109e64..a13da795 100644 --- a/controller/listscontroller.php +++ b/controller/listscontroller.php @@ -1,25 +1,24 @@ . -* -*/ + * ownCloud - Tasks + * + * @author Raimund Schlüßler + * @copyright 2015 Raimund Schlüßler raimund.schluessler@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ namespace OCA\Tasks\Controller; @@ -73,4 +72,4 @@ class ListsController extends Controller { return $this->listsService->setName($listID, $name); }); } -} \ No newline at end of file +} diff --git a/controller/pagecontroller.php b/controller/pagecontroller.php index 5d0ebc8f..7b7469d7 100644 --- a/controller/pagecontroller.php +++ b/controller/pagecontroller.php @@ -1,11 +1,23 @@ . * - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. */ namespace OCA\Tasks\Controller; diff --git a/controller/searchcontroller.php b/controller/searchcontroller.php deleted file mode 100644 index 8a2af6ee..00000000 --- a/controller/searchcontroller.php +++ /dev/null @@ -1,93 +0,0 @@ -. - * - */ - -namespace OCA\Tasks\Controller; - -/** - * Tasks search provider - */ -class SearchController extends \OCP\Search\Provider { - - /** - * Search for query in tasks - * - * @param string $query - * @return array list of \OCA\Tasks\Controller\Task - */ - function search($query) { - $calendars = \OC_Calendar_Calendar::allCalendars(\OC::$server->getUserSession()->getUser()->getUID(), true); - $user_timezone = \OC_Calendar_App::getTimezone(); - // check if the calenar is enabled - if (count($calendars) == 0 || !\OCP\App::isEnabled('tasks')) { - return array(); - } - $results = array(); - foreach ($calendars as $calendar) { - // $calendar_entries = \OC_Calendar_Object::all($calendar['id']); - $objects = \OC_Calendar_Object::all($calendar['id']); - // $date = strtotime($query); - // // search all calendar objects, one by one - foreach ($objects as $object) { - // skip non-todos - if ($object['objecttype'] != 'VTODO') { - continue; - } - if(!($vtodo = Helper::parseVTODO($object))){ - continue; - } - $id = $object['id']; - $calendarId = $object['calendarid']; - - // check these properties - $properties = array('SUMMARY', 'DESCRIPTION', 'LOCATION', 'CATEGORIES'); - - foreach ($properties as $property) { - $string = $vtodo->{$property}; - if (stripos($string, $query) !== false) { - // $results[] = new \OCA\Tasks\Controller\Task($id,$calendarId,$vtodo,$property,$query,$user_timezone); - $results[] = Helper::arrayForJSON($id, $vtodo, $user_timezone, $calendarId); - continue 2; - } - } - $comments = $vtodo->COMMENT; - if($comments) { - foreach($comments as $com) { - if (stripos($com->getValue(), $query) !== false) { - // $results[] = new \OCA\Tasks\Controller\Task($id,$calendarId,$vtodo,'COMMENTS',$query,$user_timezone); - $results[] = Helper::arrayForJSON($id, $vtodo, $user_timezone, $calendarId); - continue 2; - } - } - } - } - } - usort($results, array($this, 'sort_completed')); - return $results; - } - - private static function sort_completed($a, $b){ - $t1 = $a['completed']; - $t2 = $b['completed']; - if ($t1 == $t2) { - return 0; - } - return $t1 > $t2 ? 1 : -1; - } -} diff --git a/controller/searchprovider.php b/controller/searchprovider.php new file mode 100644 index 00000000..4a2b24b7 --- /dev/null +++ b/controller/searchprovider.php @@ -0,0 +1,51 @@ +. + * + */ + +namespace OCA\Tasks\Controller; + +use OCA\Tasks\AppInfo\Application; + +/** + * Tasks search provider + */ +class SearchProvider extends \OCP\Search\Provider { + + private $tasksService; + + public function __construct() { + $app = new Application(); + $container = $app->getContainer(); + $this->app = $app; + $this->tasksService = $container->query('TasksService'); + } + + + /** + * Search for query in tasks + * + * @param string $query + * @return array + */ + public function search($query) { + return $this->tasksService->search($query); + } +} diff --git a/controller/settingscontroller.php b/controller/settingscontroller.php index 88801130..e8d220c3 100644 --- a/controller/settingscontroller.php +++ b/controller/settingscontroller.php @@ -1,25 +1,24 @@ . -* -*/ + * ownCloud - Tasks + * + * @author Raimund Schlüßler + * @copyright 2015 Raimund Schlüßler raimund.schluessler@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ namespace OCA\Tasks\Controller; @@ -55,4 +54,4 @@ class SettingsController extends Controller { return $this->settingsService->set($setting, $type, $value); }); } -} \ No newline at end of file +} diff --git a/controller/taskscontroller.php b/controller/taskscontroller.php index 0f8b4794..71a48f24 100644 --- a/controller/taskscontroller.php +++ b/controller/taskscontroller.php @@ -1,29 +1,30 @@ . -* -*/ + * ownCloud - Tasks + * + * @author Raimund Schlüßler + * @copyright 2015 Raimund Schlüßler raimund.schluessler@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ namespace OCA\Tasks\Controller; use \OCA\Tasks\Service\TasksService; +use \OCA\Tasks\Service\ReminderService; +use \OCA\Tasks\Service\CommentsService; use \OCP\IRequest; use \OCP\AppFramework\Controller; @@ -31,12 +32,16 @@ use \OCP\AppFramework\Controller; class TasksController extends Controller { private $tasksService; + private $reminderService; + private $commentsService; use Response; - public function __construct($appName, IRequest $request, TasksService $tasksService){ + public function __construct($appName, IRequest $request, TasksService $tasksService, ReminderService $reminderService, CommentsService $commentsService){ parent::__construct($appName, $request); $this->tasksService = $tasksService; + $this->reminderService = $reminderService; + $this->commentsService = $commentsService; } /** @@ -53,7 +58,7 @@ class TasksController extends Controller { */ public function getTask($taskID){ return $this->generateResponse(function () use ($taskID) { - return $this->tasksService->get($taskID); + return $this->tasksService->getTask($taskID); }); } @@ -143,7 +148,7 @@ class TasksController extends Controller { */ public function setReminderDate($taskID, $type, $action, $date, $invert, $related = null, $week, $day, $hour, $minute, $second){ return $this->generateResponse(function () use ($taskID, $type, $action, $date, $invert, $related, $week, $day, $hour, $minute, $second) { - return $this->tasksService->setReminderDate($taskID, $type, $action, $date, $invert, $related, $week, $day, $hour, $minute, $second); + return $this->reminderService->createReminder($taskID, $type, $action, $date, $invert, $related, $week, $day, $hour, $minute, $second); }); } @@ -179,7 +184,7 @@ class TasksController extends Controller { */ public function addComment($taskID, $comment, $tmpID){ return $this->generateResponse(function () use ($taskID, $comment, $tmpID) { - return $this->tasksService->addComment($taskID, $comment, $tmpID); + return $this->commentsService->addComment($taskID, $comment, $tmpID); }); } @@ -188,7 +193,7 @@ class TasksController extends Controller { */ public function deleteComment($taskID, $commentID){ return $this->generateResponse(function () use ($taskID, $commentID) { - return $this->tasksService->deleteComment($taskID, $commentID); + return $this->commentsService->deleteComment($taskID, $commentID); }); } } diff --git a/db/tasks.php b/db/tasks.php new file mode 100644 index 00000000..a7df744e --- /dev/null +++ b/db/tasks.php @@ -0,0 +1,42 @@ +. + * + */ + +namespace OCA\Tasks\Db; + +use \OCP\AppFramework\Db\Entity; + +class Tasks extends Entity { + + public $calendarid; + public $objecttype; + public $startdate; + public $enddate; + public $repeating; + public $summary; + public $calendardata; + public $uri; + public $lastmodified; + + public function __construct() { + $this->addType('calendarid', 'integer'); + } +} diff --git a/db/tasksmapper.php b/db/tasksmapper.php new file mode 100644 index 00000000..da648b38 --- /dev/null +++ b/db/tasksmapper.php @@ -0,0 +1,43 @@ +. + * + */ + +namespace OCA\Tasks\Db; + +use OCP\IDb; +use OCP\AppFramework\Db\Mapper; + +class TasksMapper extends Mapper { + + public function __construct(IDb $db) { + parent::__construct($db, 'tasks_tasks', '\OCA\Tasks\Db\Tasks'); + } + + public function findAllVTODOs($calendarID, $limit=null, $offset=null) { + $sql = 'SELECT * FROM `*PREFIX*clndr_objects` WHERE `calendarid` = ? AND `objecttype`= ?'; + return $this->findEntities($sql, array($calendarID, 'VTODO'), $limit, $offset); + } + + public function findVTODOById($taskID, $limit=null, $offset=null) { + $sql = 'SELECT * FROM `*PREFIX*clndr_objects` WHERE `id` = ? AND `objecttype`= ?'; + return $this->findEntity($sql, array($taskID, 'VTODO'), $limit, $offset); + } +} diff --git a/service/collectionsservice.php b/service/collectionsservice.php index d098ca96..ac013cd3 100644 --- a/service/collectionsservice.php +++ b/service/collectionsservice.php @@ -1,4 +1,24 @@ . + * + */ namespace OCA\Tasks\Service; use OCP\IConfig; @@ -50,31 +70,27 @@ class CollectionsService { 'show' => 2) ); foreach ($collections as $key => $collection){ - try{ - $tmp = $this->settings->getUserValue($this->userId, $this->appName,'show_'.$collection['id']); - if (!in_array($tmp, array('0','1','2'))) { - $this->settings->setUserValue($this->userId, $this->appName,'show_'.$collection['id'],$collections[$key]['show']); - } else { - $collections[$key]['show'] = (int)$tmp; - } - } catch(\Exception $e) { - \OCP\Util::writeLog($this->appName, $e->getMessage(), \OCP\Util::ERROR); + $tmp = $this->settings->getUserValue($this->userId, $this->appName,'show_'.$collection['id']); + if (!in_array($tmp, array('0','1','2'))) { + $this->settings->setUserValue($this->userId, $this->appName,'show_'.$collection['id'],$collections[$key]['show']); + } else { + $collections[$key]['show'] = (int)$tmp; } } return $collections; } /** - * set the visibility of a list by id + * set the visibility of a collection by collectionID * - * @param int $id + * @param int $collectionID * @param int $visibility * @return bool */ - public function setVisibility($id, $visibility){ + public function setVisibility($collectionID, $visibility){ if (in_array($visibility, array(0,1,2))){ - $this->settings->setUserValue($this->userId, $this->appName,'show_'.$id,$visibility); + $this->settings->setUserValue($this->userId, $this->appName,'show_'.$collectionID,$visibility); } return true; } -} \ No newline at end of file +} diff --git a/service/commentsservice.php b/service/commentsservice.php new file mode 100644 index 00000000..bdc08db3 --- /dev/null +++ b/service/commentsservice.php @@ -0,0 +1,122 @@ +. + * + */ + +namespace OCA\Tasks\Service; + +use \OCA\Tasks\Service\Helper; + +Class CommentsService { + + private $userId; + private $helper; + + public function __construct($userId, Helper $helper){ + $this->userId = $userId; + $this->helper = $helper; + } + + /** + * add comment to task by id + * @param int $taskID + * @param string $comment + * @param int $tmpID + * @return array + * @throws \Exception + */ + public function addComment($taskID, $comment, $tmpID){ + $vcalendar = \OC_Calendar_App::getVCalendar($taskID); + $vtodo = $vcalendar->VTODO; + + if($vtodo->COMMENT == "") { + // if this is the first comment set the id to 0 + $commentId = 0; + } else { + // Determine new commentId by looping through all comments + $commentIds = array(); + foreach($vtodo->COMMENT as $com) { + $commentIds[] = (int)$com['X-OC-ID']->getValue(); + } + $commentId = 1+max($commentIds); + } + + $now = new \DateTime(); + $vtodo->add('COMMENT',$comment, + array( + 'X-OC-ID' => $commentId, + 'X-OC-USERID' => $this->userId, + 'X-OC-DATE-TIME' => $now->format('Ymd\THis\Z') + ) + ); + $this->helper->editVCalendar($vcalendar, $taskID); + $user_timezone = \OC_Calendar_App::getTimezone(); + $now->setTimezone(new \DateTimeZone($user_timezone)); + $comment = array( + 'taskID' => $taskID, + 'id' => $commentId, + 'tmpID' => $tmpID, + 'name' => \OC::$server->getUserManager()->get($this->userId)->getDisplayName(), + 'userID' => $this->userId, + 'comment' => $comment, + 'time' => $now->format('Ymd\THis') + ); + return $comment; + } + + /** + * delete comment of task by id + * @param int $taskID + * @param int $commentID + * @return bool + * @throws \Exception + */ + public function deleteComment($taskID, $commentID){ + $vcalendar = \OC_Calendar_App::getVCalendar($taskID); + $vtodo = $vcalendar->VTODO; + $commentIndex = $this->getCommentById($vtodo,$commentID); + $comment = $vtodo->children[$commentIndex]; + if($comment['X-OC-USERID']->getValue() == $this->userId){ + unset($vtodo->children[$commentIndex]); + return $this->helper->editVCalendar($vcalendar, $taskID); + } else { + throw new \Exception('Not allowed.'); + } + } + + /** + * sort get comment by ID + * + * @param object $vtodo + * @param string $commentID + * @return int + * @throws \Exception + */ + private function getCommentById($vtodo,$commentID) { + $idx = 0; + foreach ($vtodo->children as $i => &$property) { + if ( $property->name == 'COMMENT' && $property['X-OC-ID']->getValue() == $commentID ) { + return $idx; + } + $idx += 1; + } + throw new \Exception('Commment not found.'); + } +} diff --git a/service/helper.php b/service/helper.php new file mode 100644 index 00000000..aad33c50 --- /dev/null +++ b/service/helper.php @@ -0,0 +1,319 @@ +. + * + */ + +namespace OCA\Tasks\Service; + +use Sabre\VObject; + +Class Helper { + + public function __construct(){ + } + + /** + * check if task is valid + * + * @param \OCA\Tasks\Db\Tasks $task + * @return mixed + */ + public function checkTask($task) { + $object = $this->readTask($task); + if(!$object){ + return false; + } + if(\OC_Calendar_Object::getowner($task->getId()) !== \OC::$server->getUserSession()->getUser()->getUID()){ + $sharedAccessClassPermissions = \OC_Calendar_Object::getAccessClassPermissions($object); + if (!($sharedAccessClassPermissions & \OCP\Constants::PERMISSION_READ)) { + return false; + } + } + $taskID = $task->getId(); + $object = \OC_Calendar_Object::cleanByAccessClass($taskID, $object); + $vtodo = $object->VTODO; + $vtodo->ID = $taskID; + return $vtodo; + } + + /** + * read object from calendar data + * + * @param \OCA\Tasks\Db\Tasks $task + * @return mixed + */ + private function readTask($task){ + if (is_null($task->getSummary())) { + return false; + } + return \Sabre\VObject\Reader::read($task->getCalendardata()); + } + + /** + * select tasks + * + * @param array $tasks + * @param string $type + * @return array + */ + public function selectTasks($tasks, $type) { + $notLoaded = 0; + switch($type){ + case 'init': // Only select uncompleted tasks and the five most recent completed ones + $count = count($tasks); + $tasks_completed = $this->selectCompletedTasks($tasks); + usort($tasks_completed, array($this, 'sortCompletedDate')); + $tasks = array_merge($this->selectUncompletedTasks($tasks), array_slice($tasks_completed,0,5)); + $notLoaded = $count - count($tasks); + break; + case 'completed': + $tasks = $this->selectCompletedTasks($tasks); + break; + case 'uncompleted': + $tasks = $this->selectUncompletedTasks($tasks); + break; + } + return array($notLoaded, $tasks); + } + + /** + * select completed tasks + * + * @param array $tasks + * @return array + */ + private function selectCompletedTasks($tasks) { + return array_filter($tasks, function($task) { + return $task['completed']; + }); + } + + /** + * select uncompleted tasks + * + * @param array $tasks + * @return array + */ + private function selectUncompletedTasks($tasks) { + return array_filter($tasks, function($task) { + return !$task['completed']; + }); + } + + /** + * set property of a task + * + * @param int $taskID + * @param string $property + * @param mixed $value + * @return bool + * @throws \Exception + */ + public function setProperty($taskID,$property,$value){ + $vcalendar = \OC_Calendar_App::getVCalendar($taskID); + $vtodo = $vcalendar->VTODO; + if($value){ + $vtodo->{$property} = $value; + }else{ + unset($vtodo->{$property}); + } + return $this->editVCalendar($vcalendar, $taskID); + } + + /** + * edit VCalendar and set modification dates + * + * @param mixed $vcalendar + * @param string $taskID + * @return bool + * @throws \Exception + */ + public function editVCalendar($vcalendar, $taskID) { + $vtodo = $vcalendar->VTODO; + $vtodo->{'LAST-MODIFIED'}->setValue(new \DateTime('now', new \DateTimeZone('UTC'))); + $vtodo->DTSTAMP = new \DateTime('now', new \DateTimeZone('UTC')); + return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); + } + + /** + * format date + * + * @param string $date + * @return \DateTime + */ + public function createDateFromUNIX($date) { + if (!$date) { + return null; + } + $timezone = \OC_Calendar_App::getTimezone(); + $date = new \DateTime('@'.$date); + return $date->setTimezone(new \DateTimeZone($timezone)); + } + + /** + * parse reminder string + * + * @param string $date + * @return string + */ + public function parseDateString($date) { + $date = new \DateTime($date); + return $this->formatDate($date); + } + + /** + * parse reminder object + * + * @param object $date + * @param string $modifier + * @return string + */ + public function parseDateObject($date, $modifier='+0 days') { + if(!$date) { + return null; + } + return $this->formatDate($date->getDateTime(), $modifier); + } + + /** + * format date object + * + * @param object $date + * @param string $modifier + * @return string + */ + private function formatDate($date, $modifier='+0 days') { + $user_timezone = \OC_Calendar_App::getTimezone(); + $date->setTimezone(new \DateTimeZone($user_timezone)); + return $date->modify($modifier)->format('Ymd\THis'); + } + + /** + * sort tasks + * + * @param array $a + * @param array $b + * @return array + */ + public function sortCompletedDate($a, $b) { + return $this->sort(\DateTime::createFromFormat('Ymd\THis', $a['completed_date']), \DateTime::createFromFormat('Ymd\THis', $b['completed_date'])); + } + + + /** + * sort tasks by completed + * + * @param array $a + * @param array $b + * @return int + */ + public function sortCompleted($a, $b) { + return $this->sort($a['completed'], $b['completed']); + } + + /** + * sort tasks by completed + * + * @param mixed $t1 + * @param mixed $t2 + * @return int + */ + public function sort($t1, $t2) { + if ($t1 == $t2) { + return 0; + } + return $t1 > $t2 ? 1 : -1; + } + + /** + * create calendar entry from request + * + * @param array $request + * @return mixed + */ + public function createVCalendar($request){ + $vcalendar = new \Sabre\VObject\Component\VCalendar(); + $vcalendar->PRODID = 'ownCloud Calendar'; + $vcalendar->VERSION = '2.0'; + + $vtodo = $vcalendar->createComponent('VTODO'); + $vcalendar->add($vtodo); + + $vtodo->CREATED = new \DateTime('now', new \DateTimeZone('UTC')); + + $vtodo->UID = \Sabre\VObject\UUIDUtil::getUUID(); + return $this->addVTODO($vcalendar, $request); + } + + /** + * update task from request + * + * @param array $request + * @param mixed $vcalendar + * @return mixed + */ + public function addVTODO($vcalendar, $request){ + $vtodo = $vcalendar->VTODO; + $timezone = \OC_Calendar_App::getTimezone(); + $timezone = new \DateTimeZone($timezone); + + $vtodo->{'LAST-MODIFIED'} = new \DateTime('now', new \DateTimeZone('UTC')); + $vtodo->DTSTAMP = new \DateTime('now', new \DateTimeZone('UTC')); + $vtodo->SUMMARY = $request['summary']; + + if($request['starred']) { + $vtodo->PRIORITY = 1; // prio: high + } + $due = $request['due']; + if ($due) { + $vtodo->DUE = new \DateTime($due, $timezone); + } + $start = $request['start']; + if ($start) { + $vtodo->DTSTART = new \DateTime($start, $timezone); + } + + return $vcalendar; + } + + /** + * check if task contains query + * + * @param mixed $vtodo + * @param string $query + * @return array + */ + public function checkTaskByQuery($vtodo, $query) { + // check these properties + $properties = array('SUMMARY', 'DESCRIPTION', 'LOCATION', 'CATEGORIES', 'COMMENT'); + foreach ($properties as $property) { + $strings = $vtodo->{$property}; + if ($strings) { + foreach ($strings as $string) { + $needle = $string->getValue(); + if (stripos($needle, $query) !== false) { + return true; + } + } + } + } + return false; + } +} diff --git a/service/listsservice.php b/service/listsservice.php index 5184c97c..e2ec8f9c 100644 --- a/service/listsservice.php +++ b/service/listsservice.php @@ -1,4 +1,25 @@ . + * + */ + namespace OCA\Tasks\Service; class ListsService { @@ -10,95 +31,88 @@ class ListsService { } /** - * get all lists + * get all calendars * * @return array */ public function getAll() { - $calendar = new \OC_Calendar_Calendar(); - $lists = $calendar::allCalendars($this->userId, true); - return $lists; + return \OC_Calendar_Calendar::allCalendars($this->userId, true); } /** * add a list * - * @param $name - * @param $tmpID + * @param string $name + * @param string $tmpID * @return array * @throws \Exception */ public function add($name, $tmpID) { if(trim($name) == '') { - // OCP\JSON::error(array('message'=>'empty')); - exit; + throw new \Exception('An empty name is not allowed.'); } - $calendars = \OC_Calendar_Calendar::allCalendars($this->userId, true); - foreach($calendars as $cal) { - if($cal['displayname'] == $name) { - // OCP\JSON::error(array('message'=>'namenotavailable')); - exit; - } + if ($this->isListnameUsed($name)) { + throw new \Exception('Calendar name already used.'); } $color = '#CCCCCC'; - $calendarId = \OC_Calendar_Calendar::addCalendar($this->userId, strip_tags($name), 'VEVENT,VTODO,VJOURNAL', null, 0, $color); - \OC_Calendar_Calendar::setCalendarActive($calendarId, 1); - $list = \OC_Calendar_Calendar::find($calendarId); + $listID = \OC_Calendar_Calendar::addCalendar($this->userId, strip_tags($name), 'VEVENT,VTODO,VJOURNAL', null, 0, $color); + \OC_Calendar_Calendar::setCalendarActive($listID, 1); + $list = \OC_Calendar_Calendar::find($listID); $list['tmpID'] = $tmpID; return $list; } /** - * delete list by id + * delete list by listID * - * @param $id + * @param string $listID * @return array */ - public function delete($id) { - try { - $del = \OC_Calendar_Calendar::deleteCalendar($id); - if($del == true) { - return array( - 'data' => array() - ); - } else { - return array('error'=>'dberror'); - } - } catch(\Exception $e) { - return array('message'=>$e->getMessage()); + public function delete($listID) { + $del = \OC_Calendar_Calendar::deleteCalendar($listID); + if(!$del) { + throw new \Exception('Calendar cannot be deleted.'); } + return array( + 'data' => array() + ); } /** - * set name of list by id + * set name of list by listID * - * @param $id - * @param $name + * @param string $listID + * @param string $name * @return array * @throws \Exception */ - public function setName($id, $name) { + public function setName($listID, $name) { if(trim($name) == '') { - // OCP\JSON::error(array('message'=>'empty')); - exit; + throw new \Exception('An empty name is not allowed.'); } - $calendars = \OC_Calendar_Calendar::allCalendars($this->userId, true); - foreach($calendars as $cal) { - if($cal['userid'] != $this->userId){ - continue; - } - if($cal['displayname'] == $name && $cal['id'] != $id) { - // OCP\JSON::error(array('message'=>'namenotavailable')); - exit; - } + if ($this->isListnameUsed($name, $listID)) { + throw new \Exception('Calendar name already used.'); } - $color = '#CCCCCC'; - \OC_Calendar_Calendar::editCalendar($id, strip_tags($name), null, null, null, $color); - + \OC_Calendar_Calendar::editCalendar($listID, strip_tags($name), null, null, null, $color); return array(); } - -} \ No newline at end of file + /** + * check if list name is used by other list + * + * @param string $name + * @param string $listID + * @return bool + */ + private function isListnameUsed($name, $listID=null) { + $calendars = \OC_Calendar_Calendar::allCalendars($this->userId, true); + foreach($calendars as $cal) { + if($cal['displayname'] == $name && $cal['id'] != $listID) { + return true; + } + } + return false; + } +} diff --git a/service/reminderservice.php b/service/reminderservice.php new file mode 100644 index 00000000..e0692596 --- /dev/null +++ b/service/reminderservice.php @@ -0,0 +1,228 @@ +. + * + */ + +namespace OCA\Tasks\Service; + +use \OCA\Tasks\Service\Helper; +use Sabre\VObject; + +Class ReminderService { + + private $helper; + + public function __construct(Helper $helper){ + $this->helper = $helper; + } + + /** + * create reminder for task + * @param int $taskID + * @param string $type + * @param mixed $action + * @param mixed $date + * @param bool $invert + * @param string $related + * @param mixed $week + * @param mixed $day + * @param mixed $hour + * @param mixed $minute + * @param mixed $second + * @return bool + * @throws \Exception + */ + public function createReminder($taskID, $type, $action, $date, $invert, $related = null, $week, $day, $hour, $minute, $second) { + $types = array('DATE-TIME','DURATION'); + $vcalendar = \OC_Calendar_App::getVCalendar($taskID); + $vtodo = $vcalendar->VTODO; + $valarm = $vtodo->VALARM; + + if (in_array($type,$types)) { + if($valarm == null) { + $valarm = $vcalendar->createComponent('VALARM'); + $valarm->ACTION = $action; + $valarm->DESCRIPTION = 'Default Event Notification'; + $vtodo->add($valarm); + } else { + unset($valarm->TRIGGER); + } + $string = ''; + if ($type == 'DATE-TIME') { + $string = $this->createReminderDateTime($date); + } elseif ($type == 'DURATION') { + $string = $this->createReminderDuration($week, $day, $hour, $minute, $second, $invert); + } + if($related == 'END'){ + $valarm->add('TRIGGER', $string, array('VALUE' => $type, 'RELATED' => $related)); + } else { + $valarm->add('TRIGGER', $string, array('VALUE' => $type)); + } + } else { + unset($vtodo->VALARM); + } + return $this->helper->editVCalendar($vcalendar, $taskID); + } + + /** + * parse reminder date-time + * + * @param string $date + * @return string + */ + private function createReminderDateTime($date) { + $date = new \DateTime('@'.$date); + return $date->format('Ymd\THis\Z'); + } + + /** + * parse reminder duration + * + * @param bool $invert + * @param mixed $week + * @param mixed $day + * @param mixed $hour + * @param mixed $minute + * @param mixed $second + * @return string + */ + private function createReminderDuration($week, $day, $hour, $minute, $second, $invert) { + // Create duration string + $string = 'PT0S'; + $P = array( + 'W' => $week, + 'D' => $day, + ); + $P = array_filter($P); + $T = array( + 'H' => $hour, + 'M' => $minute, + 'S' => $second, + ); + $T = array_filter($T); + if(count($P) || count($T)) { + $string = ''; + $string .= $invert ? '-' : ''; + $string .= 'P'; + $string .= implode('' , array_map(function($value, $key) { + return $value.$key; + }, $P, array_keys($P)) + ); + $string .= 'T'; + $string .= implode('' , array_map(function($value, $key) { + return $value.$key; + }, $T, array_keys($T)) + ); + } + return $string; + } + + /** + * parse reminder + * + * @param mixed $reminder + * @param object $start + * @param object $due + * @return array + */ + public function parseReminder($reminder, $start, $due) { + if(!$reminder) { + return false; + } + + if (!$reminder->TRIGGER['VALUE']){ + throw new \Exception('Reminder type not specified.'); + } + $reminderType = $reminder->TRIGGER['VALUE']->getValue(); + + if (!$reminder->ACTION) { + throw new \Exception('Reminder action not specified.'); + } + $reminderAction = $reminder->ACTION->getValue(); + + $reminderDate = null; + $reminderDuration = null; + + if($reminderType == 'DATE-TIME'){ + $reminderDate = $this->helper->parseDateString($reminder->TRIGGER); + } elseif ($reminderType == 'DURATION') { + list($related, $reminderDate) = $this->parseReminderDuration($reminder, $due, $start); + $reminderDuration = $this->parseReminderDurationString($reminder, $related); + } + return array( + 'type' => $reminderType, + 'action' => $reminderAction, + 'date' => $reminderDate, + 'duration' => $reminderDuration, + ); + } + + /** + * parse reminder duration + * + * @param mixed $reminder + * @param object $due + * @param object $start + * @return string + */ + private function parseReminderDuration($reminder, $due, $start) { + $parsed = VObject\DateTimeParser::parseDuration($reminder->TRIGGER,true); + // Calculate the reminder date from duration and start date + $related = null; + if(is_object($reminder->TRIGGER['RELATED'])){ + $related = $reminder->TRIGGER['RELATED']->getValue(); + if($related == 'END' && $due){ + $reminderDate = $this->helper->parseDateObject($due, $parsed); + } else { + throw new \Exception('Reminder duration related to not available date.'); + } + } elseif ($start) { + $reminderDate = $this->helper->parseDateObject($start, $parsed); + } else{ + throw new \Exception('Reminder duration related to not available date.'); + }; + return array($related, $reminderDate); + } + + /** + * parse reminder date-time + * + * @param mixed $reminder + * @param string $related + * @return string + */ + private function parseReminderDurationString($reminder, $related) { + preg_match('/^(?P\+|-)?P((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$/', $reminder->TRIGGER, $matches); + $invert = $matches['plusminus']==='-' ? true: false; + $keys = array('week', 'day', 'hour', 'minute', 'second'); + $reminderDuration = array(); + foreach($keys as $key) { + $reminderDuration[$key] = empty($matches[$key]) ? 0 : (int) $matches[$key]; + } + $tmp = array_keys(array_filter($reminderDuration)); + $reminderDuration['token'] = count($tmp) ? $tmp[0] : 'week'; + $reminderDuration['params'] = array( + 'id' => (int)$invert.(int)($related == 'END'), + 'related' => $related?$related:'START', + 'invert' => $invert, + ); + return $reminderDuration; + } +} diff --git a/service/settingsservice.php b/service/settingsservice.php index 83fb5146..10985d22 100644 --- a/service/settingsservice.php +++ b/service/settingsservice.php @@ -1,4 +1,25 @@ . + * + */ + namespace OCA\Tasks\Service; use OCP\IConfig; @@ -45,4 +66,4 @@ class SettingsService { $this->settings->setUserValue($this->userId, $this->appName, $type.'_'.$setting, $value); return true; } -} \ No newline at end of file +} diff --git a/service/taskparser.php b/service/taskparser.php new file mode 100644 index 00000000..e797013a --- /dev/null +++ b/service/taskparser.php @@ -0,0 +1,169 @@ +. + * + */ + +namespace OCA\Tasks\Service; + +use \OCA\Tasks\Service\ReminderService; +use \OCA\Tasks\Service\Helper; +use Sabre\VObject; + +Class TaskParser { + + private $reminderService; + private $helper; + + public function __construct(ReminderService $reminderService, Helper $helper){ + $this->reminderService = $reminderService; + $this->helper = $helper; + } + + /** + * parse task + * + * @param mixed $vtodo + * @param string $calendarID + * @return string + */ + public function parseTask($vtodo, $calendarID){ + $task = array( 'id' => (string) $vtodo->ID); + $task['calendarid'] = $calendarID; + $task['type'] = 'task'; + $task['name'] = (string) $vtodo->SUMMARY; + $task['created'] = (string) $vtodo->CREATED; + $task['note'] = (string) $vtodo->DESCRIPTION; + $task['location'] = (string) $vtodo->LOCATION; + $task['categories'] = $this->parseCategories($vtodo->CATEGORIES); + $task['start'] = $this->helper->parseDateObject($vtodo->DTSTART); + $task['due'] = $this->helper->parseDateObject($vtodo->DUE); + $task['completed_date'] = $this->helper->parseDateObject($vtodo->COMPLETED); + $task['completed'] = (bool) $task['completed_date']; + $task['reminder'] = $this->reminderService->parseReminder($vtodo->VALARM, $vtodo->DTSTART, $vtodo->DUE); + $task['priority'] = $this->parsePriority($vtodo->PRIORITY); + $task['starred'] = $this->parseStarred($task['priority']); + $task['complete'] = $this->parsePercentCompleted($vtodo->{'PERCENT-COMPLETE'}); + $task['comments'] = $this->parseComments($vtodo->COMMENT); + return $task; + } + + /** + * parse starred + * + * @param mixed $priority + * @return bool + */ + private function parseStarred($priority) { + if ((int) $priority > 5) { + return true; + } else { + return false; + } + } + + /** + * parse priority + * + * @param mixed $priority + * @return string + */ + private function parsePriority($priority) { + if(isset($priority)){ + return (string) (10 - $priority->getValue()) % 10; + } else { + return '0'; + } + } + + /** + * parse categories + * + * @param mixed $categories + * @return array + */ + private function parseCategories($categories) { + if ($categories){ + return $categories->getParts(); + } else { + return array(); + } + } + + /** + * parse percent completed + * + * @param mixed $percentComplete + * @return string + */ + private function parsePercentCompleted($percentComplete) { + if($percentComplete){ + return $percentComplete->getValue(); + } else { + return '0'; + } + } + + /** + * parse comments + * + * @param mixed $comments + * @return array + */ + private function parseComments($comments){ + $comments_parsed = array(); + if($comments){ + foreach($comments as $com) { + // parse time + $time = $this->helper->parseDateString($com['X-OC-DATE-TIME']); + // parse comment ID + $comID = $com['X-OC-ID']; + // parse user ID + $userID = $com['X-OC-USERID']; + + if ($this->isCommentValid($time, $comID, $userID)) { + $userID = (string) $userID->getValue(); + $user = \OC::$server->getUserManager()->get($userID); + if ($user) { + $comments_parsed[] = array( + 'id' => $comID->getValue(), + 'userID' => $userID, + 'name' => $user->getDisplayName(), + 'comment' => $com->getValue(), + 'time' => $time, + ); + } + } + } + } + return $comments_parsed; + } + + /** + * check if comment is valid + * + * @param string $time + * @param string $comID + * @param string $userID + * @return bool + */ + private function isCommentValid($time, $comID, $userID) { + return ($time && $comID && $userID); + } +} diff --git a/service/tasksservice.php b/service/tasksservice.php index cf031818..968e5a81 100644 --- a/service/tasksservice.php +++ b/service/tasksservice.php @@ -1,14 +1,43 @@ . + * + */ + namespace OCA\Tasks\Service; -use \OCA\Tasks\Controller\Helper; +use \OCA\Tasks\Service\Helper; +use \OCA\Tasks\Service\TaskParser; +use \OCA\Tasks\Db\TasksMapper; class TasksService { private $userId; + private $tasksMapper; + private $helper; + private $taskParser; - public function __construct($userId){ + public function __construct($userId, TasksMapper $tasksMapper, Helper $helper, TaskParser $taskParser){ $this->userId = $userId; + $this->tasksMapper = $tasksMapper; + $this->helper = $helper; + $this->taskParser = $taskParser; } /** @@ -21,7 +50,6 @@ class TasksService { */ public function getAll($listID = 'all', $type = 'all'){ - $user_timezone = \OC_Calendar_App::getTimezone(); if ($listID == 'all'){ $calendars = \OC_Calendar_Calendar::allCalendars($this->userId, true); } else { @@ -32,57 +60,11 @@ class TasksService { $tasks = array(); $lists = array(); foreach( $calendars as $calendar ) { - $calendar_entries = \OC_Calendar_Object::all($calendar['id']); - $tasks_selected = array(); - foreach( $calendar_entries as $task ) { - if($task['objecttype']!='VTODO') { - continue; - } - if(is_null($task['summary'])) { - continue; - } - if(!($vtodo = Helper::parseVTODO($task))){ - continue; - } - $task_data = Helper::arrayForJSON($task['id'], $vtodo, $user_timezone, $calendar['id']); - switch($type){ - case 'all': - $tasks[] = $task_data; - break; - case 'init': - if (!$task_data['completed']){ - $tasks[] = $task_data; - } else { - $tasks_selected[] = $task_data; - } - break; - case 'completed': - if ($task_data['completed']){ - $tasks[] = $task_data; - } - break; - case 'uncompleted': - if (!$task_data['completed']){ - $tasks[] = $task_data; - } - break; - } - } - $nrCompleted = 0; - $notLoaded = 0; - usort($tasks_selected, array($this, 'sort_completed')); - foreach( $tasks_selected as $task_selected){ - $nrCompleted++; - if ($nrCompleted > 5){ - $notLoaded++; - continue; - } - $tasks[] = $task_selected; - } - $lists[] = array( - 'id' => $calendar['id'], - 'notLoaded' => $notLoaded - ); + $calendar_entries = $this->tasksMapper->findAllVTODOs($calendar['id']); + + list($lists[], $tasks_calendar) = $this->getTasks($calendar_entries, $type, $calendar['id']); + + $tasks = array_merge($tasks, $tasks_calendar); } return array( 'tasks' => $tasks, @@ -90,6 +72,33 @@ class TasksService { ); } + /** + * get tasks + * + * @param array $calendar_entries + * @param string $type + * @param string $calendarID + * @return array + */ + public function getTasks($calendar_entries, $type, $calendarID) { + $list = array( + 'id' => $calendarID, + 'notLoaded' => 0 + ); + $host = $this; + $VTODOs = array_map(function($task) use ($host) { + return $host->helper->checkTask($task); + }, $calendar_entries); + $VTODOs = array_filter($VTODOs); + + $tasks = array_map(function($task) use ($host, $calendarID) { + return $host->taskParser->parseTask($task, $calendarID); + },$VTODOs); + + list($list['notLoaded'], $tasks) = $this->helper->selectTasks($tasks, $type); + return array($list, $tasks); + } + /** * get task by id * @@ -97,21 +106,45 @@ class TasksService { * @return array * @throws \Exception */ - public function get($taskID){ - $object = \OC_Calendar_App::getEventObject($taskID); - $user_timezone = \OC_Calendar_App::getTimezone(); + public function getTask($taskID){ + $calendar_entry = $this->tasksMapper->findVTODOById($taskID); $task = array(); - if($object['objecttype']=='VTODO' && !is_null($object['summary'])) { - if($vtodo = Helper::parseVTODO($object)){ - $task_data = Helper::arrayForJSON($object['id'], $vtodo, $user_timezone, $object['calendarid']); - $task[] = $task_data; - } + $vtodo = $this->helper->checkTask($calendar_entry); + if($vtodo){ + $task_data = $this->taskParser->parseTask($vtodo, $calendar_entry->getCalendarid()); + $task[] = $task_data; } return array( 'tasks' => $task ); } + /** + * Search for query in tasks + * + * @param string $query + * @return array + */ + public function search($query) { + $calendars = \OC_Calendar_Calendar::allCalendars($this->userId, true); + $results = array(); + foreach ($calendars as $calendar) { + $calendar_entries = $this->tasksMapper->findAllVTODOs($calendar['id']); + // search all calendar objects, one by one + foreach ($calendar_entries as $calendar_entry) { + $vtodo = $this->helper->checkTask($calendar_entry); + if(!$vtodo){ + continue; + } + if($this->helper->checkTaskByQuery($vtodo, $query)) { + $results[] = $this->taskParser->parseTask($vtodo, $calendar_entry->getCalendarid()); + } + } + } + usort($results, array($this->helper, 'sortCompleted')); + return $results; + } + /** * create new task * @@ -124,20 +157,17 @@ class TasksService { * @return array */ public function add($taskName, $calendarId, $starred, $due, $start, $tmpID){ - $user_timezone = \OC_Calendar_App::getTimezone(); $request = array( 'summary' => $taskName, - 'categories' => null, - 'priority' => $starred, - 'location' => null, + 'starred' => $starred, 'due' => $due, 'start' => $start, - 'description' => null ); - $vcalendar = Helper::createVCalendarFromRequest($request); - $taskID = \OC_Calendar_Object::add($calendarId, $vcalendar->serialize()); + $vcalendar = $this->helper->createVCalendar($request); + $vtodo = $vcalendar->VTODO; + $vtodo->ID = \OC_Calendar_Object::add($calendarId, $vcalendar->serialize()); - $task = Helper::arrayForJSON($taskID, $vcalendar->VTODO, $user_timezone, $calendarId); + $task = $this->taskParser->parseTask($vtodo, $calendarId); $task['tmpID'] = $tmpID; return $task; @@ -161,10 +191,7 @@ class TasksService { * @throws \Exception */ public function setName($taskID, $name) { - $vcalendar = \OC_Calendar_App::getVCalendar($taskID); - $vtodo = $vcalendar->VTODO; - $vtodo->SUMMARY = $name; - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); + return $this->helper->setProperty($taskID,'SUMMARY',$name); } /** @@ -210,7 +237,7 @@ class TasksService { $vtodo->STATUS = 'NEEDS-ACTION'; unset($vtodo->COMPLETED); } - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); + return $this->helper->editVCalendar($vcalendar, $taskID); } /** @@ -222,14 +249,8 @@ class TasksService { * @throws \Exception */ public function setPriority($taskID, $priority){ - $vcalendar = \OC_Calendar_App::getVCalendar($taskID); - $vtodo = $vcalendar->VTODO; - if($priority){ - $vtodo->PRIORITY = (10 - $priority) % 10; - }else{ - unset($vtodo->{'PRIORITY'}); - } - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); + $priority = (10 - $priority) % 10; + return $this->helper->setProperty($taskID,'PRIORITY',$priority); } /** @@ -241,115 +262,19 @@ class TasksService { * @throws \Exception */ public function setDueDate($taskID, $dueDate) { - $vcalendar = \OC_Calendar_App::getVCalendar($taskID); - $vtodo = $vcalendar->VTODO; - if ($dueDate != false) { - $timezone = \OC_Calendar_App::getTimezone(); - $timezone = new \DateTimeZone($timezone); - - $dueDate = new \DateTime('@'.$dueDate); - $dueDate->setTimezone($timezone); - $vtodo->DUE = $dueDate; - } else { - unset($vtodo->DUE); - } - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); - } - - public function setStartDate($taskID, $startDate) { - $vcalendar = \OC_Calendar_App::getVCalendar($taskID); - $vtodo = $vcalendar->VTODO; - if ($startDate != false) { - $timezone = \OC_Calendar_App::getTimezone(); - $timezone = new \DateTimeZone($timezone); - - $startDate = new \DateTime('@'.$startDate); - $startDate->setTimezone($timezone); - $vtodo->DTSTART = $startDate; - } else { - unset($vtodo->DTSTART); - } - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); + return $this->helper->setProperty($taskID, 'DUE', $this->helper->createDateFromUNIX($dueDate)); } /** - * set reminder date of task by id + * set start date of task by id + * * @param int $taskID - * @param string $type - * @param mixed $action - * @param mixed $date - * @param bool $invert - * @param string $related - * @param mixed $week - * @param mixed $day - * @param mixed $hour - * @param mixed $minute - * @param mixed $second + * @param mixed $startDate * @return bool * @throws \Exception */ - public function setReminderDate($taskID, $type, $action, $date, $invert, $related = null, $week, $day, $hour, $minute, $second){ - $types = array('DATE-TIME','DURATION'); - - $vcalendar = \OC_Calendar_App::getVCalendar($taskID); - $vtodo = $vcalendar->VTODO; - $valarm = $vtodo->VALARM; - - if ($type == false){ - unset($vtodo->VALARM); - $vtodo->{'LAST-MODIFIED'}->setValue(new \DateTime('now', new \DateTimeZone('UTC'))); - $vtodo->DTSTAMP = new \DateTime('now', new \DateTimeZone('UTC')); - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); - } - elseif (in_array($type,$types)) { - if($valarm == null) { - $valarm = $vcalendar->createComponent('VALARM'); - $valarm->ACTION = $action; - $valarm->DESCRIPTION = 'Default Event Notification'; - $vtodo->add($valarm); - } else { - unset($valarm->TRIGGER); - } - $tv = ''; - if ($type == 'DATE-TIME') { - $date = new \DateTime('@'.$date); - $tv = $date->format('Ymd\THis\Z'); - } elseif ($type == 'DURATION') { - // Create duration string - if($week || $day || $hour || $minute || $second) { - if ($invert){ - $tv.='-'; - } - $tv.='P'; - if ($week){ - $tv.=$week.'W'; - } - if ($day){ - $tv.=$day.'D'; - } - $tv.='T'; - if ($hour){ - $tv.=$hour.'H'; - } - if ($minute){ - $tv.=$minute.'M'; - } - if ($second){ - $tv.=$second.'S'; - } - }else{ - $tv = 'PT0S'; - } - } - if($related == 'END'){ - $valarm->add('TRIGGER', $tv, array('VALUE' => $type, 'RELATED' => $related)); - } else { - $valarm->add('TRIGGER', $tv, array('VALUE' => $type)); - } - $vtodo->{'LAST-MODIFIED'}->setValue(new \DateTime('now', new \DateTimeZone('UTC'))); - $vtodo->DTSTAMP = new \DateTime('now', new \DateTimeZone('UTC')); - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); - } + public function setStartDate($taskID, $startDate) { + return $this->helper->setProperty($taskID, 'DTSTART', $this->helper->createDateFromUNIX($startDate)); } /** @@ -372,7 +297,7 @@ class TasksService { if (!in_array($category, $taskcategories)){ $taskcategories[] = $category; $vtodo->CATEGORIES = $taskcategories; - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); + return $this->helper->editVCalendar($vcalendar, $taskID); } else { return true; } @@ -392,18 +317,19 @@ class TasksService { $categories = $vtodo->CATEGORIES; if ($categories){ $taskcategories = $categories->getParts(); - } - // remove category - $key = array_search($category, $taskcategories); - if ($key !== null && $key !== false){ - unset($taskcategories[$key]); - if(count($taskcategories)){ - $vtodo->CATEGORIES = $taskcategories; - } else{ - unset($vtodo->{'CATEGORIES'}); + // remove category + $key = array_search($category, $taskcategories); + if ($key !== null && $key !== false){ + unset($taskcategories[$key]); + if(count($taskcategories)){ + $vtodo->CATEGORIES = $taskcategories; + } else{ + unset($vtodo->{'CATEGORIES'}); + } + return $this->helper->editVCalendar($vcalendar, $taskID); } - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); } + return true; } /** @@ -414,10 +340,7 @@ class TasksService { * @throws \Exception */ public function setLocation($taskID, $location){ - $vcalendar = \OC_Calendar_App::getVCalendar($taskID); - $vtodo = $vcalendar->VTODO; - $vtodo->LOCATION = $location; - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); + return $this->helper->setProperty($taskID,'LOCATION',$location); } /** @@ -429,98 +352,6 @@ class TasksService { * @throws \Exception */ public function setDescription($taskID, $description){ - $vcalendar = \OC_Calendar_App::getVCalendar($taskID); - $vtodo = $vcalendar->VTODO; - $vtodo->DESCRIPTION = $description; - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); + return $this->helper->setProperty($taskID,'DESCRIPTION',$description); } - - /** - * add comment to task by id - * @param int $taskID - * @param string $comment - * @param int $tmpID - * @return array - * @throws \Exception - */ - public function addComment($taskID, $comment, $tmpID){ - $vcalendar = \OC_Calendar_App::getVCalendar($taskID); - $vtodo = $vcalendar->VTODO; - - if($vtodo->COMMENT == "") { - // if this is the first comment set the id to 0 - $commentId = 0; - } else { - // Determine new commentId by looping through all comments - $commentIds = array(); - foreach($vtodo->COMMENT as $com) { - $commentIds[] = (int)$com['X-OC-ID']->getValue(); - } - $commentId = 1+max($commentIds); - } - - $now = new \DateTime(); - $vtodo->add('COMMENT',$comment, - array( - 'X-OC-ID' => $commentId, - 'X-OC-USERID' => $this->userId, - 'X-OC-DATE-TIME' => $now->format('Ymd\THis\Z') - ) - ); - \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); - $user_timezone = \OC_Calendar_App::getTimezone(); - $now->setTimezone(new \DateTimeZone($user_timezone)); - $comment = array( - 'taskID' => $taskID, - 'id' => $commentId, - 'tmpID' => $tmpID, - 'name' => \OC::$server->getUserManager()->get($this->userId)->getDisplayName(), - 'userID' => $this->userId, - 'comment' => $comment, - 'time' => $now->format('Ymd\THis') - ); - return $comment; - } - - /** - * delete comment of task by id - * @param int $taskID - * @param int $commentID - * @return bool - * @throws \Exception - */ - public function deleteComment($taskID, $commentID){ - $vcalendar = \OC_Calendar_App::getVCalendar($taskID); - $vtodo = $vcalendar->VTODO; - $commentIndex = $this->getCommentById($vtodo,$commentID); - $comment = $vtodo->children[$commentIndex]; - if($comment['X-OC-USERID']->getValue() == $this->userId){ - unset($vtodo->children[$commentIndex]); - return \OC_Calendar_Object::edit($taskID, $vcalendar->serialize()); - } else { - throw new \Exception('Not allowed.'); - } - } - - - private static function sort_completed($a, $b) { - $t1 = \DateTime::createFromFormat('Ymd\THis', $a['completed_date']); - $t2 = \DateTime::createFromFormat('Ymd\THis', $b['completed_date']); - if ($t1 == $t2) { - return 0; - } - return $t1 < $t2 ? 1 : -1; - } - - private function getCommentById($vtodo,$commentId) { - $idx = 0; - foreach ($vtodo->children as $i => &$property) { - if ( $property->name == 'COMMENT' && $property['X-OC-ID']->getValue() == $commentId ) { - return $idx; - } - $idx += 1; - } - throw new \Exception('Commment not found.'); - } - }