866 lines
31 KiB
Python
866 lines
31 KiB
Python
# Copyright (c) 2015-2016 Codethink Limited
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import copy
|
|
|
|
from oslo_config import cfg
|
|
from pecan import abort
|
|
from pecan import request
|
|
from pecan import response
|
|
from pecan import rest
|
|
from pecan.secure import secure
|
|
import six
|
|
from wsme import types as wtypes
|
|
import wsmeext.pecan as wsme_pecan
|
|
|
|
from storyboard._i18n import _
|
|
from storyboard.api.auth import authorization_checks as checks
|
|
from storyboard.api.v1 import wmodels
|
|
from storyboard.common import decorators
|
|
from storyboard.common import exception as exc
|
|
from storyboard.db.api import stories as stories_api
|
|
from storyboard.db.api import tasks as tasks_api
|
|
from storyboard.db.api import timeline_events as events_api
|
|
from storyboard.db.api import users as users_api
|
|
from storyboard.db.api import worklists as worklists_api
|
|
from storyboard.db import models
|
|
|
|
|
|
CONF = cfg.CONF
|
|
|
|
|
|
def serialize_filter(filter):
|
|
serialized = {
|
|
"id": filter.id,
|
|
"type": filter.type,
|
|
"criteria": []
|
|
}
|
|
for criterion in filter.criteria:
|
|
serialized['criteria'].append({
|
|
"title": criterion.title,
|
|
"negative": criterion.negative,
|
|
"value": criterion.value,
|
|
"field": criterion.field
|
|
})
|
|
return serialized
|
|
|
|
|
|
def post_timeline_events(original, updated):
|
|
author_id = request.current_user_id
|
|
|
|
if original.title != updated.title:
|
|
events_api.worklist_details_changed_event(
|
|
original.id,
|
|
author_id,
|
|
'title',
|
|
original.title,
|
|
updated.title)
|
|
|
|
if original.private != updated.private:
|
|
events_api.worklist_details_changed_event(
|
|
original.id,
|
|
author_id,
|
|
'private',
|
|
original.private,
|
|
updated.private)
|
|
|
|
if original.automatic != updated.automatic:
|
|
events_api.worklist_details_changed_event(
|
|
original.id,
|
|
author_id,
|
|
'automatic',
|
|
original.automatic,
|
|
updated.automatic)
|
|
|
|
if original.archived != updated.archived:
|
|
events_api.worklist_details_changed_event(
|
|
original.id,
|
|
author_id,
|
|
'archived',
|
|
original.archived,
|
|
updated.archived)
|
|
|
|
|
|
class PermissionsController(rest.RestController):
|
|
"""Manages operations on worklist permissions."""
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.guest)
|
|
@wsme_pecan.wsexpose([wtypes.text], int)
|
|
def get(self, worklist_id):
|
|
"""Get worklist permissions for the current user.
|
|
|
|
Example::
|
|
|
|
curl https://my.example.org/api/v1/worklists/31/permissions \\
|
|
-H 'Authorization: Bearer MY_ACCESS_TOKEN'
|
|
|
|
:param worklist_id: The ID of the worklist.
|
|
|
|
"""
|
|
worklist = worklists_api.get(worklist_id)
|
|
if worklists_api.visible(worklist, request.current_user_id):
|
|
return worklists_api.get_permissions(worklist,
|
|
request.current_user_id)
|
|
else:
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(wtypes.text, int,
|
|
body=wtypes.DictType(wtypes.text, wtypes.text))
|
|
def post(self, worklist_id, permission):
|
|
"""Add a new permission to the worklist.
|
|
|
|
Example::
|
|
|
|
TODO
|
|
|
|
:param worklist_id: The ID of the worklist.
|
|
:param permission: The dict to use to create the permission.
|
|
|
|
"""
|
|
user_id = request.current_user_id
|
|
if worklists_api.editable(worklists_api.get(worklist_id), user_id):
|
|
created = worklists_api.create_permission(worklist_id, permission)
|
|
|
|
users = [{user.id: user.full_name} for user in created.users]
|
|
events_api.worklist_permission_created_event(worklist_id,
|
|
user_id,
|
|
created.id,
|
|
created.codename,
|
|
users)
|
|
|
|
return created.codename
|
|
else:
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(wtypes.text, int,
|
|
body=wtypes.DictType(wtypes.text, wtypes.text))
|
|
def put(self, worklist_id, permission):
|
|
"""Update a permission of the worklist.
|
|
|
|
Example::
|
|
|
|
TODO
|
|
|
|
This takes a dict in the form::
|
|
|
|
{
|
|
"codename": "my-permission",
|
|
"users": [
|
|
1,
|
|
2,
|
|
3
|
|
]
|
|
}
|
|
|
|
The given codename must match an existing permission's
|
|
codename.
|
|
|
|
:param worklist_id: The ID of the worklist.
|
|
:param permission: The new contents of the permission.
|
|
|
|
"""
|
|
user_id = request.current_user_id
|
|
worklist = worklists_api.get(worklist_id)
|
|
|
|
old = None
|
|
for perm in worklist.permissions:
|
|
if perm.codename == permission['codename']:
|
|
old = perm
|
|
|
|
if old is None:
|
|
raise exc.NotFound(_("Permission with codename %s not found")
|
|
% permission['codename'])
|
|
|
|
old_users = {user.id: user.full_name for user in old.users}
|
|
|
|
if worklists_api.editable(worklist, user_id):
|
|
updated = worklists_api.update_permission(
|
|
worklist_id, permission)
|
|
new_users = {user.id: user.full_name for user in updated.users}
|
|
|
|
added = [{id: name} for id, name in six.iteritems(new_users)
|
|
if id not in old_users]
|
|
removed = [{id: name} for id, name in six.iteritems(old_users)
|
|
if id not in new_users]
|
|
|
|
if added or removed:
|
|
events_api.worklist_permissions_changed_event(
|
|
worklist_id,
|
|
user_id,
|
|
updated.id,
|
|
updated.codename,
|
|
added,
|
|
removed)
|
|
return updated.codename
|
|
|
|
else:
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
|
|
class FilterSubcontroller(rest.RestController):
|
|
"""Manages filters on automatic worklists."""
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.guest)
|
|
@wsme_pecan.wsexpose(wmodels.WorklistFilter, int, int)
|
|
def get_one(self, worklist_id, filter_id):
|
|
"""Get a single filter for the worklist.
|
|
|
|
Example::
|
|
|
|
curl https://my.example.org/api/v1/worklists/49/filters/20
|
|
|
|
:param worklist_id: The ID of the worklist.
|
|
:param filter_id: The ID of the filter.
|
|
|
|
"""
|
|
worklist = worklists_api.get(worklist_id)
|
|
user_id = request.current_user_id
|
|
if not worklist or not worklists_api.visible(worklist, user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
filter = worklists_api.get_filter(filter_id)
|
|
wmodel = wmodels.WorklistFilter.from_db_model(filter)
|
|
wmodel.resolve_criteria(filter)
|
|
|
|
return wmodel
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.guest)
|
|
@wsme_pecan.wsexpose([wmodels.WorklistFilter], int)
|
|
def get(self, worklist_id):
|
|
"""Get filters for an automatic worklist.
|
|
|
|
Example::
|
|
|
|
curl https://my.example.org/api/v1/worklists/49/filters
|
|
|
|
:param worklist_id: The ID of the worklist.
|
|
|
|
"""
|
|
worklist = worklists_api.get(worklist_id)
|
|
user_id = request.current_user_id
|
|
if not worklist or not worklists_api.visible(worklist, user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
resolved = []
|
|
for filter in worklist.filters:
|
|
wmodel = wmodels.WorklistFilter.from_db_model(filter)
|
|
wmodel.resolve_criteria(filter)
|
|
resolved.append(wmodel)
|
|
|
|
return resolved
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(wmodels.WorklistFilter, int,
|
|
body=wmodels.WorklistFilter)
|
|
def post(self, worklist_id, filter):
|
|
"""Create a new filter for the worklist.
|
|
|
|
Example::
|
|
|
|
TODO
|
|
|
|
:param worklist_id: The ID of the worklist to set the filter on.
|
|
:param filter: The filter to set.
|
|
|
|
"""
|
|
worklist = worklists_api.get(worklist_id)
|
|
user_id = request.current_user_id
|
|
if not worklists_api.editable(worklist, user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
created = worklists_api.create_filter(worklist_id, filter.as_dict())
|
|
|
|
added = serialize_filter(created)
|
|
events_api.worklist_filters_changed_event(worklist_id,
|
|
user_id,
|
|
added=added)
|
|
|
|
model = wmodels.WorklistFilter.from_db_model(created)
|
|
model.resolve_criteria(created)
|
|
return model
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(wmodels.WorklistFilter, int, int,
|
|
body=wmodels.WorklistFilter)
|
|
def put(self, worklist_id, filter_id, filter):
|
|
"""Update a filter on the worklist.
|
|
|
|
Example::
|
|
|
|
TODO
|
|
|
|
:param worklist_id: The ID of the worklist.
|
|
:param filter_id: The ID of the filter to be updated.
|
|
:param filter: The new contents of the filter.
|
|
|
|
"""
|
|
worklist = worklists_api.get(worklist_id)
|
|
user_id = request.current_user_id
|
|
if not worklists_api.editable(worklist, user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
old = serialize_filter(worklists_api.get_filter(filter_id))
|
|
|
|
update_dict = filter.as_dict(omit_unset=True)
|
|
updated = worklists_api.update_filter(filter_id, update_dict)
|
|
|
|
changes = {
|
|
"old": old,
|
|
"new": serialize_filter(updated)
|
|
}
|
|
events_api.worklist_filters_changed_event(worklist_id,
|
|
user_id,
|
|
updated=changes)
|
|
|
|
updated_model = wmodels.WorklistFilter.from_db_model(updated)
|
|
updated_model.resolve_criteria(updated)
|
|
|
|
return updated_model
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(None, int, int)
|
|
def delete(self, worklist_id, filter_id):
|
|
"""Delete a filter from a worklist.
|
|
|
|
Example::
|
|
|
|
TODO
|
|
|
|
:param worklist_id: The ID of the worklist.
|
|
:param filter_id: The ID of the filter to be deleted.
|
|
|
|
"""
|
|
worklist = worklists_api.get(worklist_id)
|
|
user_id = request.current_user_id
|
|
if not worklists_api.editable(worklist, user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
filter = serialize_filter(worklists_api.get_filter(filter_id))
|
|
|
|
events_api.worklist_filters_changed_event(worklist_id,
|
|
user_id,
|
|
removed=filter)
|
|
|
|
worklists_api.delete_filter(filter_id)
|
|
|
|
|
|
class ItemsSubcontroller(rest.RestController):
|
|
"""Manages operations on the items in worklists."""
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.guest)
|
|
@wsme_pecan.wsexpose([wmodels.WorklistItem], int)
|
|
def get(self, worklist_id):
|
|
"""Get items inside a worklist.
|
|
|
|
Example::
|
|
|
|
curl https://my.example.org/api/v1/worklists/49/items
|
|
|
|
:param worklist_id: The ID of the worklist.
|
|
|
|
"""
|
|
worklist = worklists_api.get(worklist_id)
|
|
user_id = request.current_user_id
|
|
if not worklist or not worklists_api.visible(worklist, user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
if worklist.automatic:
|
|
return [wmodels.WorklistItem(**item)
|
|
for item in worklists_api.filter_items(worklist)]
|
|
|
|
if worklist.items is None:
|
|
return []
|
|
|
|
worklist.items.order_by(models.WorklistItem.list_position)
|
|
|
|
visible_items = worklists_api.get_visible_items(
|
|
worklist, current_user=request.current_user_id)
|
|
return [
|
|
wmodels.WorklistItem.from_db_model(item)
|
|
for item in visible_items
|
|
]
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(wmodels.WorklistItem, int, int, wtypes.text, int)
|
|
def post(self, id, item_id, item_type, list_position):
|
|
"""Add an item to a worklist.
|
|
|
|
Example::
|
|
|
|
TODO
|
|
|
|
:param id: The ID of the worklist.
|
|
:param item_id: The ID of the item.
|
|
:param item_type: The type of the item (i.e. "story" or "task").
|
|
:param list_position: The position in the list to add the item.
|
|
|
|
"""
|
|
user_id = request.current_user_id
|
|
if not worklists_api.editable_contents(worklists_api.get(id),
|
|
user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % id)
|
|
item = None
|
|
if item_type == 'story':
|
|
item = stories_api.story_get(
|
|
item_id, current_user=request.current_user_id)
|
|
elif item_type == 'task':
|
|
item = tasks_api.task_get(
|
|
item_id, current_user=request.current_user_id)
|
|
if item is None:
|
|
raise exc.NotFound(_("Item %s refers to a non-existent task or "
|
|
"story.") % item_id)
|
|
|
|
worklists_api.add_item(
|
|
id, item_id, item_type, list_position,
|
|
current_user=request.current_user_id)
|
|
|
|
added = {
|
|
"worklist_id": id,
|
|
"item_id": item_id,
|
|
"item_title": item.title,
|
|
"item_type": item_type,
|
|
"position": list_position
|
|
}
|
|
|
|
events_api.worklist_contents_changed_event(id, user_id, added=added)
|
|
|
|
return wmodels.WorklistItem.from_db_model(
|
|
worklists_api.get_item_at_position(id, list_position))
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(wmodels.WorklistItem, int, int, int, int, int)
|
|
def put(self, id, item_id, list_position, list_id=None,
|
|
display_due_date=None):
|
|
"""Update a WorklistItem.
|
|
|
|
Example::
|
|
|
|
TODO
|
|
|
|
This method also updates the positions of other items in affected
|
|
worklists, if necessary.
|
|
|
|
:param id: The ID of the worklist.
|
|
:param item_id: The ID of the worklist_item to be moved.
|
|
:param display_due_date: The ID of the due date displayed on the item.
|
|
|
|
"""
|
|
user_id = request.current_user_id
|
|
if not worklists_api.editable_contents(worklists_api.get(id),
|
|
user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % id)
|
|
card = worklists_api.get_item_by_id(item_id)
|
|
if card is None:
|
|
raise exc.NotFound(_("Item %s seems to have been deleted, "
|
|
"try refreshing your page.") % item_id)
|
|
|
|
item = None
|
|
if card.item_type == 'story':
|
|
item = stories_api.story_get(
|
|
card.item_id, current_user=request.current_user_id)
|
|
elif card.item_type == 'task':
|
|
item = tasks_api.task_get(
|
|
card.item_id, current_user=request.current_user_id)
|
|
|
|
if item is None:
|
|
raise exc.NotFound(_("Item %s refers to a non-existent task or "
|
|
"story.") % item_id)
|
|
|
|
old = {
|
|
"worklist_id": card.list_id,
|
|
"item_id": card.item_id,
|
|
"item_title": item.title,
|
|
"item_type": card.item_type,
|
|
"position": card.list_position,
|
|
"due_date_id": card.display_due_date
|
|
}
|
|
|
|
new = {
|
|
"item_id": card.item_id,
|
|
"item_title": item.title,
|
|
"item_type": card.item_type
|
|
}
|
|
|
|
if list_position != card.list_position and list_position is not None:
|
|
new['position'] = list_position
|
|
if list_id != card.list_id and list_id is not None:
|
|
new['worklist_id'] = list_id
|
|
|
|
worklists_api.move_item(id, item_id, list_position, list_id)
|
|
|
|
if display_due_date is not None:
|
|
if display_due_date == -1:
|
|
display_due_date = None
|
|
update_dict = {
|
|
'display_due_date': display_due_date
|
|
}
|
|
worklists_api.update_item(item_id, update_dict)
|
|
new['due_date_id'] = display_due_date
|
|
|
|
updated = {
|
|
"old": old,
|
|
"new": new
|
|
}
|
|
events_api.worklist_contents_changed_event(id,
|
|
user_id,
|
|
updated=updated)
|
|
|
|
updated = worklists_api.get_item_by_id(item_id)
|
|
result = wmodels.WorklistItem.from_db_model(updated)
|
|
result.resolve_due_date(updated)
|
|
return result
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(None, int, int, status_code=204)
|
|
def delete(self, id, item_id):
|
|
"""Remove an item from a worklist.
|
|
|
|
Example::
|
|
|
|
TODO
|
|
|
|
:param id: The ID of the worklist.
|
|
:param item_id: The ID of the worklist item to be removed.
|
|
|
|
"""
|
|
user_id = request.current_user_id
|
|
worklist = worklists_api.get(id)
|
|
if not worklists_api.editable_contents(worklist, user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % id)
|
|
card = worklists_api.get_item_by_id(item_id)
|
|
if card is None:
|
|
raise exc.NotFound(_("Item %s seems to have already been deleted,"
|
|
" try refreshing your page.") % item_id)
|
|
worklists_api.update_item(item_id, {'archived': True})
|
|
worklists_api.normalize_positions(worklist)
|
|
|
|
item = None
|
|
if card.item_type == 'story':
|
|
item = stories_api.story_get(
|
|
card.item_id, current_user=user_id)
|
|
elif card.item_type == 'task':
|
|
item = tasks_api.task_get(
|
|
card.item_id, current_user=user_id)
|
|
if item is None:
|
|
item.title = ''
|
|
|
|
removed = {
|
|
"worklist_id": id,
|
|
"item_id": card.item_id,
|
|
"item_type": card.item_type,
|
|
"item_title": item.title
|
|
}
|
|
events_api.worklist_contents_changed_event(id,
|
|
user_id,
|
|
removed=removed)
|
|
|
|
|
|
class WorklistsController(rest.RestController):
|
|
"""Manages operations on worklists."""
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.guest)
|
|
@wsme_pecan.wsexpose(wmodels.Worklist, int)
|
|
def get_one(self, worklist_id):
|
|
"""Retrieve details about one worklist.
|
|
|
|
Example::
|
|
|
|
curl https://my.example.org/api/v1/worklists/27
|
|
|
|
:param worklist_id: The ID of the worklist.
|
|
|
|
"""
|
|
worklist = worklists_api.get(worklist_id)
|
|
|
|
user_id = request.current_user_id
|
|
if worklist and worklists_api.visible(worklist, user_id):
|
|
worklist_model = wmodels.Worklist.from_db_model(worklist)
|
|
worklist_model.resolve_items(worklist)
|
|
worklist_model.resolve_permissions(worklist)
|
|
worklist_model.resolve_filters(worklist)
|
|
return worklist_model
|
|
else:
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.guest)
|
|
@wsme_pecan.wsexpose([wmodels.Worklist], wtypes.text, int, int,
|
|
bool, int, int, int, bool, wtypes.text, wtypes.text,
|
|
wtypes.text, int, int, int, int)
|
|
def get_all(self, title=None, creator_id=None, project_id=None,
|
|
archived=False, user_id=None, story_id=None, task_id=None,
|
|
hide_lanes=True, sort_field='id', sort_dir='asc',
|
|
item_type=None, board_id=None, subscriber_id=None,
|
|
offset=None, limit=None):
|
|
"""Retrieve definitions of all of the worklists.
|
|
|
|
Example::
|
|
|
|
curl https://my.example.org/api/v1/worklists
|
|
|
|
:param title: A string to filter the title by.
|
|
:param creator_id: Filter worklists by their creator.
|
|
:param project_id: Filter worklists by project ID.
|
|
:param archived: Filter worklists by whether they are archived or not.
|
|
:param user_id: Filter worklists by the users with permissions.
|
|
:param story_id: Filter worklists by whether they contain a story.
|
|
:param task_id: Filter worklists by whether they contain a task.
|
|
:param hide_lanes: If true, don't return worklists which are lanes in
|
|
a board.
|
|
:param sort_field: The name of the field to sort on.
|
|
:param sort_dir: Sort direction for results (asc, desc).
|
|
:param item_type: Used when filtering by story_id. If item_type is
|
|
'story' then only return worklists that contain the story, if
|
|
item_type is 'task' then only return worklists that contain tasks from
|
|
the story, otherwise return worklists that contain the story or tasks
|
|
from the story.
|
|
:param board_id: Get all worklists in the board with this id. Other
|
|
filters are not applied.
|
|
:param subscriber_id: Filter worklists by whether a user is subscribed.
|
|
:param offset: Offset at which to begin the results.
|
|
:param limit: Maximum number of results to return.
|
|
|
|
"""
|
|
current_user = request.current_user_id
|
|
|
|
# If a non existent story/task is requested, there is no point trying
|
|
# to find worklists which contain it
|
|
if story_id:
|
|
story = stories_api.story_get(story_id, current_user=current_user)
|
|
if story is None:
|
|
response.headers['X-Total'] = '0'
|
|
return []
|
|
if task_id:
|
|
task = tasks_api.task_get(task_id, current_user=current_user)
|
|
if task is None:
|
|
response.headers['X-Total'] = '0'
|
|
return []
|
|
|
|
worklists = worklists_api.get_all(title=title,
|
|
creator_id=creator_id,
|
|
project_id=project_id,
|
|
archived=archived,
|
|
board_id=board_id,
|
|
user_id=user_id,
|
|
story_id=story_id,
|
|
task_id=task_id,
|
|
subscriber_id=subscriber_id,
|
|
sort_field=sort_field,
|
|
sort_dir=sort_dir,
|
|
offset=offset,
|
|
limit=limit,
|
|
current_user=current_user,
|
|
hide_lanes=hide_lanes,
|
|
item_type=item_type)
|
|
count = worklists_api.get_count(title=title,
|
|
creator_id=creator_id,
|
|
project_id=project_id,
|
|
archived=archived,
|
|
board_id=board_id,
|
|
user_id=user_id,
|
|
story_id=story_id,
|
|
task_id=task_id,
|
|
subscriber_id=subscriber_id,
|
|
current_user=current_user,
|
|
hide_lanes=hide_lanes,
|
|
item_type=item_type)
|
|
|
|
visible_worklists = []
|
|
for worklist in worklists:
|
|
worklist_model = wmodels.Worklist.from_db_model(worklist)
|
|
worklist_model.resolve_permissions(worklist)
|
|
visible_items = worklists_api.get_visible_items(
|
|
worklist, request.current_user_id)
|
|
worklist_model.items = [
|
|
wmodels.WorklistItem.from_db_model(item)
|
|
for item in visible_items
|
|
]
|
|
visible_worklists.append(worklist_model)
|
|
|
|
# Apply the query response headers
|
|
response.headers['X-Total'] = str(count)
|
|
if limit is not None:
|
|
response.headers['X-Limit'] = str(limit)
|
|
if offset is not None:
|
|
response.headers['X-Offset'] = str(offset)
|
|
|
|
return visible_worklists
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(wmodels.Worklist, body=wmodels.Worklist)
|
|
def post(self, worklist):
|
|
"""Create a new worklist.
|
|
|
|
Example::
|
|
|
|
curl https://my.example.org/api/v1/worklists \\
|
|
-H 'Authorization: Bearer MY_ACCESS_TOKEN' \\
|
|
-H 'Content-Type: application/json;charset=UTF-8' \\
|
|
--data-binary '{"title":"create worklist via api"}'
|
|
|
|
:param worklist: A worklist within the request body.
|
|
|
|
"""
|
|
worklist_dict = worklist.as_dict()
|
|
user_id = request.current_user_id
|
|
|
|
if worklist.creator_id and worklist.creator_id != user_id:
|
|
abort(400, _("You can't select the creator of a worklist."))
|
|
worklist_dict.update({"creator_id": user_id})
|
|
if 'items' in worklist_dict:
|
|
del worklist_dict['items']
|
|
|
|
filters = worklist_dict.pop('filters')
|
|
owners = worklist_dict.pop('owners')
|
|
users = worklist_dict.pop('users')
|
|
if not owners:
|
|
owners = [user_id]
|
|
if not users:
|
|
users = []
|
|
|
|
created_worklist = worklists_api.create(worklist_dict)
|
|
events_api.worklist_created_event(created_worklist.id,
|
|
user_id,
|
|
created_worklist.title)
|
|
|
|
edit_permission = {
|
|
'name': 'edit_worklist_%d' % created_worklist.id,
|
|
'codename': 'edit_worklist',
|
|
'users': owners
|
|
}
|
|
move_permission = {
|
|
'name': 'move_items_%d' % created_worklist.id,
|
|
'codename': 'move_items',
|
|
'users': users
|
|
}
|
|
edit = worklists_api.create_permission(
|
|
created_worklist.id, edit_permission)
|
|
move = worklists_api.create_permission(
|
|
created_worklist.id, move_permission)
|
|
|
|
event_owners = [{id: users_api.user_get(id).full_name}
|
|
for id in owners]
|
|
event_users = [{id: users_api.user_get(id).full_name}
|
|
for id in users]
|
|
|
|
events_api.worklist_permission_created_event(created_worklist.id,
|
|
user_id,
|
|
edit.id,
|
|
edit.codename,
|
|
event_owners)
|
|
events_api.worklist_permission_created_event(created_worklist.id,
|
|
user_id,
|
|
move.id,
|
|
move.codename,
|
|
event_users)
|
|
|
|
if worklist_dict['automatic']:
|
|
for filter in filters:
|
|
created_filter = worklists_api.create_filter(
|
|
created_worklist.id, filter.as_dict())
|
|
added = serialize_filter(created_filter)
|
|
events_api.worklist_filters_changed_event(created_worklist.id,
|
|
user_id,
|
|
added=added)
|
|
|
|
return wmodels.Worklist.from_db_model(created_worklist)
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(wmodels.Worklist, int, body=wmodels.Worklist)
|
|
def put(self, id, worklist):
|
|
"""Modify this worklist.
|
|
|
|
Example::
|
|
|
|
TODO
|
|
|
|
:param id: The ID of the worklist.
|
|
:param worklist: A worklist within the request body.
|
|
|
|
"""
|
|
user_id = request.current_user_id
|
|
if not worklists_api.editable(worklists_api.get(id), user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % id)
|
|
|
|
# We don't use this endpoint to update the worklist's contents
|
|
if worklist.items != wtypes.Unset:
|
|
del worklist.items
|
|
|
|
# We don't use this endpoint to update the worklist's filters either
|
|
if worklist.filters != wtypes.Unset:
|
|
del worklist.filters
|
|
|
|
worklist_dict = worklist.as_dict(omit_unset=True)
|
|
|
|
original = copy.deepcopy(worklists_api.get(id))
|
|
updated_worklist = worklists_api.update(id, worklist_dict)
|
|
|
|
post_timeline_events(original, updated_worklist)
|
|
if worklists_api.visible(updated_worklist, user_id):
|
|
worklist_model = wmodels.Worklist.from_db_model(updated_worklist)
|
|
worklist_model.resolve_items(updated_worklist)
|
|
worklist_model.resolve_permissions(updated_worklist)
|
|
return worklist_model
|
|
else:
|
|
raise exc.NotFound(_("Worklist %s not found"))
|
|
|
|
@decorators.db_exceptions
|
|
@secure(checks.authenticated)
|
|
@wsme_pecan.wsexpose(None, int, status_code=204)
|
|
def delete(self, worklist_id):
|
|
"""Archive this worklist.
|
|
Though this uses the DELETE command, the worklist is not deleted.
|
|
Archived worklists remain viewable at the designated URL, but
|
|
are not returned in search results nor appear on your dashboard.
|
|
|
|
Example::
|
|
|
|
curl https://my.example.org/api/v1/worklists/30 -X DELETE \\
|
|
-H 'Authorization: Bearer MY_ACCESS_TOKEN'
|
|
|
|
:param worklist_id: The ID of the worklist to be archived.
|
|
|
|
"""
|
|
worklist = worklists_api.get(worklist_id)
|
|
original = copy.deepcopy(worklist)
|
|
user_id = request.current_user_id
|
|
if not worklists_api.editable(worklist, user_id):
|
|
raise exc.NotFound(_("Worklist %s not found") % worklist_id)
|
|
|
|
updated = worklists_api.update(worklist_id, {"archived": True})
|
|
|
|
post_timeline_events(original, updated)
|
|
|
|
items = ItemsSubcontroller()
|
|
permissions = PermissionsController()
|
|
filters = FilterSubcontroller()
|