Beispiel #1
0
Datei: views.py Projekt: quru/qis
def portfolio_download(human_id, filename):
    logger.debug('GET ' + request.url)
    try:
        # Find the portfolio
        folio = data_engine.get_portfolio(human_id=human_id)
        if not folio:
            raise DoesNotExistError('Portfolio \'%s\' does not exist' %
                                    human_id)

        # Ensure that the user has permission to download the portfolio
        user = get_session_user()
        permissions_engine.ensure_portfolio_permitted(
            folio, FolioPermission.ACCESS_DOWNLOAD, user)

        # Check that the filename is valid (note: assumes folio.downloads is eager loaded)
        if not filename:
            raise DoesNotExistError('No filename specified')
        folio_exports = [
            dl for dl in folio.downloads if dl.filename == filename
        ]
        if not folio_exports:
            raise DoesNotExistError('Download \'%s\' is not available' %
                                    filename)
        folio_export = folio_exports[0]
        # The physical file should always exist when the data+filename exists
        # This also checks that the file path lies inside IMAGES_BASE_DIR
        zip_path = get_portfolio_export_file_path(folio_export)
        ensure_path_exists(zip_path, require_file=True)

        # Prepare to serve the file
        response = send_file(
            get_abs_path(zip_path),
            mimetype='application/zip',
            as_attachment=True,
            conditional=True,
            cache_timeout=31536000  # zips never change once created
        )

        # Lastly write an audit record
        data_engine.add_portfolio_history(folio, user,
                                          FolioHistory.ACTION_DOWNLOADED,
                                          folio_export.filename)
        return response

    except httpexc.HTTPException:
        # Pass through HTTP 4xx and 5xx
        raise
    except SecurityError as e:
        if app.config['DEBUG']:
            raise
        log_security_error(e, request)
        raise httpexc.Forbidden()
    except DoesNotExistError as e:
        logger.warning('404 Not found: ' + str(e))
        raise httpexc.NotFound(safe_error_str(e))
    except Exception as e:
        if app.config['DEBUG']:
            raise
        logger.error('500 Error for ' + request.url + '\n' + str(e))
        raise httpexc.InternalServerError(safe_error_str(e))
Beispiel #2
0
 def delete(self, folio_id, image_id):
     db_session = data_engine.db_get_session()
     try:
         folio_image = data_engine.get_portfolio_image(
             AttrObject(id=folio_id),
             AttrObject(id=image_id),
             _db_session=db_session)
         if folio_image is None:
             raise DoesNotExistError(str(folio_id) + '/' + str(image_id))
         # Check permissions
         permissions_engine.ensure_portfolio_permitted(
             folio_image.portfolio, FolioPermission.ACCESS_EDIT,
             get_session_user())
         # Add history first so that we only commit once at the end
         folio = folio_image.portfolio
         data_engine.add_portfolio_history(folio,
                                           get_session_user(),
                                           FolioHistory.ACTION_IMAGE_CHANGE,
                                           '%s removed' %
                                           folio_image.image.src,
                                           _db_session=db_session,
                                           _commit=False)
         # Flag that exported zips will be out of date
         folio.last_updated = datetime.utcnow()
         # Delete the image from the portfolio and commit changes
         data_engine.delete_object(folio_image,
                                   _db_session=db_session,
                                   _commit=True)
         return make_api_success_response()
     finally:
         db_session.close()
Beispiel #3
0
def delete_portfolio_export(folio_export,
                            history_user,
                            history_info,
                            _db_session=None):
    """
    Deletes a portfolio export record and the associated zip file (if it exists),
    and adds an audit trail entry for the parent portfolio. If you supply a
    database session it will be committed before the zip file is deleted, so
    that files are only deleted once the database operations are known to have
    worked.

    Raises a ServerTooBusyError if the export is still in progress.
    Raises an OSError if the zip file or directory cannot be deleted.
    """
    db_session = _db_session or data_engine.db_get_session()
    try:
        # Ensure we can access folio_export.portfolio
        if not data_engine.object_in_session(folio_export, db_session):
            folio_export = data_engine.get_object(FolioExport,
                                                  folio_export.id,
                                                  _db_session=db_session)

        # Check whether the export task is running
        if folio_export.task_id:
            task = task_engine.get_task(folio_export.task_id,
                                        _db_session=db_session)
            if (task and task.status == Task.STATUS_ACTIVE) or (
                    task and task.status == Task.STATUS_PENDING
                    and not task_engine.cancel_task(task)):
                raise ServerTooBusyError(
                    'this export is currently in progress, wait a while then try again'
                )

        # Delete and add history in one commit
        data_engine.add_portfolio_history(folio_export.portfolio,
                                          history_user,
                                          FolioHistory.ACTION_UNPUBLISHED,
                                          history_info,
                                          _db_session=db_session,
                                          _commit=False)
        data_engine.delete_object(folio_export,
                                  _db_session=db_session,
                                  _commit=True)
        # If we got this far the database delete worked and we now need to
        # delete the exported zip file
        zip_rel_path = get_portfolio_export_file_path(folio_export)
        if folio_export.filename:
            delete_file(zip_rel_path)
        # And if the zip directory is now empty, delete the directory too
        zip_rel_dir = get_portfolio_directory(folio_export.portfolio)
        if path_exists(zip_rel_dir, require_directory=True):
            zips_count = count_files(zip_rel_dir, recurse=False)
            if zips_count[0] == 0:
                delete_dir(zip_rel_dir)
    finally:
        if not _db_session:
            db_session.close()
