예제 #1
0
파일: views_pages.py 프로젝트: quru/qis
def upload_form():
    # Get upload directory options, and convert path templates to actual paths
    upload_dirs = [
        get_upload_directory(i) for i in range(len(app.config['IMAGE_UPLOAD_DIRS']))
    ]
    # Add in whether the user is allowed to upload and view
    upload_dirs = [(
        udir[0],
        udir[1],
        permissions_engine.is_folder_permitted(
            udir[1],
            FolderPermission.ACCESS_UPLOAD,
            get_session_user(),
            folder_must_exist=False
        ),
        permissions_engine.is_folder_permitted(
            udir[1],
            FolderPermission.ACCESS_VIEW,
            get_session_user(),
            folder_must_exist=False
        )
    ) for udir in upload_dirs]

    # Determine which radio button to pre-select
    dir_idx = -1
    to_path = request.args.get('path', None)
    if not to_path or to_path == os.path.sep:
        # Default to the first entry that allows upload
        for (idx, udir) in enumerate(upload_dirs):
            if udir[2]:
                dir_idx = idx
                break
    else:
        # Try matching the defined paths
        to_path = strip_seps(to_path)
        for (idx, udir) in enumerate(upload_dirs):
            if strip_seps(udir[1]) == to_path:
                dir_idx = idx
                break

    # If it's a manual path, use it only if it exists
    manual_path = ''
    if dir_idx == -1 and to_path and path_exists(to_path, require_directory=True):
        manual_path = to_path

    return render_template(
        'upload.html',
        upload_dirs=upload_dirs,
        sel_radio_num=dir_idx,
        manual_path=manual_path
    )
예제 #2
0
 def _normalize_path(self, path):
     """
     Converts a path to a standard format (removes leading and trailing
     slashes, apart from the root folder).
     """
     np = strip_seps(filepath_normalize(path))
     return np if np else os.path.sep
예제 #3
0
파일: filesystem_sync.py 프로젝트: quru/qis
def move_folder(db_folder, target_path, user_account, data_manager, permissions_manager, logger):
    """
    Moves a disk folder to the given new path (which must not already exist),
    and updates the associated database records. The folder is effectively
    renamed if the parent folder path remains the same.

    This method may take a long time, as the folder's sub-folders and images
    must also be moved, both on disk and in the database. The audit trail is
    also updated for every affected image, image IDs cached under the old path
    are cleared, and folder tree permissions are re-calculated.

    The user account must have Delete Folder permission for the original
    parent folder and Create Folder permission for the target parent folder,
    or alternatively have the file admin system permission.

    This method creates and commits its own separate database connection
    in an attempt to keep the operation is as atomic as possible. Note however
    that if there is an error moving the folder tree (in the database or on
    disk), operations already performed are not rolled back, and the database
    may become out of sync with the file system.

    Returns the updated folder object, including all affected sub-folders.

    Raises a DoesNotExistError if the source folder does not exist.
    Raises an AlreadyExistsError if the target path already exists.
    Raises an IOError or OSError on error moving the disk files or folders.
    Raises a ValueError if the source folder or target path is invalid.
    Raises a DBError for database errors.
    Raises a SecurityError if the current user does not have sufficient
    permission to perform the move or if the target path is outside of
    IMAGES_BASE_DIR.
    """
    db_session = data_manager.db_get_session()
    success = False
    try:
        _validate_path_chars(target_path)
        target_path = filepath_normalize(target_path)
        target_path = _secure_folder_path(
            target_path,
            True,
            app.config['ALLOW_UNICODE_FILENAMES']
        )
        norm_src = strip_seps(db_folder.path)
        norm_tgt = strip_seps(target_path)

        # Cannot move the root folder
        if norm_src == '':
            raise ValueError('Cannot move the root folder')
        # Don't allow blank path (move to become root) either
        if norm_tgt == '':
            raise ValueError('Target folder path cannot be empty')
        # Cannot move a folder into itself
        if norm_tgt.startswith(add_sep(norm_src)):
            raise ValueError('Cannot move a folder into itself')

        # Do nothing if target path is the same as the source
        if norm_src == norm_tgt:
            success = True
            return db_folder

        # Connect db_folder to our database session
        db_folder = data_manager.get_folder(db_folder.id, _db_session=db_session)
        if not db_folder:
            raise DoesNotExistError('Folder ID %d does not exist' % db_folder.id)

        # Source folder must exist
        ensure_path_exists(db_folder.path, require_directory=True)
        # Target folder must not yet exist (we cannot merge)
        if path_exists(target_path):
            raise AlreadyExistsError('Path already exists: ' + target_path)

        renaming = (
            strip_seps(filepath_parent(db_folder.path)) ==
            strip_seps(filepath_parent(target_path))
        )

        # Get parent folders for permissions checking
        # Target parent may not exist yet so use the closest node in the tree
        db_source_parent = db_folder.parent
        db_target_parent = _get_nearest_parent_folder(
            target_path, data_manager, db_session
        )
        # Require Create Folder permission for destination folder
        if user_account:
            permissions_manager.ensure_folder_permitted(
                db_target_parent,
                FolderPermission.ACCESS_CREATE_FOLDER,
                user_account
            )
        # Require Delete Folder permission for source parent folder
        if user_account and not renaming:
            permissions_manager.ensure_folder_permitted(
                db_source_parent,
                FolderPermission.ACCESS_DELETE_FOLDER,
                user_account
            )

        logger.info(
            'Disk folder %s is being moved to %s by %s' %
            (db_folder.path, target_path,
             user_account.username if user_account else 'System')
        )

        # We know there's no physical target folder, but if there is an
        # old (deleted) db record for the target path, purge it first.
        db_old_target_folder = data_manager.get_folder(
            folder_path=target_path, _db_session=db_session
        )
        if db_old_target_folder:
            # This recurses to purge files and sub-folders too
            data_manager.delete_folder(
                db_old_target_folder,
                purge=True,
                _db_session=db_session,
                _commit=False
            )

        # Move the disk files first, as this is the most likely thing to fail.
        # Note that this might involve moving files and directories we haven't
        # got database entries for (but that doesn't matter).
        filesystem_manager.move(db_folder.path, target_path)

        # Prep image history
        if renaming:
            history_info = 'Folder renamed from ' + filepath_filename(db_folder.path) + \
                           ' to ' + filepath_filename(target_path)
        else:
            history_info = 'Folder moved from ' + db_folder.path + ' to ' + target_path

        # Update the database
        data_manager.set_folder_path(
            db_folder,
            target_path,
            user_account,
            history_info,
            _db_session=db_session,
            _commit=False
        )

        # OK!
        logger.info(
            'Disk folder %s successfully moved to %s by %s' %
            (db_folder.path, target_path,
             user_account.username if user_account else 'System')
        )
        success = True
        return db_folder

    finally:
        # Commit or rollback database
        try:
            if success:
                db_session.commit()
            else:
                db_session.rollback()
        finally:
            db_session.close()

        # Clear folder permissions cache as folder tree has changed
        if success:
            permissions_manager.reset()
