예제 #1
0
def makeFileThumbnail(srcFileId, mimeType, thumbnailFormat,
                      fileStorageDirectory):
    """Given an image file, extract a thumbnail out of it."""
    resizeMethod = imageMimeTypeToResizeMethodMap.get(mimeType)
    if resizeMethod == 'resize':
        newId = uuid4().hex
        if resizeToThumbnail(
                fileIdToPath(srcFileId,
                             fileStorageDirectory=fileStorageDirectory),
                fileIdToPath(newId, fileStorageDirectory=fileStorageDirectory),
                thumbnailFormat=thumbnailFormat,
        ):
            return newId, mimeType
        else:
            return None, None
    elif resizeMethod == 'copy':
        newId = uuid4().hex
        shutil.copy(
            fileIdToPath(srcFileId, fileStorageDirectory=fileStorageDirectory),
            fileIdToPath(newId, fileStorageDirectory=fileStorageDirectory),
        )
        return newId, mimeType
    elif resizeMethod is None:
        # if mime type not enumerated, fail and that's it
        return None, None
    else:
        raise NotImplementedError('resize method not supported: "%s"' %
                                  resizeMethod)
예제 #2
0
 def collectArchivablePairs(tr, accPath=''):
     hereFiles = [{
         'type':
         'file',
         'path':
         fileIdToPath(
             file['file'].file_id,
             fileStorageDirectory,
         ),
         'zip_path':
         os.path.join(
             accPath,
             file['file'].name,
         ),
     } for file in tr['contents']['files']] + [{
         'type':
         'data',
         'data':
         link['link'].formatAsString(),
         'zip_path':
         os.path.join(
             accPath,
             '%s' % link['link'].name,
         ),
     } for link in tr['contents']['links']]
     subFiles = [
         fp for subBox in tr['contents']['boxes'] for fp in
         collectArchivablePairs(subBox,
                                accPath=os.path.join(
                                    accPath,
                                    subBox['box'].box_name,
                                ))
     ]
     return hereFiles + subFiles
예제 #3
0
def updateBoxThumbnail(
        db, box, tId, tMT, user, fileStorageDirectory,
        accountDeletionInProgress=False, skipCommit=False):
    """ Update the thumbnail info for a box.
        Return a list of zero or one full paths for deletion."""
    prevId = box.icon_file_id
    if prevId != '':
        delQueue = [
            fileIdToPath(prevId, fileStorageDirectory=fileStorageDirectory)
        ]
    else:
        delQueue = []
    #
    box.icon_file_id = tId if tId is not None else ''
    box.icon_mime_type = tMT if tMT is not None else ''
    box.icon_file_id_username = (user.username
                                 if not accountDeletionInProgress
                                 else '')
    dbUpdateRecordOnTable(
        db,
        'boxes',
        box.asDict(),
        dbTablesDesc=dbSchema,
    )
    #
    if not skipCommit:
        db.commit()
    return delQueue
예제 #4
0
def updateUserThumbnail(
        db, targetUser, tId, tMT, user,
        fileStorageDirectory, skipCommit=False):
    """ Update the thumbnail info for a user.
        Return a list of zero or one full paths for deletion."""
    prevId = targetUser.icon_file_id
    if prevId != '':
        delQueue = [
            fileIdToPath(prevId, fileStorageDirectory=fileStorageDirectory)
        ]
    else:
        delQueue = []
    #
    targetUser.icon_file_id = tId if tId is not None else ''
    targetUser.icon_mime_type = tMT if tMT is not None else ''
    targetUser.icon_file_id_username = user.username
    dbUpdateRecordOnTable(
        db,
        'users',
        targetUser.asDict(),
        dbTablesDesc=dbSchema,
    )
    #
    if not skipCommit:
        db.commit()
    return delQueue
