diff --git a/application/classes/Ushahidi/Repository/Tag.php b/application/classes/Ushahidi/Repository/Tag.php index fc32ec121c..c7ac058129 100644 --- a/application/classes/Ushahidi/Repository/Tag.php +++ b/application/classes/Ushahidi/Repository/Tag.php @@ -42,14 +42,13 @@ protected function getTable() // ReadRepository public function getEntity(Array $data = null) { - if (!empty($data['id'])) - { + if (!empty($data['id'])) { // If this is a top level category - if(empty($data['parent_id'])) { + if (empty($data['parent_id'])) { // Load children $data['children'] = DB::select('id') ->from('tags') - ->where('parent_id','=',$data['id']) + ->where('parent_id', '=', $data['id']) ->execute($this->db) ->as_array(null, 'id'); } @@ -74,10 +73,9 @@ public function getSearchFields() protected function setSearchConditions(SearchData $search) { $query = $this->search_query; - foreach (['tag', 'type', 'parent_id'] as $key) - { + foreach (['tag', 'type', 'parent_id'] as $key) { if ($search->$key) { - $query->where($key, '=', $search->$key); + $query->where($key, '=', $search->$key); } } @@ -86,9 +84,9 @@ protected function setSearchConditions(SearchData $search) $query->where('tag', 'LIKE', "%{$search->q}%"); } - if($search->level) { + if ($search->level) { //searching for top-level-tags - if($search->level === 'parent') { + if ($search->level === 'parent') { $query->where('parent_id', '=', null); } } @@ -162,8 +160,30 @@ public function delete(Entity $entity) public function deleteTag($id) { // Remove tag from attribute options - $this->removeTagFromAttributeOptions($entity->id); - + $this->removeTagFromAttributeOptions($id); return $this->delete(compact('id')); } + + /** + * Checks if the assigned role is valid for this tag. + * True if there is no role or if it's a parent with no children + * @param Validation $validation + * @param $fullData + * @return bool + */ + public function isRoleValid(Validation $validation, $fullData) + { + $valid = true; + $isChild = $fullData['parent_id']; + $hasRole = !!$fullData['role']; + $parent = null; + if ($hasRole && $isChild) { + $parent = $this->selectOne(['id' => $fullData['parent_id']]); + $valid = $parent['role'] !== $fullData['role']; + } + if (!$valid) { + $validation->error('role', 'tag.role'); + } + return $valid; + } } diff --git a/application/classes/Ushahidi/Validator/Tag/Update.php b/application/classes/Ushahidi/Validator/Tag/Update.php index ce7e180df7..0bfa29aee7 100644 --- a/application/classes/Ushahidi/Validator/Tag/Update.php +++ b/application/classes/Ushahidi/Validator/Tag/Update.php @@ -63,6 +63,7 @@ protected function getRules() ], 'role' => [ [[$this->role_repo, 'exists'], [':value']], + [[$this->repo, 'isRoleValid'], [':validation', ':fulldata']] ] ]; } diff --git a/application/messages/tag.php b/application/messages/tag.php index 7569690013..a4b75b3451 100644 --- a/application/messages/tag.php +++ b/application/messages/tag.php @@ -2,7 +2,9 @@ return array( 'isSlugAvailable' => ':field :value is already in use', - 'description.regex' => 'The description must contain only letters, numbers, spaces and punctuation', - 'tag.regex' => 'The category name must contain only letters, numbers, spaces and punctuation', + 'isRoleValid' => 'Role must match the parent category', + 'tag.role.isRoleValid' => 'Role must match the parent category', + 'description.regex' => 'The description must contain only letters, numbers, spaces and punctuation', + 'tag.regex' => 'The category name must contain only letters, numbers, spaces and punctuation', ); diff --git a/tests/datasets/ushahidi/Base.yml b/tests/datasets/ushahidi/Base.yml index ff2ebff34c..8354a1f5ce 100644 --- a/tests/datasets/ushahidi/Base.yml +++ b/tests/datasets/ushahidi/Base.yml @@ -1128,6 +1128,38 @@ tags: priority: 0 type: "category" role: '["admin"]' + - + id: 8 + parent_id: + role: 'admin' + tag: "Test tag - no children" + slug: "test-tag-no-children" + priority: 0 + type: 'category' + - + id: 9 + parent_id: + role: 'admin' + tag: "Test tag - with children" + slug: "test-tag-with-children" + priority: 0 + type: 'category' + - + id: 10 + role: 'admin' + parent_id: 9 + tag: "Child 1" + slug: "child-one" + priority: 0 + type: 'category' + - + id: 11 + role: 'admin' + parent_id: 9 + tag: "Child 2" + slug: "child-two" + priority: 0 + type: 'category' posts_tags: - post_id: 1 diff --git a/tests/integration/acl.feature b/tests/integration/acl.feature index 9fff8ac6ab..6435538ee9 100644 --- a/tests/integration/acl.feature +++ b/tests/integration/acl.feature @@ -601,7 +601,7 @@ Feature: API Access Control Layer Then the response is JSON And the response has a "count" property And the type of the "count" property is "numeric" - And the "count" property equals "7" + And the "count" property equals "11" Then the guzzle status code should be 200 @rolesEnabled diff --git a/tests/integration/media.feature b/tests/integration/media.feature index c0bd0f6cbc..eb4d0dda36 100644 --- a/tests/integration/media.feature +++ b/tests/integration/media.feature @@ -114,6 +114,7 @@ Feature: Testing the Media API And the response has a "errors" property Then the guzzle status code should be 404 + @resetFixture Scenario: Fail to create a new Media with size greater than limit Given that I want to make a new "Media" And that the post field "caption" is "ihub" diff --git a/tests/integration/tags.feature b/tests/integration/tags.feature index fe7e7053a4..3342472012 100644 --- a/tests/integration/tags.feature +++ b/tests/integration/tags.feature @@ -218,7 +218,7 @@ Feature: Testing the Tags API Then the response is JSON And the response has a "count" property And the type of the "count" property is "numeric" - And the "count" property equals "7" + And the "count" property equals "11" Then the guzzle status code should be 200 @resetFixture @@ -243,7 +243,7 @@ Feature: Testing the Tags API """ When I request "/tags" Then the response is JSON - And the "count" property equals "5" + And the "count" property equals "9" Then the guzzle status code should be 200 @resetFixture @@ -301,3 +301,53 @@ Feature: Testing the Tags API When I request "/tags" And the response has a "errors" property Then the guzzle status code should be 404 + + Scenario: Creating a new child for a tag with role=admin + Given that I want to make a new "Tag" + And that the request "data" is: + """ + { + "parent_id":9, + "tag":"Valid child", + "slug":"valid-child", + "description":"I am a valid tag", + "type":"category", + "priority":1, + "color":"00ff00", + "role":"admin" + } + """ + When I request "/tags" + Then the response is JSON + And the response has a "id" property + And the type of the "id" property is "numeric" + And the "tag" property equals "Valid child" + And the "slug" property equals "valid-child" + And the "description" property equals "I am a valid tag" + And the "color" property equals "#00ff00" + And the "priority" property equals "1" + And the "type" property equals "category" + And the response has a "role" property + And the type of the "role" property is "array" + And the "parent.id" property equals "9" + Then the guzzle status code should be 200 + + Scenario: Creating a new invalid child for a tag with role=admin + Given that I want to make a new "Tag" + And that the request "data" is: + """ + { + "parent_id":9, + "tag":"Not a valid tag role", + "slug":"not-valid-tag-role", + "description":"My role is invalid", + "type":"category", + "priority":1, + "color":"00ff00", + "role":"nope" + } + """ + When I request "/tags" + Then the response is JSON + And the response has a "errors" property + Then the guzzle status code should be 422