예제 #1
0
    def restore_version(self, id, doc):
        item_id = id
        old_version = int(doc.get('old_version', 0))
        last_version = int(doc.get('last_version', 0))
        if (not all([item_id, old_version, last_version])):
            return None

        old = get_resource_service('archive_versions').find_one(req=None, _id_document=item_id, _version=old_version)
        if old is None:
            raise SuperdeskApiError.notFoundError('Invalid version %s' % old_version)

        curr = get_resource_service(SOURCE).find_one(req=None, _id=item_id)
        if curr is None:
            raise SuperdeskApiError.notFoundError('Invalid item id %s' % item_id)

        if curr[config.VERSION] != last_version:
            raise SuperdeskApiError.preconditionFailedError('Invalid last version %s' % last_version)
        old['_id'] = old['_id_document']
        old['_updated'] = old['versioncreated'] = utcnow()
        set_item_expiry(old, doc)
        del old['_id_document']

        resolve_document_version(old, 'archive', 'PATCH', curr)

        remove_unwanted(old)
        res = super().replace(id=item_id, document=old)

        del doc['old_version']
        del doc['last_version']
        doc.update(old)
        return res
예제 #2
0
def get_expiry(desk_id, stage_id, offset=None):
    """
    Calculates the expiry for a content from fetching the expiry duration from one of the below
        1. desk identified by desk_id
        2. stage identified by stage_id
    :param desk_id: desk identifier
    :param stage_id: stage identifier
    :return: when the doc will expire
    """
    stage = None
    desk = None

    if desk_id:
        desk = superdesk.get_resource_service('desks').find_one(req=None, _id=desk_id)

        if not desk:
            raise SuperdeskApiError.notFoundError('Invalid desk identifier %s' % desk_id)

    if stage_id:
        stage = get_resource_service('stages').find_one(req=None, _id=stage_id)

        if not stage:
                raise SuperdeskApiError.notFoundError('Invalid stage identifier %s' % stage_id)

    return get_item_expiry(desk, stage, offset)
예제 #3
0
def get_expiry(desk_id=None, stage_id=None, desk_or_stage_doc=None):
    """
    Calculates the expiry for a content from fetching the expiry duration from one of the below
        1. desk identified by desk_id
        2. stage identified by stage_id. This will ignore desk_id if specified
        3. desk doc or stage doc identified by doc_or_stage_doc. This will ignore desk_id and stage_id if specified

    :param desk_id: desk identifier
    :param stage_id: stage identifier
    :param desk_or_stage_doc: doc from either desks collection or stages collection
    :return: when the doc will expire
    """

    stage = None

    if desk_or_stage_doc is None and desk_id:
        desk = superdesk.get_resource_service('desks').find_one(req=None, _id=desk_id)

        if not desk:
            raise SuperdeskApiError.notFoundError('Invalid desk identifier %s' % desk_id)

        if not stage_id:
            stage = get_resource_service('stages').find_one(req=None, _id=desk['incoming_stage'])

            if not stage:
                raise SuperdeskApiError.notFoundError('Invalid stage identifier %s' % stage_id)

    if desk_or_stage_doc is None and stage_id:
        stage = get_resource_service('stages').find_one(req=None, _id=stage_id)

        if not stage:
                raise SuperdeskApiError.notFoundError('Invalid stage identifier %s' % stage_id)

    return get_item_expiry(app=app, stage=desk_or_stage_doc or stage)
예제 #4
0
    def restore_version(self, id, doc, original):
        item_id = id
        old_version = int(doc.get('old_version', 0))
        last_version = int(doc.get('last_version', 0))
        if (not all([item_id, old_version, last_version])):
            return None

        old = get_resource_service('archive_versions').find_one(
            req=None, _id_document=item_id, _version=old_version)
        if old is None:
            raise SuperdeskApiError.notFoundError('Invalid version %s' %
                                                  old_version)

        curr = get_resource_service(SOURCE).find_one(req=None, _id=item_id)
        if curr is None:
            raise SuperdeskApiError.notFoundError('Invalid item id %s' %
                                                  item_id)

        if curr[config.VERSION] != last_version:
            raise SuperdeskApiError.preconditionFailedError(
                'Invalid last version %s' % last_version)
        old['_id'] = old['_id_document']
        old['_updated'] = old['versioncreated'] = utcnow()
        set_item_expiry(old, doc)
        del old['_id_document']

        resolve_document_version(old, 'archive', 'PATCH', curr)

        remove_unwanted(old)
        super().replace(id=item_id, document=old, original=curr)

        del doc['old_version']
        del doc['last_version']
        doc.update(old)
        return item_id
예제 #5
0
    def restore_version(self, id, doc, original):
        item_id = id
        old_version = int(doc.get("old_version", 0))
        last_version = int(doc.get("last_version", 0))
        if not all([item_id, old_version, last_version]):
            return None

        old = get_resource_service("archive_versions").find_one(
            req=None, _id_document=item_id, _current_version=old_version
        )
        if old is None:
            raise SuperdeskApiError.notFoundError("Invalid version %s" % old_version)

        curr = get_resource_service(SOURCE).find_one(req=None, _id=item_id)
        if curr is None:
            raise SuperdeskApiError.notFoundError("Invalid item id %s" % item_id)

        if curr[config.VERSION] != last_version:
            raise SuperdeskApiError.preconditionFailedError("Invalid last version %s" % last_version)
        old["_id"] = old["_id_document"]
        old["_updated"] = old["versioncreated"] = utcnow()
        set_item_expiry(old, doc)
        del old["_id_document"]

        resolve_document_version(old, "archive", "PATCH", curr)

        remove_unwanted(old)
        super().replace(id=item_id, document=old, original=curr)

        del doc["old_version"]
        del doc["last_version"]
        doc.update(old)
        return item_id
예제 #6
0
def get_expiry(desk_id, stage_id, offset=None):
    """Calculates the expiry for an item.

    Fetches the expiry duration from one of the below
        1. desk identified by desk_id
        2. stage identified by stage_id

    :param desk_id: desk identifier
    :param stage_id: stage identifier
    :return: when the doc will expire
    """
    stage = None
    desk = None

    if desk_id:
        desk = superdesk.get_resource_service("desks").find_one(req=None,
                                                                _id=desk_id)

        if not desk:
            raise SuperdeskApiError.notFoundError(
                _("Invalid desk identifier {desk_id}").format(desk_id=desk_id))

    if stage_id:
        stage = get_resource_service("stages").find_one(req=None, _id=stage_id)

        if not stage:
            raise SuperdeskApiError.notFoundError(
                _("Invalid stage identifier {stage_id}").format(
                    stage_id=stage_id))

    return get_item_expiry(desk, stage, offset)
예제 #7
0
    def get_keywords(self, text):
        if not app.config['KEYWORDS_KEY_API']:
            raise SuperdeskApiError.notFoundError('AlchemyAPI key is not set')

        params = {
            'apikey': app.config['KEYWORDS_KEY_API'],
            'outputMode': 'json'
        }

        url = app.config['KEYWORDS_BASE_URL'] + '/text/TextGetRankedNamedEntities' + \
            '?' + urllib.parse.urlencode(params)

        values = {'text': text}

        result = ""
        try:
            result = self._http.post(url, data=values)
        except Exception:
            raise SuperdeskApiError.notFoundError(
                'Fail to connect to Alchemy service')

        try:
            keywords = result.json()
            return keywords.get('entities', [])
        except Exception:
            raise SuperdeskApiError.notFoundError(
                'Fail to parse the response from Alchemy service')
예제 #8
0
def get_expiry(desk_id=None, stage_id=None):

    stage = None
    if desk_id:
        desk = superdesk.get_resource_service('desks').find_one(req=None,
                                                                _id=desk_id)

        if not desk:
            raise SuperdeskApiError.notFoundError(
                'Invalid desk identifier %s' % desk_id)

        if not stage_id:
            stage = get_resource_service('stages').find_one(
                req=None, _id=desk['incoming_stage'])

            if not stage:
                raise SuperdeskApiError.notFoundError(
                    'Invalid stage identifier %s' % stage_id)

    if stage_id:
        stage = get_resource_service('stages').find_one(req=None, _id=stage_id)

        if not stage:
            raise SuperdeskApiError.notFoundError(
                'Invalid stage identifier %s' % stage_id)

    return get_item_expiry(app=app, stage=stage)