예제 #5
0
def deleteLink(
        db, parentBox, link, user, fileStorageDirectory,
        skipCommit=False, accountDeletionInProgress=False):
    """ Delete a link (either because permissions allow it
        or the caller is working as part of an account deletion,
        in which case the caller is responsible for lifting
        permission checks).
        Returns the filesystem delete queue.
    """
    if (accountDeletionInProgress or
            userHasPermission(db, user, parentBox.permissions, 'w')):
        #
        fsDeleteQueue = (
            [
                fileIdToPath(
                    link.icon_file_id,
                    fileStorageDirectory=fileStorageDirectory,
                )
            ]
            if link.icon_file_id != '' else []
        )
        dbDeleteRecordsByKey(
            db,
            'links',
            {'link_id': link.link_id},
            dbTablesDesc=dbSchema,
        )
        if not skipCommit:
            db.commit()
        return fsDeleteQueue
    else:
        raise OstracionError('User has no write permission')
예제 #6
0
def makeTempFileIntoThumbnail(tmpFileFullPath, thumbnailFormat,
                              fileStorageDirectory):
    """Given a temporarily saved file, prepare and store a thumbnail."""
    mimeType = determineFileProperties(tmpFileFullPath).get('file_mime_type')
    resizeMethod = imageMimeTypeToResizeMethodMap.get(mimeType)
    if resizeMethod == 'resize':
        newId = uuid4().hex
        if resizeToThumbnail(
                tmpFileFullPath,
                fileIdToPath(newId, fileStorageDirectory=fileStorageDirectory),
                thumbnailFormat=thumbnailFormat,
        ):
            return newId, mimeType
    elif resizeMethod == 'copy':
        newId = uuid4().hex
        shutil.copy(
            tmpFileFullPath,
            fileIdToPath(newId, fileStorageDirectory=fileStorageDirectory),
        )
        return newId, mimeType
    else:
        # if mime type not listed, fail and that's it
        return None, None
예제 #7
0
def _recursiveBoxDeletion(
        db, box, parentBox, user,
        fileStorageDirectory, accountDeletionInProgress=False):
    """ Take care of deleting a box, invoking itself on children boxes
        and erasing contained files, joining the results (the delete queue)
        accumulated in the sub-deletions. Used internaly by deleteBox.
    """
    if box.box_id == '':
        raise OstracionError('Box cannot be deleted')
    else:
        if (accountDeletionInProgress or
                (
                    userHasPermission(db, user, box.permissions, 'w') and
                    userHasPermission(db, user, parentBox.permissions, 'c')
                )):
            fsDeleteQueue = ([fileIdToPath(
                box.icon_file_id,
                fileStorageDirectory=fileStorageDirectory,
            )]) if box.icon_file_id != '' else []
            allChildren = list(getBoxesFromParent(db, box, user))
            if any(c is None for c in allChildren):
                raise OstracionError('User is not allowed to delete box')
            #
            for c in allChildren:
                if c is not None:
                    fsDeleteQueue += _recursiveBoxDeletion(
                        db, c, box, user,
                        fileStorageDirectory=fileStorageDirectory,
                        accountDeletionInProgress=accountDeletionInProgress)
            allChildFiles = getFilesFromBox(db, box)
            for f in allChildFiles:
                fsDeleteQueue += deleteFile(
                    db, box, f, user,
                    fileStorageDirectory=fileStorageDirectory,
                    skipCommit=True,
                    accountDeletionInProgress=accountDeletionInProgress)
            # actual deletion
            dbDeleteRecordsByKey(
                db, 'box_role_permissions', {'box_id': box.box_id},
                dbTablesDesc=dbSchema)
            dbDeleteRecordsByKey(
                db, 'boxes', {'box_id': box.box_id},
                dbTablesDesc=dbSchema)
            return fsDeleteQueue
        else:
            raise OstracionError('User is not allowed to delete box')
