diff options
-rw-r--r-- | app/Api/Action.php | 98 | ||||
-rw-r--r-- | app/Api/App.php | 22 | ||||
-rw-r--r-- | app/Api/Base.php | 81 | ||||
-rw-r--r-- | app/Api/Board.php | 52 | ||||
-rw-r--r-- | app/Api/Category.php | 49 | ||||
-rw-r--r-- | app/Api/Comment.php | 51 | ||||
-rw-r--r-- | app/Api/Link.php | 111 | ||||
-rw-r--r-- | app/Api/Project.php | 85 | ||||
-rw-r--r-- | app/Api/ProjectPermission.php | 27 | ||||
-rw-r--r-- | app/Api/Subtask.php | 64 | ||||
-rw-r--r-- | app/Api/Swimlane.php | 77 | ||||
-rw-r--r-- | app/Api/Task.php | 113 | ||||
-rw-r--r-- | app/Api/TaskLink.php | 77 | ||||
-rw-r--r-- | app/Api/User.php | 88 | ||||
-rw-r--r-- | app/Model/Link.php | 46 | ||||
-rw-r--r-- | app/Model/Swimlane.php | 2 | ||||
-rw-r--r-- | composer.json | 20 | ||||
-rw-r--r-- | composer.lock | 22 | ||||
-rw-r--r-- | docs/api-json-rpc.markdown | 345 | ||||
-rw-r--r-- | jsonrpc.php | 449 | ||||
-rw-r--r-- | tests/functionals.mysql.xml | 1 | ||||
-rw-r--r-- | tests/functionals.postgres.xml | 1 | ||||
-rw-r--r-- | tests/functionals/ApiTest.php | 228 | ||||
-rw-r--r-- | tests/units/LinkTest.php | 8 |
24 files changed, 1515 insertions, 602 deletions
diff --git a/app/Api/Action.php b/app/Api/Action.php new file mode 100644 index 00000000..6187b776 --- /dev/null +++ b/app/Api/Action.php @@ -0,0 +1,98 @@ +<?php + +namespace Api; + +/** + * Action API controller + * + * @package api + * @author Frederic Guillot + */ +class Action extends Base +{ + public function getAvailableActions() + { + return $this->action->getAvailableActions(); + } + + public function getAvailableActionEvents() + { + return $this->action->getAvailableEvents(); + } + + public function getCompatibleActionEvents($action_name) + { + return $this->action->getCompatibleEvents($action_name); + } + + public function removeAction($action_id) + { + return $this->action->remove($action_id); + } + + public function getActions($project_id) + { + $actions = $this->action->getAllByProject($project_id); + + foreach ($actions as $index => $action) { + + $params = array(); + + foreach($action['params'] as $param) { + $params[$param['name']] = $param['value']; + } + + $actions[$index]['params'] = $params; + } + + return $actions; + } + + public function createAction($project_id, $event_name, $action_name, $params) + { + $values = array( + 'project_id' => $project_id, + 'event_name' => $event_name, + 'action_name' => $action_name, + 'params' => $params, + ); + + list($valid,) = $this->action->validateCreation($values); + + if (! $valid) { + return false; + } + + // Check if the action exists + $actions = $this->action->getAvailableActions(); + + if (! isset($actions[$action_name])) { + return false; + } + + // Check the event + $action = $this->action->load($action_name, $project_id, $event_name); + + if (! in_array($event_name, $action->getCompatibleEvents())) { + return false; + } + + $required_params = $action->getActionRequiredParameters(); + + // Check missing parameters + foreach($required_params as $param => $value) { + if (! isset($params[$param])) { + return false; + } + } + + // Check extra parameters + foreach($params as $param => $value) { + if (! isset($required_params[$param])) { + return false; + } + } + + return $this->action->create($values); + } +} diff --git a/app/Api/App.php b/app/Api/App.php new file mode 100644 index 00000000..2fc32e91 --- /dev/null +++ b/app/Api/App.php @@ -0,0 +1,22 @@ +<?php + +namespace Api; + +/** + * App API controller + * + * @package api + * @author Frederic Guillot + */ +class App extends Base +{ + public function getTimezone() + { + return $this->config->get('application_timezone'); + } + + public function getVersion() + { + return APP_VERSION; + } +} diff --git a/app/Api/Base.php b/app/Api/Base.php new file mode 100644 index 00000000..e9494b58 --- /dev/null +++ b/app/Api/Base.php @@ -0,0 +1,81 @@ +<?php + +namespace Api; + +use Pimple\Container; +use JsonRPC\AuthenticationFailure; +use Symfony\Component\EventDispatcher\Event; + +/** + * Base class + * + * @package api + * @author Frederic Guillot + * + * @property \Model\Board $board + * @property \Model\Config $config + * @property \Model\Comment $comment + * @property \Model\LastLogin $lastLogin + * @property \Model\Notification $notification + * @property \Model\Project $project + * @property \Model\ProjectPermission $projectPermission + * @property \Model\ProjectActivity $projectActivity + * @property \Model\ProjectAnalytic $projectAnalytic + * @property \Model\ProjectDailySummary $projectDailySummary + * @property \Model\Subtask $subtask + * @property \Model\Task $task + * @property \Model\TaskDuplication $taskDuplication + * @property \Model\TaskExport $taskExport + * @property \Model\TaskFinder $taskFinder + */ +abstract class Base +{ + /** + * Container instance + * + * @access protected + * @var \Pimple\Container + */ + protected $container; + + /** + * Constructor + * + * @access public + * @param \Pimple\Container $container + */ + public function __construct(Container $container) + { + $this->container = $container; + } + + /** + * Load automatically models + * + * @access public + * @param string $name Model name + * @return mixed + */ + public function __get($name) + { + return $this->container[$name]; + } + + /** + * Check api credentials + * + * @access public + * @param string $username + * @param string $password + * @param string $class + * @param string $method + */ + public function authentication($username, $password, $class, $method) + { + $this->container['dispatcher']->dispatch('api.bootstrap', new Event); + + if (! ($username === 'jsonrpc' && $password === $this->config->get('api_token'))) { + throw new AuthenticationFailure('Wrond credentials'); + } + } +} diff --git a/app/Api/Board.php b/app/Api/Board.php new file mode 100644 index 00000000..163131b6 --- /dev/null +++ b/app/Api/Board.php @@ -0,0 +1,52 @@ +<?php + +namespace Api; + +/** + * Board API controller + * + * @package api + * @author Frederic Guillot + */ +class Board extends Base +{ + public function getBoard($project_id) + { + return $this->board->getBoard($project_id); + } + + public function getColumns($project_id) + { + return $this->board->getColumns($project_id); + } + + public function getColumn($column_id) + { + return $this->board->getColumn($column_id); + } + + public function moveColumnUp($project_id, $column_id) + { + return $this->board->moveUp($project_id, $column_id); + } + + public function moveColumnDown($project_id, $column_id) + { + return $this->board->moveDown($project_id, $column_id); + } + + public function updateColumn($column_id, $title, $task_limit = 0, $description = '') + { + return $this->board->updateColumn($column_id, $title, $task_limit, $description); + } + + public function addColumn($project_id, $title, $task_limit = 0, $description = '') + { + return $this->board->addColumn($project_id, $title, $task_limit, $description); + } + + public function removeColumn($column_id) + { + return $this->board->removeColumn($column_id); + } +} diff --git a/app/Api/Category.php b/app/Api/Category.php new file mode 100644 index 00000000..f457ddf1 --- /dev/null +++ b/app/Api/Category.php @@ -0,0 +1,49 @@ +<?php + +namespace Api; + +/** + * Category API controller + * + * @package api + * @author Frederic Guillot + */ +class Category extends Base +{ + public function getCategory($category_id) + { + return $this->category->getById($category_id); + } + + public function getAllCategories($project_id) + { + return $this->category->getAll($project_id); + } + + public function removeCategory($category_id) + { + return $this->category->remove($category_id); + } + + public function createCategory($project_id, $name) + { + $values = array( + 'project_id' => $project_id, + 'name' => $name, + ); + + list($valid,) = $this->category->validateCreation($values); + return $valid ? $this->category->create($values) : false; + } + + public function updateCategory($id, $name) + { + $values = array( + 'id' => $id, + 'name' => $name, + ); + + list($valid,) = $this->category->validateModification($values); + return $valid && $this->category->update($values); + } +} diff --git a/app/Api/Comment.php b/app/Api/Comment.php new file mode 100644 index 00000000..19b84383 --- /dev/null +++ b/app/Api/Comment.php @@ -0,0 +1,51 @@ +<?php + +namespace Api; + +/** + * Comment API controller + * + * @package api + * @author Frederic Guillot + */ +class Comment extends Base +{ + public function getComment($comment_id) + { + return $this->comment->getById($comment_id); + } + + public function getAllComments($task_id) + { + return $this->comment->getAll($task_id); + } + + public function removeComment($comment_id) + { + return $this->comment->remove($comment_id); + } + + public function createComment($task_id, $user_id, $content) + { + $values = array( + 'task_id' => $task_id, + 'user_id' => $user_id, + 'comment' => $content, + ); + + list($valid,) = $this->comment->validateCreation($values); + + return $valid ? $this->comment->create($values) : false; + } + + public function updateComment($id, $content) + { + $values = array( + 'id' => $id, + 'comment' => $content, + ); + + list($valid,) = $this->comment->validateModification($values); + return $valid && $this->comment->update($values); + } +} diff --git a/app/Api/Link.php b/app/Api/Link.php new file mode 100644 index 00000000..b9084784 --- /dev/null +++ b/app/Api/Link.php @@ -0,0 +1,111 @@ +<?php + +namespace Api; + +/** + * Link API controller + * + * @package api + * @author Frederic Guillot + */ +class Link extends Base +{ + /** + * Get a link by id + * + * @access public + * @param integer $link_id Link id + * @return array + */ + public function getLinkById($link_id) + { + return $this->link->getById($link_id); + } + + /** + * Get a link by name + * + * @access public + * @param string $label + * @return array + */ + public function getLinkByLabel($label) + { + return $this->link->getByLabel($label); + } + + /** + * Get the opposite link id + * + * @access public + * @param integer $link_id Link id + * @return integer + */ + public function getOppositeLinkId($link_id) + { + return $this->link->getOppositeLinkId($link_id); + } + + /** + * Get all links + * + * @access public + * @return array + */ + public function getAllLinks() + { + return $this->link->getAll(); + } + + /** + * Create a new link label + * + * @access public + * @param string $label + * @param string $opposite_label + * @return boolean|integer + */ + public function createLink($label, $opposite_label = '') + { + $values = array( + 'label' => $label, + 'opposite_label' => $opposite_label, + ); + + list($valid,) = $this->link->validateCreation($values); + return $valid ? $this->link->create($label, $opposite_label) : false; + } + + /** + * Update a link + * + * @access public + * @param integer $link_id + * @param integer $opposite_link_id + * @param string $label + * @return boolean + */ + public function updateLink($link_id, $opposite_link_id, $label) + { + $values = array( + 'id' => $link_id, + 'opposite_id' => $opposite_link_id, + 'label' => $label, + ); + + list($valid,) = $this->link->validateModification($values); + return $valid && $this->link->update($values); + } + + /** + * Remove a link a the relation to its opposite + * + * @access public + * @param integer $link_id + * @return boolean + */ + public function removeLink($link_id) + { + return $this->link->remove($link_id); + } +} diff --git a/app/Api/Project.php b/app/Api/Project.php new file mode 100644 index 00000000..2451cd9c --- /dev/null +++ b/app/Api/Project.php @@ -0,0 +1,85 @@ +<?php + +namespace Api; + +/** + * Project API controller + * + * @package api + * @author Frederic Guillot + */ +class Project extends Base +{ + public function getProjectById($project_id) + { + return $this->project->getById($project_id); + } + + public function getProjectByName($name) + { + return $this->project->getByName($name); + } + + public function getAllProjects() + { + return $this->project->getAll(); + } + + public function removeProject($project_id) + { + return $this->project->remove($project_id); + } + + public function enableProject($project_id) + { + return $this->project->enable($project_id); + } + + public function disableProject($project_id) + { + return $this->project->disable($project_id); + } + + public function enableProjectPublicAccess($project_id) + { + return $this->project->enablePublicAccess($project_id); + } + + public function disableProjectPublicAccess($project_id) + { + return $this->project->disablePublicAccess($project_id); + } + + public function getProjectActivities(array $project_ids) + { + return $this->projectActivity->getProjects($project_ids); + } + + public function getProjectActivity($project_id) + { + return $this->projectActivity->getProject($project_id); + } + + public function createProject($name, $description = null) + { + $values = array( + 'name' => $name, + 'description' => $description + ); + + list($valid,) = $this->project->validateCreation($values); + return $valid ? $this->project->create($values) : false; + } + + public function updateProject($id, $name, $description = null) + { + $values = array( + 'id' => $id, + 'name' => $name, + 'description' => $description + ); + + list($valid,) = $this->project->validateModification($values); + return $valid && $this->project->update($values); + } +} diff --git a/app/Api/ProjectPermission.php b/app/Api/ProjectPermission.php new file mode 100644 index 00000000..a31faf3d --- /dev/null +++ b/app/Api/ProjectPermission.php @@ -0,0 +1,27 @@ +<?php + +namespace Api; + +/** + * ProjectPermission API controller + * + * @package api + * @author Frederic Guillot + */ +class ProjectPermission extends Base +{ + public function getMembers($project_id) + { + return $this->projectPermission->getMembers($project_id); + } + + public function revokeUser($project_id, $user_id) + { + return $this->projectPermission->revokeMember($project_id, $user_id); + } + + public function allowUser($project_id, $user_id) + { + return $this->projectPermission->addMember($project_id, $user_id); + } +} diff --git a/app/Api/Subtask.php b/app/Api/Subtask.php new file mode 100644 index 00000000..2e6c30f2 --- /dev/null +++ b/app/Api/Subtask.php @@ -0,0 +1,64 @@ +<?php + +namespace Api; + +/** + * Subtask API controller + * + * @package api + * @author Frederic Guillot + */ +class Subtask extends Base +{ + public function getSubtask($subtask_id) + { + return $this->subtask->getById($subtask_id); + } + + public function getAllSubtasks($task_id) + { + return $this->subtask->getAll($task_id); + } + + public function removeSubtask($subtask_id) + { + return $this->subtask->remove($subtask_id); + } + + public function createSubtask($task_id, $title, $user_id = 0, $time_estimated = 0, $time_spent = 0, $status = 0) + { + $values = array( + 'title' => $title, + 'task_id' => $task_id, + 'user_id' => $user_id, + 'time_estimated' => $time_estimated, + 'time_spent' => $time_spent, + 'status' => $status, + ); + + list($valid,) = $this->subtask->validateCreation($values); + return $valid ? $this->subtask->create($values) : false; + } + + public function updateSubtask($id, $task_id, $title = null, $user_id = null, $time_estimated = null, $time_spent = null, $status = null) + { + $values = array( + 'id' => $id, + 'task_id' => $task_id, + 'title' => $title, + 'user_id' => $user_id, + 'time_estimated' => $time_estimated, + 'time_spent' => $time_spent, + 'status' => $status, + ); + + foreach ($values as $key => $value) { + if (is_null($value)) { + unset($values[$key]); + } + } + + list($valid,) = $this->subtask->validateApiModification($values); + return $valid && $this->subtask->update($values); + } +} diff --git a/app/Api/Swimlane.php b/app/Api/Swimlane.php new file mode 100644 index 00000000..322b0805 --- /dev/null +++ b/app/Api/Swimlane.php @@ -0,0 +1,77 @@ +<?php + +namespace Api; + +/** + * Swimlane API controller + * + * @package api + * @author Frederic Guillot + */ +class Swimlane extends Base +{ + public function getActiveSwimlanes($project_id) + { + return $this->swimlane->getSwimlanes($project_id); + } + + public function getAllSwimlanes($project_id) + { + return $this->swimlane->getAll($project_id); + } + + public function getSwimlaneById($swimlane_id) + { + return $this->swimlane->getById($swimlane_id); + } + + public function getSwimlaneByName($project_id, $name) + { + return $this->swimlane->getByName($project_id, $name); + } + + public function getSwimlane($swimlane_id) + { + return $this->swimlane->getById($swimlane_id); + } + + public function getDefaultSwimlane($project_id) + { + return $this->swimlane->getDefault($project_id); + } + + public function addSwimlane($project_id, $name) + { + return $this->swimlane->create($project_id, $name); + } + + public function updateSwimlane($swimlane_id, $name) + { + return $this->swimlane->rename($swimlane_id, $name); + } + + public function removeSwimlane($project_id, $swimlane_id) + { + return $this->swimlane->remove($project_id, $swimlane_id); + } + + public function disableSwimlane($project_id, $swimlane_id) + { + return $this->swimlane->disable($project_id, $swimlane_id); + } + + public function enableSwimlane($project_id, $swimlane_id) + { + return $this->swimlane->enable($project_id, $swimlane_id); + } + + public function moveSwimlaneUp($project_id, $swimlane_id) + { + return $this->swimlane->moveUp($project_id, $swimlane_id); + } + + public function moveSwimlaneDown($project_id, $swimlane_id) + { + return $this->swimlane->moveDown($project_id, $swimlane_id); + } +} diff --git a/app/Api/Task.php b/app/Api/Task.php new file mode 100644 index 00000000..c98b24a6 --- /dev/null +++ b/app/Api/Task.php @@ -0,0 +1,113 @@ +<?php + +namespace Api; + +use Model\Task as TaskModel; + +/** + * Task API controller + * + * @package api + * @author Frederic Guillot + */ +class Task extends Base +{ + public function getTask($task_id) + { + return $this->taskFinder->getById($task_id); + } + + public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN) + { + return $this->taskFinder->getAll($project_id, $status_id); + } + + public function getOverdueTasks() + { + return $this->taskFinder->getOverdueTasks(); + } + + public function openTask($task_id) + { + return $this->taskStatus->open($task_id); + } + + public function closeTask($task_id) + { + return $this->taskStatus->close($task_id); + } + + public function removeTask($task_id) + { + return $this->task->remove($task_id); + } + + public function moveTaskPosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0) + { + return $this->taskPosition->movePosition($project_id, $task_id, $column_id, $position, $swimlane_id); + } + + public function createTask($title, $project_id, $color_id = '', $column_id = 0, $owner_id = 0, $creator_id = 0, + $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, + $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0, + $recurrence_basedate = 0) + { + $values = array( + 'title' => $title, + 'project_id' => $project_id, + 'color_id' => $color_id, + 'column_id' => $column_id, + 'owner_id' => $owner_id, + 'creator_id' => $creator_id, + 'date_due' => $date_due, + 'description' => $description, + 'category_id' => $category_id, + 'score' => $score, + 'swimlane_id' => $swimlane_id, + 'recurrence_status' => $recurrence_status, + 'recurrence_trigger' => $recurrence_trigger, + 'recurrence_factor' => $recurrence_factor, + 'recurrence_timeframe' => $recurrence_timeframe, + 'recurrence_basedate' => $recurrence_basedate, + ); + + list($valid,) = $this->taskValidator->validateCreation($values); + + return $valid ? $this->taskCreation->create($values) : false; + } + + public function updateTask($id, $title = null, $project_id = null, $color_id = null, $column_id = null, $owner_id = null, + $creator_id = null, $date_due = null, $description = null, $category_id = null, $score = null, + $swimlane_id = null, $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null, + $recurrence_timeframe = null, $recurrence_basedate = null) + { + $values = array( + 'id' => $id, + 'title' => $title, + 'project_id' => $project_id, + 'color_id' => $color_id, + 'column_id' => $column_id, + 'owner_id' => $owner_id, + 'creator_id' => $creator_id, + 'date_due' => $date_due, + 'description' => $description, + 'category_id' => $category_id, + 'score' => $score, + 'swimlane_id' => $swimlane_id, + 'recurrence_status' => $recurrence_status, + 'recurrence_trigger' => $recurrence_trigger, + 'recurrence_factor' => $recurrence_factor, + 'recurrence_timeframe' => $recurrence_timeframe, + 'recurrence_basedate' => $recurrence_basedate, + ); + + foreach ($values as $key => $value) { + if (is_null($value)) { + unset($values[$key]); + } + } + + list($valid) = $this->taskValidator->validateApiModification($values); + return $valid && $this->taskModification->update($values); + } +} diff --git a/app/Api/TaskLink.php b/app/Api/TaskLink.php new file mode 100644 index 00000000..c3e1a83c --- /dev/null +++ b/app/Api/TaskLink.php @@ -0,0 +1,77 @@ +<?php + +namespace Api; + +/** + * TaskLink API controller + * + * @package api + * @author Frederic Guillot + */ +class TaskLink extends Base +{ + /** + * Get a task link + * + * @access public + * @param integer $task_link_id Task link id + * @return array + */ + public function getTaskLinkById($task_link_id) + { + return $this->taskLink->getById($task_link_id); + } + + /** + * Get all links attached to a task + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getAllTaskLinks($task_id) + { + return $this->taskLink->getAll($task_id); + } + + /** + * Create a new link + * + * @access public + * @param integer $task_id Task id + * @param integer $opposite_task_id Opposite task id + * @param integer $link_id Link id + * @return integer Task link id + */ + public function createTaskLink($task_id, $opposite_task_id, $link_id) + { + return $this->taskLink->create($task_id, $opposite_task_id, $link_id); + } + + /** + * Update a task link + * + * @access public + * @param integer $task_link_id Task link id + * @param integer $task_id Task id + * @param integer $opposite_task_id Opposite task id + * @param integer $link_id Link id + * @return boolean + */ + public function updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id) + { + return $this->taskLink->update($task_link_id, $task_id, $opposite_task_id, $link_id); + } + + /** + * Remove a link between two tasks + * + * @access public + * @param integer $task_link_id + * @return boolean + */ + public function removeTaskLink($task_link_id) + { + return $this->taskLink->remove($task_link_id); + } +} diff --git a/app/Api/User.php b/app/Api/User.php new file mode 100644 index 00000000..166ef2c1 --- /dev/null +++ b/app/Api/User.php @@ -0,0 +1,88 @@ +<?php + +namespace Api; + +use Auth\Ldap; + +/** + * User API controller + * + * @package api + * @author Frederic Guillot + */ +class User extends Base +{ + public function getUser($user_id) + { + return $this->user->getById($user_id); + } + + public function getAllUsers() + { + return $this->user->getAll(); + } + + public function removeUser($user_id) + { + return $this->user->remove($user_id); + } + + public function createUser($username, $password, $name = '', $email = '', $is_admin = 0, $default_project_id = 0) + { + $values = array( + 'username' => $username, + 'password' => $password, + 'confirmation' => $password, + 'name' => $name, + 'email' => $email, + 'is_admin' => $is_admin, + 'default_project_id' => $default_project_id, + ); + + list($valid,) = $this->user->validateCreation($values); + + return $valid ? $this->user->create($values) : false; + } + + public function createLdapUser($username = '', $email = '', $is_admin = 0, $default_project_id = 0) + { + $ldap = new Ldap($this->container); + $user = $ldap->lookup($username, $email); + + if (! $user) { + return false; + } + + $values = array( + 'username' => $user['username'], + 'name' => $user['name'], + 'email' => $user['email'], + 'is_ldap_user' => 1, + 'is_admin' => $is_admin, + 'default_project_id' => $default_project_id, + ); + + return $this->user->create($values); + } + + public function updateUser($id, $username = null, $name = null, $email = null, $is_admin = null, $default_project_id = null) + { + $values = array( + 'id' => $id, + 'username' => $username, + 'name' => $name, + 'email' => $email, + 'is_admin' => $is_admin, + 'default_project_id' => $default_project_id, + ); + + foreach ($values as $key => $value) { + if (is_null($value)) { + unset($values[$key]); + } + } + + list($valid,) = $this->user->validateApiModification($values); + return $valid && $this->user->update($values); + } +} diff --git a/app/Model/Link.php b/app/Model/Link.php index 42b8382c..b26a01e4 100644 --- a/app/Model/Link.php +++ b/app/Model/Link.php @@ -112,7 +112,7 @@ class Link extends Base * @access public * @param string $label * @param string $opposite_label - * @return boolean + * @return boolean|integer */ public function create($label, $opposite_label = '') { @@ -123,38 +123,28 @@ class Link extends Base return false; } + $label_id = $this->db->getConnection()->getLastId(); + if ($opposite_label !== '') { - $this->createOpposite($opposite_label); + + $this->db + ->table(self::TABLE) + ->insert(array( + 'label' => $opposite_label, + 'opposite_id' => $label_id, + )); + + $this->db + ->table(self::TABLE) + ->eq('id', $label_id) + ->update(array( + 'opposite_id' => $this->db->getConnection()->getLastId() + )); } $this->db->closeTransaction(); - return true; - } - - /** - * Create the opposite label (executed inside create() method) - * - * @access private - * @param string $label - */ - private function createOpposite($label) - { - $label_id = $this->db->getConnection()->getLastId(); - - $this->db - ->table(self::TABLE) - ->insert(array( - 'label' => $label, - 'opposite_id' => $label_id, - )); - - $this->db - ->table(self::TABLE) - ->eq('id', $label_id) - ->update(array( - 'opposite_id' => $this->db->getConnection()->getLastId() - )); + return $label_id; } /** diff --git a/app/Model/Swimlane.php b/app/Model/Swimlane.php index cfd6d4a7..3b78a406 100644 --- a/app/Model/Swimlane.php +++ b/app/Model/Swimlane.php @@ -87,7 +87,7 @@ class Swimlane extends Base return $this->db->table(self::TABLE) ->eq('project_id', $project_id) ->eq('name', $name) - ->findAll(); + ->findOne(); } /** diff --git a/composer.json b/composer.json index c1381c02..b330aa73 100644 --- a/composer.json +++ b/composer.json @@ -3,20 +3,20 @@ "php" : ">=5.3", "ext-mbstring" : "*", "ext-gd" : "*", - "fguillot/simple-validator" : "dev-master", - "swiftmailer/swiftmailer" : "@stable", - "fguillot/json-rpc" : "0.0.1", - "fguillot/picodb" : "dev-master", + "christian-riesen/otp" : "1.4", + "eluceo/ical": "*", "erusev/parsedown" : "1.5.1", + "fabiang/xmpp" : "0.6.1", + "fguillot/json-rpc" : "0.0.3", + "fguillot/picodb" : "0.0.3", + "fguillot/simpleLogger" : "0.0.1", + "fguillot/simple-validator" : "0.0.3", "lusitanian/oauth" : "0.3.5", + "nickcernis/html-to-markdown" : "2.2.1", "pimple/pimple" : "~3.0", + "swiftmailer/swiftmailer" : "@stable", "symfony/console" : "@stable", - "symfony/event-dispatcher" : "~2.6", - "fguillot/simpleLogger" : "0.0.1", - "christian-riesen/otp" : "1.4", - "nickcernis/html-to-markdown" : "2.2.1", - "fabiang/xmpp" : "0.6.1", - "eluceo/ical": "*" + "symfony/event-dispatcher" : "~2.6" }, "autoload" : { "psr-0" : { diff --git a/composer.lock b/composer.lock index cebfcbbc..11f1e0f6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "d5e3dbbd2a1a260e2d0bfa0750d11754", + "hash": "3610e6ed82392eda9699a9332158c12e", "packages": [ { "name": "christian-riesen/base32", @@ -260,16 +260,16 @@ }, { "name": "fguillot/json-rpc", - "version": "v0.0.1", + "version": "v0.0.3", "source": { "type": "git", "url": "https://github.com/fguillot/JsonRPC.git", - "reference": "86e8339205616ad9b09d581957cc084a99c0ed27" + "reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/86e8339205616ad9b09d581957cc084a99c0ed27", - "reference": "86e8339205616ad9b09d581957cc084a99c0ed27", + "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8", + "reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8", "shasum": "" }, "require": { @@ -283,7 +283,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Unlicense" + "MIT" ], "authors": [ { @@ -291,13 +291,13 @@ "homepage": "http://fredericguillot.com" } ], - "description": "A simple Json-RPC client/server library that just works", + "description": "Simple Json-RPC client/server library that just works", "homepage": "https://github.com/fguillot/JsonRPC", - "time": "2014-11-22 20:32:14" + "time": "2015-05-20 15:08:40" }, { "name": "fguillot/picodb", - "version": "dev-master", + "version": "v0.0.3", "source": { "type": "git", "url": "https://github.com/fguillot/picoDb.git", @@ -334,7 +334,7 @@ }, { "name": "fguillot/simple-validator", - "version": "dev-master", + "version": "v0.0.3", "source": { "type": "git", "url": "https://github.com/fguillot/simpleValidator.git", @@ -822,9 +822,7 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "fguillot/simple-validator": 20, "swiftmailer/swiftmailer": 0, - "fguillot/picodb": 20, "symfony/console": 0 }, "prefer-stable": false, diff --git a/docs/api-json-rpc.markdown b/docs/api-json-rpc.markdown index 8250b1c4..d8fe896d 100644 --- a/docs/api-json-rpc.markdown +++ b/docs/api-json-rpc.markdown @@ -360,9 +360,6 @@ Response example: - Parameters: - **id** (integer, required) - **name** (string, required) - - **is_active** (integer, optional) - - **token** (string, optional) - - **is_public** (integer, optional) - **description** (string, optional) - Result on success: **true** - Result on failure: **false** @@ -548,13 +545,31 @@ Response example: ### getProjectActivity +- Purpose: **Get activity stream for a project** +- Parameters: + - **project_id** (integer, required) +- Result on success: **List of events** +- Result on failure: **false** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "getProjectActivity", + "id": 942472945, + "params": [ + "project_id": 1 + ] +} +``` + +### getProjectActivities + - Purpose: **Get Activityfeed for Project(s)** - Parameters: - **project_ids** (integer array, required) - - **limit** (integer, optional) - - **start** (timestamp, optional) - - **end** (timestamp, optional) -- Result on success: **true** +- Result on success: **List of events** - Result on failure: **false** Request example: @@ -562,7 +577,7 @@ Request example: ```json { "jsonrpc": "2.0", - "method": "getProjectActivity", + "method": "getProjectActivities", "id": 942472945, "params": [ "project_ids": [1,2] @@ -1086,12 +1101,47 @@ Response example: } ``` -### getSwimlanes +### getDefaultSwimlane -- Purpose: **Get the list of enabled swimlanes of a project** +- Purpose: **Get the default swimlane for a project** - Parameters: - **project_id** (integer, required) -- Result on success: **swimlane properties** +- Result on success: **true** +- Result on failure: **false** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "getDefaultSwimlane", + "id": 898774713, + "params": [ + 1 + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 898774713, + "result": { + "id": "1", + "default_swimlane": "Default swimlane", + "show_default_swimlane": "1" + } +} +``` + +### getActiveSwimlanes + +- Purpose: **Get the list of enabled swimlanes of a project (include default swimlane if enabled)** +- Parameters: + - **project_id** (integer, required) +- Result on success: **List of swimlanes** - Result on failure: **null** Request example: @@ -1099,10 +1149,10 @@ Request example: ```json { "jsonrpc": "2.0", - "method": "getSwimlanes", - "id": 1242049935, + "method": "getActiveSwimlanes", + "id": 934789422, "params": [ - 2 + 1 ] } ``` @@ -1112,26 +1162,26 @@ Response example: ```json { "jsonrpc": "2.0", - "id": 1242049935, + "id": 934789422, "result": [ { - "id": "0", - "name": "Default" + "id": 0, + "name": "Default swimlane" }, { "id": "2", - "name": "Version 7.0" - }, + "name": "Swimlane A" + } ] } ``` ### getAllSwimlanes -- Purpose: **Get the list of all swimlanes of a project** +- Purpose: **Get the list of all swimlanes of a project (enabled or disabled) and sorted by position** - Parameters: - **project_id** (integer, required) -- Result on success: **swimlane properties** +- Result on success: **List of swimlanes** - Result on failure: **null** Request example: @@ -1140,9 +1190,9 @@ Request example: { "jsonrpc": "2.0", "method": "getAllSwimlanes", - "id": 1242049935, + "id": 509791576, "params": [ - 2 + 1 ] } ``` @@ -1152,25 +1202,21 @@ Response example: ```json { "jsonrpc": "2.0", - "id": 1242049935, + "id": 509791576, "result": [ { - "id": "0", - "name": "Default" - }, - { - "id": "3", - "name": "Version 1.0", - "is_active": "0", - "position": 1, - "project_id": 2 + "id": "1", + "name": "Another swimlane", + "position": "1", + "is_active": "1", + "project_id": "1" }, { "id": "2", - "name": "Version 7.0", + "name": "Swimlane A", + "position": "2", "is_active": "1", - "position": 2, - "project_id": 2 + "project_id": "1" } ] } @@ -1178,7 +1224,81 @@ Response example: ### getSwimlane -- Purpose: **Get the a swimlane** +- Purpose: **Get the a swimlane by id** +- Parameters: + - **swimlane_id** (integer, required) +- Result on success: **swimlane properties** +- Result on failure: **null** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "getSwimlane", + "id": 131071870, + "params": [ + 1 + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 131071870, + "result": { + "id": "1", + "name": "Swimlane 1", + "position": "1", + "is_active": "1", + "project_id": "1" + } +} +``` + +### getSwimlaneById + +- Purpose: **Get the a swimlane by id** +- Parameters: + - **swimlane_id** (integer, required) +- Result on success: **swimlane properties** +- Result on failure: **null** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "getSwimlaneById", + "id": 131071870, + "params": [ + 1 + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 131071870, + "result": { + "id": "1", + "name": "Swimlane 1", + "position": "1", + "is_active": "1", + "project_id": "1" + } +} +``` + +### getSwimlaneByName + +- Purpose: **Get the a swimlane by name** - Parameters: - **project_id** (integer, required) - **name** (string, required) @@ -1190,11 +1310,11 @@ Request example: ```json { "jsonrpc": "2.0", - "method": "getSwimlane", - "id": 1242049935, + "method": "getSwimlaneByName", + "id": 824623567, "params": [ - 2, - "Version 1.0" + 1, + "Swimlane 1" ] } ``` @@ -1204,13 +1324,13 @@ Response example: ```json { "jsonrpc": "2.0", - "id": 1242049935, + "id": 824623567, "result": { - "id": "3", - "name": "Version 1.0", - "is_active": "0", - "position": 2, - "project_id": 2 + "id": "1", + "name": "Swimlane 1", + "position": "1", + "is_active": "1", + "project_id": "1" } } ``` @@ -1296,10 +1416,10 @@ Request example: { "jsonrpc": "2.0", "method": "updateSwimlane", - "id": 480740641, + "id": 87102426, "params": [ - 2, - "Version 4.1" + "1", + "Another swimlane" ] } ``` @@ -1309,7 +1429,7 @@ Response example: ```json { "jsonrpc": "2.0", - "id": 480740641, + "id": 87102426, "result": true } ``` @@ -1329,10 +1449,10 @@ Request example: { "jsonrpc": "2.0", "method": "addSwimlane", - "id": 638544704, + "id": 849940086, "params": [ 1, - "Version 1.0" + "Swimlane 1" ] } ``` @@ -1342,8 +1462,8 @@ Response example: ```json { "jsonrpc": "2.0", - "id": 638544704, - "result": 5 + "id": 849940086, + "result": 1 } ``` @@ -1448,7 +1568,7 @@ Response example: ### getAvailableActions -- Purpose: **Get list of available actions** +- Purpose: **Get list of available automatic actions** - Parameters: none - Result on success: **list of actions** - Result on failure: **false** @@ -1459,7 +1579,7 @@ Request example: { "jsonrpc": "2.0", "method": "getAvailableActions", - "id": 1433237746, + "id": 1217735483 } ``` @@ -1468,30 +1588,33 @@ Response example: ```json { "jsonrpc": "2.0", - "id": 1433237746, + "id": 1217735483, "result": { - "TaskLogMoveAnotherColumn" : "Add a comment logging moving the task between columns", - "TaskAssignColorColumn" : "Assign a color when the task is moved to a specific column", - "TaskAssignColorUser" : "Assign a color to a specific user", - "TaskAssignCategoryColor" : "Assign automatically a category based on a color", - "TaskAssignColorCategory" : "Assign automatically a color based on a category", - "TaskAssignSpecificUser" : "Assign the task to a specific user", - "TaskAssignCurrentUser" : "Assign the task to the person who does the action", - "TaskAssignUser" : "Change the assignee based on an external username", - "TaskAssignCategoryLabel" : "Change the category based on an external label", - "TaskClose" : "Close a task", - "CommentCreation" : "Create a comment from an external provider", - "TaskCreation" : "Create a task from an external provider", - "TaskDuplicateAnotherProject" : "Duplicate the task to another project", - "TaskMoveAnotherProject" : "Move the task to another project", - "TaskOpen" : "Open a task" + "TaskLogMoveAnotherColumn": "Add a comment logging moving the task between columns", + "TaskAssignColorUser": "Assign a color to a specific user", + "TaskAssignColorColumn": "Assign a color when the task is moved to a specific column", + "TaskAssignCategoryColor": "Assign automatically a category based on a color", + "TaskAssignColorCategory": "Assign automatically a color based on a category", + "TaskAssignSpecificUser": "Assign the task to a specific user", + "TaskAssignCurrentUser": "Assign the task to the person who does the action", + "TaskUpdateStartDate": "Automatically update the start date", + "TaskAssignUser": "Change the assignee based on an external username", + "TaskAssignCategoryLabel": "Change the category based on an external label", + "TaskClose": "Close a task", + "CommentCreation": "Create a comment from an external provider", + "TaskCreation": "Create a task from an external provider", + "TaskDuplicateAnotherProject": "Duplicate the task to another project", + "TaskMoveColumnAssigned": "Move the task to another column when assigned to a user", + "TaskMoveColumnUnAssigned": "Move the task to another column when assignee is cleared", + "TaskMoveAnotherProject": "Move the task to another project", + "TaskOpen": "Open a task" } } ``` -### getAvailableEvents +### getAvailableActionEvents -- Purpose: **Get list of available events** +- Purpose: **Get list of available events for actions** - Parameters: none - Result on success: **list of events** - Result on failure: **false** @@ -1501,8 +1624,8 @@ Request example: ```json { "jsonrpc": "2.0", - "method": "getAvailableEvents", - "id": 1433237746, + "method": "getAvailableActionEvents", + "id": 2116665643 } ``` @@ -1511,31 +1634,31 @@ Response example: ```json { "jsonrpc": "2.0", - "id": 1433237746, + "id": 2116665643, "result": { - "bitbucket.webhook.commit" : "Bitbucket commit received", - "task.close" : "Closing a task", - "github.webhook.commit" : "Github commit received", - "github.webhook.issue.assignee" : "Github issue assignee change", - "github.webhook.issue.closed" : "Github issue closed", - "github.webhook.issue.commented" : "Github issue comment created", - "github.webhook.issue.label" : "Github issue label change", - "github.webhook.issue.opened" : "Github issue opened", - "github.webhook.issue.reopened" : "Github issue reopened", - "gitlab.webhook.commit" : "Gitlab commit received", - "gitlab.webhook.issue.closed" : "Gitlab issue closed", - "gitlab.webhook.issue.opened" : "Gitlab issue opened", - "task.move.column" : "Move a task to another column", - "task.open" : "Open a closed task", - "task.assignee_change" : "Task assignee change", - "task.create" : "Task creation", - "task.create_update" : "Task creation or modification", - "task.update" : "Task modification" + "bitbucket.webhook.commit": "Bitbucket commit received", + "task.close": "Closing a task", + "github.webhook.commit": "Github commit received", + "github.webhook.issue.assignee": "Github issue assignee change", + "github.webhook.issue.closed": "Github issue closed", + "github.webhook.issue.commented": "Github issue comment created", + "github.webhook.issue.label": "Github issue label change", + "github.webhook.issue.opened": "Github issue opened", + "github.webhook.issue.reopened": "Github issue reopened", + "gitlab.webhook.commit": "Gitlab commit received", + "gitlab.webhook.issue.closed": "Gitlab issue closed", + "gitlab.webhook.issue.opened": "Gitlab issue opened", + "task.move.column": "Move a task to another column", + "task.open": "Open a closed task", + "task.assignee_change": "Task assignee change", + "task.create": "Task creation", + "task.create_update": "Task creation or modification", + "task.update": "Task modification" } } ``` -### getCompatibleEvents +### getCompatibleActionEvents - Purpose: **Get list of events compatible with an action** - Parameters: @@ -1548,10 +1671,10 @@ Request example: ```json { "jsonrpc": "2.0", - "method": "getCompatibleEvents", - "id": 1433237746, + "method": "getCompatibleActionEvents", + "id": 899370297, "params": [ - "TaskAssignSpecificUser" + "TaskClose" ] } ``` @@ -1561,10 +1684,14 @@ Response example: ```json { "jsonrpc": "2.0", - "id": 1433237746, + "id": 899370297, "result": { - "task.move.column" : "Move a task to another column", - "task.create_update" : "Task creation or modification", + "bitbucket.webhook.commit": "Bitbucket commit received", + "github.webhook.commit": "Github commit received", + "github.webhook.issue.closed": "Github issue closed", + "gitlab.webhook.commit": "Gitlab commit received", + "gitlab.webhook.issue.closed": "Gitlab issue closed", + "task.move.column": "Move a task to another column" } } ``` @@ -1574,7 +1701,7 @@ Response example: - Purpose: **Get list of actions for a project** - Parameters: - **project_id** (integer, required) -- Result on success: **list of actions info** +- Result on success: **list of actions properties** - Result on failure: **false** Request example: @@ -1618,7 +1745,7 @@ Response example: - **project_id** (integer, required) - **event_name** (string, required) - **action_name** (string, required) - - **params** (list of string pairs, required) + - **params** (key/value parameters, required) - Result on success: **action_id** - Result on failure: **false** @@ -1664,10 +1791,10 @@ Request example: ```json { "jsonrpc": "2.0", - "method": "getAvailableEvents", - "id": 1433237746, + "method": "removeAction", + "id": 1510741671, "params": [ - "2", + 1 ] } ``` @@ -1677,7 +1804,7 @@ Response example: ```json { "jsonrpc": "2.0", - "id": 1433237746, + "id": 1510741671, "result": true } ``` diff --git a/jsonrpc.php b/jsonrpc.php index e5c000c0..b6ec8291 100644 --- a/jsonrpc.php +++ b/jsonrpc.php @@ -2,441 +2,20 @@ require __DIR__.'/app/common.php'; -use Symfony\Component\EventDispatcher\Event; - -$container['dispatcher']->dispatch('api.bootstrap', new Event); - $server = new JsonRPC\Server; -$server->authentication(array('jsonrpc' => $container['config']->get('api_token'))); - -/** - * Project procedures - */ -$server->bind('getProjectById', $container['project'], 'getById'); -$server->bind('getProjectByName', $container['project'], 'getByName'); -$server->bind('getAllProjects', $container['project'], 'getAll'); -$server->bind('removeProject', $container['project'], 'remove'); -$server->bind('enableProject', $container['project'], 'enable'); -$server->bind('disableProject', $container['project'], 'disable'); -$server->bind('enableProjectPublicAccess', $container['project'], 'enablePublicAccess'); -$server->bind('disableProjectPublicAccess', $container['project'], 'disablePublicAccess'); -$server->bind('getProjectActivity', $container['projectActivity'], 'getProjects'); - -$server->register('createProject', function($name, $description = null) use ($container) { - $values = array( - 'name' => $name, - 'description' => $description - ); - list($valid,) = $container['project']->validateCreation($values); - return $valid ? $container['project']->create($values) : false; -}); - -$server->register('updateProject', function($id, $name, $is_active = null, $is_public = null, $token = null, $description = null) use ($container) { - - $values = array( - 'id' => $id, - 'name' => $name, - 'is_active' => $is_active, - 'is_public' => $is_public, - 'token' => $token, - 'description' => $description - ); - - foreach ($values as $key => $value) { - if (is_null($value)) { - unset($values[$key]); - } - } - - list($valid,) = $container['project']->validateModification($values); - return $valid && $container['project']->update($values); -}); - -/** - * Board procedures - */ -$server->bind('getBoard', $container['board'], 'getBoard'); -$server->bind('getColumns', $container['board'], 'getColumns'); -$server->bind('getColumn', $container['board'], 'getColumn'); -$server->bind('moveColumnUp', $container['board'], 'moveUp'); -$server->bind('moveColumnDown', $container['board'], 'moveDown'); -$server->bind('updateColumn', $container['board'], 'updateColumn'); -$server->bind('addColumn', $container['board'], 'addColumn'); -$server->bind('removeColumn', $container['board'], 'removeColumn'); - -/** - * Swimlane procedures - */ -$server->bind('getSwimlanes', $container['swimlane'], 'getSwimlanes'); -$server->bind('getAllSwimlanes', $container['swimlane'], 'getAll'); -$server->bind('getSwimlane', $container['swimlane'], 'getByName'); -$server->bind('addSwimlane', $container['swimlane'], 'create'); -$server->bind('updateSwimlane', $container['swimlane'], 'rename'); -$server->bind('removeSwimlane', $container['swimlane'], 'remove'); -$server->bind('disableSwimlane', $container['swimlane'], 'disable'); -$server->bind('enableSwimlane', $container['swimlane'], 'enable'); -$server->bind('moveSwimlaneUp', $container['swimlane'], 'moveUp'); -$server->bind('moveSwimlaneDown', $container['swimlane'], 'moveDown'); - -/** - * Actions procedures - */ -$server->bind('getAvailableActions', $container['action'], 'getAvailableActions'); -$server->bind('getAvailableEvents', $container['action'], 'getAvailableEvents'); -$server->bind('getCompatibleEvents', $container['action'], 'getCompatibleEvents'); -$server->bind('removeAction', $container['action'], 'remove'); - -$server->register('getActions', function($project_id) use ($container) { - $actions = $container['action']->getAllByProject($project_id); - - foreach ($actions as $index => $action) { - $params = array(); - - foreach($action['params'] as $param) { - $params[$param['name']] = $param['value']; - } - - $actions[$index]['params'] = $params; - } - - return $actions; -}); - -$server->register('createAction', function($project_id, $event_name, $action_name, $params) use ($container) { - - $values = array( - 'project_id' => $project_id, - 'event_name' => $event_name, - 'action_name' => $action_name, - 'params' => $params, - ); - - list($valid,) = $container['action']->validateCreation($values); - - if (! $valid) { - return false; - } - - // Check the action exists - $actions = $container['action']->getAvailableActions(); - - if (! isset($actions[$action_name])) { - return false; - } - - // Check the event - $action = $container['action']->load($action_name, $project_id, $event_name); - - if (! in_array($event_name, $action->getCompatibleEvents())) { - return false; - } - - $required_params = $action->getActionRequiredParameters(); - - // Check missing parameters - foreach($required_params as $param => $value) { - if (! isset($params[$param])) { - return false; - } - } - - // Check extra parameters - foreach($params as $param => $value) { - if (! isset($required_params[$param])) { - return false; - } - } - - return $container['action']->create($values); -}); - - -/** - * Project permissions procedures - */ -$server->bind('getMembers', $container['projectPermission'], 'getMembers'); -$server->bind('revokeUser', $container['projectPermission'], 'revokeMember'); -$server->bind('allowUser', $container['projectPermission'], 'addMember'); - -/** - * Task procedures - */ -$server->bind('getTask', $container['taskFinder'], 'getById'); -$server->bind('getAllTasks', $container['taskFinder'], 'getAll'); -$server->bind('getOverdueTasks', $container['taskFinder'], 'getOverdueTasks'); -$server->bind('openTask', $container['taskStatus'], 'open'); -$server->bind('closeTask', $container['taskStatus'], 'close'); -$server->bind('removeTask', $container['task'], 'remove'); -$server->bind('moveTaskPosition', $container['taskPosition'], 'movePosition'); - -$server->register('createTask', function($title, $project_id, $color_id = '', $column_id = 0, $owner_id = 0, $creator_id = 0, $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0, $recurrence_basedate = 0) use ($container) { - - $values = array( - 'title' => $title, - 'project_id' => $project_id, - 'color_id' => $color_id, - 'column_id' => $column_id, - 'owner_id' => $owner_id, - 'creator_id' => $creator_id, - 'date_due' => $date_due, - 'description' => $description, - 'category_id' => $category_id, - 'score' => $score, - 'swimlane_id' => $swimlane_id, - 'recurrence_status' => $recurrence_status, - 'recurrence_trigger' => $recurrence_trigger, - 'recurrence_factor' => $recurrence_factor, - 'recurrence_timeframe' => $recurrence_timeframe, - 'recurrence_basedate' => $recurrence_basedate, - ); - - list($valid,) = $container['taskValidator']->validateCreation($values); - - if (! $valid) { - return false; - } - - return $container['taskCreation']->create($values); -}); - -$server->register('updateTask', function($id, $title = null, $project_id = null, $color_id = null, $column_id = null, $owner_id = null, $creator_id = null, $date_due = null, $description = null, $category_id = null, $score = null, $swimlane_id = null, $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null, $recurrence_timeframe = null, $recurrence_basedate = null) use ($container) { - - $values = array( - 'id' => $id, - 'title' => $title, - 'project_id' => $project_id, - 'color_id' => $color_id, - 'column_id' => $column_id, - 'owner_id' => $owner_id, - 'creator_id' => $creator_id, - 'date_due' => $date_due, - 'description' => $description, - 'category_id' => $category_id, - 'score' => $score, - 'swimlane_id' => $swimlane_id, - 'recurrence_status' => $recurrence_status, - 'recurrence_trigger' => $recurrence_trigger, - 'recurrence_factor' => $recurrence_factor, - 'recurrence_timeframe' => $recurrence_timeframe, - 'recurrence_basedate' => $recurrence_basedate, - ); - - foreach ($values as $key => $value) { - if (is_null($value)) { - unset($values[$key]); - } - } - - list($valid) = $container['taskValidator']->validateApiModification($values); - return $valid && $container['taskModification']->update($values); -}); - - -/** - * User procedures - */ -$server->bind('getUser', $container['user'], 'getById'); -$server->bind('getAllUsers', $container['user'], 'getAll'); -$server->bind('removeUser', $container['user'], 'remove'); - -$server->register('createUser', function($username, $password, $name = '', $email = '', $is_admin = 0, $default_project_id = 0) use ($container) { - - $values = array( - 'username' => $username, - 'password' => $password, - 'confirmation' => $password, - 'name' => $name, - 'email' => $email, - 'is_admin' => $is_admin, - 'default_project_id' => $default_project_id, - ); - - list($valid,) = $container['user']->validateCreation($values); - - if (! $valid) { - return false; - } - - return $container['user']->create($values); -}); - -$server->register('createLdapUser', function($username = '', $email = '', $is_admin = 0, $default_project_id = 0) use ($container) { - - $ldap = new Auth\Ldap($container); - $user = $ldap->lookup($username, $email); - - if (! $user) { - return false; - } - - $values = array( - 'username' => $user['username'], - 'name' => $user['name'], - 'email' => $user['email'], - 'is_ldap_user' => 1, - 'is_admin' => $is_admin, - 'default_project_id' => $default_project_id, - ); - - return $container['user']->create($values); -}); - -$server->register('updateUser', function($id, $username = null, $name = null, $email = null, $is_admin = null, $default_project_id = null) use ($container) { - - $values = array( - 'id' => $id, - 'username' => $username, - 'name' => $name, - 'email' => $email, - 'is_admin' => $is_admin, - 'default_project_id' => $default_project_id, - ); - - foreach ($values as $key => $value) { - if (is_null($value)) { - unset($values[$key]); - } - } - - list($valid,) = $container['user']->validateApiModification($values); - return $valid && $container['user']->update($values); -}); - -/** - * Category procedures - */ -$server->bind('getCategory', $container['category'], 'getById'); -$server->bind('getAllCategories', $container['category'], 'getAll'); -$server->bind('removeCategory', $container['category'], 'remove'); - -$server->register('createCategory', function($project_id, $name) use ($container) { - - $values = array( - 'project_id' => $project_id, - 'name' => $name, - ); - - list($valid,) = $container['category']->validateCreation($values); - - if (! $valid) { - return false; - } - - return $container['category']->create($values); -}); - -$server->register('updateCategory', function($id, $name) use ($container) { - - $values = array( - 'id' => $id, - 'name' => $name, - ); - - list($valid,) = $container['category']->validateModification($values); - return $valid && $container['category']->update($values); -}); - -/** - * Comments procedures - */ -$server->bind('getComment', $container['comment'], 'getById'); -$server->bind('getAllComments', $container['comment'], 'getAll'); -$server->bind('removeComment', $container['comment'], 'remove'); - -$server->register('createComment', function($task_id, $user_id, $content) use ($container) { - - $values = array( - 'task_id' => $task_id, - 'user_id' => $user_id, - 'comment' => $content, - ); - - list($valid,) = $container['comment']->validateCreation($values); - - if (! $valid) { - return false; - } - - return $container['comment']->create($values); -}); - -$server->register('updateComment', function($id, $content) use ($container) { - - $values = array( - 'id' => $id, - 'comment' => $content, - ); - - list($valid,) = $container['comment']->validateModification($values); - return $valid && $container['comment']->update($values); -}); - -/** - * Subtask procedures - */ -$server->bind('getSubtask', $container['subtask'], 'getById'); -$server->bind('getAllSubtasks', $container['subtask'], 'getAll'); -$server->bind('removeSubtask', $container['subtask'], 'remove'); - -$server->register('createSubtask', function($task_id, $title, $user_id = 0, $time_estimated = 0, $time_spent = 0, $status = 0) use ($container) { - - $values = array( - 'title' => $title, - 'task_id' => $task_id, - 'user_id' => $user_id, - 'time_estimated' => $time_estimated, - 'time_spent' => $time_spent, - 'status' => $status, - ); - - foreach ($values as $key => $value) { - if (is_null($value)) { - unset($values[$key]); - } - } - list($valid,) = $container['subtask']->validateCreation($values); - - if (! $valid) { - return false; - } - - return $container['subtask']->create($values); -}); - -$server->register('updateSubtask', function($id, $task_id, $title = null, $user_id = null, $time_estimated = null, $time_spent = null, $status = null) use ($container) { - - $values = array( - 'id' => $id, - 'task_id' => $task_id, - 'title' => $title, - 'user_id' => $user_id, - 'time_estimated' => $time_estimated, - 'time_spent' => $time_spent, - 'status' => $status, - ); - - foreach ($values as $key => $value) { - if (is_null($value)) { - unset($values[$key]); - } - } - - list($valid,) = $container['subtask']->validateApiModification($values); - return $valid && $container['subtask']->update($values); -}); - -/** - * Application procedures - */ -$server->register('getTimezone', function() use ($container) { - return $container['config']->get('application_timezone'); -}); - -$server->register('getVersion', function() use ($container) { - return APP_VERSION; -}); +$server->before('authentication'); +$server->attach(new Api\Action($container)); +$server->attach(new Api\App($container)); +$server->attach(new Api\Board($container)); +$server->attach(new Api\Category($container)); +$server->attach(new Api\Comment($container)); +$server->attach(new Api\Link($container)); +$server->attach(new Api\Project($container)); +$server->attach(new Api\ProjectPermission($container)); +$server->attach(new Api\Subtask($container)); +$server->attach(new Api\Swimlane($container)); +$server->attach(new Api\Task($container)); +$server->attach(new Api\TaskLink($container)); +$server->attach(new Api\User($container)); -/** - * Parse incoming requests - */ echo $server->execute(); diff --git a/tests/functionals.mysql.xml b/tests/functionals.mysql.xml index f667cafa..f5c3e16a 100644 --- a/tests/functionals.mysql.xml +++ b/tests/functionals.mysql.xml @@ -12,5 +12,6 @@ <const name="DB_HOSTNAME" value="localhost" /> <const name="DB_USERNAME" value="root" /> <const name="DB_PASSWORD" value="" /> + <const name="DB_PORT" value="3306" /> </php> </phpunit>
\ No newline at end of file diff --git a/tests/functionals.postgres.xml b/tests/functionals.postgres.xml index 38904d1a..40fcef00 100644 --- a/tests/functionals.postgres.xml +++ b/tests/functionals.postgres.xml @@ -12,5 +12,6 @@ <const name="DB_HOSTNAME" value="localhost" /> <const name="DB_USERNAME" value="postgres" /> <const name="DB_PASSWORD" value="postgres" /> + <const name="DB_PORT" value="5432" /> </php> </phpunit>
\ No newline at end of file diff --git a/tests/functionals/ApiTest.php b/tests/functionals/ApiTest.php index 9fdfd1ba..7b7a2bed 100644 --- a/tests/functionals/ApiTest.php +++ b/tests/functionals/ApiTest.php @@ -105,19 +105,66 @@ class Api extends PHPUnit_Framework_TestCase { $project = $this->client->getProjectById(1); $this->assertNotEmpty($project); - $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test 2', 'is_active' => 0))); + $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test 2'))); $project = $this->client->getProjectById(1); $this->assertEquals('API test 2', $project['name']); - $this->assertEquals(0, $project['is_active']); - $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test', 'is_active' => 1))); + $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test', 'description' => 'test'))); $project = $this->client->getProjectById(1); $this->assertEquals('API test', $project['name']); + $this->assertEquals('test', $project['description']); + } + + public function testDisableProject() + { + $this->assertTrue($this->client->disableProject(1)); + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); + $this->assertEquals(0, $project['is_active']); + } + + public function testEnableProject() + { + $this->assertTrue($this->client->enableProject(1)); + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); $this->assertEquals(1, $project['is_active']); } + public function testEnableProjectPublicAccess() + { + $this->assertTrue($this->client->enableProjectPublicAccess(1)); + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); + $this->assertEquals(1, $project['is_public']); + $this->assertNotEmpty($project['token']); + } + + public function testDisableProjectPublicAccess() + { + $this->assertTrue($this->client->disableProjectPublicAccess(1)); + $project = $this->client->getProjectById(1); + $this->assertNotEmpty($project); + $this->assertEquals(0, $project['is_public']); + $this->assertEmpty($project['token']); + } + + public function testgetProjectActivities() + { + $activities = $this->client->getProjectActivities(array('project_ids' => array(1))); + $this->assertInternalType('array', $activities); + $this->assertCount(0, $activities); + } + + public function testgetProjectActivity() + { + $activities = $this->client->getProjectActivity(1); + $this->assertInternalType('array', $activities); + $this->assertCount(0, $activities); + } + public function testGetBoard() { $board = $this->client->getBoard(1); @@ -188,6 +235,127 @@ class Api extends PHPUnit_Framework_TestCase $this->assertEquals(4, count($columns)); } + public function testGetDefaultSwimlane() + { + $swimlane = $this->client->getDefaultSwimlane(1); + $this->assertNotEmpty($swimlane); + $this->assertEquals('Default swimlane', $swimlane['default_swimlane']); + } + + public function testAddSwimlane() + { + $swimlane_id = $this->client->addSwimlane(1, 'Swimlane 1'); + $this->assertNotFalse($swimlane_id); + $this->assertInternalType('int', $swimlane_id); + + $swimlane = $this->client->getSwimlaneById($swimlane_id); + $this->assertNotEmpty($swimlane); + $this->assertInternalType('array', $swimlane); + $this->assertEquals('Swimlane 1', $swimlane['name']); + } + + public function testGetSwimlane() + { + $swimlane = $this->client->getSwimlane(1); + $this->assertNotEmpty($swimlane); + $this->assertInternalType('array', $swimlane); + $this->assertEquals('Swimlane 1', $swimlane['name']); + } + + public function testUpdateSwimlane() + { + $swimlane = $this->client->getSwimlaneByName(1, 'Swimlane 1'); + $this->assertNotEmpty($swimlane); + $this->assertInternalType('array', $swimlane); + $this->assertEquals(1, $swimlane['id']); + $this->assertEquals('Swimlane 1', $swimlane['name']); + + $this->assertTrue($this->client->updateSwimlane($swimlane['id'], 'Another swimlane')); + + $swimlane = $this->client->getSwimlaneById($swimlane['id']); + $this->assertNotEmpty($swimlane); + $this->assertEquals('Another swimlane', $swimlane['name']); + } + + public function testDisableSwimlane() + { + $this->assertTrue($this->client->disableSwimlane(1, 1)); + + $swimlane = $this->client->getSwimlaneById(1); + $this->assertNotEmpty($swimlane); + $this->assertEquals(0, $swimlane['is_active']); + } + + public function testEnableSwimlane() + { + $this->assertTrue($this->client->enableSwimlane(1, 1)); + + $swimlane = $this->client->getSwimlaneById(1); + $this->assertNotEmpty($swimlane); + $this->assertEquals(1, $swimlane['is_active']); + } + + public function testGetAllSwimlanes() + { + $this->assertNotFalse($this->client->addSwimlane(1, 'Swimlane A')); + + $swimlanes = $this->client->getAllSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(2, $swimlanes); + $this->assertEquals('Another swimlane', $swimlanes[0]['name']); + $this->assertEquals('Swimlane A', $swimlanes[1]['name']); + } + + public function testGetActiveSwimlane() + { + $this->assertTrue($this->client->disableSwimlane(1, 1)); + + $swimlanes = $this->client->getActiveSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(2, $swimlanes); + $this->assertEquals('Default swimlane', $swimlanes[0]['name']); + $this->assertEquals('Swimlane A', $swimlanes[1]['name']); + } + + public function testMoveSwimlaneUp() + { + $this->assertTrue($this->client->enableSwimlane(1, 1)); + $this->assertTrue($this->client->moveSwimlaneUp(1, 1)); + + $swimlanes = $this->client->getActiveSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(3, $swimlanes); + $this->assertEquals('Default swimlane', $swimlanes[0]['name']); + $this->assertEquals('Another swimlane', $swimlanes[1]['name']); + $this->assertEquals('Swimlane A', $swimlanes[2]['name']); + + $this->assertTrue($this->client->moveSwimlaneUp(1, 2)); + + $swimlanes = $this->client->getActiveSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(3, $swimlanes); + $this->assertEquals('Default swimlane', $swimlanes[0]['name']); + $this->assertEquals('Swimlane A', $swimlanes[1]['name']); + $this->assertEquals('Another swimlane', $swimlanes[2]['name']); + } + + public function testMoveSwimlaneDown() + { + $this->assertTrue($this->client->moveSwimlaneDown(1, 2)); + + $swimlanes = $this->client->getActiveSwimlanes(1); + $this->assertNotEmpty($swimlanes); + $this->assertCount(3, $swimlanes); + $this->assertEquals('Default swimlane', $swimlanes[0]['name']); + $this->assertEquals('Another swimlane', $swimlanes[1]['name']); + $this->assertEquals('Swimlane A', $swimlanes[2]['name']); + } + + public function testRemoveSwimlane() + { + $this->assertTrue($this->client->removeSwimlane(1, 2)); + } + public function testCreateTask() { $task = array( @@ -615,4 +783,58 @@ class Api extends PHPUnit_Framework_TestCase $this->assertFalse($this->client->removeCategory(1)); $this->assertFalse($this->client->removeCategory(1111)); } + + public function testGetAvailableActions() + { + $actions = $this->client->getAvailableActions(); + $this->assertNotEmpty($actions); + $this->assertInternalType('array', $actions); + $this->assertArrayHasKey('TaskLogMoveAnotherColumn', $actions); + } + + public function testGetAvailableActionEvents() + { + $events = $this->client->getAvailableActionEvents(); + $this->assertNotEmpty($events); + $this->assertInternalType('array', $events); + $this->assertArrayHasKey('task.move.column', $events); + } + + public function testGetCompatibleActionEvents() + { + $events = $this->client->getCompatibleActionEvents('TaskClose'); + $this->assertNotEmpty($events); + $this->assertInternalType('array', $events); + $this->assertArrayHasKey('task.move.column', $events); + } + + public function testCreateAction() + { + $action_id = $this->client->createAction(1, 'task.move.column', 'TaskClose', array('column_id' => 1)); + $this->assertNotFalse($action_id); + $this->assertEquals(1, $action_id); + } + + public function testGetActions() + { + $actions = $this->client->getActions(1); + $this->assertNotEmpty($actions); + $this->assertInternalType('array', $actions); + $this->assertCount(1, $actions); + $this->assertArrayHasKey('id', $actions[0]); + $this->assertArrayHasKey('project_id', $actions[0]); + $this->assertArrayHasKey('event_name', $actions[0]); + $this->assertArrayHasKey('action_name', $actions[0]); + $this->assertArrayHasKey('params', $actions[0]); + $this->assertArrayHasKey('column_id', $actions[0]['params']); + } + + public function testRemoveAction() + { + $this->assertTrue($this->client->removeAction(1)); + + $actions = $this->client->getActions(1); + $this->assertEmpty($actions); + $this->assertCount(0, $actions); + } } diff --git a/tests/units/LinkTest.php b/tests/units/LinkTest.php index 076e1b3b..45e9796c 100644 --- a/tests/units/LinkTest.php +++ b/tests/units/LinkTest.php @@ -10,9 +10,9 @@ class LinkTest extends Base { $l = new Link($this->container); - $this->assertTrue($l->create('Link A')); + $this->assertNotFalse($l->create('Link A')); $this->assertFalse($l->create('Link A')); - $this->assertTrue($l->create('Link B', 'Link C')); + $this->assertNotFalse($l->create('Link B', 'Link C')); $links = $l->getAll(); $this->assertNotEmpty($links); @@ -40,8 +40,8 @@ class LinkTest extends Base { $l = new Link($this->container); - $this->assertTrue($l->create('Link A')); - $this->assertTrue($l->create('Link B', 'Link C')); + $this->assertNotFalse($l->create('Link A')); + $this->assertNotFalse($l->create('Link B', 'Link C')); $this->assertEquals(1, $l->getOppositeLinkId(1)); $this->assertEquals(3, $l->getOppositeLinkId(2)); |