예제 #9
0
    def _validate_rewrite(self, original):
        """
        Validates the article to be rewritten
        :param original: article to be rewritten
        :raises: SuperdeskApiError
        """
        if not original:
            raise SuperdeskApiError.notFoundError(
                message='Cannot find the article')

        if original.get(EMBARGO):
            raise SuperdeskApiError.badRequestError(
                "Rewrite of an Item having embargo isn't possible")

        if not original.get('event_id'):
            raise SuperdeskApiError.notFoundError(
                message='Event id does not exist')

        if get_resource_service('published').is_rewritten_before(
                original['_id']):
            raise SuperdeskApiError.badRequestError(
                message='Article has been rewritten before !')

        if not is_workflow_state_transition_valid('rewrite',
                                                  original[ITEM_STATE]):
            raise InvalidStateTransitionError()

        if not TakesPackageService().is_last_takes_package_item(original):
            raise SuperdeskApiError.badRequestError(
                message="Only last take of the package can be rewritten.")
예제 #10
0
def send_to(doc, desk_id=None, stage_id=None):
    """Send item to given desk and stage.

    :param doc: item to be sent
    :param desk: id of desk where item should be sent
    :param stage: optional stage within the desk
    """
    task = doc.get('task', {})
    task.setdefault('desk', desk_id)
    task.setdefault('stage', stage_id)

    calculate_expiry_from = None

    if desk_id and not stage_id:
        desk = superdesk.get_resource_service('desks').find_one(req=None, _id=desk_id)
        if not desk:
            raise SuperdeskApiError.notFoundError('Invalid desk identifier %s' % desk_id)

        calculate_expiry_from = desk
        task['desk'] = desk_id
        task['stage'] = desk.get('incoming_stage')

    if stage_id:
        stage = get_resource_service('stages').find_one(req=None, _id=stage_id)
        if not stage:
            raise SuperdeskApiError.notFoundError('Invalid stage identifier %s' % stage_id)

        calculate_expiry_from = stage
        task['desk'] = stage['desk']
        task['stage'] = stage_id
        if stage.get('task_status'):
            task['status'] = stage['task_status']

    doc['task'] = task
    doc['expiry'] = get_expiry(desk_or_stage_doc=calculate_expiry_from)
예제 #11
0
    def _change_request(self, endpoint_name, id, updates, original):
        backend = self._backend(endpoint_name)
        search_backend = self._lookup_backend(endpoint_name)

        try:
            backend.update(endpoint_name, id, updates, original)
        except eve.io.base.DataLayer.OriginalChangedError:
            if not backend.find_one(endpoint_name, req=None, _id=id) and search_backend:
                # item is in elastic, not in mongo - not good
                logger.warn("Item is missing in mongo resource=%s id=%s".format(endpoint_name, id))
                self.remove_from_search(endpoint_name, id)
                raise SuperdeskApiError.notFoundError()
            else:
                # item is there, but no change was done - ok
                logger.error('Item : {} not updated in collection {}. '
                             'Updates are : {}'.format(id, endpoint_name, updates))
                return updates

        if search_backend:
            doc = backend.find_one(endpoint_name, req=None, _id=id)
            if not doc:  # there is no doc in mongo, remove it from elastic
                logger.warn("Item is missing in mongo resource=%s id=%s".format(endpoint_name, id))
                self.remove_from_search(endpoint_name, id)
                raise SuperdeskApiError.notFoundError()
            search_backend.update(endpoint_name, id, doc)

        return updates
예제 #12
0
def send_to(doc, desk_id=None, stage_id=None):
    """Send item to given desk and stage.

    :param doc: item to be sent
    :param desk: id of desk where item should be sent
    :param stage: optional stage within the desk
    """
    task = doc.get('task', {})
    task.setdefault('desk', desk_id)
    task.setdefault('stage', stage_id)

    if desk_id and not stage_id:
        desk = superdesk.get_resource_service('desks').find_one(req=None,
                                                                _id=desk_id)
        if not desk:
            raise SuperdeskApiError.notFoundError(
                'Invalid desk identifier %s' % desk_id)
        task['stage'] = desk.get('incoming_stage')
    if task['stage']:
        stage = get_resource_service('stages').find_one(req=None,
                                                        _id=task['stage'])
        if not stage:
            raise SuperdeskApiError.notFoundError(
                'Invalid stage identifier %s' % task['stage'])
        if stage.get('task_status'):
            doc['task'] = doc.get('task', {})
            doc['task']['status'] = stage['task_status']
    doc['task'] = task
    doc['expiry'] = get_expiry(desk_id, stage_id)
예제 #13
0
    def _change_request(self, endpoint_name, id, updates, original):
        backend = self._backend(endpoint_name)
        search_backend = self._lookup_backend(endpoint_name)

        try:
            backend.update(endpoint_name, id, updates, original)
        except eve.io.base.DataLayer.OriginalChangedError:
            if not backend.find_one(endpoint_name, req=None, _id=id) and search_backend:
                # item is in elastic, not in mongo - not good
                logger.warn("Item is missing in mongo resource=%s id=%s".format(endpoint_name, id))
                item = search_backend.find_one(endpoint_name, req=None, _id=id)
                if item:
                    self.remove_from_search(endpoint_name, item)
                raise SuperdeskApiError.notFoundError()
            else:
                # item is there, but no change was done - ok
                logger.error('Item : {} not updated in collection {}. '
                             'Updates are : {}'.format(id, endpoint_name, updates))
                return updates

        if search_backend:

            doc = backend.find_one(endpoint_name, req=None, _id=id)
            if not doc:  # there is no doc in mongo, remove it from elastic
                logger.warn("Item is missing in mongo resource=%s id=%s".format(endpoint_name, id))
                item = search_backend.find_one(endpoint_name, req=None, _id=id)
                if item:
                    self.remove_from_search(endpoint_name, item)
                raise SuperdeskApiError.notFoundError()
            search_backend.update(endpoint_name, id, doc)

        return updates
예제 #14
0
def send_to(doc, update=None, desk_id=None, stage_id=None, user_id=None):
    """Send item to given desk and stage.
    Applies the outgoing and incoming macros of current and destination stages

    :param doc: original document to be sent
    :param update: updates for the document
    :param desk: id of desk where item should be sent
    :param stage: optional stage within the desk
    """

    original_task = doc.setdefault('task', {})
    current_stage = None
    if original_task.get('stage'):
        current_stage = get_resource_service('stages').find_one(
            req=None, _id=original_task.get('stage'))
    desk = destination_stage = None
    task = {
        'desk': desk_id,
        'stage': stage_id,
        'user': original_task.get('user') if user_id is None else user_id
    }

    if current_stage:
        apply_stage_rule(doc, update, current_stage, is_incoming=False)

    if desk_id:
        desk = superdesk.get_resource_service('desks').find_one(req=None,
                                                                _id=desk_id)
        if not desk:
            raise SuperdeskApiError.notFoundError(
                'Invalid desk identifier %s' % desk_id)

        task['desk'] = desk_id
        if not stage_id:
            task['stage'] = desk.get('incoming_stage')
            destination_stage = get_resource_service('stages').find_one(
                req=None, _id=desk.get('incoming_stage'))

    if stage_id:
        destination_stage = get_resource_service('stages').find_one(
            req=None, _id=stage_id)
        if not destination_stage:
            raise SuperdeskApiError.notFoundError(
                'Invalid stage identifier %s' % stage_id)

        task['desk'] = destination_stage['desk']
        task['stage'] = stage_id

    if destination_stage:
        apply_stage_rule(doc, update, destination_stage, is_incoming=True)
        if destination_stage.get('task_status'):
            task['status'] = destination_stage['task_status']

    if update:
        update.setdefault('task', {})
        update['task'].update(task)
        update['expiry'] = get_item_expiry(desk=desk, stage=destination_stage)
    else:
        doc['task'].update(task)
        doc['expiry'] = get_item_expiry(desk=desk, stage=destination_stage)
예제 #15
0
    def _validate_rewrite(self, original, update):
        """
        Validates the article to be rewritten
        :param original: article to be rewritten
        :param update: article as the rewrite
        :raises: SuperdeskApiError
        """
        if not original:
            raise SuperdeskApiError.notFoundError(
                message='Cannot find the article')

        if original.get(EMBARGO):
            raise SuperdeskApiError.badRequestError(
                "Rewrite of an Item having embargo isn't possible")

        if not original.get('event_id'):
            raise SuperdeskApiError.notFoundError(
                message='Event id does not exist')

        if original.get('rewritten_by'):
            raise SuperdeskApiError.badRequestError(
                message='Article has been rewritten before !')

        if not is_workflow_state_transition_valid('rewrite',
                                                  original[ITEM_STATE]):
            raise InvalidStateTransitionError()

        if not TakesPackageService().is_last_takes_package_item(original):
            raise SuperdeskApiError.badRequestError(
                message="Only last take of the package can be rewritten.")

        if original.get('rewrite_of') and not (original.get(ITEM_STATE)
                                               in PUBLISH_STATES):
            raise SuperdeskApiError.badRequestError(
                message=
                "Rewrite is not published. Cannot rewrite the story again.")

        if update:
            # in case of associate as update
            if update.get('rewrite_of'):
                raise SuperdeskApiError.badRequestError(
                    "Rewrite story has been used as update before !")

            if update.get(ITEM_STATE) in [
                    CONTENT_STATE.PUBLISHED, CONTENT_STATE.CORRECTED,
                    CONTENT_STATE.KILLED, CONTENT_STATE.SCHEDULED,
                    CONTENT_STATE.SPIKED
            ]:
                raise InvalidStateTransitionError()

            if update.get(ITEM_TYPE) not in [
                    CONTENT_TYPE.TEXT, CONTENT_TYPE.PREFORMATTED
            ]:
                raise SuperdeskApiError.badRequestError(
                    "Rewrite story can only be text or pre-formatted !")

            if update.get('genre') and \
                    any(genre.get('value', '').lower() == BROADCAST_GENRE.lower() for genre in update.get('genre')):
                raise SuperdeskApiError.badRequestError(
                    "Broadcast cannot be a update story !")