예제 #8
0
def updateSettingThumbnail(db,
                           richSetting,
                           tId,
                           tMT,
                           user,
                           fileStorageDirectory,
                           skipCommit=False):
    """ Update the image in a setting of type Image. This returns
        the (actual) filesystem deletion queue after taking care of
        the DB part.
        The new thumbnail ID is passed to this function ('' to reset).
        richSetting['setting'] is the DB object, at top-level we
        have enrichment info (such as the default/value resolved etc)
    """
    prevId = richSetting['setting'].value
    if prevId != '':
        delQueue = [
            fileIdToPath(prevId, fileStorageDirectory=fileStorageDirectory)
        ]
    else:
        delQueue = []
    #
    newSettingDict = recursivelyMergeDictionaries(
        {
            'value': tId if tId is not None else '',
            'icon_mime_type': tMT if tMT is not None else '',
        },
        defaultMap={
            k: v
            for k, v in richSetting['setting'].asDict().items()
            if k in {'value', 'icon_mime_type', 'group_id', 'id'}
        },
    )
    dbUpdateRecordOnTable(
        db,
        'settings',
        newSettingDict,
        dbTablesDesc=dbSchema,
        allowPartial=True,
    )
    #
    if not skipCommit:
        db.commit()
    return delQueue
예제 #9
0
def calendarMakerGenerateCalendar():
    """
        Calendar actual generation view.
        Handles everything:
            building temporary image files
            driving the engine functions to make the pdf
            inserting the pdf in the box
            removing temporary files
    """
    user = g.user
    db = dbGetDatabase()
    request._onErrorUrl = url_for('calendarMakerIndexView', )
    #
    destBoxString = request.cookies.get('apps_calendarmaker_destbox')
    currentCalendar = cookiesToCurrentCalendar(request.cookies)
    coverImagePathString = currentCalendar.get('cover_image_path_string')
    calendarImagePaths = currentCalendar.get('image_path_strings', [])
    cProps = currentCalendar.get('properties', {})
    #
    if destBoxString is None:
        destBox = None
    else:
        destBoxPath = splitPathString(destBoxString)
        destBox = getBoxFromPath(db, destBoxPath, user)
    if coverImagePathString is None:
        coverImageFileObject = None
    else:
        coverImageFileObject = pathToFileStructure(
            db,
            user,
            coverImagePathString,
        )
    #
    calendarImages = [
        pathToFileStructure(db, user, imgPath)
        for imgPath in calendarImagePaths
    ]
    numRequiredImages = countMonths(
        cProps.get('year0'),
        cProps.get('month0'),
        cProps.get('year1'),
        cProps.get('month1'),
    )
    if (destBox is None or coverImageFileObject is None
            or any([ci is None
                    for ci in calendarImages]) or numRequiredImages is None
            or numRequiredImages > len(calendarImages)):
        raise OstracionError('Cannot generate calendar')
    else:
        fileStorageDirectory = g.settings['system']['system_directories'][
            'fs_directory']['value']
        # proceed with generation
        tempFileDirectory = g.settings['system']['system_directories'][
            'temp_directory']['value']
        mkDirP(tempFileDirectory)
        texImageCoverPath = duplicateImageForCalendar(
            fileIdToPath(
                coverImageFileObject['file'].file_id,
                fileStorageDirectory=fileStorageDirectory,
            ),
            os.path.join(
                tempFileDirectory, '%s.%s' % (
                    uuid4().hex,
                    admittedImageMimeTypeToExtension[
                        coverImageFileObject['file'].mime_type],
                )),
        )
        texImagePaths = [
            duplicateImageForCalendar(
                fileIdToPath(
                    imageFile['file'].file_id,
                    fileStorageDirectory=fileStorageDirectory,
                ),
                os.path.join(
                    tempFileDirectory, '%s.%s' % (
                        uuid4().hex,
                        admittedImageMimeTypeToExtension[
                            imageFile['file'].mime_type],
                    )),
            ) for imageFile in calendarImages
        ]
        #
        fsDeletionQueue = [texImageCoverPath] + texImagePaths
        createdPdfTitle = uuid4().hex
        createdFile, creationToDelete = makeCalendarPdf(
            cProps,
            texImagePaths,
            texImageCoverPath,
            tempFileDirectory,
            createdPdfTitle,
        )
        #
        if createdFile is not None:
            # name and description
            calDescription = 'Calendar %i/%i - %i/%i' % (
                cProps['month0'],
                cProps['year0'],
                cProps['month1'],
                cProps['year1'],
            )
            calFileName = findFirstAvailableObjectNameInBox(
                db,
                destBox,
                'calendar_',
                '.pdf',
            )
            # place the pdf in the box
            placeFSFileInBox(
                db,
                user,
                fileStorageDirectory,
                destBox,
                createdFile,
                calFileName,
                calDescription,
            )
            # flushing the delete queue
            flushFsDeleteQueue(fsDeletionQueue + creationToDelete +
                               [createdFile])
            # messaging the user
            successMessage = ('Calendar generated. Please find the file '
                              '"%s" in the destination box.') % calFileName
            flashMessage('Success', 'Info', successMessage)
            # redirecting user to box
            return redirect(url_for('lsView', lsPathString=destBoxString))
        else:
            # flushing the delete queue
            flushFsDeleteQueue(
                fsDeletionQueue + creationToDelete + [createdFile], )
            # messaging the user
            flashMessage('Error', 'Error', 'Could not generate the calendar')
            return redirect(url_for('calendarMakerIndexView'))