예제 #4
0
파일: filesystem_sync.py 프로젝트: quru/qis
def create_folder(rel_path, user_account, data_manager, permissions_manager, logger):
    """
    Creates a folder on disk and the associated database record.
    The folder path cannot be blank and should not already exist.

    The user account must have Create Folder permission for the parent folder,
    or alternatively have the file admin system permission.

    This method creates and commits its own separate database connection
    so that the operation is atomic.

    Returns the new folder object.

    Raises an AlreadyExistsError if the folder path already exists.
    Raises an OSError if the new folder cannot be created.
    Raises a ValueError if the folder path is invalid.
    Raises a DBError for database errors.
    Raises a SecurityError if the current user does not have sufficient
    permission to create the folder, or if the folder path is outside of
    IMAGES_BASE_DIR.
    """
    db_session = data_manager.db_get_session()
    success = False
    try:
        _validate_path_chars(rel_path)
        rel_path = filepath_normalize(rel_path)
        rel_path = _secure_folder_path(
            rel_path,
            True,
            app.config['ALLOW_UNICODE_FILENAMES']
        )

        # Don't allow blank path
        if strip_seps(rel_path) == '':
            raise ValueError('Folder path to create cannot be empty')
        # Check for existing (physical) path
        if path_exists(rel_path):
            raise AlreadyExistsError('Path already exists: ' + rel_path)

        # Check permissions for the (nearest existing) db parent folder
        if user_account:
            db_parent_folder = _get_nearest_parent_folder(
                rel_path, data_manager, db_session
            )
            permissions_manager.ensure_folder_permitted(
                db_parent_folder,
                FolderPermission.ACCESS_CREATE_FOLDER,
                user_account
            )

        # Create the physical folder
        filesystem_manager.make_dirs(rel_path)

        # Update the database
        db_folder = auto_sync_existing_folder(
            rel_path,
            data_manager,
            _db_session=db_session
        )

        # OK!
        logger.info(
            'Disk folder %s created by %s' %
            (rel_path, user_account.username if user_account else 'System')
        )
        success = True
        return db_folder

    finally:
        # Commit or rollback database
        try:
            if success:
                db_session.commit()
            else:
                db_session.rollback()
        finally:
            db_session.close()