예제 #16
0
    def _change_request(self,
                        endpoint_name,
                        id,
                        updates,
                        original,
                        change_request=False,
                        push_notification=True):
        backend = self._backend(endpoint_name)
        search_backend = self._lookup_backend(endpoint_name)

        try:
            if change_request:  # allows using mongo operations other than $set
                backend._change_request(endpoint_name, id, updates, original)
            else:
                backend.update(endpoint_name, id, updates, original)
            if push_notification:
                self._push_resource_notification("updated",
                                                 endpoint_name,
                                                 _id=str(id),
                                                 fields=get_diff_keys(
                                                     updates, original))
        except eve.io.base.DataLayer.OriginalChangedError:
            if not backend.find_one(endpoint_name, req=None,
                                    _id=id) and search_backend:
                # item is in elastic, not in mongo - not good
                logger.warn(
                    "Item is missing in mongo resource={} id={}".format(
                        endpoint_name, id))
                item = search_backend.find_one(endpoint_name, req=None, _id=id)
                if item:
                    self.remove_from_search(endpoint_name, item)
                raise SuperdeskApiError.notFoundError()
            else:
                # item is there, but no change was done
                logger.error(
                    "Item was not updated in mongo, it has changed from the original.",
                    extra=dict(
                        id=id,
                        resource=endpoint_name,
                        updates=updates,
                        original=original,
                    ),
                )
                return updates

        if search_backend:
            doc = backend.find_one(endpoint_name, req=None, _id=id)
            if not doc:  # there is no doc in mongo, remove it from elastic
                logger.warn(
                    "Item is missing in mongo resource={} id={}".format(
                        endpoint_name, id))
                item = search_backend.find_one(endpoint_name, req=None, _id=id)
                if item:
                    self.remove_from_search(endpoint_name, item)
                raise SuperdeskApiError.notFoundError()
            search_backend.update(endpoint_name, id, doc)

        return updates
예제 #17
0
    def _validate_rewrite(self, original, update):
        """Validates the article to be rewritten.

        :param original: article to be rewritten
        :param update: article as the rewrite
        :raises: SuperdeskApiError
        """
        if not original:
            raise SuperdeskApiError.notFoundError(message=_('Cannot find the article'))

        if original.get(EMBARGO):
            raise SuperdeskApiError.badRequestError(_("Rewrite of an Item having embargo isn't possible"))

        if not original.get('event_id'):
            raise SuperdeskApiError.notFoundError(message=_('Event id does not exist'))

        if original.get('rewritten_by'):
            raise SuperdeskApiError.badRequestError(message=_('Article has been rewritten before !'))

        if (not is_workflow_state_transition_valid('rewrite', original[ITEM_STATE])
                and not config.ALLOW_UPDATING_SCHEDULED_ITEMS):
            raise InvalidStateTransitionError()

        if (
            original.get('rewrite_of')
            and not (original.get(ITEM_STATE) in PUBLISH_STATES)
            and not app.config['WORKFLOW_ALLOW_MULTIPLE_UPDATES']
        ):
            raise SuperdeskApiError.badRequestError(
                message=_("Rewrite is not published. Cannot rewrite the story again."))

        if update:
            # in case of associate as update
            if update.get('rewrite_of'):
                raise SuperdeskApiError.badRequestError(_("Rewrite story has been used as update before !"))

            if update.get(ITEM_STATE) in [CONTENT_STATE.PUBLISHED,
                                          CONTENT_STATE.CORRECTED,
                                          CONTENT_STATE.KILLED,
                                          CONTENT_STATE.RECALLED,
                                          CONTENT_STATE.SCHEDULED,
                                          CONTENT_STATE.SPIKED]:
                raise InvalidStateTransitionError()

            if update.get(ITEM_TYPE) not in [CONTENT_TYPE.TEXT, CONTENT_TYPE.PREFORMATTED]:
                raise SuperdeskApiError.badRequestError(_("Rewrite story can only be text or pre-formatted !"))

            if update.get('genre') and \
                    any(genre.get('value', '').lower() == BROADCAST_GENRE.lower() for genre in update.get('genre')):
                raise SuperdeskApiError.badRequestError(_("Broadcast cannot be a update story !"))

            if original.get('profile') and original.get('profile') != update.get('profile'):
                raise SuperdeskApiError.badRequestError(_("Rewrite item content profile does "
                                                        "not match with Original item."))
예제 #18
0
def send_to(doc, update=None, desk_id=None, stage_id=None, user_id=None, default_stage='incoming_stage'):
    """Send item to given desk and stage.

    Applies the outgoing and incoming macros of current and destination stages

    :param doc: original document to be sent
    :param update: updates for the document
    :param desk: id of desk where item should be sent
    :param stage: optional stage within the desk
    :param default_stage: if no stage_id is passed then it determines the stage in that desk the doc is assigned,
            either the the incomming stage or the working stage.
    """

    original_task = doc.setdefault('task', {})
    current_stage = None
    if original_task.get('stage'):
        current_stage = get_resource_service('stages').find_one(req=None, _id=original_task.get('stage'))
    desk = destination_stage = None
    task = {'desk': desk_id, 'stage': stage_id, 'user': original_task.get('user') if user_id is None else user_id}

    if current_stage:
        apply_stage_rule(doc, update, current_stage, MACRO_OUTGOING)

    if desk_id:
        desk = superdesk.get_resource_service('desks').find_one(req=None, _id=desk_id)
        if not desk:
            raise SuperdeskApiError.notFoundError('Invalid desk identifier %s' % desk_id)

        task['desk'] = desk_id
        if not stage_id:
            task['stage'] = desk.get(default_stage)
            destination_stage = get_resource_service('stages').find_one(req=None, _id=desk.get(default_stage))

    if stage_id:
        destination_stage = get_resource_service('stages').find_one(req=None, _id=stage_id)
        if not destination_stage:
            raise SuperdeskApiError.notFoundError('Invalid stage identifier %s' % stage_id)

        task['desk'] = destination_stage['desk']
        task['stage'] = stage_id

    if destination_stage:
        apply_stage_rule(doc, update, destination_stage, MACRO_INCOMING, desk=desk)
        if destination_stage.get('task_status'):
            task['status'] = destination_stage['task_status']

    if update:
        update.setdefault('task', {})
        update['task'].update(task)
        update['expiry'] = get_item_expiry(desk=desk, stage=destination_stage)
    else:
        doc['task'].update(task)
        doc['expiry'] = get_item_expiry(desk=desk, stage=destination_stage)
        superdesk.get_resource_service('desks').apply_desk_metadata(doc, doc)
예제 #19
0
def send_to(doc, update=None, desk_id=None, stage_id=None, user_id=None):
    """Send item to given desk and stage.
    Applies the outgoing and incoming macros of current and destination stages

    :param doc: original document to be sent
    :param update: updates for the document
    :param desk: id of desk where item should be sent
    :param stage: optional stage within the desk
    """

    original_task = doc.setdefault("task", {})
    current_stage = None
    if original_task.get("stage"):
        current_stage = get_resource_service("stages").find_one(req=None, _id=original_task.get("stage"))
    destination_stage = calculate_expiry_from = None
    task = {"desk": desk_id, "stage": stage_id, "user": original_task.get("user") if user_id is None else user_id}

    if current_stage:
        apply_stage_rule(doc, update, current_stage, is_incoming=False)

    if desk_id and not stage_id:
        desk = superdesk.get_resource_service("desks").find_one(req=None, _id=desk_id)
        if not desk:
            raise SuperdeskApiError.notFoundError("Invalid desk identifier %s" % desk_id)

        calculate_expiry_from = desk
        task["desk"] = desk_id
        task["stage"] = desk.get("incoming_stage")
        destination_stage = get_resource_service("stages").find_one(req=None, _id=desk.get("incoming_stage"))

    if stage_id:
        destination_stage = get_resource_service("stages").find_one(req=None, _id=stage_id)
        if not destination_stage:
            raise SuperdeskApiError.notFoundError("Invalid stage identifier %s" % stage_id)

        calculate_expiry_from = destination_stage
        task["desk"] = destination_stage["desk"]
        task["stage"] = stage_id

    if destination_stage:
        apply_stage_rule(doc, update, destination_stage, is_incoming=True)
        if destination_stage.get("task_status"):
            task["status"] = destination_stage["task_status"]

    if update:
        update.setdefault("task", {})
        update["task"].update(task)
        update["expiry"] = get_expiry(desk_or_stage_doc=calculate_expiry_from)
    else:
        doc["task"].update(task)
        doc["expiry"] = get_expiry(desk_or_stage_doc=calculate_expiry_from)