예제 #10
0
def placeFSFileInBox(db,
                     user,
                     fileStorageDirectory,
                     box,
                     filePhysicalPath,
                     fileName,
                     fileDescription,
                     fileTextualMode='',
                     fileThumbnailFormat=None):
    """
        Given an actual file at a physical location,
        insert it as a Ostracion file by handling the
        filesystem DB as well as the actual file copy
        into the storage directories.
    """
    # can user write to box?
    if userHasPermission(db, user, box.permissions, 'w'):
        # is the name available?
        if not isNameUnderParentBox(db, box, fileName):
            protoFile = {
                'box_id': box.box_id,
                'name': fileName,
                'description': fileDescription,
                'date': datetime.datetime.now(),
            }
            userName = user.username
            newFile = File(**recursivelyMergeDictionaries(
                protoFile,
                defaultMap={
                    'creator_username': userName,
                    'icon_file_id_username': userName,
                    'metadata_username': userName,
                    'editor_username': userName,
                    'textual_mode': fileTextualMode,
                },
            ))
            filePath = fileIdToPath(
                newFile.file_id,
                fileStorageDirectory=fileStorageDirectory,
            )
            shutil.copy2(filePhysicalPath, filePath)
            #
            fileProperties = determineFileProperties(filePath)
            newFile.mime_type = fileProperties['file_mime_type']
            newFile.type = fileProperties['file_type']
            newFile.size = fileProperties['file_size']
            #
            if (fileThumbnailFormat is not None
                    and isImageMimeType(newFile.mime_type)):
                # thumbnail preparation
                fileThumbnailId, fileThumbnailMimeType = makeFileThumbnail(
                    newFile.file_id,
                    newFile.mime_type,
                    thumbnailFormat=fileThumbnailFormat,
                    fileStorageDirectory=fileStorageDirectory,
                )
                if fileThumbnailId is not None:
                    newFile.icon_file_id = fileThumbnailId
                    newFile.icon_mime_type = fileThumbnailMimeType
            #
            makeFileInParent(db, parentBox=box, newFile=newFile)
            db.commit()
        else:
            raise OstracionError('Cannot create a file with that name')
    else:
        raise OstracionError('User has no write permission on box')