Beispiel #4
0
 def post(self, folio_id):
     db_session = data_engine.db_get_session()
     try:
         # Get the portfolio
         folio = data_engine.get_portfolio(folio_id, _db_session=db_session)
         if folio is None:
             raise DoesNotExistError(str(folio_id))
         # Check portfolio permissions
         permissions_engine.ensure_portfolio_permitted(
             folio, FolioPermission.ACCESS_EDIT, get_session_user())
         # Get the image by either ID or src
         params = self._get_validated_object_parameters(request.form, True)
         if 'image_id' in params:
             image = data_engine.get_image(params['image_id'],
                                           _db_session=db_session)
             if image is None:
                 raise DoesNotExistError(str(params['image_id']))
         else:
             image = auto_sync_file(params['image_src'],
                                    data_engine,
                                    task_engine,
                                    anon_history=True,
                                    burst_pdf=False,
                                    _db_session=db_session)
             if image is None or image.status == Image.STATUS_DELETED:
                 raise DoesNotExistError(params['image_src'])
         # Check image permissions
         permissions_engine.ensure_folder_permitted(
             image.folder, FolderPermission.ACCESS_VIEW, get_session_user(),
             False)
         # Add history first so that we only commit once at the end
         data_engine.add_portfolio_history(folio,
                                           get_session_user(),
                                           FolioHistory.ACTION_IMAGE_CHANGE,
                                           '%s added' % image.src,
                                           _db_session=db_session,
                                           _commit=False)
         # Flag that exported zips are now out of date
         folio.last_updated = datetime.utcnow()
         # Add the image and commit changes
         db_folio_image = data_engine.save_object(FolioImage(
             folio, image, params['image_parameters'], params['filename'],
             params['index']),
                                                  refresh=True,
                                                  _db_session=db_session,
                                                  _commit=True)
         return make_api_success_response(
             object_to_dict(_prep_folioimage_object(db_folio_image),
                            _omit_fields + ['portfolio']))
     finally:
         db_session.close()
Beispiel #5
0
 def put(self, folio_id):
     db_session = data_engine.db_get_session()
     try:
         # Get portfolio
         folio = data_engine.get_portfolio(folio_id, _db_session=db_session)
         if folio is None:
             raise DoesNotExistError(str(folio_id))
         # Check permissions
         permissions_engine.ensure_portfolio_permitted(
             folio, FolioPermission.ACCESS_EDIT, get_session_user())
         # Update the object
         params = self._get_validated_object_parameters(request.form)
         permissions_changed = self._set_permissions(
             folio, params, db_session)
         changes = []
         if params['human_id'] != folio.human_id:
             changes.append('short URL changed')
         if params['name'] != folio.name:
             changes.append('name changed')
         if params['description'] != folio.description:
             changes.append('description changed')
         if permissions_changed:
             changes.append('permissions changed')
         folio.human_id = params['human_id'] or Folio.create_human_id()
         folio.name = params['name']
         folio.description = params['description']
         # Note: folio.last_updated is only for image changes
         #       (to know when to invalidate the exported zips)
         data_engine.add_portfolio_history(folio,
                                           get_session_user(),
                                           FolioHistory.ACTION_EDITED,
                                           ', '.join(changes).capitalize(),
                                           _db_session=db_session,
                                           _commit=False)
         data_engine.save_object(
             folio,
             _db_session=db_session,
             _commit=True  # fail here if human_id not unique
         )
         if permissions_changed:
             permissions_engine.reset_portfolio_permissions()
         # Return a clean object the same as for get(id)
         folio = data_engine.get_portfolio(folio.id,
                                           load_images=True,
                                           load_history=True)
         folio = _prep_folio_object(folio)
         return make_api_success_response(
             object_to_dict(folio, _omit_fields))
     finally:
         db_session.close()