예제 #20
0
def send_to(doc, update=None, desk_id=None, stage_id=None, user_id=None):
    """Send item to given desk and stage.
    Applies the outgoing and incoming macros of current and destination stages

    :param doc: original document to be sent
    :param update: updates for the document
    :param desk: id of desk where item should be sent
    :param stage: optional stage within the desk
    """
    task = doc.get('task', {})
    current_stage = get_resource_service('stages').find_one(req=None, _id=task.get('stage'))
    destination_stage = None
    task.setdefault('desk', desk_id)
    task.setdefault('stage', stage_id)
    task.setdefault('user', user_id)

    calculate_expiry_from = None

    if current_stage:
        apply_stage_rule(doc, update, current_stage, is_incoming=False)

    if desk_id and not stage_id:
        desk = superdesk.get_resource_service('desks').find_one(req=None, _id=desk_id)
        if not desk:
            raise SuperdeskApiError.notFoundError('Invalid desk identifier %s' % desk_id)

        calculate_expiry_from = desk
        task['desk'] = desk_id
        task['stage'] = desk.get('incoming_stage')
        destination_stage = get_resource_service('stages').find_one(req=None, _id=desk.get('incoming_stage'))

    if stage_id:
        destination_stage = get_resource_service('stages').find_one(req=None, _id=stage_id)
        if not destination_stage:
            raise SuperdeskApiError.notFoundError('Invalid stage identifier %s' % stage_id)

        calculate_expiry_from = destination_stage
        task['desk'] = destination_stage['desk']
        task['stage'] = stage_id

    if destination_stage:
        apply_stage_rule(doc, update, destination_stage, is_incoming=True)
        if destination_stage.get('task_status'):
            task['status'] = destination_stage['task_status']

    if update:
        update['task'] = task
        update['expiry'] = get_expiry(desk_or_stage_doc=calculate_expiry_from)
    else:
        doc['task'] = task
        doc['expiry'] = get_expiry(desk_or_stage_doc=calculate_expiry_from)
    def _validate_rewrite(self, original, update):
        """Validates the article to be rewritten.

        :param original: article to be rewritten
        :param update: article as the rewrite
        :raises: SuperdeskApiError
        """
        if not original:
            raise SuperdeskApiError.notFoundError(message='Cannot find the article')

        if original.get(EMBARGO):
            raise SuperdeskApiError.badRequestError("Rewrite of an Item having embargo isn't possible")

        if not original.get('event_id'):
            raise SuperdeskApiError.notFoundError(message='Event id does not exist')

        if original.get('rewritten_by'):
            raise SuperdeskApiError.badRequestError(message='Article has been rewritten before !')

        if not is_workflow_state_transition_valid('rewrite', original[ITEM_STATE]):
            raise InvalidStateTransitionError()

        if original.get('rewrite_of') and not (original.get(ITEM_STATE) in PUBLISH_STATES):
            raise SuperdeskApiError.badRequestError(message="Rewrite is not published. Cannot rewrite the story again.")

        if update:
            # in case of associate as update
            if update.get('rewrite_of'):
                raise SuperdeskApiError.badRequestError("Rewrite story has been used as update before !")

            if update.get(ITEM_STATE) in [CONTENT_STATE.PUBLISHED,
                                          CONTENT_STATE.CORRECTED,
                                          CONTENT_STATE.KILLED,
                                          CONTENT_STATE.RECALLED,
                                          CONTENT_STATE.SCHEDULED,
                                          CONTENT_STATE.SPIKED]:
                raise InvalidStateTransitionError()

            if update.get(ITEM_TYPE) not in [CONTENT_TYPE.TEXT, CONTENT_TYPE.PREFORMATTED]:
                raise SuperdeskApiError.badRequestError("Rewrite story can only be text or pre-formatted !")

            if update.get('genre') and \
                    any(genre.get('value', '').lower() == BROADCAST_GENRE.lower() for genre in update.get('genre')):
                raise SuperdeskApiError.badRequestError("Broadcast cannot be a update story !")

            if original.get('profile') and original.get('profile') != update.get('profile'):
                raise SuperdeskApiError.badRequestError("Rewrite item content profile does "
                                                        "not match with Original item.")
예제 #22
0
    def create(self, docs, **kwargs):
        guid_of_item_to_be_copied = request.view_args['guid']

        guid_of_copied_items = []

        for doc in docs:
            archive_service = get_resource_service(ARCHIVE)

            archived_doc = archive_service.find_one(req=None, _id=guid_of_item_to_be_copied)
            if not archived_doc:
                raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' %
                                                      guid_of_item_to_be_copied)

            current_desk_of_item = archived_doc.get('task', {}).get('desk')
            if current_desk_of_item:
                raise SuperdeskApiError.preconditionFailedError(message='Copy is not allowed on items in a desk.')

            if not is_workflow_state_transition_valid('copy', archived_doc[ITEM_STATE]):
                raise InvalidStateTransitionError()

            new_guid = archive_service.duplicate_content(archived_doc)
            guid_of_copied_items.append(new_guid)

        if kwargs.get('notify', True):
            push_notification('item:copy', copied=1)

        return guid_of_copied_items
예제 #23
0
def get_upload_as_data_uri(media_id):
    if not request.args.get('resource'):
        media_id = app.media.getFilename(media_id)
        media_file = app.media.get(media_id, 'upload')
    else:
        media_file = app.media.get(media_id, request.args['resource'])
    if media_file:
        data = wrap_file(request.environ, media_file, buffer_size=1024 * 256)
        response = app.response_class(data,
                                      mimetype=media_file.content_type,
                                      direct_passthrough=True)
        response.content_length = media_file.length
        response.last_modified = media_file.upload_date
        response.set_etag(media_file.md5)
        response.cache_control.max_age = cache_for
        response.cache_control.s_max_age = cache_for
        response.cache_control.public = True
        response.make_conditional(request)

        if strtobool(request.args.get('download', 'False')):
            response.headers['Content-Disposition'] = 'attachment'
        else:
            response.headers['Content-Disposition'] = 'inline'
        return response

    raise SuperdeskApiError.notFoundError('File not found on media storage.')
예제 #24
0
    def create(self, docs, **kwargs):
        guid_of_item_to_be_duplicated = request.view_args['guid']

        guid_of_duplicated_items = []

        for doc in docs:
            archive_service = get_resource_service(ARCHIVE)

            archived_doc = archive_service.find_one(req=None, _id=guid_of_item_to_be_duplicated)
            if not archived_doc:
                raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' %
                                                      guid_of_item_to_be_duplicated)

            current_desk_of_item = archived_doc.get('task', {}).get('desk')
            if current_desk_of_item is None or str(current_desk_of_item) != str(doc.get('desk')):
                raise SuperdeskApiError.preconditionFailedError(message='Duplicate is allowed within the same desk.')

            if not is_workflow_state_transition_valid('duplicate', archived_doc[ITEM_STATE]):
                raise InvalidStateTransitionError()

            send_to(doc=archived_doc, desk_id=doc.get('desk'))
            new_guid = archive_service.duplicate_content(archived_doc)
            guid_of_duplicated_items.append(new_guid)

        if kwargs.get('notify', True):
            push_content_notification([archived_doc])

        return guid_of_duplicated_items
예제 #25
0
    def _send_report(scheduled_report):
        email_service = get_resource_service('email_report')
        saved_report = get_resource_service('saved_reports').find_one(
            req=None, _id=scheduled_report.get('saved_report'))

        if not saved_report:
            raise SuperdeskApiError.notFoundError('Saved report not found')

        extra = scheduled_report.get('extra') or {}
        body = extra.get('body') or 'Superdesk Analytics - {}'.format(
            scheduled_report.get('name'))

        email_service.post([{
            'report': {
                'type': saved_report.get('report'),
                'params': saved_report.get('params'),
                'mimetype': scheduled_report.get('mimetype'),
                'width': scheduled_report.get('report_width')
            },
            'email': {
                'recipients':
                scheduled_report.get('recipients'),
                'subject':
                'Superdesk Analytics - {}'.format(
                    scheduled_report.get('name')),
                'txt': {
                    'body': body
                },
                'html': {
                    'body': body
                }
            }
        }])