예제 #11
0
def saveAndAnalyseFilesInBox(db,
                             files,
                             parentBox,
                             user,
                             thumbnailFormat,
                             fileStorageDirectory,
                             pastActionVerbForm='uploaded'):
    """ Save files and enrich them with type/mimetype,
        unless something fails - this handles overwrites
        (file-on-file) and blockades (file has same name as box)
    """
    #
    # checking for name clashes with boxes
    if any(
            isBoxNameUnderParentBox(db, parentBox, fName)
            for fName in (fObj['name'] for fObj in files)):
        raise OstracionError('Files cannot have the name of existing boxes')
    else:
        if not userHasPermission(db, user, parentBox.permissions, 'w'):
            raise OstracionError('User has no write permission on this box')
        else:
            userName = user.username
            fsDeletionQueue = []
            numReplacements = 0
            #
            for file in files:
                newFile = File(**recursivelyMergeDictionaries(
                    {k: v
                     for k, v in file.items() if k != 'fileObject'},
                    defaultMap={
                        'creator_username': userName,
                        'icon_file_id_username': userName,
                        'metadata_username': userName,
                        'editor_username': userName,
                        'textual_mode': 'plain',
                    },
                ))
                # are we overwriting a file?
                if isFileNameUnderParentBox(db, parentBox, newFile.name):
                    # delete the old file entry
                    # AND mark the file and its thumbnail
                    # (if any) for later deletion
                    prevFile = getFileFromParent(
                        db,
                        parentBox,
                        newFile.name,
                        user,
                    )
                    fsDeletionQueue += deleteFile(
                        db,
                        parentBox,
                        prevFile,
                        user,
                        fileStorageDirectory=fileStorageDirectory,
                        skipCommit=True,
                    )
                    numReplacements += 1
                #
                filePath = fileIdToPath(
                    newFile.file_id,
                    fileStorageDirectory=fileStorageDirectory,
                )
                file['fileObject'].save(filePath)
                #
                fileProperties = determineFileProperties(filePath)
                newFile.mime_type = fileProperties['file_mime_type']
                newFile.type = fileProperties['file_type']
                newFile.size = fileProperties['file_size']
                #
                if (thumbnailFormat is not None
                        and isImageMimeType(newFile.mime_type)):
                    # thumbnail preparation
                    fileThumbnailId, fileThumbnailMimeType = makeFileThumbnail(
                        newFile.file_id,
                        newFile.mime_type,
                        thumbnailFormat=thumbnailFormat,
                        fileStorageDirectory=fileStorageDirectory,
                    )
                    if fileThumbnailId is not None:
                        newFile.icon_file_id = fileThumbnailId
                        newFile.icon_mime_type = fileThumbnailMimeType
                #
                makeFileInParent(db, parentBox=parentBox, newFile=newFile)
            flushFsDeleteQueue(fsDeletionQueue)
            db.commit()
            return '%i file%s %s successfully%s.' % (
                len(files), '' if len(files) == 1 else 's', pastActionVerbForm,
                '' if numReplacements == 0 else
                (' (%i replaced)' % numReplacements))
예제 #12
0
def editTextFileView(fsPathString=''):
    """Edit-text-file route."""
    user = g.user
    db = dbGetDatabase()
    lsPath = splitPathString(fsPathString)
    boxPath, fileName = lsPath[:-1], lsPath[-1]
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    parentBox = getBoxFromPath(db, boxPath, user)
    file = getFileFromParent(db, parentBox, fileName, user)
    if file is None:
        raise OstracionError('File not found.')
    else:
        fileActions = prepareFileActions(
            db,
            file,
            boxPath[1:] + [file.name],
            parentBox,
            user,
            discardedActions={'text_edit'},
        )
        fileInfo = prepareFileInfo(db, file)
        form = EditTextFileForm()
        form.textformat.choices = [
            (mId, mDesc['title'])
            for mId, mDesc in sorted(
                textFileViewingModes.items(),
                key=lambda kv: kv[1]['index'],
            )
        ]
        ##
        if form.validate_on_submit():
            newcontents = form.filecontents.data
            filePath = fileIdToPath(
                file.file_id,
                fileStorageDirectory=fileStorageDirectory,
            )
            with open(filePath, 'w') as openFile:
                openFile.write('%s' % newcontents)
            # file properties
            fileProperties = determineFileProperties(filePath)
            newFile = File(**file.asDict())
            newFile.mime_type = fileProperties['file_mime_type']
            newFile.type = fileProperties['file_type']
            newFile.size = fileProperties['file_size']
            newFile.textual_mode = form.textformat.data
            newFile.editor_username = user.username
            updateFile(db, boxPath, file.name, newFile, user)
            #
            flashMessage('Info', 'Info', 'File "%s" saved.' % file.name)
            return redirect(
                url_for(
                    'lsView',
                    lsPathString='/'.join(boxPath[1:]),
                )
            )
        else:
            form.filecontents.data = applyDefault(
                form.filecontents.data,
                open(
                    fileIdToPath(
                        file.file_id,
                        fileStorageDirectory=fileStorageDirectory,
                    )
                ).read(),
            )
            form.textformat.data = applyDefault(
                form.textformat.data,
                file.textual_mode,
                additionalNulls=['None'],
            )
            pathBCrumbs = makeBreadCrumbs(
                boxPath,
                g,
                appendedItems=[
                    {
                        'kind': 'file',
                        'target': file,
                    },
                    {
                        'kind': 'link',
                        'target': None,
                        'name': 'Edit text',
                    }
                ],
            )
            return render_template(
                'edittextfile.html',
                user=user,
                form=form,
                fileActions=fileActions,
                fileInfo=fileInfo,
                pageTitle='"%s" file edit' % file.name,
                pageSubtitle='Click "Save" to commit the changes',
                breadCrumbs=pathBCrumbs,
            )
