def unlock_asset(asset_id): docs = request.json unlock_asset_response = get_sams_client().assets.unlock_asset( item_id=asset_id, external_user_id=get_user_id(True), external_session_id=get_auth()["_id"], docs=docs ) if unlock_asset_response.status_code == 200: push_notification( "sams:asset:unlock_asset", item_id=asset_id, user_id=get_user_id(True), session_id=get_auth()["_id"], _etag=unlock_asset_response.json()["_etag"], extension="sams", ) return unlock_asset_response.json(), unlock_asset_response.status_code
def create(self, docs): ids = [] production = get_resource_service('archive') for doc in docs: item_type = doc.pop('type') item_list = get_items(doc.pop('items', []), item_type) desk = get_resource_service('desks').find_one(req=None, _id=doc.pop('desk')) or {} article_template = doc.pop('article_template', None) if article_template: content_template = superdesk.get_resource_service('content_templates').find_one( req=None, _id=article_template) or {} else: content_template = get_desk_template(desk) item = get_item_from_template(content_template) item[current_app.config['VERSION']] = 1 item.setdefault('type', 'text') item.setdefault('slugline', 'Planning' if item_type == 'planning' else 'Event') item['task'] = { 'desk': desk.get('_id'), 'user': get_user_id(), 'stage': desk.get('working_stage'), } item_from_template = generate_text_item(item_list, doc.pop('template', None), item_type) for key, val in item_from_template.items(): placeholder = PLACEHOLDER_HTML if '_html' in key else PLACEHOLDER_TEXT if item.get(key) and placeholder in item[key]: item[key] = item[key].replace(placeholder, val) else: item[key] = val ids = production.post([item]) insert_into_versions(doc=item) doc.update(item) ids.append(doc['_id']) return ids
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
def on_update(self, updates, original): updated = deepcopy(original) updated.update(updates) self.is_valid(updated) user_id = get_user_id() if user_id: updates['version_creator'] = user_id
def _push_notification(doc, operation): push_notification('savedreports:update', report_type=doc['report'], operation=operation, report_id=str(doc.get('_id')), user_id=str(get_user_id()), session_id=str(get_auth().get('_id')))
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)
def delete(item_id): """ Uses item_id and deletes the corresponding asset """ try: etag = request.headers["If-Match"] except KeyError: raise SuperdeskApiError.badRequestError( "If-Match field missing in header") if get_attachments_from_asset_id(item_id).count(): raise SuperdeskApiError.badRequestError( _("Asset is attached to a news item, cannot delete")) delete_response = get_sams_client().assets.delete( item_id=item_id, headers={"If-Match": etag}) if delete_response.status_code != 204: return delete_response.json(), delete_response.status_code if delete_response.status_code == 204: push_notification( "sams:asset:deleted", item_id=item_id, user_id=get_user_id(True), session_id=get_auth()["_id"], extension="sams", ) return "", delete_response.status_code
def on_updated(self, updates, original): # If this Event was converted to a recurring series # Then update all associated Planning items with the recurrence_id if updates.get('recurrence_id') and not original.get('recurrence_id'): get_resource_service('planning').on_event_converted_to_recurring( updates, original) if not updates.get('duplicate_to'): posted = update_post_item(updates, original) if posted: new_event = get_resource_service('events').find_one( req=None, _id=original.get(config.ID_FIELD)) updates['_etag'] = new_event['_etag'] updates['state_reason'] = new_event.get('state_reason') if original.get( 'lock_user' ) and 'lock_user' in updates and updates.get('lock_user') is None: # when the event is unlocked by the patch. push_notification('events:unlock', item=str(original.get(config.ID_FIELD)), user=str(get_user_id()), lock_session=str(get_auth().get('_id')), etag=updates['_etag'], recurrence_id=original.get('recurrence_id') or None) self.delete_event_files(updates, original) if 'location' not in updates and original.get('location'): updates['location'] = original['location'] self._enhance_event_item(updates)
def on_deleted(self, doc): push_notification( 'events:delete', item=str(doc.get(config.ID_FIELD)), user=str(get_user_id()), lock_session=str(get_auth().get('_id')), )
def update(item_id): """ Uses item_id and updates the corresponding asset """ try: etag = request.headers["If-Match"] except KeyError: raise SuperdeskApiError.badRequestError("If-Match field missing in header") if request.files.get("binary"): # The binary data was supplied so this must be a multipart request # Get the updates from the `request.form` attribute files = {"binary": request.files["binary"]} updates = request.form.to_dict() else: # Only the metadata was supplied so this must be a standard JSON request # Get the updates from the `request.get_json` function files = {} updates = request.get_json() update_response = get_sams_client().assets.update( item_id=item_id, updates=updates, headers={"If-Match": etag}, files=files, external_user_id=get_user_id(True) ) if update_response.status_code == 200: push_notification( "sams:asset:updated", item_id=update_response.json()["_id"], user_id=get_user_id(True), session_id=get_auth()["_id"], _etag=update_response.json()["_etag"], extension="sams", ) return update_response.json(), update_response.status_code
def _push_notification(self, _id, event_name): """Push socket notifiction""" push_notification( event_name, item=str(_id), user=str(get_user_id()) )
def on_updated(self, updates, original): planning_featured_service = get_resource_service('planning_featured') planning_featured_service.remove_planning_item(original) if original.get( 'lock_user' ) and 'lock_user' in updates and updates.get('lock_user') is None: push_notification('planning:unlock', item=str(original.get(config.ID_FIELD)), user=str(get_user_id()), lock_session=str(get_auth().get('_id')), etag=updates.get('_etag'), event_item=original.get('event_item') or None, recurrence_id=original.get('recurrence_id') or None) # Delete assignments in workflow assignments_to_delete = [] coverages = original.get('coverages') or [] for coverage in coverages: if coverage.get('workflow_status') == WORKFLOW_STATE.ACTIVE: assignments_to_delete.append(coverage) notify = True if original.get('event_item'): event = get_resource_service('events').find_one( req=None, _id=original.get('event_item')) notify = event.get('state') != WORKFLOW_STATE.SPIKED get_resource_service('planning').delete_assignments_for_coverages( assignments_to_delete, notify)
def on_update(self, updates, original): self.set_schedule(updates) updated = deepcopy(original) updated.update(updates) user_id = get_user_id() if user_id: updates['version_creator'] = user_id
def post_featured_planning(self, updates, original={}): if updates.get('posted', False): self.validate_post_status( updates.get('items', original.get('items' or []))) updates['posted'] = True updates['last_posted_time'] = utcnow() updates['last_posted_by'] = str(get_user_id())
def on_update(self, updates, original): """ Process the action on the event provided Automatically sets the `version_creator`, then calls the appropriate method for single event (`update_single_event`) or a series of events (`update_recurring_events`) """ user_id = get_user_id() if user_id: updates['version_creator'] = user_id set_ingested_event_state(updates, original) # If `skip_on_update` is provided in the updates # Then return here so no further processing is performed on this event. if 'skip_on_update' in updates: return # We only validate the original event, # not the events that are automatically updated by the system self.validate(updates, original) # Run the specific method based on if the original is a single or a series of recurring events # Or if the 'update_method' is 'UPDATE_SINGLE' update_method = self.get_update_method(original, updates) if update_method == UPDATE_SINGLE: self.update_single_event(updates, original) else: self.update_recurring_events(updates, original, update_method)
def on_create(self, docs): for doc in docs: doc['user'] = get_user_id() if doc.get('media'): media = current_app.media.get(doc['media'], RESOURCE) doc.setdefault('filename', secure_filename(os.path.basename(getattr(media, 'filename')))) doc.setdefault('mimetype', getattr(media, 'content_type')) doc.setdefault('length', getattr(media, 'length'))
def on_create(self, docs): for doc in docs: doc['user'] = get_user_id(required=True) if 'subscribers' in doc: raise SuperdeskApiError.forbiddenError("User's subscriptions are not allowed on create") self.process(doc) push_notification(UPDATE_NOTIFICATION)
def create(): """ Creates new sets """ docs = request.get_json() post_response = get_sams_client().sets.create( docs=docs, external_user_id=get_user_id(True)) if post_response.status_code == 201: push_notification( "sams:set:created", item_id=post_response.json()["_id"], user_id=get_user_id(True), session_id=get_auth()["_id"], _etag=post_response.json()["_etag"], extension="sams", ) return post_response.json(), post_response.status_code
def create(): """ Creates new Asset """ files = {"binary": request.files["binary"]} docs = request.form.to_dict() sams_client = get_sams_client() post_response = sams_client.assets.create( docs=docs, files=files, external_user_id=get_user_id(True)) response = post_response.json() if post_response.status_code == 201: if response.get("mimetype", "").startswith("image/"): # Create renditions. renditions = [k for k in app.config["RENDITIONS"]["sams"].keys()] for rendition in renditions: dimensions = app.config["RENDITIONS"]["sams"][rendition] rendition_response = sams_client.images.generate_rendition( response["_id"], width=dimensions.get("width"), height=dimensions.get("height"), name=rendition, keep_proportions=True, ) if not rendition_response.ok: # We want to continue, even if SAMS failed to generate the rendition # Instead we just log the error here and continue error_json = rendition_response.json() error_code = rendition_response.status_code description = error_json.get( "description") or f"Error [{error_code}]" logger.error( f"Failed to generate SAMS image rendition: {description}" ) push_notification( "sams:asset:created", item_id=response["_id"], user_id=get_user_id(True), session_id=get_auth()["_id"], _etag=response["_etag"], extension="sams", ) return response, post_response.status_code
def create(): """ Creates new Asset """ files = {"binary": request.files["binary"]} docs = request.form.to_dict() post_response = get_sams_client().assets.create( docs=docs, files=files, external_user_id=get_user_id(True)) if post_response.status_code == 201: push_notification( "sams:asset:created", item_id=post_response.json()["_id"], user_id=get_user_id(True), session_id=get_auth()["_id"], _etag=post_response.json()["_etag"], extension="sams", ) return post_response.json(), post_response.status_code
def on_update(self, updates, original): # Find all planning items in the list added_featured = [ id for id in updates.get('items') if id not in original.get('items') ] self.validate_featured_attrribute(added_featured) updates['version_creator'] = str(get_user_id()) self.post_featured_planning(updates, original)
def unlock_asset_by_user(user_id, session_id): unlock_asset_response = get_sams_client().assets.unlock_assets_by_user( external_user_id=user_id, external_session_id=session_id) if unlock_asset_response.status_code == 200: push_notification("sams:asset:session_unlock", user_id=get_user_id(True), session_id=get_auth()["_id"], extension="sams") return unlock_asset_response.status_code
def create(self, docs, **kwargs): ids = [] for doc in docs: item_type = doc.pop('type') item_list = get_items(doc.pop('items', []), item_type) desk = get_resource_service('desks').find_one( req=None, _id=doc.pop('desk')) or {} article_template = doc.pop('article_template', None) if article_template: content_template = get_resource_service( 'content_templates').find_one(req=None, _id=article_template) or {} else: content_template = get_desk_template(desk) item = get_item_from_template(content_template) item[current_app.config['VERSION']] = 1 item.setdefault('type', 'text') if item_type == 'planning': item.setdefault('slugline', 'Planning') elif item_type == 'event': item.setdefault('slugline', 'Event') else: item.setdefault('slugline', 'Events and Planning') item['task'] = { 'desk': desk.get('_id'), 'user': get_user_id(), 'stage': desk.get('working_stage'), } item_from_template = generate_text_item(item_list, doc.pop('template', None), item_type) fields_to_override = [] for key, val in item_from_template.items(): if item.get(key): fields_to_override.append(key) placeholder = PLACEHOLDER_HTML if '_html' in key else PLACEHOLDER_TEXT if placeholder in item[key]: # The placeholder is found in the current field # So replace {{content}} with the generated text item[key] = item[key].replace(placeholder, val) else: # Otherwise append the generated text to the field item[key] += val else: item[key] = val item = create_item_from_template(item, fields_to_override) doc.update(item) ids.append(doc['_id']) return ids
def on_create(self, docs): for doc in docs: doc["user"] = get_user_id() if doc.get("media"): media = current_app.media.get(doc["media"], RESOURCE) doc.setdefault( "filename", secure_filename( os.path.basename(getattr(media, "filename")))) doc.setdefault("mimetype", getattr(media, "content_type")) doc.setdefault("length", getattr(media, "length"))
def send_to_original_desk(self, updates, original): if ( app.config.get("CORRECTIONS_WORKFLOW") and original.get("state") == "correction" and original.get("task", {}).get("desk_history") ): send_to( doc=updates, desk_id=(original["task"]["desk_history"][0]), default_stage="working_stage", user_id=get_user_id(), )
def get(self, req, lookup): """ Overriding to pass user as a search parameter """ session_user = str(get_user_id(required=True)) if not req: req = ParsedRequest() if lookup: req.where = json.dumps({'$or': [{'is_global': True}, {'user': session_user}, lookup]}) else: req.where = json.dumps({'$or': [{'is_global': True}, {'user': session_user}]}) return super().get(req, lookup=None)
def create(self, docs, **kwargs): ids = [] for doc in docs: task = None if doc.get('desk'): desk = get_resource_service('desks').find_one( req=None, _id=doc['desk']) or {} task = dict(desk=desk.get('_id'), stage=desk.get('working_stage'), user=get_user_id()) ids.append( self._translate_item(doc['guid'], doc['language'], task, **kwargs)) return ids
def create(self, docs, **kwargs): ids = [] for doc in docs: task = None if doc.get("desk"): desk = get_resource_service("desks").find_one( req=None, _id=doc["desk"]) or {} task = dict(desk=desk.get("_id"), stage=desk.get("working_stage"), user=get_user_id()) ids.append( self._translate_item(doc["guid"], doc["language"], task, **kwargs)) return ids
def on_updated(self, updates, original): planning_featured_service = get_resource_service('planning_featured') planning_featured_service.remove_planning_item(original) if original.get('lock_user') and 'lock_user' in updates and updates.get('lock_user') is None: push_notification( 'planning:unlock', item=str(original.get(config.ID_FIELD)), user=str(get_user_id()), lock_session=str(get_auth().get('_id')), etag=updates.get('_etag'), event_item=original.get('event_item') or None, recurrence_id=original.get('recurrence_id') or None )
def on_create(self, docs): for doc in docs: doc["user"] = get_user_id() # If a `media` argument is passed into the request url then use that as the id for the media item # This is so that SAMS client can manually create this link between SAMS and the article if request.args.get("media"): doc["media"] = request.args["media"] if doc.get("media"): media = current_app.media.get(doc["media"], RESOURCE) doc.setdefault( "filename", secure_filename( os.path.basename(getattr(media, "filename")))) doc.setdefault("mimetype", getattr(media, "content_type")) doc.setdefault("length", getattr(media, "length"))
def on_create(self, docs): for doc in docs: doc['user'] = get_user_id(required=True) self.process(doc) push_notification(UPDATE_NOTIFICATION)
def _set_created_by(self, doc): doc['created_by'] = get_user_id()
def _set_updated_by(self, doc): doc['updated_by'] = get_user_id()
def create(self, docs, **kwargs): ids = [] for doc in docs: task = None if doc.get('desk'): desk = get_resource_service('desks').find_one(req=None, _id=doc['desk']) or {} task = dict(desk=desk.get('_id'), stage=desk.get('working_stage'), user=get_user_id()) ids.append(self._translate_item(doc['guid'], doc['language'], task, **kwargs)) return ids