예제 #26
0
    def move_content(self, id, doc):
        archive_service = get_resource_service(ARCHIVE)
        archived_doc = archive_service.find_one(req=None, _id=id)

        if not archived_doc:
            raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' % id)

        current_stage_of_item = archived_doc.get('task', {}).get('stage')
        if current_stage_of_item and str(current_stage_of_item) == str(doc.get('task', {}).get('stage')):
            raise SuperdeskApiError.preconditionFailedError(message='Move is not allowed within the same stage.')

        if not is_workflow_state_transition_valid('submit_to_desk', archived_doc[config.CONTENT_STATE]):
            raise InvalidStateTransitionError()

        original = dict(archived_doc)

        send_to(archived_doc, doc.get('task', {}).get('desc'), doc.get('task', {}).get('stage'))

        if archived_doc[config.CONTENT_STATE] not in ['published', 'scheduled', 'killed']:
            archived_doc[config.CONTENT_STATE] = 'submitted'

        resolve_document_version(archived_doc, ARCHIVE, 'PATCH', original)

        del archived_doc['_id']
        archive_service.update(original['_id'], archived_doc, original)

        insert_into_versions(id_=original['_id'])

        return archived_doc
예제 #27
0
    def unlock(self, item_filter, user_id, session_id, etag):
        item_model = get_model(ItemModel)
        item = item_model.find_one(item_filter)

        if not item:
            raise SuperdeskApiError.notFoundError()

        if not item.get(LOCK_USER):
            raise SuperdeskApiError.badRequestError(
                message="Item is not locked.")

        can_user_unlock, error_message = self.can_unlock(item, user_id)

        if can_user_unlock:
            self.app.on_item_unlock(item, user_id)
            updates = {
                LOCK_USER: None,
                LOCK_SESSION: None,
                'lock_time': None,
                'force_unlock': True
            }
            item_model.update(item_filter, updates)
            self.app.on_item_unlocked(item, user_id)
            push_notification('item:unlock',
                              item=str(item_filter.get(config.ID_FIELD)),
                              user=str(user_id),
                              lock_session=str(session_id))
        else:
            raise SuperdeskApiError.forbiddenError(message=error_message)

        item = item_model.find_one(item_filter)
        return item
예제 #28
0
    def create(self, docs, **kwargs):
        guid_of_item_to_be_copied = request.view_args['guid']

        guid_of_copied_items = []

        for doc in docs:
            archive_service = get_resource_service(ARCHIVE)

            archived_doc = archive_service.find_one(req=None, _id=guid_of_item_to_be_copied)
            if not archived_doc:
                raise SuperdeskApiError.notFoundError(_(
                    'Fail to found item with guid: {guid}').format(guid=guid_of_item_to_be_copied))

            current_desk_of_item = archived_doc.get('task', {}).get('desk')
            if current_desk_of_item:
                raise SuperdeskApiError.preconditionFailedError(message=_('Copy is not allowed on items in a desk.'))

            if not is_workflow_state_transition_valid('copy', archived_doc[ITEM_STATE]):
                raise InvalidStateTransitionError()

            new_guid = archive_service.duplicate_content(archived_doc)
            guid_of_copied_items.append(new_guid)

        if kwargs.get('notify', True):
            user = get_user()
            push_notification('item:copy', copied=1, user=str(user.get(config.ID_FIELD, '')))

        return guid_of_copied_items
예제 #29
0
    def create(self, docs, **kwargs):
        guid_of_item_to_be_copied = request.view_args["guid"]

        guid_of_copied_items = []

        for doc in docs:
            archive_service = get_resource_service(ARCHIVE)

            archived_doc = archive_service.find_one(req=None, _id=guid_of_item_to_be_copied)
            if not archived_doc:
                raise SuperdeskApiError.notFoundError(
                    _("Fail to found item with guid: {guid}").format(guid=guid_of_item_to_be_copied)
                )

            current_desk_of_item = archived_doc.get("task", {}).get("desk")
            if current_desk_of_item and not app.config["WORKFLOW_ALLOW_COPY_TO_PERSONAL"]:
                raise SuperdeskApiError.preconditionFailedError(message=_("Copy is not allowed on items in a desk."))
            elif current_desk_of_item:
                archived_doc["task"] = {}
                archived_doc["original_creator"] = get_user_id()

            if not is_workflow_state_transition_valid("copy", archived_doc[ITEM_STATE]):
                raise InvalidStateTransitionError()

            new_guid = archive_service.duplicate_content(archived_doc)
            guid_of_copied_items.append(new_guid)

        if kwargs.get("notify", True):
            user = get_user()
            push_notification("item:copy", copied=1, user=str(user.get(config.ID_FIELD, "")))

        return guid_of_copied_items
예제 #30
0
    def create(self, docs, **kwargs):
        # we override create because we don't want anything stored in database
        doc = docs[0]
        sc_name = doc["spellchecker"]
        language = doc.get("language")
        try:
            spellchecker = registered_spellcheckers[sc_name]
        except KeyError:
            raise SuperdeskApiError.notFoundError(
                "{sc_name} spellchecker can't be found".format(
                    sc_name=sc_name))

        if doc["suggestions"]:
            check_data = spellchecker.suggest(doc["text"], language)
            assert "suggestions" in check_data
        else:
            check_data = spellchecker.check(doc["text"], language)
            assert "errors" in check_data
            if doc["use_internal_dict"]:
                self.remove_errors_in_dict(spellchecker, language, check_data)
            ignore = doc.get("ignore")
            if ignore:
                self.remove_ignored(check_data, ignore)
        docs[0].update(check_data)
        return [0]
예제 #31
0
    def on_update(self, updates, original):
        """ Called on the patch request to mark a activity/notification/comment as having been read and
        nothing else
        :param updates:
        :param original:
        :return:
        """
        user = getattr(g, "user", None)
        if not user:
            raise SuperdeskApiError.notFoundError("Can not determine user")
        user_id = str(user.get("_id"))

        # make sure that the user making the read notification is in the notification list
        if user_id not in updates.get("read").keys():
            raise SuperdeskApiError.forbiddenError("User is not in the notification list")

        # make sure the transition is from not read to read
        if not (updates.get("read")[user_id] == 1 and original.get("read")[user_id] == 0):
            raise SuperdeskApiError.forbiddenError("Can not set notification as read")

        # make sure that no other users are being marked as read
        for read_entry in updates.get("read"):
            if read_entry != user_id:
                if updates.get("read")[read_entry] != original.get("read")[read_entry]:
                    raise SuperdeskApiError.forbiddenError("Can not set other users notification as read")

        # make sure that no other fields are being up dated just read and _updated
        if len(updates) != 2:
            raise SuperdeskApiError.forbiddenError("Can not update")
예제 #32
0
    def on_update(self, updates, original):
        """Called on the patch request to mark a activity/notification/comment as read and nothing else

        :param updates:
        :param original:
        :return:
        """
        user = getattr(g, 'user', None)
        if not user:
            raise SuperdeskApiError.notFoundError('Can not determine user')
        user_id = user.get('_id')

        # make sure that the user making the read notification is in the notification list
        if not self.is_recipient(updates, user_id):
            raise SuperdeskApiError.forbiddenError(
                'User is not in the notification list')

        # make sure the transition is from not read to read
        if not self.is_read(updates, user_id) and self.is_read(
                original, user_id):
            raise SuperdeskApiError.forbiddenError(
                'Can not set notification as read')

        # make sure that no other users are being marked as read
        for recipient in updates.get('recipients', []):
            if recipient['user_id'] != user_id:
                if self.is_read(updates, recipient['user_id']) != self.is_read(
                        original, recipient['user_id']):
                    raise SuperdeskApiError.forbiddenError(
                        'Can not set other users notification as read')

        # make sure that no other fields are being up dated just read and _updated
        if len(updates) != 2:
            raise SuperdeskApiError.forbiddenError('Can not update')
예제 #33
0
    def create(self, docs, **kwargs):
        guid_of_translated_items = []

        for doc in docs:
            guid_of_item_to_be_translated = doc.get('guid')
            archive_service = get_resource_service(ARCHIVE)

            archived_doc = archive_service.find_one(
                req=None, _id=guid_of_item_to_be_translated)
            if not archived_doc:
                raise SuperdeskApiError.notFoundError(
                    'Fail to found item with guid: %s' %
                    guid_of_item_to_be_translated)

            if not is_workflow_state_transition_valid(
                    'translate', archived_doc[ITEM_STATE]):
                raise InvalidStateTransitionError()

            get_resource_service('macros').execute_translation_macro(
                archived_doc, archived_doc.get('language', None),
                doc.get('language'))
            archived_doc['language'] = doc.get('language')
            new_guid = archive_service.duplicate_content(archived_doc)
            guid_of_translated_items.append(new_guid)

            if kwargs.get('notify', True):
                push_content_notification([archived_doc])

        return guid_of_translated_items