예제 #13
0
def dbDeleteUser(db, username, user, fileStorageDirectory):
    """ Delete a user altogether. Return a fsDeletionQueue for deletions."""
    if username != '':
        if not userIsAdmin(db, dbGetUser(db, username)):
            try:
                if username == user.username or userIsAdmin(db, user):
                    fsDeleteQueue = []
                    # 1. cleanup of file system
                    rootBox = getRootBox(db)
                    fsDeleteQueue += _traverseForAccountDeletion(
                        db,
                        rootBox,
                        username,
                        user,
                        path=[''],
                        fileStorageDirectory=fileStorageDirectory,
                    )
                    # 2. deleting user-related data around
                    dbDeleteRecordsByKey(
                        db,
                        'user_roles',
                        {'username': username},
                        dbTablesDesc=dbSchema,
                    )
                    deleteeUser = dbGetUser(db, username)
                    if deleteeUser.icon_file_id != '':
                        fsDeleteQueue.append(fileIdToPath(
                            deleteeUser.icon_file_id,
                            fileStorageDirectory=fileStorageDirectory,
                        ))
                    dbDeleteRecordsByKey(
                        db,
                        'tickets',
                        {'username': username},
                        dbTablesDesc=dbSchema,
                    )
                    for u in dbGetAllUsers(db, user,
                                           accountDeletionInProgress=True):
                        if u.username != username:
                            if u.icon_file_id_username == username:
                                if u.icon_file_id != '':
                                    fsDeleteQueue.append(fileIdToPath(
                                        u.icon_file_id,
                                        fileStorageDirectory,
                                    ))
                                dbUpdateUser(
                                    db,
                                    User(**recursivelyMergeDictionaries(
                                        {
                                            'icon_file_id': '',
                                            'icon_file_id_username': '',
                                        },
                                        defaultMap=u.asDict(),
                                    )),
                                    user,
                                    skipCommit=True,
                                )
                    # 3. delete user-specific role association from boxes
                    dbDeleteRecordsByKey(
                        db,
                        'box_role_permissions',
                        {'role_class': 'user', 'role_id': username},
                        dbTablesDesc=dbSchema,
                    )
                    # 4. delete user-specific role
                    dbDeleteRecordsByKey(
                        db,
                        'roles',
                        {'role_class': 'user', 'role_id': username},
                        dbTablesDesc=dbSchema
                    )
                    # 5. finally, delete the user
                    dbDeleteRecordsByKey(
                        db,
                        'users',
                        {'username': username},
                        dbTablesDesc=dbSchema
                    )
                    db.commit()
                    return fsDeleteQueue
                else:
                    raise OstracionError('Insufficient permissions')
            except Exception as e:
                db.rollback()
                raise e
        else:
            raise OstracionError('Cannot delete an admin')
    else:
        raise OstracionError('Cannot alter system user')