From 386f858b3982a74483604881bf1dd7cbfe2fdd22 Mon Sep 17 00:00:00 2001 From: ishifoev Date: Mon, 26 Dec 2022 12:14:16 +0500 Subject: [PATCH] Working with teamwork writer --- cyb-app/.env.example | 3 + cyb-app/app/Applications/Teamwork/Auth.php | 55 ++++ .../app/Applications/Teamwork/Exception.php | 36 +++ cyb-app/app/Applications/Teamwork/Factory.php | 21 ++ .../app/Applications/Teamwork/Helper/Str.php | 43 +++ .../app/Applications/Teamwork/Milestone.php | 165 +++++++++++ cyb-app/app/Applications/Teamwork/Model.php | 69 +++++ cyb-app/app/Applications/Teamwork/People.php | 279 ++++++++++++++++++ cyb-app/app/Applications/Teamwork/Project.php | 165 +++++++++++ .../Applications/Teamwork/Request/JSON.php | 69 +++++ .../Applications/Teamwork/Request/Model.php | 136 +++++++++ .../app/Applications/Teamwork/Request/XML.php | 77 +++++ .../Applications/Teamwork/Response/JSON.php | 189 ++++++++++++ .../Applications/Teamwork/Response/Model.php | 110 +++++++ .../Applications/Teamwork/Response/XML.php | 198 +++++++++++++ cyb-app/app/Applications/Teamwork/Rest.php | 222 ++++++++++++++ .../app/Applications/Teamwork/Rest/Model.php | 99 +++++++ cyb-app/app/Applications/Teamwork/Task.php | 213 +++++++++++++ .../app/Applications/Teamwork/Task_List.php | 147 +++++++++ cyb-app/app/Applications/Teamwork/Time.php | 124 ++++++++ .../Controllers/TeamworkWriteController.php | 99 +++++++ cyb-app/config/services.php | 4 + cyb-app/routes/web.php | 5 + 23 files changed, 2528 insertions(+) create mode 100644 cyb-app/app/Applications/Teamwork/Auth.php create mode 100644 cyb-app/app/Applications/Teamwork/Exception.php create mode 100644 cyb-app/app/Applications/Teamwork/Factory.php create mode 100644 cyb-app/app/Applications/Teamwork/Helper/Str.php create mode 100644 cyb-app/app/Applications/Teamwork/Milestone.php create mode 100644 cyb-app/app/Applications/Teamwork/Model.php create mode 100644 cyb-app/app/Applications/Teamwork/People.php create mode 100644 cyb-app/app/Applications/Teamwork/Project.php create mode 100644 cyb-app/app/Applications/Teamwork/Request/JSON.php create mode 100644 cyb-app/app/Applications/Teamwork/Request/Model.php create mode 100644 cyb-app/app/Applications/Teamwork/Request/XML.php create mode 100644 cyb-app/app/Applications/Teamwork/Response/JSON.php create mode 100644 cyb-app/app/Applications/Teamwork/Response/Model.php create mode 100644 cyb-app/app/Applications/Teamwork/Response/XML.php create mode 100644 cyb-app/app/Applications/Teamwork/Rest.php create mode 100644 cyb-app/app/Applications/Teamwork/Rest/Model.php create mode 100644 cyb-app/app/Applications/Teamwork/Task.php create mode 100644 cyb-app/app/Applications/Teamwork/Task_List.php create mode 100644 cyb-app/app/Applications/Teamwork/Time.php create mode 100644 cyb-app/app/Http/Controllers/TeamworkWriteController.php diff --git a/cyb-app/.env.example b/cyb-app/.env.example index 00b6110..ba278dc 100644 --- a/cyb-app/.env.example +++ b/cyb-app/.env.example @@ -22,6 +22,9 @@ QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 +TEAMWORK_DOMAIN=https://pondersource.teamwork.com/ +TEAMWORK_SECRET=twp_o36bDN1kMF8gX9Df9vexWB4Dm32W + MEMCACHED_HOST=127.0.0.1 REDIS_HOST=127.0.0.1 diff --git a/cyb-app/app/Applications/Teamwork/Auth.php b/cyb-app/app/Applications/Teamwork/Auth.php new file mode 100644 index 0000000..9156527 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Auth.php @@ -0,0 +1,55 @@ + null, + 'key' => null + ]; + + private static $is_subdomain = false; + + public static function set() + { + $num_args = func_num_args(); + if ($num_args === 1) { + self::$config['url'] = self::$url; + self::$config['key'] = func_get_arg(0); + self::$config['url'] = Factory::build('account')->authenticate()->url; + } elseif ($num_args === 2) { + self::$config['url'] = $url = func_get_arg(0); + self::checkSubDomain($url); + if (self::$is_subdomain) { + self::$config['url'] = self::$url; + } + self::$config['key'] = func_get_arg(1); + if (self::$is_subdomain) { + $test = Factory::build('account')->authenticate(); + $url = $test->url; + } + self::$config['url'] = $url; + } + } + + public static function get() + { + return array_values(self::$config); + } + + private static function checkSubDomain($url) + { + $eu_domain = strpos($url, '.eu'); + + if ($eu_domain !== false) { + self::$url = 'https://authenticate.eu.teamwork.com/'; + $url = substr($url, 0, $eu_domain); + } + if (strpos($url, '.') === false) { + self::$is_subdomain = true; + } + } +} diff --git a/cyb-app/app/Applications/Teamwork/Exception.php b/cyb-app/app/Applications/Teamwork/Exception.php new file mode 100644 index 0000000..c22b1f9 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Exception.php @@ -0,0 +1,36 @@ +message = trim($errorInfo['Message']); + if (isset($errorInfo['Response'])) { + $this->response = $errorInfo['Response']; + } + if (isset($errorInfo['Headers'])) { + $this->headers = $errorInfo['Headers']; + } + } + + public function getResponse() + { + return $this->response; + } + + public function getHeaders() + { + return $this->headers; + } +} diff --git a/cyb-app/app/Applications/Teamwork/Factory.php b/cyb-app/app/Applications/Teamwork/Factory.php new file mode 100644 index 0000000..05660af --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Factory.php @@ -0,0 +1,21 @@ +fields = [ + 'title' => true, + 'description' => false, + 'deadline' => [ + 'required'=>true, + 'attributes'=>[ + 'type'=>'integer' + ] + ],//format YYYYMMDD + 'notify' => [ + 'required'=>false, + 'attributes'=>[ + 'type'=>'boolean' + ] + ], + 'reminder'=>[ + 'required'=>false, + 'attributes'=>[ + 'type'=>'boolean' + ] + ], + 'private'=>[ + 'required'=>false, + 'attributes'=>[ + 'type'=>'boolean' + ] + ], + 'responsible_party_ids' => true, + # USE ONLY FOR UPDATE OR PUT METHOD + 'move_upcoming_milestones'=>[ + 'sibling'=>true, + 'required'=>false, + 'attributes'=>['type'=>'boolean'] + ], + 'move_upcoming_milestones_off_weekends'=>[ + 'sibling'=>true, + 'required'=>false, + 'attributes'=>['type'=>'boolean'] + ] + ]; + } + + /** + * Complete + * + * PUT /milestones/#{id}/complete.xml + * + * Marks the specified milestone as complete. Returns Status 200 OK. + * + * @param int $id + * + * @return bool + * @throws App\Applications\Teamwork\Exception + */ + public function complete($id) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + return $this->rest->put("$this->action/$id/complete"); + } + + /** + * Uncomplete + * + * PUT /milestones/#{id}/uncomplete.xml + * + * Marks the specified milestone as uncomplete. Returns Status 200 OK. + * + * @param int $id + * + * @return bool + * @throws \App\Applications\Teamwork\Exception + */ + public function uncomplete($id) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + return $this->rest->put("$this->action/$id/uncomplete"); + } + + /** + * Get all milestone + * + * @param string $filter + * + * @return @return \App\Applications\Teamwork\Response\Model + * @throws @return \App\Applications\Teamwork\Exception + */ + public function getAll($filter = 'all') + { + return $this->rest->get("$this->action", $this->getParams($filter)); + } + + /** + * Get all milestone + * + * @param $project_id + * @param string $filter + * + * @return @return \App\Applications\Teamwork\Response\Model + * @throws @return \App\Applications\Teamwork\Exception + */ + public function getByProject($project_id, $filter = 'all') + { + $project_id = (int) $project_id; + if ($project_id <= 0) { + throw new Exception('Invalid param project_id'); + } + return $this->rest->get( + "projects/$project_id/$this->action", + $this->getParams($filter) + ); + } + + /** + * @param $filter + * + * @return array + * @throws \App\Applications\Teamwork\Exception + */ + private function getParams($filter) + { + $params = []; + if ($filter) { + $filter = (string) $filter; + $filter = strtolower($filter); + if ($filter !== 'all') { + $validate = ['completed', 'incomplete', 'late', 'upcoming']; + if (!in_array($filter, $validate)) { + throw new Exception('Invalid value for param filter'); + } + $params['find'] = $filter; + } + } + return $params; + } + + /** + * @param array $data + * + * @return int + * @throws \App\Applications\Teamwork\Exception + */ + public function insert(array $data) + { + $project_id = empty($data['project_id']) ? 0: $data['project_id']; + if ($project_id <= 0) { + throw new Exception('Required field project_id'); + } + return $this->rest->post("projects/$project_id/$this->action", $data); + } +} \ No newline at end of file diff --git a/cyb-app/app/Applications/Teamwork/Model.php b/cyb-app/app/Applications/Teamwork/Model.php new file mode 100644 index 0000000..881bcac --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Model.php @@ -0,0 +1,69 @@ +rest->get("$this->action/$id", $params); + } + + /** + * @param array $data + * @return int + */ + public function insert(array $data) + { + return $this->rest->post($this->action, $data); + } + + /** + * @param array $data + * + * @return bool + */ + public function update(array $data) + { + $id = empty($data['id']) ? 0: (int) $data['id']; + if ($id <= 0) { + throw new Exception('Required field id'); + } + return $this->rest->put("$this->action/$id", $data); + } + + /** + * @param array $data + * + * @return [bool|int] + */ + final public function save(array $data) + { + return array_key_exists('id', $data) ? + $this->update($data): + $this->insert($data); + } + + /** + * @param int $id + * + * @return bool + */ + public function delete($id) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + return $this->rest->delete("$this->action/$id"); + } +} diff --git a/cyb-app/app/Applications/Teamwork/People.php b/cyb-app/app/Applications/Teamwork/People.php new file mode 100644 index 0000000..5322b87 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/People.php @@ -0,0 +1,279 @@ +fields = [ + 'first_name' => true, + 'last_name' => true, + 'email_address'=>true, + 'user_name' => true, + 'password' => false, + 'company_id' => false, + 'title' => false, + 'phone_number_mobile'=>false, + 'phone_number_office'=>false, + 'phone_number_office_ext'=>false, + 'phone_number_fax'=>false, + 'phone_number_home'=>false, + 'im_handle'=>false, + 'im_service'=>[ + 'required'=>false, + 'validate'=>[ + 'GTalk', + 'AOL', + 'ICQ', + 'MSN', + 'Jabber', + 'Yahoo', + 'Skype', + 'Twitter' + ] + ], + 'date_format'=>[ + 'required'=>false, + 'validate'=>[ + 'dd.mm.yyyy', + 'dd/mm/yyyy', + 'mm.dd.yyyy', + 'mm/dd/yyyy', + 'yyyy-mm-dd', + 'yyyy.mm.dd' + ] + ], + 'send_welcome_email'=>[ + 'required'=>false, + 'type'=>'boolean' + ], + 'receive_daily_reports'=>[ + 'required'=>false, + 'type'=>'boolean' + ], + 'welcome_email_message'=>false, + 'auto_give_project_access'=>[ + 'required'=>false, + 'type'=>'boolean' + ], + 'open_id'=>false, + 'notes'=>[ + 'required'=>false, + 'type'=>'boolean' + ], + 'user_language'=>[ + 'required'=>false, + 'validate'=>[ + 'EN', + 'FR', + 'AR', + 'BG', + 'ZH', + 'HR', + 'CS', + 'DA', + 'NL', + 'FI', + 'DE', + 'EL', + 'HU', + 'ID', + 'IT', + 'JA', + 'KO', + 'NO', + 'PL', + 'PT', + 'RO', + 'RU', + 'ES', + 'SV' + ] + ], + 'administrator'=>false, + 'can_add_projects'=>[ + 'required'=>false, + 'type'=>'boolean' + ] + ]; + $this->parent = 'person'; + $this->action = 'people'; + } + + /** + * @param id + * @param null project id + * @return \App\Applications\Teamwork\Response\Model + * @return \App\Applications\Teamwork\Exception + */ + public function get($id, $project_id = null) { + $id = (int) $id; + if($id <= 0) throw new Exception("Invalid param id"); + + $project_id = (int) $project_id; + $action = "$this->action/$id"; + if($project_id) { + $action = "projects/$project_id/$action"; + } + return $this->rest->get($action); + } + + /** + * get people + * GET /people + * All user will be return including the user + * @param $pageSize int + * @param $size int + * @return \App\Applications\Teamwork\Response\Model + * @return \App\Applications\Teamwork\Exception + */ + public function getAll($pageSize = 200, $page = 1) { + return $this->rest->get($this->action, [ + 'pageSize' => $pageSize, + 'page' => $page + ]); + } + + /** + * Get all people within project + * GET /projects/{project_id}/people + * @param int id + * @return \App\Applications\Teamwork\Response\Model + * @return \App\Applications\Teamwork\Exception + */ + public function getByProject($id) { + $id = (int) $id; + return $this->rest->get("projects/$id/$this->action"); + } + + /** + * Get people with in a company + * GET /companies/#{company_id}/people + * Retreives the details for all the people from the submitted company + * @param int $id + * @return \App\Applications\Teamwork\Response\Model + * @return \App\Applications\Teamwork\Exception + */ + public function getByCompany($id) { + $id = (int) $id; + return $this->rest->get("companies/$id/$this->action"); + } + + /** + * Get user by email + * GET /people + * Retreive user by email + * @param string $email_address + * @return \App\Applications\Teamwork\Response\Model + * @return \App\Applications\Teamwork\Exception + */ + public function getByEmail($email_address) { + $email_address = (string) $email_address; + return $this->rest->get($this->action, [ + 'email_address' => $email_address, + ]); + } + + /** + * Add new user + * POST /people + * @param array $data + * @return int + * @return \App\Applications\Teamwork\Exception + */ + public function insert(array $data) + { + //validate email address + + if (!empty($data['email_address']) && + !filter_var($data['email_address'], FILTER_VALIDATE_EMAIL)) { + throw new Exception( + 'Invalid value for field email_address' + ); + } + $project_id = empty($data["project_id"]) ? 0 : $data["project_id"]; + $permissions = empty($data["permissions"]) ? null : (array) $data["permissions"]; + + unset($data["project_id"], $data["permissions"]); + + $id = parent::insert($data); + + //add permission to project + if($project_id) { + $permission = Factory::build("project/people"); + $permission->add($project_id, $id); + + if($permissions) { + $permissions["person_id"] = $id; + $permissions["project_id"] = $project_id; + $permission->update($permissions); + } + } + } + + /** + * @param array $data + * @return bool + * @return \App\Applications\Teamwork\Exception + */ + public function update(array $data) + { + // validate email address + if (!empty($data['email_address']) && + !filter_var($data['email_address'], FILTER_VALIDATE_EMAIL)) { + throw new Exception( + 'Invalid value for field email_address' + ); + } + + $project_id = empty($data['project_id']) ? 0 : $data['project_id']; + $permissions = empty($data['permissions']) ? null : + (array) $data['permissions']; + unset($data['project_id'], $data['permissions']); + $save = false; + + if(!empty($save)) { + $save = parent::update($data); + } + + if($project_id) { + $permission = Factory::build("project/people"); + + try { + $add = $permission->add($project_id, $data["id"]); + } catch(Exception $e) { + $add = $e->getMessage() === 'User is already on project'; + } + $save = $save && $add; + + if($add && $permissions) { + $permissions["person_id"] = $data["id"]; + $permissions["project_id"] = $project_id; + $save = $permission->update($permissions); + } + } + return $save; + } + + /** + * @param int $id + * @param null $project_id + * @return bool + * @return \App\Applications\Teamwork\Exception + */ + public function delete($id, $project_id = null) { + $id = (int) $id; + + if($id <= 0) throw new Exception("Invalid param id"); + + $project_id = (int) $project_id; + + $action = "$this->action/$id"; + if($project_id) { + $action = "projects/$project_id/$action"; + } + + return $this->rest->delete($action); + } +} \ No newline at end of file diff --git a/cyb-app/app/Applications/Teamwork/Project.php b/cyb-app/app/Applications/Teamwork/Project.php new file mode 100644 index 0000000..a74d680 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Project.php @@ -0,0 +1,165 @@ +fields = [ + // New Project Name + 'name' => true, + // [Optional. Project Description] + 'description' => false, + // [Optional. Start date in yyyymmdd format] + 'startDate' => [ + 'required'=> false, + 'attributes' => [ + 'type'=>'integer' + ] + ], + // [Optional. End date in yyyymmdd format] + 'endDate' => [ + 'required' => false, + 'attributes' => [ + 'type'=>'integer' + ] + ], + // [Optional. Id of company to assign the project to] + 'companyId' => [ + 'required' => false, + 'attributes' => [ + 'type' => 'integer' + ] + ], + // [Optional. Name of a new company to assign the project to] + 'newCompany' => false, + //[Optional. Numeric ID of project category, 0 = no category] + 'category-id' => false, + // [Optional. Comma separated list of tags to apply to project] + 'tags' => false, + + 'notifyeveryone' => [ + 'required' => false, + 'attributes' => [ + 'type'=>'boolean' + ] + ], + 'status' => false + ]; + } + + /** + * Retrieves all accessible projects; including active/inactive and archived projects. + * You can optionally append a date to the call to return only those projects recently updated. + * This is very useful if you are implementing local caching as you won't have to recheck + * everything therefore making your applicaton much faster. You can pass in a date and/or a date + * with a time using the variables updatedAfterDate and updatedAfterTime. + * + * @param array $params + */ + public function getAll(array $params = []) + { + return $this->getByStatus('all', $params); + } + + /** + * @param array $params + * + */ + public function getActive(array $params = []) + { + return $this->getByStatus('active', $params); + } + + /** + * @param array $params + * + */ + public function getArchived(array $params = []) + { + return $this->getByStatus('archived', $params); + } + + /** + * + * @param string $status + * @param array $params + */ + private function getByStatus($status, $params) + { + $params = (array) $params; + $params['status'] = strtoupper($status); + return $this->rest->get("$this->action", $params); + } + + /** + * Surprisingly, this will retrieve all of your projects, which have been starred! + * + */ + public function getStarred() + { + return $this->rest->get("$this->action/starred"); + } + + /** + * Adds a project to your list of favourite projects. + */ + public function star($id) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + return $this->rest->put("$this->action/$id/star"); + } + + /** + * Removes a project from your list of favourite projects. + * + * @param int $id + */ + public function unStar($id) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + return $this->rest->put("$this->action/$id/unstar"); + } + + /** + * Shortcut for active project + * + * @param int $id + * + */ + public function activate($id) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + $data = []; + $data['id'] = $id; + $data['status'] = 'active'; + return $this->update($data); + } + + /** + * Shortcut for archive project + * + * @param int $id + */ + public function archive($id) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + $data = []; + $data['id'] = $id; + $data['status'] = 'archived'; + return $this->update($data); + } +} diff --git a/cyb-app/app/Applications/Teamwork/Request/JSON.php b/cyb-app/app/Applications/Teamwork/Request/JSON.php new file mode 100644 index 0000000..5609976 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Request/JSON.php @@ -0,0 +1,69 @@ +getParent(); + $object->$parent = new stdClass(); + $parent = $object->$parent; + + if ($this->actionInclude('/reorder')) { + foreach ($parameters as $id) { + $item = new stdClass(); + $item->id = $id; + $parent->{$this->parent}[] = $item; + } + } else { + foreach ($this->fields as $field=>$options) { + $value = $this->getValue($field, $options, $parameters); + if (isset($options['attributes'])) { + foreach ($options['attributes'] as $name=>$type) { + if (null !== $value) { + if ($name === 'type') { + if ($type === 'array') { + if (is_string($value) || + is_numeric($value)) { + $value = (array) $value; + } + } else { + settype($value, $type); + } + } + } + } + } + if (null !== $value) { + if (is_string($value)) { + $value = mb_encode_numericentity( + $value, + [0x80, 0xffff, 0, 0xffff], + 'utf-8' + ); + } + !empty($options['sibling']) ? + $object->$field = $value : + $parent->$field = $value; + } + } + } + $parameters = json_encode($object); + $parameters = mb_decode_numericentity( + $parameters, + [0x80, 0xffff, 0, 0xffff], + 'utf-8' + ); + } else { + $parameters = '{}'; + } + + return $parameters; + } +} diff --git a/cyb-app/app/Applications/Teamwork/Request/Model.php b/cyb-app/app/Applications/Teamwork/Request/Model.php new file mode 100644 index 0000000..8cb1823 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Request/Model.php @@ -0,0 +1,136 @@ +parent = $parent; + return $this; + } + + public function setAction($action) + { + $this->action = $action; + return $this; + } + + public function setFields(array $fields) + { + $this->fields = $fields; + return $this; + } + + /** + * @param $field + * @param $options + * @param array $parameters + * + * @return mixed|null + */ + protected function getValue(& $field, & $options, array $parameters) + { + static + $camelize = [ + 'pending_file_attachments' => true, + 'date_format' => true, + 'send_welcome_email' => true, + 'receive_daily_reports' => true, + 'welcome_email_message' => true, + 'auto_give_project_access' => true, + 'open_id' => true, + 'user_language' => true, + 'pending_file_ref' => true, + 'new_company' => true + ], + $yes_no_boolean = [ + 'welcome_email_message', + 'send_welcome_email', + 'receive_daily_reports', + 'notes', + 'auto_give_project_access' + ], + $preserve = [ + 'address_one' => true, + 'address_two' => true + ]; + $value = isset($parameters[$field]) ? $parameters[$field] : null; + if (!is_array($options)) { + $options = ['required'=>$options, 'attributes'=> []]; + } + $isNull = null === $value; + if ($this->method == 'POST' && $options['required']) { + if ($isNull) { + throw new Exception('Required field ' . $field); + } + } + + if (!$isNull && isset($options['validate']) && + !in_array($value, $options['validate'])) { + throw new Exception('Invalid value for field ' . + $field); + } + + if (isset($camelize[$field])) { + if ($field === 'open_id') { + $field = 'openID'; + } else { + $field = Str::camel($field); + } + } elseif (!isset($preserve[$field])) { + if ($field === 'company_id') { + if ($this->action === 'projects') { + $field = Str::camel($field); + } elseif ($this->action == 'people') { + $field = Str::dash($field); + } + } else { + $field = Str::dash($field); + } + } + return $value; + } + + protected function actionInclude($value) + { + return false !== strrpos($this->action, $value); + } + + protected function getParent() + { + return $this->parent . ($this->actionInclude('/reorder') ? 's' : ''); + } + + public function getParameters($method, $parameters) + { + if ($parameters) { + $this->method = $method; + if ($method === 'GET') { + if (is_array($parameters)) { + $parameters = http_build_query($parameters); + } + } elseif ($method === 'POST' || $method === 'PUT') { + $parameters = $this->parseParameters($parameters); + } + } else { + $parameters = null; + } + return $parameters; + } + + /** + * Return parameters for post and put request + * @param array $parameters + * @return string + */ + abstract protected function parseParameters($parameters); +} diff --git a/cyb-app/app/Applications/Teamwork/Request/XML.php b/cyb-app/app/Applications/Teamwork/Request/XML.php new file mode 100644 index 0000000..e942393 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Request/XML.php @@ -0,0 +1,77 @@ +doc = new \DOMDocument(); + $this->doc->formatOutput = true; + } + + protected function parseParameters($parameters) + { + if (!empty($parameters) && is_array($parameters)) { + $wrapper = $this->doc->createElement($this->getWrapper()); + $parent = $this->doc->createElement($this->getParent()); + if ($this->actionInclude('/reorder')) { + $parent->setAttribute('type', 'array'); + foreach ($parameters as $id) { + $element = $this->doc->createElement($this->parent); + $item = $this->doc->createElement('id'); + $item->appendChild($this->doc->createTextNode($id)); + $element->appendChild($item); + $parent->appendChild($element); + } + } else { + foreach ($this->fields as $field=>$options) { + $value = $this->getValue($field, $options, $parameters); + $element = $this->doc->createElement($field); + if (isset($options['attributes'])) { + foreach ($options['attributes'] as $name=>$type) { + if (null !== $value) { + $element->setAttribute($name, $type); + if ($name == 'type') { + if ($type == 'array') { + $internal = $this->doc->createElement($options['element']); + foreach ($value as $v) { + $internal->appendChild($this->doc->createTextNode($v)); + $element->appendChild($internal); + } + } else { + settype($value, $type); + } + } + } + } + } + if (null !== $value) { + if (is_bool($value)) { + $value = var_export($value, true); + } + $element->appendChild($this->doc->createTextNode($value)); + !empty($options['sibling']) ? + $wrapper->appendChild($element) : + $parent->appendChild($element); + } + } + } + $wrapper->appendChild($parent); + $this->doc->appendChild($wrapper); + + $parameters = $this->doc->saveXML(); + } else { + $parameters = null; + } + + return $parameters; + } + + protected function getWrapper() + { + return 'request'; + } +} diff --git a/cyb-app/app/Applications/Teamwork/Response/JSON.php b/cyb-app/app/Applications/Teamwork/Response/JSON.php new file mode 100644 index 0000000..ece93b9 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Response/JSON.php @@ -0,0 +1,189 @@ +getJsonErrors(); + $this->string = $data; + if (!$errors) { + if (!( + $headers['Status'] === 201 || + $headers['Status'] === 200 || + $headers['Status'] === 409 || + $headers['Status'] === 422 || + $headers['Status'] === 400 + )) { + throw new Exception([ + 'Message' => $errors, + 'Response' => $data, + 'Headers' => $headers + ]); + } + if ($headers['Status'] === 201 || $headers['Status'] === 200) { + switch ($headers['Method']) { + case 'UPLOAD': + return empty($source->pendingFile->ref) ? null : (string) $source->pendingFile->ref; + case 'POST': + // print_r($headers); + if (!empty($headers['id'])) { + return (int) $headers['id']; + } elseif (!empty($source->fileId)) { + return (int) $source->fileId; + } + // no break + case 'PUT': + return isset($source->id) ? $source->id : true; + case 'DELETE': + return true; + + default: + if (!empty($source->STATUS)) { + unset($source->STATUS); + } + if (!empty($source->project->files)) { + $source = $source->project->files; + } elseif (!empty($source->project->notebooks)) { + $source = $source->project->notebooks; + } elseif (!empty($source->project->links)) { + $source = $source->project->links; + } elseif ( + !empty($source->messageReplies) && + preg_match('!messageReplies/(\d+)!', $headers['X-Action']) + ) { + $source = current($source->messageReplies); + } elseif ( + !empty($source->people) && + preg_match('!projects/(\d+)/people/(\d+)!', $headers['X-Action']) + ) { + $source = current($source->people); + } elseif ( + !empty($source->project) && + preg_match('!projects/(\d+)/notebooks!', $headers['X-Action']) + ) { + $source = []; + } elseif ( + isset($source->cards) && + preg_match('!portfolio/columns/(\d+)/cards!', $headers['X-Action']) + ) { + $source = $source->cards; + } else { + $source = current($source); + } + if ($headers['X-Action'] === 'links' || $headers['X-Action'] === 'notebooks') { + $_source = []; + $wrapper = $headers['X-Action']; + foreach ($source as $project) { + foreach ($project->$wrapper as $object) { + $_source[] = $object; + } + } + $source = $_source; + } elseif (strpos($headers['X-Action'], 'time_entries') !== false && !$source) { + $source = []; + } + $this->headers = $headers; + $this->string = json_encode($source); + + $this->data = is_object($source) ? self::camelizeObject($source) : $source; + + if (!empty($this->data->id)) { + $this->data->id = (int) $this->data->id; + } + + return $this; + } + } elseif (!empty($source->MESSAGE)) { + $errors = $source->MESSAGE; + } else { + $errors = null; + } + } + + throw new Exception([ + 'Message' => $errors, + 'Response' => $data, + 'Headers' => $headers + ]); + } + + /** + * @return string + */ + protected function getContent() + { + $object = json_decode($this->string); + + return json_encode($object, JSON_PRETTY_PRINT); + } + + /** + * @param array $source + * + * @return ArrayObject + */ + protected static function camelizeObject($source) + { + if (!is_object($source) && !is_array($source)) { + return $source; + } + + $destination = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS); + foreach ($source as $key => $value) { + if (ctype_upper($key)) { + $key = strtolower($key); + } + $key = Str::camel($key); + $destination->$key = is_scalar($value) ? $value : self::camelizeObject($value); + } + return $destination; + } + + /** + * @codeCoverageIgnore + */ + private function getJsonErrors() + { + $errorCode = json_last_error(); + if (!$errorCode) { + return; + } + + if (function_exists('json_last_error_msg')) { + return json_last_error_msg(); + } + + switch ($errorCode) { + case JSON_ERROR_DEPTH: + return 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + return 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + return 'Unexpected control character found'; + break; + case JSON_ERROR_SYNTAX: + return 'Syntax error, malformed JSON'; + break; + case JSON_ERROR_UTF8: + return 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + } + } +} diff --git a/cyb-app/app/Applications/Teamwork/Response/Model.php b/cyb-app/app/Applications/Teamwork/Response/Model.php new file mode 100644 index 0000000..e3416d0 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Response/Model.php @@ -0,0 +1,110 @@ +getContent()); + } + + abstract protected function getContent(); + + public function __toString() + { + return $this->getContent(); + } + + public function toArray() + { + return $this->data; + } + + public function getHeaders() + { + return $this->headers; + } + + public function getIterator() : ArrayIterator + { + return new ArrayIterator($this->data); + } + + public function count() : int + { + return count($this->data); + } + + public function offsetSet($offset, $value) : void + { + if (is_null($offset)) { + $this->data[] = $value; + } else { + $this->data[$offset] = $value; + } + } + + public function offsetExists($offset) : bool + { + return isset($this->data[$offset]); + } + + public function offsetUnset($offset) : void + { + unset($this->data[$offset]); + } + + public function offsetGet($offset): bool + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + + public function __get($name) + { + return isset($this->data[$name]) ? $this->data[$name] : null; + } + + public function __set($name, $value) + { + $this->data[$name] = $value; + } + + public function __isset($name) + { + return isset($this->data[$name]); + } + + public function __unset($name) + { + unset($this->data[$name]); + } +} diff --git a/cyb-app/app/Applications/Teamwork/Response/XML.php b/cyb-app/app/Applications/Teamwork/Response/XML.php new file mode 100644 index 0000000..a234cca --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Response/XML.php @@ -0,0 +1,198 @@ +string = $data; + $source = simplexml_load_string($data); + $errors = $this->getXmlErrors($source); + if ($source) { + if ($headers['Status'] === 201 || $headers['Status'] === 200) { + switch ($headers['Method']) { + case 'UPLOAD': + if (!empty($source->ref)) { + return (string) $source->ref; + } + break; + case 'POST': + if (!empty($headers['id'])) { + return $headers['id']; + } else { + $property = 0; + $value = (int) $source->$property; + // this case the fileid + if ($value > 0) { + return $value; + } + } + break; + case 'PUT': + case 'DELETE': + return true; + default: + if (!empty($source->files->file)) { + $source = $source->files->file; + $isArray = true; + } elseif (!empty($source->notebooks->notebook)) { + $source = $source->notebooks->notebook; + $isArray = true; + } elseif (!empty($source->project->links)) { + $source = $source->project->links; + $isArray = true; + } else { + $attrs = $source->attributes(); + $isArray = !empty($attrs->type) && + (string) $attrs->type === 'array'; + } + $this->headers = $headers; + + $_this = self::toStdClass($source, $isArray); + + foreach ($_this as $key=>$value) { + $this->$key = $value; + } + return $this; + } + } else { + if (!empty($source->error)) { + foreach ($source as $error) { + $errors .= $error ."\n"; + } + } else { + $property = 0; + $errors .= $source->$property; + } + } + } + throw new Exception([ + 'Message'=> $errors, + 'Response'=> $data, + 'Headers'=> $headers + ]); + } + + /** + * Devuelve un xml formateado + * + * @return string + */ + protected function getContent() + { + $dom = new DOMDocument('1.0'); + $dom->loadXML($this->string); + $dom->preserveWhiteSpace = false; + $dom->formatOutput = true; + + return $dom->saveXML(); + } + + /** + * Convierte un objecto SimpleXMLElement a stdClass + * + * @param SimpleXMLElement $source + * @param bool $isArray + * @return stdClass + */ + private static function toStdClass( + SimpleXMLElement $source, + $isArray = false + ) { + $destination = $isArray ? [] : new stdClass(); + foreach ($source as $key=>$value) { + $key = Str::camel($key); + $attrs = $value->attributes(); + if (!empty($attrs->type)) { + $type = (string) $attrs->type; + switch ($type) { + case 'integer': + $destination->$key = (int) $value; + break; + case 'boolean': + $value = (string) $value; + $destination->$key = (bool) $value === 'true'; + break; + case 'array': + if (is_array($destination)) { + $destination[$key] = self::toStdClass($value, true); + } else { + $destination->$key = self::toStdClass($value, true); + } + break; + default: + $destination->$key = (string) $value; + break; + } + } else { + $children = $value->children(); + if (!empty($children)) { + if ($isArray) { + $i = count($destination); + $destination[$i] = self::toStdClass($value); + } else { + $destination->$key = self::toStdClass($value); + } + } else { + $destination->$key = (string) $value; + } + } + } + + return $destination; + } + + private function getXmlErrors($xml) + { + $errors = ''; + foreach (libxml_get_errors() as $error) { + $errors .= $this->getXmlError($error, $xml) . "\n"; + } + libxml_clear_errors(); + return $errors; + } + + private function getXmlError($error, $xml) + { + $return = $xml[$error->line - 1] . "\n"; + $return .= str_repeat('-', $error->column) . "^\n"; + + switch ($error->level) { + case LIBXML_ERR_WARNING: + $return .= "Warning $error->code: "; + break; + case LIBXML_ERR_ERROR: + $return .= "Error $error->code: "; + break; + case LIBXML_ERR_FATAL: + $return .= "Fatal Error $error->code: "; + break; + } + + $return .= trim($error->message) . + "\n Line: $error->line" . + "\n Column: $error->column"; + + if ($error->file) { + $return .= "\n File: $error->file"; + } + + return "$return\n\n--------------------------------------------\n\n"; + } +} diff --git a/cyb-app/app/Applications/Teamwork/Rest.php b/cyb-app/app/Applications/Teamwork/Rest.php new file mode 100644 index 0000000..0ff9bca --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Rest.php @@ -0,0 +1,222 @@ +key = $key; + $this->url = $url; + } + $format = strtoupper(self::$FORMAT); + $request = '\App\Applications\Teamwork\Request\\' . $format; + $this->request = new $request; + } + + /** + * Call to api + * + * @param string $method + * @param string $action + * @param mixed $request + * @return mixed + */ + private function execute($method, $action, $request = null) + { + $url = "{$this->url}$action." . self::$FORMAT; + $headers = ['Authorization: BASIC '. base64_encode( + $this->key . ':xxx' + )]; + $request = $this->request + ->setAction($action) + ->getParameters($method, $request); + $ch = static::initCurl($method, $url, $request, $headers); + $i = 0; + while ($i < 5) { + $data = curl_exec($ch); + $status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); + $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $headers = $this->parseHeaders(substr($data, 0, $header_size)); + if ( + $status === 400 && + (int) $headers['x-ratelimit-remaining'] === 0 + ) { + $i ++; + $reset = $headers['x-ratelimit-reset']; + sleep($reset); + } else { + break; + } + } + // echo $data, PHP_EOL, PHP_EOL; + $body = substr($data, $header_size); + $errorInfo = curl_error($ch); + $error = curl_errno($ch); + curl_close($ch); + if ($error) { + throw new Exception($errorInfo); + } + + $headers['Status'] = $status; + $headers['Method'] = $method; + $headers['X-Url'] = $url; + $headers['X-Request'] = $request; + $headers['X-Action'] = $action; + // for chrome use + $headers['X-Authorization'] = 'BASIC '. base64_encode($this->key . ':xxx'); + $response = '\\App\\Applications\\Teamwork\\Response\\' . strtoupper(self::$FORMAT); + $response = new $response; + + return $response->parse($body, $headers); + } + + private static function initCurl($method, $url, $params, $headers) + { + $ch = curl_init(); + switch ($method) { + case 'GET': + if (!empty($params)) { + $url .= '?' . $params; + } + break; + case 'UPLOAD': + curl_setopt_array($ch, [ + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $params + ]); + break; + case 'DELETE': + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + break; + case 'PUT': + case 'POST': + if ($method === 'POST') { + curl_setopt($ch, CURLOPT_POST, true); + } else { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + } + if ($params) { + curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + } + $headers = array_merge($headers, [ + 'Content-Type: application/' . self::$FORMAT, + 'Content-Length:' . strlen($params) + ]); + break; + } + curl_setopt_array($ch, [ + CURLOPT_HTTPHEADER => $headers, + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_SSL_VERIFYHOST => false, + CURLOPT_SSL_VERIFYPEER => false + ]); + + return $ch; + } + + /** + * Shortcut call get method to api + * + * @param string $action + * @param mixed $request + */ + public function get($action, $request = null) + { + return $this->execute('GET', $action, $request); + } + + public function put($action, $request = null) + { + return $this->execute('PUT', $action, $request); + } + + public function post($action, $request = null) + { + return $this->execute('POST', $action, $request); + } + + public function delete($action) + { + return $this->execute('DELETE', $action, null); + } + + public function upload($action, $request = null) + { + return $this->execute('UPLOAD', $action, $request); + } + + public function getRequest() + { + return $this->request; + } + /** + * @codeCoverageIgnore + */ + public static function setFormat($value) + { + static $format = ['json', 'xml']; + $value = strtolower($value); + if (in_array($value, $format)) { + self::$FORMAT = $value; + } + } + + private function parseHeaders($stringHeaders) + { + $headers = []; + $stringHeaders = trim($stringHeaders); + if ($stringHeaders) { + $parts = explode("\n", $stringHeaders); + foreach ($parts as $header) { + $header = trim($header); + if ($header && false !== strpos($header, ':')) { + list($name, $value) = explode(':', $header, 2); + $value = trim($value); + $name = trim($name); + if (isset($headers[$name])) { + if (is_array($headers[$name])) { + $headers[$name][] = $value; + } else { + $_val = $headers[$name]; + $headers[$name] = [$_val, $value]; + } + } else { + $headers[$name] = $value; + } + } + } + } + return $headers; + } +} diff --git a/cyb-app/app/Applications/Teamwork/Rest/Model.php b/cyb-app/app/Applications/Teamwork/Rest/Model.php new file mode 100644 index 0000000..60b3ae6 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Rest/Model.php @@ -0,0 +1,99 @@ +rest = new Rest($url, $key); + $this->hash = $hash; + $this->parent = strtolower(str_replace( + ['App\Applications\Teamwork\\', '\\'], + ['', '-'], + $class + )); + if (method_exists($this, 'init')) { + $this->init(); + } + if (null === $this->action) { + $this->action = str_replace('-', '_', $this->parent); + // pluralize + if (substr($this->action, -1) === 'y') { + $this->action = substr($this->action, 0, -1) . 'ies'; + } else { + $this->action .= 's'; + } + } + //configure request para put y post fields + $this->rest->getRequest() + ->setParent($this->parent) + ->setFields($this->fields); + } + + /** + * @codeCoverageIgnore + */ + final public function __destruct() + { + unset(self::$instances[$this->hash]); + } + + /** + * @codeCoverageIgnore + */ + final protected function __clone() + { + } + + /** + * @param $url + * @param string $key + */ + final public static function getInstance($url, $key) + { + $class = get_called_class(); + $hash = md5($class . '-' . $url . '-' . $key); + if (!isset(self::$instances[$hash])) { + self::$instances[$hash] = new $class($url, $key, $class, $hash); + } + + return self::$instances[$hash]; + } +} diff --git a/cyb-app/app/Applications/Teamwork/Task.php b/cyb-app/app/Applications/Teamwork/Task.php new file mode 100644 index 0000000..454b73c --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Task.php @@ -0,0 +1,213 @@ +fields = [ + 'content'=>true, + 'notify'=>[ + 'required'=>false, + 'attributes'=>[ + 'type'=>'boolean' + ] + ], + 'description'=>false, + 'due_date'=>[ + 'required'=>false, + 'attributes'=>[ + 'type'=>'integer' + ] + ], + 'start_date'=>[ + 'required'=>false, + 'attributes'=>[ + 'type'=>'integer' + ] + ], + 'private'=>[ + 'required'=>false, + 'attributes'=>[ + 'type'=>'boolean' + ] + ], + 'priority'=>[ + 'required'=>false, + 'validate'=>[ + 'low', + 'medium', + 'high' + ] + ], + 'estimated_minutes'=>[ + 'required'=>false, + 'attributes'=>[ + 'type'=>'integer' + ] + ], + 'predecessors'=>[ + 'required'=>false, + 'attributes'=>[ + 'type'=>'array' + ] + ], + 'responsible_party_id' => false, + 'attachments' => false, + 'pending_file_attachments' => false + ]; + $this->parent = 'todo-item'; + $this->action = 'todo_items'; + } + + /** + * @param $id + * @param $get_time + * @return @return \App\Applications\Teamwork\Response\Model + * @throws App\Applications\Teamwork\Exception + */ + public function get($id, $get_time = false) + { + $id = (int) $id; + + if($id <= 0) { + throw new Exception("Invalid param id"); + } + + $params = []; + if($get_time) { + $params['getTime'] = (int) $get_time; + } + return $this->rest->get("$this->action/$id", $params); + } + + /** + * Retrieve all tasks on a task list + * + * GET /todo_lists/#{todo_list_id}/tasks.json + * This is almost the same as the “Get list” action, except it does only returns the items. + * + * This is almost the same as the “Get list” action, except it does only returns the items. + * @param int $task_list_id + * @param string $filter + * @return @return \App\Applications\Teamwork\Response\Model + * @throws App\Applications\Teamwork\Exception + */ + public function getByTaskList($task_list_id, $filter = 'all') + { + $task_list_id = (int) $task_list_id; + + if($task_list_id <= 0) { + throw new Exception("Invalid param task_list_id"); + } + $params = [ + 'filter'=> $filter + ]; + $filter = strtolower($filter); + + $validate = ['all', 'pending', 'upcoming','late','today','finished']; + if(in_array($filter, $validate)) { + $params["filter"] = "all"; + } + + return $this->rest->get("todo_lists/$task_list_id/$this->action", $params); + } + /** + * Create Item on a List + * + * POST /todo_lists/#{todo_list_id}/todo_items.xml + * + * For the submitted list, creates a todo item. It will be added to the end of the list, + * and marked as uncomplete by default. If you give a persons id as the responsible-party-id value, + * they will be responsible for same, you can also use the “notify” key to indicate whether an email + * should be sent to that person to tell them about the assignment. + * Multiple people can be assigned by passing a comma delimited list for responsible-party-id. + * + * @param array $data + * + * @return int + * @throws App\Applications\Teamwork\Exception + */ + public function insert(array $data) + { + $task_list_id = empty($data['task_list_id']) ? 0 : (int) $data['task_list_id']; + if ($task_list_id <= 0) { + throw new Exception('Required field task_list_id'); + } + if(!empty($data["files"])) { + $file = Factory::build("file"); + $data['pending_file_attachments'] = $file->upload($data['files']); + unset($data['files']); + } + return $this->rest->post("todo_lists/$task_list_id/$this->action", $data); + } + + /** + * Mark an Item Complete + * + * PUT /todo_items/#{id}/complete.xml + * + * The submitted todo item is marked as complete + * + * @param int $id + * + * @return bool + * @throws App\Applications\Teamwork\Exception + */ + public function complete($id) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + return $this->rest->put("$this->action/$id/complete"); + } + + /** + * Mark an Item Uncomplete + * + * PUT /todo_items/#{id}/uncomplete.xml + * + * Changes the item to uncomplete. (if called on an uncomplete item, has no effect) + * + * @param int $id + * + * @return bool + * @throws App\Applications\Teamwork\Exceptions + */ + public function uncomplete($id) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + + return $this->rest->put("$this->action/$id/uncomplete"); + } + + /** + * Reorder the todo items + * + * POST /todo_lists/#{todo_list_id}/todo_items/reorder.xml + * + * Re-orders items on the submitted list. Completed items cannot be reordered, + * and any items not specified will be sorted after the items explicitly given + * Items can be re-parented by putting them from one list into the ordering of items for a different list/ + * + * @param int $task_list_id + * @param array $ids + * + * @return bool + * @throws App\Applications\Teamwork\Exception + */ + public function reorder($task_list_id, array $ids) + { + $task_list_id = (int) $task_list_id; + if ($task_list_id <= 0) { + throw new Exception('Invalid param task_list_id'); + } + return $this->rest->post("todo_lists/$task_list_id/" . + "$this->action/reorder", $ids); + } +} \ No newline at end of file diff --git a/cyb-app/app/Applications/Teamwork/Task_List.php b/cyb-app/app/Applications/Teamwork/Task_List.php new file mode 100644 index 0000000..a51a9b8 --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Task_List.php @@ -0,0 +1,147 @@ +fields = [ + 'name' => true, + 'private' => [ + 'required'=>false, + 'attributes'=>[ + 'type'=>'boolean' + ] + ], + 'pinned' =>[ + 'required'=>false, + 'attributes'=>[ + 'type'=>'boolean' + ] + ], + 'tracked' => [ + 'required'=>false, + 'attributes'=>[ + 'type'=>'boolean' + ] + ], + 'description' => false, + 'milestone_id' => [ + 'required'=>false, + 'attributes'=>[ + 'type'=>'integer' + ] + ], + 'todo_list_template_id' => false + ]; + $this->parent = 'todo-list'; + $this->action = 'todo_lists'; + } + + /** + * Retrieve Single todo list + * GET /todo_lists/#{id}.xml + * GET /todo_lists/#{id}.xml?showTasks=no + * + * Retrieves the todo list corresponding to the submitted integer ID. + * if you pass showTasks=no, no tasks will be returned (showTasks defaults to "yes"). + * + * @param int $id + * @param bool $show_tasks + * + * @return @return \App\Applications\Teamwork\Response\Model + * @throws App\Applications\Teamwork\Exception + */ + public function get($id, $show_tasks = true) + { + $id = (int) $id; + if ($id <= 0) { + throw new Exception('Invalid param id'); + } + $params = []; + if (!$show_tasks) { + $params['showTasks'] = 'no'; + } + return $this->rest->get("$this->action/$id", $params); + } + + /** + * Get all task lists for a project + * + * GET /projects/#{project_id}/todo_lists.xml + * GET /projects/#{project_id}/todo_lists.xml?showTasks=no + * GET /projects/#{project_id}/todo_lists.xml?responsible-party-id=#{id} + * GET /projects/#{project_id}/todo_lists.xml?getOverdueCount=yes + * GET /projects/#{project_id}/todo_lists.xml?responsible-party-id=#{id}&getOverdueCount=yes + * GET /projects/#{project_id}/todo_lists.xml?status=completed&getCompletedCount=yes + * Retrieves all project task lists + * Options: + * You can pass 'showMilestones=yes' if you would like to get information on Milestones associated with each task list + * You can pass 'showTasks=no' if you do not want to have the tasks returned (showTasks defaults to "yes"). + * + * If 'responsible-party-id' is passed lists returned will be filtered to those with tasks for the user. + * Passing "getOverdueCount" will return the number of overdue tasks ("overdue-count") for each task list. + * Passing "getCompletedCount" will return the number of completed tasks ("completed-count") for each task list. + * Status: You can use the Status option to restrict the lists return - valid values are 'all', 'active', and 'completed'. The default is "ACTIVE" + * Filter: You can use the Filter option to return specific tasks - valid values are 'all','upcoming','late','today','tomorrow'. The default is "ALL" + * If you pass FILTER as upcoming, late, today or tomorrow, you can also pass includeOverdue to also include overdue tasks + * + * @param [int] $id + * @param [string | array] $params + * + * @return object + * @throws App\Applications\Teamwork\Exception + */ + public function getByProject($id, $params = null) + { + $project_id = (int) $id; + if ($project_id <= 0) { + throw new Exception('Invalid param project_id'); + } + if ($params && is_string($params)) { + $status = ['active','completed']; + $filter = ['upcoming','late','today','tomorrow']; + if (in_array($params, $status)) { + $params = ['status'=> $params]; + } elseif (in_array($params, $filter)) { + $params = ['filter'=> $params]; + } else { + $params = null; + } + } + return $this->rest->get("projects/$project_id/$this->action", $params); + } + + /** + * Reorder lists + * + * POST /projects/#{project_id}/todo_lists/reorders + * Reorders the lists in the project according to the ordering given. + * Any lists that are not explicitly specified will be positioned after the lists that are specified. + * + * @param int $project_id + * @param array $ids + * @return bool + */ + public function reorder($project_id, array $ids) + { + $project_id = (int) $project_id; + return $this->rest->post("projects/$project_id/$this->action/reorder", $ids); + } + + /** + * @param array $data + * + * @return int + * @throws App\Applications\Teamwork\Exception + */ + public function insert(array $data) + { + $project_id = empty($data['project_id']) ? 0 : (int) $data['project_id']; + if ($project_id <= 0) { + throw new Exception('Required field project_id'); + } + return $this->rest->post("projects/$project_id/$this->action", $data); + } +} \ No newline at end of file diff --git a/cyb-app/app/Applications/Teamwork/Time.php b/cyb-app/app/Applications/Teamwork/Time.php new file mode 100644 index 0000000..e1e22da --- /dev/null +++ b/cyb-app/app/Applications/Teamwork/Time.php @@ -0,0 +1,124 @@ +fields = [ + 'description' => true, + 'person_id' => true, + 'date'=>[ + 'required' => true, + 'type' => 'integer', + 'length' => 6 + ], + 'hours' => [ + 'required' => true, + 'type' => 'integer', + 'length' => 3 + ], + 'minutes'=>[ + 'required' => false, + 'type' => 'integer', + 'length' => 2 + ], + 'time'=>true, + 'isbillable'=>false, + 'tags'=> [ + 'required' => false, + 'type' => 'string', + ], + ]; + $this->parent = 'time-entry'; + } + + /** + * insert time entry + * @param array $data + * @throws App\Applications\Teamwork\Exception + */ + public function insert(array $data) + { + $id = 0; + if(!empty($data["task_id"])) { + $id = (int) $data["task_id"]; + $resource = "todo_items"; + } elseif(!empty($data["project_id"])) { + $id = (int) $data["project_id"]; + $resource = "projects"; + } + if($id <= 0) { + throw new Exception("Required field project_id or task_id"); + } + return $this->rest->post("$resource/$id/$this->action", $data); + } + + /** + * Optional Parameters + * + * PAGE : numeric - The page to start retrieving entries from + * ( e.g: page=1 gives records 1 - 50, page=2 gives records 51-99 etc) + * FROMDATE : string (YYYYMMDD) - The start date to retrieve from + * FROMTIME : string (HH:MM) - The start time only if FROMDATE is passed + * TODATE : string (YYYYMMDD) - The end date to retrieve to + * TOTIME : string (HH:MM) - The end time only if TODATE is passed + * + * @param array $params + * + * @return @return \App\Applications\Teamwork\Response\Model + * @throws App\Applications\Teamwork\Exception + */ + public function getAll(array $params = []) + { + return $this->rest->get("$this->action", $params); + } + + /** + * Optional Parameters + * + * PAGE : numeric - The page to start retrieving entries from + * ( e.g: page=1 gives records 1 - 50, page=2 gives records 51-99 etc) + * FROMDATE : string (YYYYMMDD) - The start date to retrieve from + * FROMTIME : string (HH:MM) - The start time only if FROMDATE is passed + * TODATE : string (YYYYMMDD) - The end date to retrieve to + * TOTIME : string (HH:MM) - The end time only if TODATE is passed + * + * @param $project_id + * @param array $params + * + * @return \TeamWorkPm\Response\Model + * @throws \TeamWorkPm\Exception + */ + public function getByProject($project_id, array $params = []) + { + $project_id = (int) $project_id; + if ($project_id <= 0) { + throw new Exception('Invalid param project_id'); + } + return $this->rest->get("projects/$project_id/$this->action", $params); + } + + /** + * Retrieve all To-do Item Times + * + * GET /todo_items/#{todo_item_id}/time_entries + * + * Retrieves all of the time entries from a submitted todo item. + * + * @param $task_id + * @param array $params + * + * @return \TeamWorkPm\Response\Model + * @throws \TeamWorkPm\Exception + */ + public function getByTask($task_id, array $params = []) + { + $task_id = (int) $task_id; + if ($task_id <= 0) { + throw new Exception('Invalid param task_id'); + } + return $this->rest->get("todo_items/$task_id/$this->action", $params); + } +} \ No newline at end of file diff --git a/cyb-app/app/Http/Controllers/TeamworkWriteController.php b/cyb-app/app/Http/Controllers/TeamworkWriteController.php new file mode 100644 index 0000000..6776d58 --- /dev/null +++ b/cyb-app/app/Http/Controllers/TeamworkWriteController.php @@ -0,0 +1,99 @@ +save([ + 'name'=> 'This is a test project', + 'description'=> 'Bla, Bla, Bla' + ]); + + // create one people and add to project + //$people = Factory::build('people'); + //var_dump($people); + /*$person_id = $people->save([ + 'first_name' => 'Test', + 'last_name' => 'User', + 'user_name' => 'test', + 'email_address' => 'benz94.94@mail.ru', + 'password' => 'Alex21_21_21', + 'project_id' => $project_id + ]);*/ + + // create on milestone + $milestone = Factory::build('milestone'); + $milestone_id = $milestone->save([ + 'project_id' => $project_id, + 'responsible_party_ids' => 79831, + 'title' => 'Test milestone', + 'description' => 'Bla, Bla, Bla', + 'deadline' => date('Ymd', strtotime('+10 day')) + ]); + + // create one task list + $taskList = Factory::build('task.list'); + $task_list_id = $taskList->save([ + 'project_id' => $project_id, + 'milestone_id' => $milestone_id, + 'name' => 'My first task list', + 'description' => 'Bla, Bla' + ]); + + $task = Factory::build('task'); + $task_id = $task->save([ + 'task_list_id' => $task_list_id, + 'content' => 'Test Task', + 'notify' => false, + 'description' => 'Bla, Bla, Bla', + 'due_date' => date('Ymd', strtotime('+10 days')), + 'start_date' => date('Ymd'), + 'private' => false, + 'priority' => 'high', + 'estimated_minutes' => 1000, + 'responsible_party_id' => 79831, + ]); + + $time = Factory::build('time'); + $time_id = $time->save([ + 'task_id' => $task_id, + 'person_id' => 79831, + 'description' => 'Test Time', + 'date' => date('Ymd'), + 'hours' => 5, + 'minutes' => 30, + 'time' => '08:30', + 'isbillable' => false + ]); + + + echo 'Project Id: ' . $project_id . "\n"; + //echo 'Person Id: ' . $person_id . "\n"; + echo 'Milestone Id: ' . $milestone_id . "\n"; + echo 'Task List Id: ' . $task_list_id . "\n"; + echo 'Task Id: ' . $task_id . "\n"; + echo 'Time Id: ' . $time_id . "\n"; + + } catch (\Exception $e) { + print_r($e->getMessage()); + } + } +} diff --git a/cyb-app/config/services.php b/cyb-app/config/services.php index 2a1d616..39be7e0 100644 --- a/cyb-app/config/services.php +++ b/cyb-app/config/services.php @@ -19,6 +19,10 @@ 'secret' => env('MAILGUN_SECRET'), 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), ], + 'teamwork' => [ + 'domain' => env('TEAMWORK_DOMAIN'), + 'secret' => env('TEAMWORK_SECRET'), + ], 'postmark' => [ 'token' => env('POSTMARK_TOKEN'), diff --git a/cyb-app/routes/web.php b/cyb-app/routes/web.php index 9960daf..0fc2ed8 100644 --- a/cyb-app/routes/web.php +++ b/cyb-app/routes/web.php @@ -2,9 +2,11 @@ use App\Core\ApplicationManager; use App\Core\DataType\DataTypeManager; +use App\Http\Controllers\TeamworkWriteController; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; + /* |-------------------------------------------------------------------------- | Web Routes @@ -16,6 +18,7 @@ | */ Route::get('/', function () { + // START configurtion $applications = ApplicationManager::getApplications(); $authentications = ApplicationManager::getAuthentications(); @@ -48,6 +51,8 @@ return view('welcome', compact('applications', 'view_authentications')); }); +Route::get('/teamwork/write', TeamworkWriteController::class); + Route::post('/apps/{app_code_name}/auth', function (Request $request, $app_code_name) { return ApplicationManager::finalizeAuthentication($request, $app_code_name); });