예제 #34
0
    def unlock(self, item_filter, user_id, session_id, etag):
        item_model = get_model(ItemModel)
        item = item_model.find_one(item_filter)

        if not item:
            raise SuperdeskApiError.notFoundError()

        if not item.get(LOCK_USER):
            raise SuperdeskApiError.badRequestError(message="Item is not locked.")

        can_user_unlock, error_message = self.can_unlock(item, user_id)

        if can_user_unlock:
            self.app.on_item_unlock(item, user_id)

            # delete the item if nothing is saved so far
            if item['_version'] == 1 and item['state'] == 'draft':
                superdesk.get_resource_service('archive').delete(lookup={'_id': item['_id']})
                return

            updates = {LOCK_USER: None, LOCK_SESSION: None, 'lock_time': None, 'force_unlock': True}
            item_model.update(item_filter, updates)
            self.app.on_item_unlocked(item, user_id)
            push_notification('item:unlock', item=str(item_filter.get(config.ID_FIELD)), user=str(user_id),
                              lock_session=str(session_id))
        else:
            raise SuperdeskApiError.forbiddenError(message=error_message)

        item = item_model.find_one(item_filter)
        return item
예제 #35
0
    def create(self, docs, **kwargs):
        guid_of_translated_items = []

        for doc in docs:
            guid_of_item_to_be_translated = doc.get('guid')
            archive_service = get_resource_service(ARCHIVE)

            archived_doc = archive_service.find_one(req=None, _id=guid_of_item_to_be_translated)
            if not archived_doc:
                raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' %
                                                      guid_of_item_to_be_translated)

            if not is_workflow_state_transition_valid('translate', archived_doc[ITEM_STATE]):
                raise InvalidStateTransitionError()

            get_resource_service('macros').execute_translation_macro(
                archived_doc, archived_doc.get('language', None), doc.get('language'))
            archived_doc['language'] = doc.get('language')
            new_guid = archive_service.duplicate_content(archived_doc)
            guid_of_translated_items.append(new_guid)

            if kwargs.get('notify', True):
                push_content_notification([archived_doc])

        return guid_of_translated_items
예제 #36
0
    def authenticate(self, credentials):
        """Authenticates the user against Active Directory

        :param credentials: an object having "username" and "password" attributes
        :return: if success returns User object, otherwise throws Error
        """
        settings = app.settings
        ad_auth = ADAuth(settings['LDAP_SERVER'], settings['LDAP_SERVER_PORT'], settings['LDAP_BASE_FILTER'],
                         settings['LDAP_USER_FILTER'], settings['LDAP_USER_ATTRIBUTES'], settings['LDAP_FQDN'])

        username = credentials.get('username')
        password = credentials.get('password')
        profile_to_import = credentials.get('profile_to_import', username)

        user_data = ad_auth.authenticate_and_fetch_profile(username, password, username_for_profile=profile_to_import)

        if len(user_data) == 0:
            raise SuperdeskApiError.notFoundError(
                message='No user has been found in AD',
                payload={'profile_to_import': 1})

        query = get_user_query(profile_to_import)

        user = superdesk.get_resource_service('users').find_one(req=None, **query)

        if not user:
            add_default_values(user_data, profile_to_import,
                               user_type=None if 'user_type' not in user_data else user_data['user_type'])
            user = user_data
        else:
            superdesk.get_resource_service('users').patch(user.get('_id'), user_data)
            user = superdesk.get_resource_service('users').find_one(req=None, **query)

        return user
    def _validate(self, doc_in_archive, doc, guid_to_duplicate):
        """Validates if the given archived_doc is still eligible to be duplicated.

        Rules:
            1. Is the item requested found in archive collection?
            2. Is workflow transition valid?
            3. Is item locked by another user?

        :param doc_in_archive: object representing the doc in archive collection
        :type doc_in_archive: dict
        :param doc: object received as part of request
        :type doc: dict
        :param guid_to_duplicate: GUID of the item to duplicate
        :type guid_to_duplicate: str
        :raises
            SuperdeskApiError.notFoundError: If doc_in_archive is None
            SuperdeskApiError.forbiddenError: if item is locked
            InvalidStateTransitionError: if workflow transition is invalid
        """

        if not doc_in_archive:
            raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' % guid_to_duplicate)

        if not is_workflow_state_transition_valid('duplicate', doc_in_archive[ITEM_STATE]):
            raise InvalidStateTransitionError()

        lock_user = doc_in_archive.get('lock_user', None)
        force_unlock = doc_in_archive.get('force_unlock', False)
        user = get_user()
        str_user_id = str(user.get(config.ID_FIELD)) if user else None
        if lock_user and str(lock_user) != str_user_id and not force_unlock:
            raise SuperdeskApiError.forbiddenError('The item was locked by another user')
예제 #38
0
    def run(self, ad_username, ad_password, username, admin='false'):
        """
        Imports or Updates a User Profile from AD to Mongo.
        :param ad_username: Active Directory Username
        :param ad_password: Password of Active Directory Username
        :param username: Username as in Active Directory whose profile needs to be imported to Superdesk.
        :return: User Profile.
        """

        # force type conversion to boolean
        user_type = 'administrator' if admin is not None and admin.lower() == 'true' else 'user'

        # Authenticate and fetch profile from AD
        settings = app.settings
        ad_auth = ADAuth(settings['LDAP_SERVER'], settings['LDAP_SERVER_PORT'], settings['LDAP_BASE_FILTER'],
                         settings['LDAP_USER_FILTER'], settings['LDAP_USER_ATTRIBUTES'], settings['LDAP_FQDN'])

        user_data = ad_auth.authenticate_and_fetch_profile(ad_username, ad_password, username)

        if len(user_data) == 0:
            raise SuperdeskApiError.notFoundError('Username not found')

        # Check if User Profile already exists in Mongo
        user = superdesk.get_resource_service('users').find_one(username=username, req=None)

        if user:
            superdesk.get_resource_service('users').patch(user.get('_id'), user_data)
        else:
            add_default_values(user_data, username, user_type=user_type)
            superdesk.get_resource_service('users').post([user_data])

        return user_data
예제 #39
0
    def get(self, req, lookup):
        """Filter out personal activity on personal items if inquired by another user."""
        if req is None:
            req = ParsedRequest()
        user = getattr(g, "user", None)
        if not user:
            raise SuperdeskApiError.notFoundError("Can not determine user")
        where_cond = {}
        if req.where:
            if req.where[0] != "{":
                req.where = "{" + req.where + "}"
            where_cond = json.loads(req.where)
        for_user = where_cond.get("user", str(user.get("_id")))
        if for_user != str(user.get("_id")):
            where_item = {
                "$and": [{
                    "desk": {
                        "$ne": None
                    }
                }, {
                    "desk": {
                        "$exists": True
                    }
                }, {
                    "resource": "archive"
                }]
            }
            where_cond["$or"] = [where_item, {"resource": {"$ne": "archive"}}]
            req.where = json.dumps(where_cond)

        return self.backend.get(self.datasource, req=req, lookup=lookup)
예제 #40
0
    def create(self, docs, **kwargs):
        guid_of_item_to_be_moved = request.view_args['guid']

        guid_of_moved_items = []

        for doc in docs:
            archive_service = get_resource_service(ARCHIVE)

            archived_doc = archive_service.find_one(req=None, _id=guid_of_item_to_be_moved)
            if not archived_doc:
                raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' %
                                                      guid_of_item_to_be_moved)

            current_stage_of_item = archived_doc.get('task', {}).get('stage')
            if current_stage_of_item and str(current_stage_of_item) == str(doc.get('stage')):
                raise SuperdeskApiError.preconditionFailedError(message='Move is not allowed within the same stage.')

            if not is_workflow_state_transition_valid('submit_to_desk', archived_doc[config.CONTENT_STATE]):
                raise InvalidStateTransitionError()

            original = dict(archived_doc)

            send_to(archived_doc, doc.get('desk'), doc.get('stage'))
            archived_doc[config.CONTENT_STATE] = 'submitted'
            resolve_document_version(archived_doc, ARCHIVE, 'PATCH', original)

            del archived_doc['_id']
            archive_service.update(original['_id'], archived_doc, original)

            insert_into_versions(guid=original['_id'])

            guid_of_moved_items.append(archived_doc['guid'])

        return guid_of_moved_items