Beispiel #6
0
 def put(self, folio_id, image_id):
     db_session = data_engine.db_get_session()
     try:
         folio_image = data_engine.get_portfolio_image(
             AttrObject(id=folio_id),
             AttrObject(id=image_id),
             _db_session=db_session)
         if folio_image is None:
             raise DoesNotExistError(str(folio_id) + '/' + str(image_id))
         # Check permissions
         permissions_engine.ensure_portfolio_permitted(
             folio_image.portfolio, FolioPermission.ACCESS_EDIT,
             get_session_user())
         # Update the object with any/all parameters that were passed in
         params = self._get_validated_object_parameters(request.form, False)
         changes = []
         affects_zips = False
         if (params['image_parameters'] is not None
                 and params['image_parameters'] != folio_image.parameters):
             folio_image.parameters = params['image_parameters']
             changes.append('image attributes changed')
             affects_zips = True
         if (params['filename'] is not None
                 and params['filename'] != folio_image.filename):
             folio_image.filename = params['filename']
             changes.append('filename changed')
             affects_zips = True
         if (params['index'] is not None
                 and params['index'] != folio_image.order_num):
             folio_image.order_num = params['index']
             changes.append('set as position %d' % (params['index'] + 1))
         if changes:
             # Flag if exported zips will be out of date
             if affects_zips:
                 folio_image.portfolio.last_updated = datetime.utcnow()
             # Add history and commit changes
             data_engine.add_portfolio_history(
                 folio_image.portfolio,
                 get_session_user(),
                 FolioHistory.ACTION_IMAGE_CHANGE,
                 '%s updated: %s' %
                 (folio_image.image.src, ', '.join(changes)),
                 _db_session=db_session,
                 _commit=True)
         return make_api_success_response(
             object_to_dict(_prep_folioimage_object(folio_image),
                            _omit_fields + ['portfolio']))
     finally:
         db_session.close()
Beispiel #7
0
 def post(self, folio_id):
     db_session = data_engine.db_get_session()
     try:
         # Get the portfolio
         folio = data_engine.get_portfolio(folio_id, _db_session=db_session)
         if folio is None:
             raise DoesNotExistError(str(folio_id))
         # Check permissions
         permissions_engine.ensure_portfolio_permitted(
             folio, FolioPermission.ACCESS_EDIT, get_session_user())
         # Block the export now if it would create an empty zip file
         if len(folio.images) == 0:
             raise ParameterError(
                 'this portfolio is empty and cannot be published')
         # Create a folio-export record and start the export as a background task
         params = self._get_validated_object_parameters(request.form)
         folio_export = FolioExport(folio, params['description'],
                                    params['originals'],
                                    params['image_parameters'],
                                    params['expiry_time'])
         data_engine.add_portfolio_history(folio,
                                           get_session_user(),
                                           FolioHistory.ACTION_PUBLISHED,
                                           folio_export.describe(True),
                                           _db_session=db_session,
                                           _commit=False)
         folio_export = data_engine.save_object(folio_export,
                                                refresh=True,
                                                _db_session=db_session,
                                                _commit=True)
         export_task = task_engine.add_task(
             get_session_user(), 'Export portfolio %d / export %d' %
             (folio.id, folio_export.id), 'export_portfolio', {
                 'export_id': folio_export.id,
                 'ignore_errors': False
             }, Task.PRIORITY_NORMAL, 'info', 'error', 60)
         # Update and return the folio-export record with the task ID
         folio_export.task_id = export_task.id
         data_engine.save_object(folio_export,
                                 _db_session=db_session,
                                 _commit=True)
         return make_api_success_response(object_to_dict(
             _prep_folioexport_object(folio, folio_export),
             _omit_fields + ['portfolio']),
                                          task_accepted=True)
     finally:
         db_session.close()
Beispiel #8
0
 def put(self, folio_id, image_id):
     db_session = data_engine.db_get_session()
     try:
         # Get data
         folio_image = data_engine.get_portfolio_image(
             AttrObject(id=folio_id),
             AttrObject(id=image_id),
             _db_session=db_session)
         if folio_image is None:
             raise DoesNotExistError(str(folio_id) + '/' + str(image_id))
         # Check permissions
         permissions_engine.ensure_portfolio_permitted(
             folio_image.portfolio, FolioPermission.ACCESS_EDIT,
             get_session_user())
         # Update the portfolio
         params = self._get_validated_object_parameters(request.form)
         chd_folio_image = data_engine.reorder_portfolio(
             folio_image, params['index'])
         data_engine.add_portfolio_history(
             folio_image.portfolio,
             get_session_user(),
             FolioHistory.ACTION_IMAGE_CHANGE,
             '%s moved to position %d' %
             (folio_image.image.src, chd_folio_image.order_num + 1),
             _db_session=db_session,
             _commit=True)
         # Return the updated image list
         db_session.expire(folio_image)
         folio = data_engine.get_portfolio(folio_id,
                                           load_images=True,
                                           _db_session=db_session)
         image_list = [_prep_folioimage_object(fi) for fi in folio.images]
         return make_api_success_response(
             object_to_dict_list(image_list, _omit_fields + ['portfolio']))
     finally:
         db_session.close()