예제 #41
0
    def move_content(self, id, doc):
        archive_service = get_resource_service(ARCHIVE)
        archived_doc = archive_service.find_one(req=None, _id=id)

        if not archived_doc:
            raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' % id)

        current_stage_of_item = archived_doc.get('task', {}).get('stage')
        if current_stage_of_item and str(current_stage_of_item) == str(doc.get('task', {}).get('stage')):
            raise SuperdeskApiError.preconditionFailedError(message='Move is not allowed within the same stage.')

        if not is_workflow_state_transition_valid('submit_to_desk', archived_doc[ITEM_STATE]):
            raise InvalidStateTransitionError()

        original = dict(archived_doc)
        user = get_user()

        send_to(doc=archived_doc, desk_id=doc.get('task', {}).get('desc'), stage_id=doc.get('task', {}).get('stage'),
                user_id=user.get(config.ID_FIELD))

        if archived_doc[ITEM_STATE] not in {CONTENT_STATE.PUBLISHED, CONTENT_STATE.SCHEDULED, CONTENT_STATE.KILLED}:
            archived_doc[ITEM_STATE] = CONTENT_STATE.SUBMITTED
        archived_doc[ITEM_OPERATION] = ITEM_MOVE

        set_sign_off(archived_doc, original=original)
        resolve_document_version(archived_doc, ARCHIVE, 'PATCH', original)

        del archived_doc[config.ID_FIELD]
        archive_service.update(original[config.ID_FIELD], archived_doc, original)

        insert_into_versions(id_=original[config.ID_FIELD])

        return archived_doc
    def validate(self, updates, original):
        """
        Generic validation for event actions

        A lock must be held by the user in their current session
        As well as the lock must solely be for the action being processed,
        i.e. lock_action='update_time'
        """
        if not original:
            raise SuperdeskApiError.notFoundError()

        if self.REQUIRE_LOCK:
            user_id = get_user_id()
            session_id = get_auth().get(config.ID_FIELD, None)

            lock_user = original.get(LOCK_USER, None)
            lock_session = original.get(LOCK_SESSION, None)
            lock_action = original.get(LOCK_ACTION, None)

            if not lock_user:
                raise SuperdeskApiError.forbiddenError(
                    message='The event must be locked')
            elif str(lock_user) != str(user_id):
                raise SuperdeskApiError.forbiddenError(
                    message='The event is locked by another user')
            elif str(lock_session) != str(session_id):
                raise SuperdeskApiError.forbiddenError(
                    message='The event is locked by you in another session')
            elif str(lock_action) != self.ACTION:
                raise SuperdeskApiError.forbiddenError(
                    message='The lock must be for the `{}` action'.format(
                        self.ACTION.lower().replace('_', ' ')))

        get_resource_service('events').validate_event(updates, original)
예제 #43
0
    def create(self, docs, **kwargs):
        guid_of_item_to_be_duplicated = request.view_args['guid']

        guid_of_duplicated_items = []

        for doc in docs:
            archive_service = get_resource_service(ARCHIVE)

            archived_doc = archive_service.find_one(req=None, _id=guid_of_item_to_be_duplicated)
            if not archived_doc:
                raise SuperdeskApiError.notFoundError('Fail to found item with guid: %s' %
                                                      guid_of_item_to_be_duplicated)

            current_desk_of_item = archived_doc.get('task', {}).get('desk')
            if current_desk_of_item is None or str(current_desk_of_item) != str(doc.get('desk')):
                raise SuperdeskApiError.preconditionFailedError(message='Duplicate is allowed within the same desk.')

            send_to(doc=archived_doc, desk_id=doc.get('desk'))
            new_guid = archive_service.duplicate_content(archived_doc)
            guid_of_duplicated_items.append(new_guid)

        if kwargs.get('notify', True):
            task = archived_doc.get('task', {})
            push_notification(
                'content:update',
                duplicated=1,
                item=str(new_guid),
                desk=str(task.get('desk', '')),
                stage=str(task.get('stage', ''))
            )

        return guid_of_duplicated_items
예제 #44
0
    def get(self, req, lookup):
        """
        Return a list of items related to the given item. The given item id is retrieved
        from the lookup dictionary as 'item_id'
        """
        if 'item_id' not in lookup:
            raise SuperdeskApiError.badRequestError('The item identifier is required')
        item = get_resource_service('archive_autosave').find_one(req=None, _id=lookup['item_id'])
        if not item:
            item = get_resource_service('archive').find_one(req=None, _id=lookup['item_id'])
            if not item:
                raise SuperdeskApiError.notFoundError('Invalid item identifer')

        keywords = self.provider.get_keywords(self._transform(item))
        if not keywords:
            return ElasticCursor([])

        query = {
            'query': {
                'filtered': {
                    'query': {
                        'query_string': {
                            'query': ' '.join(kwd['text'] for kwd in keywords)
                        }
                    }
                }
            }
        }

        req = ParsedRequest()
        req.args = {'source': json.dumps(query), 'repo': 'archive,published,archived'}

        return get_resource_service('search').get(req=req, lookup=None)
예제 #45
0
    def on_update(self, updates, original):
        """Called on the patch request to mark a activity/notification/comment as read and nothing else

        :param updates:
        :param original:
        :return:
        """
        user = getattr(g, 'user', None)
        if not user:
            raise SuperdeskApiError.notFoundError('Can not determine user')
        user_id = user.get('_id')

        # make sure that the user making the read notification is in the notification list
        if not self.is_recipient(updates, user_id):
            raise SuperdeskApiError.forbiddenError('User is not in the notification list')

        # make sure the transition is from not read to read
        if not self.is_read(updates, user_id) and self.is_read(original, user_id):
            raise SuperdeskApiError.forbiddenError('Can not set notification as read')

        # make sure that no other users are being marked as read
        for recipient in updates.get('recipients', []):
            if recipient['user_id'] != user_id:
                if self.is_read(updates, recipient['user_id']) != self.is_read(original, recipient['user_id']):
                    raise SuperdeskApiError.forbiddenError('Can not set other users notification as read')

        # make sure that no other fields are being up dated just read and _updated
        if len(updates) != 2:
            raise SuperdeskApiError.forbiddenError('Can not update')
예제 #46
0
    def get(self, req, lookup):
        """Filter out personal activity on personal items if inquired by another user."""
        if req is None:
            req = ParsedRequest()
        user = getattr(g, 'user', None)
        if not user:
            raise SuperdeskApiError.notFoundError('Can not determine user')
        where_cond = {}
        if req.where:
            if req.where[0] != '{':
                req.where = '{' + req.where + '}'
            where_cond = json.loads(req.where)
        for_user = where_cond.get('user', str(user.get('_id')))
        if for_user != str(user.get('_id')):
            where_item = {
                '$and': [{
                    'desk': {
                        '$ne': None
                    }
                }, {
                    'desk': {
                        '$exists': True
                    }
                }, {
                    'resource': 'archive'
                }]
            }
            where_cond['$or'] = [where_item, {'resource': {'$ne': 'archive'}}]
            req.where = json.dumps(where_cond)

        return self.backend.get(self.datasource, req=req, lookup=lookup)
예제 #47
0
    def on_update(self, updates, original):
        """ Called on the patch request to mark a activity/notification/comment as having been read and
        nothing else
        :param updates:
        :param original:
        :return:
        """
        user = getattr(g, 'user', None)
        if not user:
            raise SuperdeskApiError.notFoundError('Can not determine user')
        user_id = str(user.get('_id'))

        # make sure that the user making the read notification is in the notification list
        if user_id not in updates.get('read').keys():
            raise SuperdeskApiError.forbiddenError(
                'User is not in the notification list')

        # make sure the transition is from not read to read
        if not (updates.get('read')[user_id] == 1
                and original.get('read')[user_id] == 0):
            raise SuperdeskApiError.forbiddenError(
                'Can not set notification as read')

        # make sure that no other users are being marked as read
        for read_entry in updates.get('read'):
            if read_entry != user_id:
                if updates.get('read')[read_entry] != original.get(
                        'read')[read_entry]:
                    raise SuperdeskApiError.forbiddenError(
                        'Can not set other users notification as read')

        # make sure that no other fields are being up dated just read and _updated
        if len(updates) != 2:
            raise SuperdeskApiError.forbiddenError('Can not update')
    def _validate(self, doc_in_archive, doc, guid_to_duplicate):
        """Validates if the given archived_doc is still eligible to be duplicated.

        Rules:
            1. Is the item requested found in archive collection?
            2. Is workflow transition valid?
            3. Is item locked by another user?

        :param doc_in_archive: object representing the doc in archive collection
        :type doc_in_archive: dict
        :param doc: object received as part of request
        :type doc: dict
        :param guid_to_duplicate: GUID of the item to duplicate
        :type guid_to_duplicate: str
        :raises
            SuperdeskApiError.notFoundError: If doc_in_archive is None
            SuperdeskApiError.forbiddenError: if item is locked
            InvalidStateTransitionError: if workflow transition is invalid
        """

        if not doc_in_archive:
            raise SuperdeskApiError.notFoundError(
                'Fail to found item with guid: %s' % guid_to_duplicate)

        if not is_workflow_state_transition_valid('duplicate',
                                                  doc_in_archive[ITEM_STATE]):
            raise InvalidStateTransitionError()

        lock_user = doc_in_archive.get('lock_user', None)
        force_unlock = doc_in_archive.get('force_unlock', False)
        user = get_user()
        str_user_id = str(user.get(config.ID_FIELD)) if user else None
        if lock_user and str(lock_user) != str_user_id and not force_unlock:
            raise SuperdeskApiError.forbiddenError(
                'The item was locked by another user')
예제 #49
0
    def unlock(self, item_filter, user_id, session_id, etag):
        item_model = get_model(ItemModel)
        item = item_model.find_one(item_filter)

        if not item:
            raise SuperdeskApiError.notFoundError()

        if not item.get(LOCK_USER):
            raise SuperdeskApiError.badRequestError(message="Item is not locked.")

        can_user_unlock, error_message = self.can_unlock(item, user_id)

        if can_user_unlock:
            self.app.on_item_unlock(item, user_id)

            # delete the item if nothing is saved so far
            # version 0 created on lock item
            if item[config.VERSION] == 0 and item["state"] == "draft":
                superdesk.get_resource_service("archive").delete(lookup={"_id": item["_id"]})
                return

            updates = {LOCK_USER: None, LOCK_SESSION: None, "lock_time": None, "force_unlock": True}
            item_model.update(item_filter, updates)
            self.app.on_item_unlocked(item, user_id)
            push_notification(
                "item:unlock",
                item=str(item_filter.get(config.ID_FIELD)),
                user=str(user_id),
                lock_session=str(session_id),
            )
        else:
            raise SuperdeskApiError.forbiddenError(message=error_message)

        item = item_model.find_one(item_filter)
        return item
예제 #50
0
    def create(self, docs, **kwargs):
        guid_of_item_to_be_duplicated = request.view_args['guid']

        guid_of_duplicated_items = []

        for doc in docs:
            archive_service = get_resource_service(ARCHIVE)

            archived_doc = archive_service.find_one(
                req=None, _id=guid_of_item_to_be_duplicated)
            if not archived_doc:
                raise SuperdeskApiError.notFoundError(
                    'Fail to found item with guid: %s' %
                    guid_of_item_to_be_duplicated)

            current_desk_of_item = archived_doc.get('task', {}).get('desk')
            if current_desk_of_item is None or str(
                    current_desk_of_item) != str(doc.get('desk')):
                raise SuperdeskApiError.preconditionFailedError(
                    message='Duplicate is allowed within the same desk.')

            if not is_workflow_state_transition_valid(
                    'duplicate', archived_doc[ITEM_STATE]):
                raise InvalidStateTransitionError()

            send_to(doc=archived_doc, desk_id=doc.get('desk'))
            new_guid = archive_service.duplicate_content(archived_doc)
            guid_of_duplicated_items.append(new_guid)

        if kwargs.get('notify', True):
            push_content_notification([archived_doc])

        return guid_of_duplicated_items
    def _translate_item(self, guid, language, task=None, service=None, state=None, **kwargs):
        if not service:
            service = ARCHIVE
        archive_service = get_resource_service(service)
        macros_service = get_resource_service("macros")
        published_service = get_resource_service("published")

        item = archive_service.find_one(req=None, guid=guid)
        if not item:
            raise SuperdeskApiError.notFoundError(_("Fail to found item with guid: {guid}").format(guid=guid))

        if not is_workflow_state_transition_valid("translate", item[ITEM_STATE]):
            raise InvalidStateTransitionError()

        if item.get("language") == language:
            return guid

        if package_service.is_package(item):
            refs = package_service.get_item_refs(item)
            for ref in refs:
                ref[RESIDREF] = self._translate_item(ref[RESIDREF], language, service=ref.get("location"), task=task)

        if not item.get("translation_id"):
            item["translation_id"] = item["guid"]

        macros_service.execute_translation_macro(item, item.get("language", None), language)

        item["language"] = language
        item["translated_from"] = guid
        item["versioncreated"] = utcnow()
        item["firstcreated"] = utcnow()
        if task:
            item["task"] = task

        extra_fields = ["translation_id", "translated_from"]

        UPDATE_TRANSLATION_METADATA_MACRO = app.config.get("UPDATE_TRANSLATION_METADATA_MACRO")

        if UPDATE_TRANSLATION_METADATA_MACRO and macros_service.get_macro_by_name(UPDATE_TRANSLATION_METADATA_MACRO):
            macros_service.execute_macro(item, UPDATE_TRANSLATION_METADATA_MACRO)

        translation_guid = archive_service.duplicate_item(
            item, extra_fields=extra_fields, state=state, operation="translate"
        )

        item.setdefault("translations", []).append(translation_guid)

        updates = {
            "translation_id": item["translation_id"],
            "translations": item["translations"],
        }

        archive_service.system_update(item["_id"], updates, item)
        published_service.update_published_items(item["_id"], "translation_id", item["_id"])
        published_service.update_published_items(item["_id"], "translations", item["translations"])

        if kwargs.get("notify", True):
            push_content_notification([item])

        return translation_guid
예제 #52
0
    def unlock(self, item_filter, user_id, session_id, etag):
        item_model = get_model(ItemModel)
        item = item_model.find_one(item_filter)

        if not item:
            raise SuperdeskApiError.notFoundError()

        if not item.get(LOCK_USER):
            raise SuperdeskApiError.badRequestError(message="Item is not locked.")

        can_user_unlock, error_message = self.can_unlock(item, user_id)

        if can_user_unlock:
            self.app.on_item_unlock(item, user_id)

            # delete the item if nothing is saved so far
            # version 0 created on lock item
            if item.get(config.VERSION, 0) == 0 and item[ITEM_STATE] == CONTENT_STATE.DRAFT:
                superdesk.get_resource_service('archive').delete_action(lookup={'_id': item['_id']})
                push_content_notification([item])
            else:
                updates = {LOCK_USER: None, LOCK_SESSION: None, 'lock_time': None, 'force_unlock': True}
                item_model.update(item_filter, updates)
                self.app.on_item_unlocked(item, user_id)

            push_notification('item:unlock',
                              item=str(item_filter.get(config.ID_FIELD)),
                              item_version=str(item.get(config.VERSION)),
                              state=item.get(ITEM_STATE),
                              user=str(user_id), lock_session=str(session_id))
        else:
            raise SuperdeskApiError.forbiddenError(message=error_message)

        item = item_model.find_one(item_filter)
        return item
예제 #53
0
    def lock(self, item_filter, user_id, session_id, etag):
        item_model = get_model(ItemModel)
        item = item_model.find_one(item_filter)

        if not item:
            raise SuperdeskApiError.notFoundError()

        can_user_lock, error_message = self.can_lock(item, user_id, session_id)

        if can_user_lock:
            self.app.on_item_lock(item, user_id)
            updates = {LOCK_USER: user_id, LOCK_SESSION: session_id, 'lock_time': utcnow()}
            item_model.update(item_filter, updates)

            if item.get(TASK):
                item[TASK]['user'] = user_id
            else:
                item[TASK] = {'user': user_id}

            superdesk.get_resource_service('tasks').assign_user(item[config.ID_FIELD], item[TASK])
            self.app.on_item_locked(item, user_id)
            push_notification('item:lock',
                              item=str(item.get(config.ID_FIELD)),
                              item_version=str(item.get(config.VERSION)),
                              user=str(user_id), lock_time=updates['lock_time'],
                              lock_session=str(session_id))
        else:
            raise SuperdeskApiError.forbiddenError(message=error_message)

        item = item_model.find_one(item_filter)
        return item
예제 #54
0
 def get_associated_item(self, assoc, throw_if_not_found=True):
     endpoint = assoc.get('location', 'archive')
     item_id = assoc[ITEM_REF]
     item = get_resource_service(endpoint).find_one(req=None, _id=item_id)
     if not item and throw_if_not_found:
         message = 'Invalid item reference: ' + assoc[ITEM_REF]
         logger.error(message)
         raise SuperdeskApiError.notFoundError(message=message)
     return item, item_id, endpoint
예제 #55
0
 def create(self, docs, **kwargs):
     if not docs:
         raise SuperdeskApiError.notFoundError('Content is missing')
     req = parse_request(self.datasource)
     try:
         get_component(ItemAutosave).autosave(docs[0]['_id'], docs[0], get_user(required=True), req.if_match)
     except InvalidEtag:
         raise SuperdeskApiError.preconditionFailedError('Client and server etags don\'t match')
     return [docs[0]['_id']]
예제 #56
0
    def get(self, req, **lookup):
        saved_search_id = lookup['lookup']['saved_search_id']
        saved_search = get_resource_service('saved_searches').find_one(req=None, _id=saved_search_id)

        if not saved_search:
            raise SuperdeskApiError.notFoundError("Invalid Saved Search")

        repo, query = super().process_query(saved_search)
        return super().validate_and_run_elastic_query(query, repo)