示例#1
0
def fsDoMoveFileView(quotedFilePath, quotedDestBox=''):
    """Move-file, view 2/2: dest box is selected, actually move."""
    user = g.user
    db = dbGetDatabase()
    # we retrieve the source file and the destination box
    srcFsPathString = urllib.parse.unquote_plus(quotedFilePath)
    srcLsPath = splitPathString(srcFsPathString)
    srcBoxPath, fileName = srcLsPath[:-1], srcLsPath[-1]
    srcBox = getBoxFromPath(db, srcBoxPath, user)
    file = getFileFromParent(db, srcBox, fileName, user)
    dstBoxPathString = urllib.parse.unquote_plus(quotedDestBox)
    dstBoxPath = splitPathString(dstBoxPathString)
    dstBox = getBoxFromPath(db, dstBoxPath, user)
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    #
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(srcBoxPath[1:]),
    )
    messages = moveFile(
        db,
        file,
        srcBox,
        dstBox,
        user,
        fileStorageDirectory=fileStorageDirectory,
    )
    for msg in messages:
        flashMessage('Info', 'Info', msg)
    return redirect(url_for(
        'lsView',
        lsPathString='/'.join(dstBoxPath[1:]),
    ))
示例#2
0
def fsDoMoveBoxView(quotedSrcBox, quotedDestBox=''):
    """
        Move-box-phase-2 route: where, with source and dest
        boxes as quoted-plus strings, the actual moving
        is performed.
    """
    user = g.user
    db = dbGetDatabase()
    # we retrieve the source box, its current parent
    # and the destination box
    srcBoxPathString = urllib.parse.unquote_plus(quotedSrcBox)
    srcBoxPath = splitPathString(srcBoxPathString)
    box = getBoxFromPath(db, srcBoxPath, user)
    srcBox = getBoxFromPath(db, srcBoxPath[:-1], user)
    dstBoxPathString = urllib.parse.unquote_plus(quotedDestBox)
    dstBoxPath = splitPathString(dstBoxPathString)
    dstBox = getBoxFromPath(db, dstBoxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(srcBoxPath[1:-1]),
    )
    # we attempt the move
    moveBox(
        db,
        box,
        srcBox,
        dstBox,
        user,
    )
    return redirect(url_for(
        'lsView',
        lsPathString='/'.join(dstBoxPath[1:]),
    ))
示例#3
0
def ticketGalleryFsView(ticketId, securityCode, fileName):
    """
        View-file-within-a-ticket-generated-gallery-view route.

        Helper endpoint to return viewable (only viewables,
        there's no 'download') files in a ticket-gallery view.
        Must take care of punching the ticket.
    """
    user = g.user
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    #
    richTicket = dbGetEnrichAndCheckTicket(
        db,
        'g',
        ticketId,
        securityCode,
        request.url_root,
    )
    if richTicket is not None:
        issuer = dbGetUser(db, richTicket['ticket'].username)
        if richTicket['redeemable']:
            # valid ticket. Further checks are on the way.
            if (not g.settings['behaviour']['behaviour_tickets']
                ['protect_banned_user_tickets']['value']
                    or issuer.banned == 0):
                #
                boxPath = richTicket['metadata']['box_path']
                request._onErrorUrl = url_for(
                    'lsView',
                    lsPathString='/'.join(boxPath[1:]),
                )
                parentBox = getBoxFromPath(db, boxPath[1:], issuer)
                if parentBox is not None:
                    # we retrieve the file and serve it
                    file = getFileFromParent(db, parentBox, fileName, issuer)
                    if file is not None:
                        dbPunchRichTicket(db, richTicket)
                        filePhysicalPath, filePhysicalName = fileIdToSplitPath(
                            file.file_id,
                            fileStorageDirectory=fileStorageDirectory,
                        )
                        return send_from_directory(
                            filePhysicalPath,
                            filePhysicalName,
                            attachment_filename=file.name,
                            as_attachment=True,
                            mimetype=file.mime_type,
                        )
                    else:
                        return abort(404, 'Content unavailable')
                else:
                    return abort(404, 'Content unavailable')
            else:
                return abort(404, 'Content unavailable')
        else:
            return abort(404, 'Content unavailable')
    else:
        return abort(404, 'Content unavailable')
示例#4
0
def boxThumbnailView(dummyId, boxPathString=''):
    """Route for access to thumbnail image files based on box path."""
    user = g.user
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    if boxPathString == '':
        # root case
        return redirect(makeSettingImageUrl(g, 'app_images', 'root_box'))
    else:
        db = dbGetDatabase()
        boxPath = splitPathString(boxPathString)
        request._onErrorUrl = url_for(
            'lsView',
            lsPathString='/'.join(boxPath[1:]),
        )
        box = getBoxFromPath(db, boxPath, user)
        if (box is not None and box.icon_file_id is not None
                and box.icon_file_id != ''):
            filePhysicalPath, filePhysicalName = fileIdToSplitPath(
                box.icon_file_id,
                fileStorageDirectory=fileStorageDirectory,
            )
            return send_from_directory(
                filePhysicalPath,
                filePhysicalName,
                mimetype=box.icon_mime_type,
            )
        else:
            return redirect(
                makeSettingImageUrl(
                    g,
                    'app_images',
                    'standard_box',
                ))
示例#5
0
def fsDownloadView(fsPathString=''):
    """Download-file view."""
    user = g.user
    lsPath = splitPathString(fsPathString)
    boxPath, fileName = lsPath[:-1], lsPath[-1]
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    parentBox = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    if parentBox is not None:
        file = getFileFromParent(db, parentBox, fileName, user)
        if file is not None:
            filePhysicalPath, filePhysicalName = fileIdToSplitPath(
                file.file_id,
                fileStorageDirectory=fileStorageDirectory,
            )
            return send_from_directory(
                filePhysicalPath,
                filePhysicalName,
                attachment_filename=file.name,
                as_attachment=True,
                mimetype=file.mime_type,
            )
        else:
            return abort(404, 'Content unavailable')
    else:
        return abort(404, 'Content unavailable')
示例#6
0
def fsDeleteFileView(fsPathString=''):
    """ Delete-file view.
        Permission checks are performed by the deleteFile filesystem call.
    """
    user = g.user
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    lsPath = splitPathString(fsPathString)
    boxPath, fileName = lsPath[:-1], lsPath[-1]
    parentBox = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    file = getFileFromParent(db, parentBox, fileName, user)
    fsDeleteQueue = deleteFile(
        db,
        parentBox,
        file,
        user,
        fileStorageDirectory=fileStorageDirectory,
    )
    flushFsDeleteQueue(fsDeleteQueue)
    return redirect(url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    ))
示例#7
0
def fsMetadataView(fsPathString=''):
    """Edit-file-metadata view, a web-form."""
    user = g.user
    db = dbGetDatabase()
    form = FileDataForm()
    lsPath = splitPathString(fsPathString)
    boxPath, prevFileName = lsPath[:-1], lsPath[-1]
    parentBox = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    file = getFileFromParent(db, parentBox, prevFileName, user)
    if form.validate_on_submit():
        newFile = File(**recursivelyMergeDictionaries(
            {
                'name': form.filename.data,
                'description': form.filedescription.data,
                'metadata_username': user.username,
            },
            defaultMap=file.asDict(),
        ))
        updateFile(db, boxPath, prevFileName, newFile, user)
        return redirect(url_for('lsView', lsPathString='/'.join(boxPath[1:])))
    else:
        form.filename.data = applyDefault(form.filename.data, file.name)
        form.filedescription.data = applyDefault(form.filedescription.data,
                                                 file.description)
        #
        pageFeatures = {
            'breadCrumbs':
            makeBreadCrumbs(
                boxPath,
                g,
                appendedItems=[{
                    'kind': 'file',
                    'target': file,
                }, {
                    'kind': 'link',
                    'target': None,
                    'name': 'Metadata',
                }],
            ),
            'pageTitle':
            'Edit file metadata (%s)' % file.name,
            'pageSubtitle':
            'Name is mandatory',
            'iconUrl':
            url_for(
                'fileThumbnailView',
                dummyId=file.icon_file_id + '_',
                fsPathString='/'.join(lsPath[1:]),
            ),
        }
        return render_template(
            'filedataedit.html',
            form=form,
            user=user,
            **pageFeatures,
        )
示例#8
0
def fileThumbnailView(dummyId, fsPathString):
    """Route for access to thumbnail image files based on file path."""
    user = g.user
    lsPath = splitPathString(fsPathString)
    boxPath, fileName = lsPath[:-1], lsPath[-1]
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    parentBox = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    file = getFileFromParent(db, parentBox, fileName, user)
    if (file is not None and file.icon_file_id is not None
            and file.icon_file_id != ''):
        filePhysicalPath, filePhysicalName = fileIdToSplitPath(
            file.icon_file_id,
            fileStorageDirectory=fileStorageDirectory,
        )
        return send_from_directory(
            filePhysicalPath,
            filePhysicalName,
            mimetype=file.icon_mime_type,
        )
    else:
        return redirect(pickFileThumbnail(file.mime_type))
示例#9
0
def linkThumbnailView(dummyId, fsPathString=''):
    """Route for access to thumbnail image files based on link path."""
    user = g.user
    lsPath = splitPathString(fsPathString)
    boxPath, linkName = lsPath[:-1], lsPath[-1]
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    parentBox = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    link = getLinkFromParent(db, parentBox, linkName, user)
    if (link is not None and link.icon_file_id is not None
            and link.icon_file_id != ''):
        filePhysicalPath, filePhysicalName = fileIdToSplitPath(
            link.icon_file_id,
            fileStorageDirectory=fileStorageDirectory,
        )
        return send_from_directory(
            filePhysicalPath,
            filePhysicalName,
            mimetype=link.icon_mime_type,
        )
    else:
        return redirect(makeSettingImageUrl(g, 'app_images', 'external_link'))
示例#10
0
def ticketFsDownloadView(ticketId, securityCode):
    """ Give-the-file-contents-based-on-ticket route.

        Helper endpoint to load-and-return a file upon a ticket;
        access to files based on a ticket.

        Used by both the direct-file-download or the view-file
        file-ticket modes.

        Note: punching occurs here.
    """
    user = g.user
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    #
    richTicket = dbGetEnrichAndCheckTicket(
        db,
        'f',
        ticketId,
        securityCode,
        request.url_root,
    )
    issuer = (dbGetUser(db, richTicket['ticket'].username)
              if richTicket is not None else None)
    noBandUsrTickets = g.settings['behaviour']['behaviour_tickets'][
        'protect_banned_user_tickets']['value']
    if (issuer is not None and (not noBandUsrTickets or issuer.banned == 0)):
        boxPath, fileName = (
            richTicket['metadata']['path'][:-1],
            richTicket['metadata']['path'][-1],
        )
        parentBox = getBoxFromPath(db, boxPath, issuer)
        #
        if parentBox is not None:
            file = getFileFromParent(db, parentBox, fileName, issuer)
            if file is not None:
                # return it and contextually punch the ticket
                dbPunchRichTicket(db, richTicket)
                # then we return the file as a download
                # (this flow assumes download is desired as opposed to view)
                filePhysicalPath, filePhysicalName = fileIdToSplitPath(
                    file.file_id,
                    fileStorageDirectory=fileStorageDirectory,
                )
                return send_from_directory(
                    filePhysicalPath,
                    filePhysicalName,
                    attachment_filename=file.name,
                    as_attachment=True,
                    mimetype=file.mime_type,
                )
            else:
                return abort(404, 'Content unavailable')
        else:
            return abort(404, 'Content unavailable')
    else:
        return abort(404, 'Content unavailable')
示例#11
0
def makeLinkView(fsPathString=''):
    """Generate-new-link route."""
    user = g.user
    form = EditLinkForm()
    db = dbGetDatabase()
    boxPath = splitPathString(fsPathString)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    parentBoxPath = boxPath
    parentBox = getBoxFromPath(db, parentBoxPath, user)
    if form.validate_on_submit():
        linkName = secure_filename(form.linkname.data)
        linkTitle = form.linktitle.data
        linkDescription = form.linkdescription.data
        linkTarget = form.linktarget.data
        openInNewWindow = form.openinnewwindow.data
        savingResult = makeLinkInParent(
            db=db,
            user=user,
            parentBox=parentBox,
            date=datetime.now(),
            linkName=linkName,
            linkTitle=linkTitle,
            linkDescription=linkDescription,
            linkTarget=linkTarget,
            linkOptions={
                'open_in_new_window': openInNewWindow,
            },
        )
        return redirect(url_for(
            'lsView',
            lsPathString=fsPathString,
        ))
    else:
        pathBCrumbs = makeBreadCrumbs(
            parentBoxPath,
            g,
            appendedItems=[{
                'kind': 'link',
                'target': None,
                'name': 'New link',
            }],
        )
        form.openinnewwindow.data = True
        return render_template(
            'editlink.html',
            form=form,
            user=user,
            breadCrumbs=pathBCrumbs,
            iconUrl=makeSettingImageUrl(g, 'app_images', 'external_link'),
            pageTitle='New link',
            pageSubtitle='Create a new link in "%s"' %
            (describeBoxTitle(parentBox)),
        )
示例#12
0
def deleteBoxView(boxPathString=''):
    """RMBOX route."""
    user = g.user
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    boxPath = splitPathString(boxPathString)
    box = getBoxFromPath(db, boxPath, user)
    parentBox = getBoxFromPath(db, boxPath[:-1], user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:-1]),
    )
    #
    canDelete = canDeleteBox(db, box, parentBox, user)
    #
    if canDelete:
        if parentBox is None:
            # deletee box was root
            raise OstracionError('Cannot act on this object')
        else:
            if box is not None:
                fsDeleteQueue = deleteBox(
                    db,
                    box,
                    parentBox,
                    user,
                    fileStorageDirectory=fileStorageDirectory,
                )
                flushFsDeleteQueue(fsDeleteQueue)
            else:
                raise OstracionError('Box not accessible')
    else:
        raise OstracionError('Cannot delete this box')
    return redirect(url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:-1]),
    ))
示例#13
0
def fsHybridView(fsPathString=''):
    """ "Hybrid" file view: if it is a textually-viewable file
        (such as plaintext but most importantly markdown), show it as 'view';
        if it is anything else (especially an image), return it as 'download':
        this is to make sure embedding of images in markdown documents works
        as usual.
    """
    user = g.user
    lsPath = splitPathString(fsPathString)
    boxPath, fileName = lsPath[:-1], lsPath[-1]
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    parentBox = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    file = getFileFromParent(db, parentBox, fileName, user)
    isTextual = isFileTextViewable(file)
    if isTextual:
        return fsView(fsPathString=fsPathString)
    else:
        return fsDownloadView(fsPathString=fsPathString)
示例#14
0
def fsGalleryView(fsPathString=''):
    """ Gallery view route on a box.
        If just box, redirect to first file.
        If a file, use the ls of files to prepare prev/next links.

        That is to say, if user has read permission on box, gallery URLs are
        not index-based, rather filename-based and recalculated on actual 'ls'.
    """
    isTopAccess = 1 if safeInt(request.args.get('top'), 0) != 0 else 0
    user = g.user
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    db = dbGetDatabase()
    lsPath = splitPathString(fsPathString)
    # is it a box or a file?
    targetBox = getBoxFromPath(db, lsPath, user)
    if targetBox is not None:
        # if there are files, we redirect to the first one
        # otherwise, a message and falling back to ls view of the box
        files = sorted(
            getFilesFromBox(db, targetBox),
            key=lambda f: (f.name.lower(), f.name),
        )
        if len(files) > 0:
            return redirect(
                url_for(
                    'fsGalleryView',
                    fsPathString='/'.join(lsPath[1:] + [files[0].name]),
                    top=str(isTopAccess),
                ))
        else:
            flashMessage(
                'Info',
                'Empty box',
                'Cannot view as gallery a box without files',
            )
            return redirect(
                url_for(
                    'lsView',
                    lsPathString='/'.join(lsPath[1:]),
                ))
    else:
        # is it a file?
        boxPath, fileName = lsPath[:-1], lsPath[-1]
        parentBox = getBoxFromPath(db, boxPath, user)
        file = getFileFromParent(db, parentBox, fileName, user)
        if file is not None:
            if isTopAccess:
                # gallery navigation calculations
                files = sorted(
                    getFilesFromBox(db, parentBox),
                    key=lambda f: (f.name.lower(), f.name),
                )
                thisFileIndex = [
                    idx for idx, fil in enumerate(files)
                    if fil.name == fileName
                ][0]
                numGalleryFiles = len(files)
                nextFileIndex = (thisFileIndex + 1) % numGalleryFiles
                prevFileIndex = (thisFileIndex - 1 +
                                 numGalleryFiles) % numGalleryFiles
                #
                pathBCrumbs = makeBreadCrumbs(
                    boxPath,
                    g,
                    appendedItems=[{
                        'kind':
                        'link',
                        'name':
                        'Gallery view %i/%i' % (
                            thisFileIndex + 1,
                            numGalleryFiles,
                        ),
                        'target':
                        None,
                    }],
                )
                fileContents = produceFileViewContents(
                    db,
                    file,
                    mode='fsview',
                    viewParameters={
                        'boxPath': boxPath,
                        'fileName': fileName,
                    },
                    fileStorageDirectory=fileStorageDirectory,
                )
                fileActions = {
                    'gallery_prev':
                    url_for(
                        'fsGalleryView',
                        fsPathString='/'.join(boxPath[1:] +
                                              [files[prevFileIndex].name]),
                        top=str(isTopAccess),
                    ),
                    'gallery_next':
                    url_for(
                        'fsGalleryView',
                        fsPathString='/'.join(boxPath[1:] +
                                              [files[nextFileIndex].name]),
                        top=str(isTopAccess),
                    ),
                    'gallery_up':
                    url_for(
                        'lsView',
                        lsPathString='/'.join(boxPath[1:]),
                    ),
                    'download':
                    url_for(
                        'fsDownloadView',
                        fsPathString=fsPathString,
                    ),
                }
                return render_template(
                    'fileview.html',
                    fileActions=fileActions,
                    fileInfo=None,
                    filecontents=fileContents,
                    breadCrumbs=pathBCrumbs,
                    user=user,
                    pageTitle='"%s" gallery, file "%s" (%i/%i)' % (
                        parentBox.title,
                        file.name,
                        thisFileIndex + 1,
                        numGalleryFiles,
                    ),
                    pageSubtitle=file.description,
                    downloadUrl=None,
                    hideBreadCrumbs=True,
                    hideNavbar=True,
                )
            else:
                return fsDownloadView(fsPathString=fsPathString)
        else:
            # extreme fallback to ls, which will hiearchically
            # deal with the retrieval
            return redirect(url_for('lsView', lsPathString=fsPathString))
示例#15
0
def fsMoveFileView(quotedFilePath):
    """Move-file, view 1/2: select destination box."""
    user = g.user
    db = dbGetDatabase()
    # first we find the file
    fsPathString = urllib.parse.unquote_plus(quotedFilePath)
    lsPath = splitPathString(fsPathString)
    boxPath, fileName = lsPath[:-1], lsPath[-1]
    parentBox = getBoxFromPath(db, boxPath, user)
    file = getFileFromParent(db, parentBox, fileName, user)
    # next we prepare the selectable destinations
    rootBox = getRootBox(db)

    def rbPredicate(richBox, idToAvoid=parentBox.box_id):
        return all((richBox['box'].box_id != idToAvoid,
                    userHasPermission(db, user, richBox['box'].permissions,
                                      'w')))

    dstBoxTree = collectTreeFromBox(
        db,
        rootBox,
        user,
        admitFiles=False,
        fileOrBoxEnricher=lambda richBox: {
            'obj_path': urllib.parse.quote_plus('/'.join(richBox['path'])),
        },
        predicate=rbPredicate,
    )
    #
    maxDepth = getMaxTreeDepth(dstBoxTree)
    colorShadeMap = prepareColorShadeMap(
        g.settings['color']['navigation_colors']['file']['value'],
        g.settings['color']['tree_shade_colors']['shade_treeview_pickbox']
        ['value'],
        numShades=1 + maxDepth,
    )
    destinationsExist = treeAny(
        dstBoxTree,
        property=lambda node: node['predicate'],
    )
    #
    return render_template(
        'dirtreeview.html',
        tree=dstBoxTree if destinationsExist else None,
        mode='file_move',
        object_quoted_path=quotedFilePath,
        colorShadeMap=colorShadeMap,
        user=user,
        iconUrl=makeSettingImageUrl(g, 'app_images', 'move'),
        pageTitle='Select file destination',
        pageSubtitle=(('Choose the box to which file "%s" shall '
                       'be moved from "%s"') % (
                           file.name,
                           describeBoxTitle(parentBox),
                       )),
        actions=None,
        backToUrl=None if destinationsExist else url_for(
            'lsView',
            lsPathString='/'.join(boxPath[1:]),
        ),
        breadCrumbs=makeBreadCrumbs(
            boxPath,
            g,
            appendedItems=[{
                'kind': 'file',
                'target': file,
            }, {
                'kind': 'link',
                'target': None,
                'name': 'Move file',
            }],
        ),
    )
示例#16
0
def fsMakeTicketView(fsPathString=''):
    """Create-file-ticket (file-read) view."""
    user = g.user
    lsPath = splitPathString(fsPathString)
    boxPath, fileName = lsPath[:-1], lsPath[-1]
    db = dbGetDatabase()
    parentBox = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    if parentBox is not None:
        file = getFileFromParent(db, parentBox, fileName, user)
        if file is not None:
            pathBCrumbs = makeBreadCrumbs(
                boxPath,
                g,
                appendedItems=[
                    {
                        'kind': 'file',
                        'target': file,
                    },
                    {
                        'name':
                        '(create ticket)',
                        'kind':
                        'link',
                        'target':
                        url_for(
                            'fsMakeTicketView',
                            fsPathString=fsPathString,
                        ),
                        'link':
                        False,
                    },
                ],
            )
            # if this succeeded, user has read permission on 'file'
            form = generateFsTicketForm(
                fileModePresent=True,
                settings=g.settings,
            )
            if form.validate_on_submit():
                magicLink = dbMakeFileTicket(
                    db=db,
                    ticketName=form.name.data,
                    validityHours=transformIfNotEmpty(
                        form.validityhours.data,
                        int,
                    ),
                    multiplicity=transformIfNotEmpty(
                        form.multiplicity.data,
                        int,
                    ),
                    ticketMessage=transformIfNotEmpty(
                        form.ticketmessage.data, ),
                    file=file,
                    fileMode=form.filemode.data,
                    lsPath=lsPath,
                    user=user,
                    urlRoot=request.url_root,
                    settings=g.settings,
                )
                flashMessage(
                    'Success',
                    'Done',
                    ('File ticket generated. Give the recipient '
                     'the following magic link:'),
                    pillText=magicLink,
                )
                return redirect(
                    url_for(
                        'lsView',
                        lsPathString='/'.join(boxPath[1:]),
                    ))
            else:
                form.name.data = applyDefault(form.name.data, file.name)
                form.ticketmessage.data = applyDefault(
                    form.ticketmessage.data,
                    'Please access this file',
                )
                form.filemode.data = applyDefault(
                    form.filemode.data,
                    'direct',
                    additionalNulls=['None'],
                )
                maxValidityHours = g.settings['behaviour'][
                    'behaviour_tickets']['max_ticket_validityhours']['value']
                form.validityhours.data = applyDefault(
                    form.validityhours.data,
                    (str(maxValidityHours)
                     if maxValidityHours is not None else ''),
                )
                maxMultiplicity = g.settings['behaviour']['behaviour_tickets'][
                    'max_ticket_multiplicity']['value']
                form.multiplicity.data = applyDefault(
                    form.multiplicity.data,
                    (str(maxMultiplicity)
                     if maxMultiplicity is not None else ''),
                )
                return render_template(
                    'fsticket.html',
                    pageTitle='Create public file ticket',
                    pageSubtitle=('Recipient(s) of the ticket will be able '
                                  'to access "%s" without an account.') %
                    (fsPathString, ),
                    baseMultiplicityCaption=('Number of granted accesses (bo'
                                             'th views and downloads count)'),
                    user=user,
                    form=form,
                    iconUrl=makeSettingImageUrl(
                        g,
                        'app_images',
                        'file_ticket',
                    ),
                    showFileMode=True,
                    breadCrumbs=pathBCrumbs,
                )
        else:
            return abort(404)
    else:
        return abort(404)
示例#17
0
def makeBoxView(parentBoxPathString=''):
    """MKBOX-in-a-box- route."""
    user = g.user
    form = makeMakeBoxForm('Create')()
    db = dbGetDatabase()
    parentBoxPath = splitPathString(parentBoxPathString)
    parentBox = getBoxFromPath(db, parentBoxPath, user)
    boxNiceName = describeBoxTitle(parentBox)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(parentBoxPath),
    )
    if form.validate_on_submit():
        #
        boxName = form.boxname.data
        boxDescription = form.boxdescription.data
        boxTitle = form.boxtitle.data
        #
        newBox = Box(
            box_name=boxName,
            title=boxTitle,
            description=boxDescription,
            icon_file_id='',
            nature='box',
            parent_id=parentBox.box_id,
            date=datetime.now(),
            creator_username=user.username,
            icon_file_id_username=user.username,
            metadata_username=user.username,
        )
        makeBoxInParent(db, parentBox, newBox, user, skipCommit=True)
        if parentBox.box_id == '':
            # the new box is a direct child of root:
            # act according to the localhost-mode policy setting
            rootChildAnonPerms = g.settings['behaviour'][
                'behaviour_permissions'][
                'rootchild_box_anonymous_permissions']['value']
            rcPermSet = {
                k: 1 if rootChildAnonPerms[i] != '_' else 0
                for i, k in enumerate('rwc')
            }
            # the above is a bit of a hack which parses the char
            # of the setting 'r__', 'rwc', ...
            rootChildBRP = BoxRolePermission(
                box_id=newBox.box_id,
                role_class='system',
                role_id='anonymous',
                **rcPermSet,
            )
            dbInsertBoxRolePermission(
                db,
                rootChildBRP,
                user,
                skipCommit=True,
            )
        #
        db.commit()
        additionalLsItems = [boxName]
        #
        return redirect(url_for(
            'lsView',
            lsPathString='/'.join(parentBoxPath[1:] + additionalLsItems),
        ))
    else:
        pathBCrumbs = makeBreadCrumbs(
            parentBoxPath,
            g,
            appendedItems=[{
                'kind': 'link',
                'target': None,
                'name': 'Make box',
            }],
        )
        return render_template(
            'makebox.html',
            form=form,
            user=user,
            breadCrumbs=pathBCrumbs,
            iconUrl=makeSettingImageUrl(g, 'app_images', 'standard_box'),
            pageTitle='Create box in "%s"' % boxNiceName,
            pageSubtitle='Name and title are mandatory',
        )
示例#18
0
def ticketGalleryView(ticketId, securityCode, page=0):
    """Actual route to view a gallery with a ticket."""
    user = g.user
    db = dbGetDatabase()
    #
    richTicket = dbGetEnrichAndCheckTicket(
        db,
        'g',
        ticketId,
        securityCode,
        request.url_root,
    )
    if richTicket is None:
        raise OstracionError('Invalid ticket magic link')
    else:
        issuer = dbGetUser(db, richTicket['ticket'].username)
        if richTicket['redeemable']:
            # valid ticket. Further checks are on the way.
            noBandUsrTickets = g.settings['behaviour']['behaviour_tickets'][
                'protect_banned_user_tickets']['value']
            if (not noBandUsrTickets or issuer.banned == 0):
                #
                boxPath = richTicket['metadata']['box_path']
                request._onErrorUrl = url_for(
                    'lsView',
                    lsPathString='/'.join(boxPath[1:]),
                )
                parentBox = getBoxFromPath(db, boxPath[1:], issuer)
                if parentBox is not None:
                    if parentBox.box_id == richTicket['metadata']['box_id']:
                        galleryMessage = richTicket['metadata'].get('message')
                        fileStorageDirectory = g.settings['system'][
                            'system_directories']['fs_directory']['value']
                        files = sorted(
                            getFilesFromBox(db, parentBox),
                            key=lambda f: (f.name.lower(), f.name),
                        )
                        numGalleryFiles = len(files)
                        if numGalleryFiles > 0:
                            thisFileIndex = page % numGalleryFiles
                            file = files[thisFileIndex]
                            # gallery navigation calculations
                            nextFileIndex = (thisFileIndex +
                                             1) % numGalleryFiles
                            prevFileIndex = (thisFileIndex - 1 +
                                             numGalleryFiles) % numGalleryFiles
                            #
                            fileContents = produceFileViewContents(
                                db,
                                file,
                                mode='galleryview',
                                viewParameters={
                                    'boxPath': boxPath,
                                    'fileName': file.name,
                                    'ticketId': ticketId,
                                    'securityCode': securityCode,
                                },
                                fileStorageDirectory=fileStorageDirectory,
                                urlRoot=request.url_root,
                                protectBannedUserTickets=noBandUsrTickets,
                            )
                            fileActions = {
                                'gallery_prev':
                                url_for(
                                    'ticketGalleryView',
                                    ticketId=ticketId,
                                    securityCode=securityCode,
                                    page=prevFileIndex,
                                ),
                                'gallery_next':
                                url_for(
                                    'ticketGalleryView',
                                    ticketId=ticketId,
                                    securityCode=securityCode,
                                    page=nextFileIndex,
                                ),
                                'homepage':
                                url_for('lsView', )
                            }
                            return render_template(
                                'fileview.html',
                                fileActions=fileActions,
                                fileInfo=None,
                                filecontents=fileContents,
                                breadCrumbs=None,
                                user=user,
                                pageTitle='Gallery view, file "%s" (%i/%i)' % (
                                    file.name,
                                    thisFileIndex + 1,
                                    numGalleryFiles,
                                ),
                                pageSubtitle=(None if galleryMessage is None
                                              else ('Message on ticket: "%s"' %
                                                    (galleryMessage, ))),
                                downloadUrl=None,
                                hideBreadCrumbs=True,
                                hideNavbar=True,
                            )
                            #
                            return fsView(fsPathString)
                        else:
                            flashMessage(
                                'Info',
                                'Empty box',
                                'Cannot view as gallery a box without files',
                            )
                            return redirect(url_for('lsView'))
                    else:
                        raise OstracionError('Ticket cannot be redeemed')
                else:
                    raise OstracionError('Invalid ticket magic link')
            else:
                raise OstracionError('Ticket cannot be redeemed')
        else:
            raise OstracionError('Invalid ticket magic link')
示例#19
0
def fsView(fsPathString=''):
    """View file route. If not viewable, display an error message."""
    user = g.user
    lsPath = splitPathString(fsPathString)
    boxPath, fileName = lsPath[:-1], lsPath[-1]
    db = dbGetDatabase()
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    parentBox = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    file = getFileFromParent(db, parentBox, fileName, user)
    if file is not None:
        fileInfo = prepareFileInfo(db, file)
        fileActions = prepareFileActions(
            db,
            file,
            boxPath[1:] + [file.name],
            parentBox,
            user,
            discardedActions={'view'},
        )
        fileContents = produceFileViewContents(
            db,
            file,
            mode='fsview',
            viewParameters={
                'boxPath': boxPath,
                'fileName': fileName,
            },
            fileStorageDirectory=fileStorageDirectory,
        )
        #
        pathBCrumbs = makeBreadCrumbs(
            boxPath,
            g,
            appendedItems=[{
                'kind': 'file',
                'target': file,
            }],
        )
        #
        return render_template(
            'fileview.html',
            fileActions=fileActions,
            fileInfo=fileInfo,
            filecontents=fileContents,
            breadCrumbs=pathBCrumbs,
            user=user,
            pageTitle='"%s" file view' % file.name,
            pageSubtitle='%s (%s; %s)' % (
                file.description,
                file.type,
                formatBytesSize(file.size),
            ),
            downloadUrl=None,
        )
    else:
        return abort(404)
示例#20
0
def makeTicketBoxGalleryView(boxPathString=''):
    """Make-gallery-ticket-of-box route."""
    user = g.user
    db = dbGetDatabase()
    boxPath = splitPathString(boxPathString)
    box = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for('lsView', lsPathString='/'.join(boxPath[1:]))
    #
    pathBCrumbs = makeBreadCrumbs(
        boxPath,
        g,
        appendedItems=[
            {
                'name': '(create gallery ticket)',
                'kind': 'link',
                'target': url_for(
                    'makeTicketBoxGalleryView',
                    boxPathString=boxPathString,
                ),
                'link': False,
            },
        ],
    )
    form = generateFsTicketForm(
        fileModePresent=False,
        settings=g.settings,
    )
    if form.validate_on_submit():
        magicLink = dbMakeGalleryTicket(
            db=db,
            ticketName=form.name.data,
            validityHours=transformIfNotEmpty(
                form.validityhours.data,
                int
            ),
            multiplicity=transformIfNotEmpty(
                form.multiplicity.data,
                int
            ),
            ticketMessage=transformIfNotEmpty(
                form.ticketmessage.data
            ),
            box=box,
            boxPath=boxPath,
            user=user,
            urlRoot=request.url_root,
            settings=g.settings,
        )
        flashMessage(
            'Success',
            'Done',
            ('Gallery ticket generated. Give the recipient '
             'the following magic link:'),
            pillText=magicLink,
        )
        return redirect(url_for(
            'lsView',
            lsPathString='/'.join(boxPath[1:]),
        ))
    else:
        defaultName = 'Gallery-View-%s' % describeBoxName(box)
        form.name.data = applyDefault(form.name.data, defaultName)
        form.ticketmessage.data = applyDefault(
            form.ticketmessage.data,
            'Please, look at this gallery',
        )
        maxValidityHours = g.settings['behaviour']['behaviour_tickets'][
            'max_ticket_validityhours']['value']
        form.validityhours.data = applyDefault(
            form.validityhours.data,
            str(maxValidityHours) if maxValidityHours is not None else '',
        )
        maxMultiplicity = g.settings['behaviour']['behaviour_tickets'][
            'max_ticket_multiplicity']['value']
        form.multiplicity.data = applyDefault(
            form.multiplicity.data,
            str(maxMultiplicity) if maxMultiplicity is not None else '',
        )
        return render_template(
            'fsticket.html',
            pageTitle='Create public gallery-view ticket',
            pageSubtitle=('Recipient(s) of the ticket will be able to view '
                          'files (and not contained boxes) in "%s", without '
                          'an account, as a gallery. If setting a number of '
                          'accesses, keep in mind that every single-file view'
                          ' counts toward the total accesses.') % (
                describeBoxTitle(box)
            ),
            baseMultiplicityCaption='Number of granted accesses',
            user=user,
            form=form,
            iconUrl=makeSettingImageUrl(g, 'app_images', 'gallery_ticket'),
            showFileMode=False,
            breadCrumbs=pathBCrumbs,
        )
示例#21
0
def downloadBoxView(boxPathString=''):
    user = g.user
    lsPath = splitPathString(boxPathString)
    db = dbGetDatabase()
    #
    canDownloadArchive = userCanDownloadArchive(
        db,
        user,
        g.settings,
    )
    #
    if not canDownloadArchive:
        return abort(404)
    else:
        thisBox = getBoxFromPath(db, lsPath, user)
        if thisBox is not None:
            boxPath = lsPath[1:]
            tempFileDirectory = g.settings['system']['system_directories'][
                'temp_directory']['value']
            fileStorageDirectory = g.settings['system']['system_directories'][
                'fs_directory']['value']
            # we extract the tree "from this box downward"
            tree = collectTreeFromBox(db, thisBox, user, admitFiles=True)
            overallSize = treeSize(tree)
            # size-dependent handling by reading settings
            mibSize = bytesToMiB(overallSize)
            archiveWarningSizeMib = g.settings['behaviour']['archives'][
                'archive_download_alert_size']['value']
            archiveBlockSizeMib = g.settings['behaviour']['archives'][
                'archive_download_blocking_size']['value']
            if not optionNumberLeq(archiveBlockSizeMib, mibSize):
                # archive is simply too big (hard limit)
                oeText = ('Cannot prepare archive: box size, %s'
                          ', exceeds configured limits')

                raise OstracionError(oeText % formatBytesSize(overallSize))
            else:
                # we may have to issue a warning to the downloader:
                # this if (1) rather large archive,
                # (2) no previous 'ok' was given
                hasConfirmed = request.args.get('confirmed') == 'yes'
                if all([
                        not optionNumberLeq(archiveWarningSizeMib, mibSize),
                        not hasConfirmed,
                ]):
                    # we issue the warning and wait
                    confirmText = ('Preparing this archive may take some '
                                   'time due to the size of the box (%s). '
                                   'Click this message to really proceed '
                                   'with the download.')
                    flashMessage(
                        'Warning',
                        'Caution',
                        confirmText % formatBytesSize(overallSize),
                        url=url_for(
                            'downloadBoxView',
                            boxPathString=boxPathString,
                            confirmed='yes',
                        ),
                    )
                    return redirect(
                        url_for(
                            'lsView',
                            lsPathString=boxPathString,
                        ))
                else:
                    # we collect the information needed to prepare the
                    # archive file. For now, no empty boxes
                    # (a zip format limitation)
                    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

                    # we make the tree into a list of filePairs, ready to zip
                    inPairs = collectArchivablePairs(tree)
                    filePairs = [p for p in inPairs if p['type'] == 'file']
                    dataPairs = [p for p in inPairs if p['type'] == 'data']
                    # we create the zip
                    _, archiveFileTitle = temporarySplitFileName(
                        tempFileDirectory, )
                    makeZipFile(
                        os.path.join(tempFileDirectory, archiveFileTitle),
                        filePairs,
                        dataPairs,
                    )

                    # Now the file exists, ready to be served.
                    # Instead of a plain send_from_directory, though,
                    # we use the answer "Stream file, then delete"
                    # by Sean Vieira here:
                    # https://stackoverflow.com/questions/24612366/
                    #   delete-an-uploaded-file-after-downloading-it-from-flask

                    def StreamFileAndRemoveIt(localFileName):
                        fileHandle = open(localFileName, 'rb')
                        yield from fileHandle
                        fileHandle.close()
                        os.remove(localFileName)

                    zipFileName = '%s.zip' % describeBoxName(thisBox, )
                    contentDisposition = 'attachment; filename="%s"' % (
                        zipFileName, )
                    return current_app.response_class(
                        StreamFileAndRemoveIt(localFileName=os.path.join(
                            tempFileDirectory,
                            archiveFileTitle,
                        ), ),
                        headers={
                            'Content-Disposition': contentDisposition,
                        },
                        mimetype='application/zip',
                    )
        else:
            request._onErrorUrl = url_for(
                'lsView',
                lsPathString='/'.join(lsPath[1:-1]),
            )
            raise OstracionWarning('Cannot access the specified box "%s"' %
                                   lsPath[-1])
示例#22
0
def calendarMakerImagesView():
    """Calendar image-selection page view."""
    user = g.user
    db = dbGetDatabase()
    request._onErrorUrl = url_for(
        'calendarMakerIndexView',
        lsPathString='',
    )
    #
    browseBoxString = request.cookies.get('apps_calendarmaker_browsebox')
    currentCalendar = cookiesToCurrentCalendar(request.cookies)
    coverImagePathString = currentCalendar.get('cover_image_path_string')
    calendarImagePaths = currentCalendar.get('image_path_strings', [])
    cProps = currentCalendar.get('properties', {})
    #
    if coverImagePathString is None:
        coverImageFileObject = None
    else:
        coverImageFileObject = pathToFileStructure(
            db,
            user,
            coverImagePathString,
        )
    #
    if browseBoxString is not None:
        browseBoxPath = splitPathString(browseBoxString)
        browseBox = getBoxFromPath(db, browseBoxPath, user)
        choosableFiles = [{
            'file':
            file,
            'path':
            browseBoxPath[1:] + [file.name],
            'obj_path':
            urllib.parse.quote_plus('/'.join(browseBoxPath[1:] +
                                             [file.name], )),
        } for file in sorted(
            getFilesFromBox(db, browseBox),
            key=lambda f: (f.name.lower(), f.name),
        ) if file.mime_type in admittedImageMimeTypeToExtension]
        browseBoxName = describeBoxName(browseBox)
    else:
        browseBox = None
        browseBoxName = None
        choosableFiles = []
    #
    calendarImages = [
        pathToFileStructure(db, user, imgPath)
        for imgPath in calendarImagePaths
    ]
    #
    numRequiredImages = countMonths(
        cProps.get('year0'),
        cProps.get('month0'),
        cProps.get('year1'),
        cProps.get('month1'),
    )
    #
    pageFeatures = prepareTaskPageFeatures(
        appsPageDescriptor,
        ['root', 'calendar_maker', 'images'],
        g,
    )
    return render_template(
        'apps/calendarmaker/images.html',
        user=user,
        browseBox=browseBox,
        browseBoxName=browseBoxName,
        choosableFiles=choosableFiles,
        coverImageFileObject=coverImageFileObject,
        bgcolor=g.settings['color']['app_colors']['calendar_maker_color']
        ['value'],
        bgcolorbrowse=g.settings['color']['app_colors']
        ['calendar_maker_browse_color']['value'],
        calendarImages=calendarImages,
        numRequiredImages=numRequiredImages,
        **pageFeatures,
    )
示例#23
0
def retrieveFileUponTicket(db, user, issuer, richTicket, urlRoot):
    """ Retrieve a file using a file ticket.

        Retrieval fails if either:
            1. user banned;
            2. object not available to the issuer.
    """
    noBandUsrTickets = g.settings['behaviour']['behaviour_tickets'][
        'protect_banned_user_tickets']['value']
    if (not noBandUsrTickets or issuer.banned == 0):
        boxPath, fileName = (
            richTicket['metadata']['path'][:-1],
            richTicket['metadata']['path'][-1],
        )
        fileStorageDirectory = g.settings['system']['system_directories'][
            'fs_directory']['value']
        parentBox = getBoxFromPath(db, boxPath, issuer)
        #
        if parentBox is not None:
            file = getFileFromParent(db, parentBox, fileName, issuer)
            if file is not None:
                if richTicket['metadata'].get('file_mode') == 'view':
                    # either we offer a view-type page
                    # (with ticketMessage and optionally view)
                    # and decide whether to punch the ticket and how ...
                    fileRetrievalUrl = url_for(
                        'ticketFsDownloadView',
                        ticketId=richTicket['ticket'].ticket_id,
                        securityCode=richTicket['ticket'].security_code,
                    )
                    # for mode='ticketview'this will internally call
                    # the punching endpoint 'ticketFsDownloadView' below.
                    fileContents = produceFileViewContents(
                        db,
                        file,
                        mode='ticketview',
                        viewParameters={
                            'ticketId': richTicket['ticket'].ticket_id,
                            'securityCode': richTicket['ticket'].security_code,
                        },
                        fileStorageDirectory=fileStorageDirectory,
                        urlRoot=urlRoot,
                        protectBannedUserTickets=noBandUsrTickets,
                    )
                    return render_template(
                        'fileview.html',
                        fileActions=None,
                        fileInfo=None,
                        filecontents=fileContents,
                        breadCrumbs=[],
                        user=user,
                        pageTitle='File access',
                        pageSubtitle='View/download the file%s' %
                        ('' if richTicket['metadata'].get('message') is None
                         else ('. Message on ticket: "%s"' %
                               (richTicket['metadata']['message']))),
                        downloadUrl=fileRetrievalUrl,
                    )
                elif richTicket['metadata'].get('file_mode') == 'direct':
                    # ... or we offer a direct download and punch the ticket...
                    return ticketFsDownloadView(
                        richTicket['ticket'].ticket_id,
                        richTicket['ticket'].security_code,
                    )
                else:
                    raise NotImplementedError(
                        'Unknown file_mode "%s" in file ticket' %
                        (richTicket['metadata'].get('file_mode'), ))
            else:
                raise OstracionError('Ticket target unavailable')
        else:
            raise OstracionError('Ticket target unavailable')
    else:
        raise OstracionError('Ticket cannot be redeemed')
示例#24
0
def makeTicketBoxUploadView(boxPathString=''):
    """Make-upload-ticket (to a box) route."""
    user = g.user
    db = dbGetDatabase()
    boxPath = splitPathString(boxPathString)
    box = getBoxFromPath(db, boxPath, user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    #
    if userHasPermission(db, user, box.permissions, 'w'):
        #
        pathBCrumbs = makeBreadCrumbs(
            boxPath,
            g,
            appendedItems=[
                {
                    'name': '(create upload ticket)',
                    'kind': 'link',
                    'target': url_for(
                        'makeTicketBoxUploadView',
                        boxPathString=boxPathString,
                    ),
                    'link': False,
                },
            ],
        )
        form = generateFsTicketForm(
            fileModePresent=False,
            settings=g.settings,
        )
        if form.validate_on_submit():
            magicLink = dbMakeUploadTicket(
                db=db,
                ticketName=form.name.data,
                validityHours=transformIfNotEmpty(
                    form.validityhours.data,
                    int,
                ),
                multiplicity=transformIfNotEmpty(
                    form.multiplicity.data,
                    int,
                ),
                ticketMessage=transformIfNotEmpty(
                    form.ticketmessage.data,
                ),
                box=box,
                boxPath=boxPath,
                user=user,
                urlRoot=request.url_root,
                settings=g.settings,
            )
            flashMessage(
                'Success',
                'Done',
                ('Upload ticket generated. Give the recipient '
                 'the following magic link:'),
                pillText=magicLink,
            )
            return redirect(url_for(
                'lsView',
                lsPathString='/'.join(boxPath[1:]),
            ))
        else:
            defaultName = 'Upload-To-%s' % (
                describeBoxName(box)
            )
            form.name.data = applyDefault(form.name.data, defaultName)
            form.ticketmessage.data = applyDefault(
                form.ticketmessage.data,
                'Please, upload files to this box',
            )
            maxValidityHours = g.settings['behaviour']['behaviour_tickets'][
                'max_ticket_validityhours']['value']
            form.validityhours.data = applyDefault(
                form.validityhours.data,
                str(maxValidityHours) if maxValidityHours is not None else '',
            )
            maxMultiplicity = g.settings['behaviour']['behaviour_tickets'][
                'max_ticket_multiplicity']['value']
            form.multiplicity.data = applyDefault(
                form.multiplicity.data,
                str(maxMultiplicity) if maxMultiplicity is not None else '',
            )
            return render_template(
                'fsticket.html',
                pageTitle='Create public upload ticket',
                pageSubtitle=('Recipient(s) of the ticket will be able to '
                              'upload to "%s", without an account, on '
                              'your behalf.') % (
                                    describeBoxTitle(box)
                               ),
                baseMultiplicityCaption='Number of granted file uploads',
                user=user,
                form=form,
                iconUrl=makeSettingImageUrl(g, 'app_images', 'upload_ticket'),
                showFileMode=False,
                breadCrumbs=pathBCrumbs,
            )
        #
    else:
        raise OstracionError('Insufficient permissions')
示例#25
0
def _resolveUponPermissions(db, richObject, user, mode, score):
    """ Verify and validate the input (rich-)box/file
        against permissions, and return either a similar structure
        or a None if permissions turn out to be blocking.

        (
            richObject has keys "path" and "box/file",
            mode = 'file' / 'box'
        )
    """
    pBox = getBoxFromPath(db, richObject['path'][:-1], user)
    if mode == 'box':
        nBox = getBoxFromPath(db, richObject['path'], user)
        if nBox is not None and pBox is not None:
            return {
                'path':
                richObject['path'][1:],
                'box':
                nBox,
                'actions':
                prepareBoxActions(db,
                                  nBox,
                                  richObject['path'][1:],
                                  pBox,
                                  user,
                                  prepareParentButton=True),
                'info':
                prepareBoxInfo(db, nBox),
                'parentInfo':
                'Parent box: "%s"' % (describeBoxTitle(pBox), ),
                'object_type':
                mode,
                'score':
                score,
            }
        else:
            return None
    elif mode == 'file':
        if pBox is not None:
            nFile = getFileFromParent(db, pBox, richObject['path'][-1], user)
            if nFile is not None:
                return {
                    'path':
                    richObject['path'][1:],
                    'file':
                    nFile,
                    'actions':
                    prepareFileActions(db,
                                       nFile,
                                       richObject['path'][1:],
                                       pBox,
                                       user,
                                       prepareParentButton=True),
                    'info':
                    prepareFileInfo(db, nFile),
                    'nice_size':
                    formatBytesSize(nFile.size),
                    'parentInfo':
                    'Container box: "%s"' % (describeBoxTitle(pBox), ),
                    'object_type':
                    mode,
                    'score':
                    score,
                }
            else:
                return None
        else:
            return None
    elif mode == 'link':
        if pBox is not None:
            nLink = getLinkFromParent(db, pBox, richObject['path'][-1], user)
            if nLink is not None:
                return {
                    'path':
                    richObject['path'][1:],
                    'link':
                    nLink,
                    'actions':
                    prepareLinkActions(db,
                                       nLink,
                                       richObject['path'][1:],
                                       pBox,
                                       user,
                                       prepareParentButton=True),
                    'info':
                    prepareLinkInfo(db, nLink),
                    'parentInfo':
                    'Container box: "%s"' % (describeBoxTitle(pBox), ),
                    'object_type':
                    mode,
                    'score':
                    score,
                }
            else:
                return None
        else:
            return None
    else:
        raise NotImplementedError('Unknown _resolveUponPermissions mode "%s"' %
                                  mode)
示例#26
0
def metadataBoxView(boxPathString=''):
    """edit-metadata-of-box route."""
    user = g.user
    db = dbGetDatabase()
    boxPath = splitPathString(boxPathString)
    box = getBoxFromPath(db, boxPath, user)
    parentBox = getBoxFromPath(db, boxPath[:-1], user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:-1]),
    )
    #
    form = makeMakeBoxForm('Save')()
    if parentBox is None:
        # target box was root
        raise OstracionError('Cannot act on this object')
    else:
        if box is not None:
            #
            if form.validate_on_submit():
                newBox = Box(
                    **recursivelyMergeDictionaries(
                        {
                            'box_name': form.boxname.data,
                            'title': form.boxtitle.data,
                            'description': form.boxdescription.data,
                            'metadata_username': user.username,
                        },
                        defaultMap=box.asDict(),
                    )
                )
                updateBox(db, boxPath, newBox, user)
                return redirect(url_for(
                    'lsView',
                    lsPathString='/'.join(boxPath[1:-1]),
                ))
            else:
                form.boxname.data = applyDefault(
                    form.boxname.data,
                    box.box_name,
                )
                form.boxdescription.data = applyDefault(
                    form.boxdescription.data,
                    box.description,
                )
                form.boxtitle.data = applyDefault(
                    form.boxtitle.data,
                    box.title,
                )
                pathBCrumbs = makeBreadCrumbs(
                    boxPath,
                    g,
                    appendedItems=[{
                        'kind': 'link',
                        'target': None,
                        'name': 'Metadata',
                    }],
                )
                return render_template(
                    'makebox.html',
                    form=form,
                    user=user,
                    breadCrumbs=pathBCrumbs,
                    pageTitle='Edit box metadata',
                    pageSubtitle='Name and title are mandatory',
                    iconUrl=url_for(
                        'boxThumbnailView',
                        dummyId=box.icon_file_id + '_',
                        boxPathString='/'.join(boxPath[1:]),
                    ),
                )
            #
        else:
            raise OstracionWarning('Box not accessible')
    return redirect(url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:-1]),
    ))
示例#27
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'))
示例#28
0
def fsMoveBoxView(quotedSrcBox):
    """
        Move-box-phase-1 route: where the source box is selected
        (and then in offering the choose-dest-box it is put in the URL
        for calling phase two).
    """
    user = g.user
    db = dbGetDatabase()
    # first we find the box
    bxPathString = urllib.parse.unquote_plus(quotedSrcBox)
    boxPath = splitPathString(bxPathString)
    parentBox = getBoxFromPath(db, boxPath[:-1], user)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:-1]),
    )
    box = getBoxFromPath(db, boxPath, user)
    if not canDeleteBox(db, box, parentBox, user):
        raise OstracionWarning('Cannot act on this object')
    # next we prepare the selectable destinations
    rootBox = getRootBox(db)

    def rbPredicate(richBox, _box=box, _srcBox=parentBox):
        _dstBox = richBox['box']
        if _box.parent_id != _dstBox.box_id:
            if all(userHasPermission(db, user, _dstBox.permissions, prm)
                    for prm in {'w', 'c'}):
                if not isNameUnderParentBox(db, _dstBox, _box.box_name):
                    if not isAncestorBoxOf(db, _box, _dstBox):
                        return True
        return False

    dstBoxTree = collectTreeFromBox(
        db,
        rootBox,
        user,
        admitFiles=False,
        fileOrBoxEnricher=lambda richBox: {
            'obj_path': urllib.parse.quote_plus('/'.join(richBox['path'])),
        },
        predicate=rbPredicate,
    )
    #
    maxDepth = getMaxTreeDepth(dstBoxTree)
    colorShadeMap = prepareColorShadeMap(
        g.settings['color']['navigation_colors']['box']['value'],
        g.settings['color']['tree_shade_colors'][
            'shade_treeview_pickbox']['value'],
        numShades=1 + maxDepth,
    )
    destinationsExist = treeAny(
        dstBoxTree,
        property=lambda node: node['predicate'],
    )
    #
    return render_template(
        'dirtreeview.html',
        tree=dstBoxTree if destinationsExist else None,
        mode='box_move',
        object_quoted_path=quotedSrcBox,
        colorShadeMap=colorShadeMap,
        user=user,
        iconUrl=makeSettingImageUrl(g, 'app_images', 'move'),
        pageTitle='Select box destination',
        pageSubtitle=('Choose the box to which box "%s" '
                      'shall be moved from "%s"') % (
                            describeBoxTitle(box),
                            describeBoxTitle(parentBox),
                     ),
        actions=None,
        backToUrl=(None
                   if destinationsExist
                   else url_for(
                        'lsView',
                        lsPathString='/'.join(boxPath[1:]))),
        breadCrumbs=makeBreadCrumbs(
            boxPath,
            g,
            appendedItems=[
                {
                    'kind': 'link',
                    'target': None,
                    'name': 'Move box',
                }
            ],
        ),
    )
示例#29
0
def calendarMakerIndexView():
    """Main calendar maker view."""
    user = g.user
    db = dbGetDatabase()
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='',
    )
    #
    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:
        destBoxMessage = 'Destination box not set.'
        destBox = None
        destBoxName = None
    else:
        destBoxMessage = 'Destination box: "%s".' % destBoxString
        destBoxPath = splitPathString(destBoxString)
        destBox = getBoxFromPath(db, destBoxPath, user)
        destBoxName = describeBoxName(destBox)
    if coverImagePathString is None:
        coverMessage = 'Please select a cover'
        coverImageFileObject = None
    else:
        coverMessage = 'Cover selected'
        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'),
    )
    #
    settingsDesc = describeSettings(cProps)
    settingsSummaryText = ('Not set' if settingsDesc is None else ' '.join(
        '%s.' % stc for stc in settingsDesc))
    #
    if numRequiredImages is not None:
        if numRequiredImages <= len(calendarImages):
            gen1 = True
            gen1Message = []
        else:
            gen1 = False
            gen1Message = ['Select enough images']
    else:
        gen1 = False
        gen1Message = ['Set valid start/end dates']
    if destBox is not None:
        gen2 = True
        gen2Message = []
    else:
        gen2 = False
        gen2Message = ['Select a destination box']
    if coverImageFileObject is not None:
        gen0 = True
        gen3Message = []
    else:
        gen0 = False
        gen3Message = ['Select a cover image']
    canGenerate = all([gen0, gen1, gen2])
    generationMessages = gen3Message + gen1Message + gen2Message
    pageFeatures = prepareTaskPageFeatures(
        appsPageDescriptor,
        ['root', 'calendar_maker'],
        g,
        overrides={
            'pageSubtitle': ('Create your own custom calendar. (1) Configure '
                             'dates and calendar appearance. (2) Select enoug'
                             'h images. (3) Choose a destination box for the '
                             'calendar. (4) Hit "Generate calendar".'),
        },
    )
    #
    return render_template(
        'apps/calendarmaker/index.html',
        user=user,
        settingsText=settingsSummaryText,
        destBox=destBox,
        destBoxName=destBoxName,
        bgcolor=g.settings['color']['app_colors']['calendar_maker_color']
        ['value'],
        calendarImages=calendarImages,
        coverImageFileObject=coverImageFileObject,
        numRequiredImages=numRequiredImages,
        canGenerate=canGenerate,
        generationMessages=generationMessages,
        **pageFeatures,
    )
示例#30
0
def uploadFilesUponTicket(db, user, issuer, richTicket, urlRoot):
    """ Upload-file(s)-upon-ticket route.

        Upload fails if
            1. user banned;
            2. issuer has no upload permission
               (incl. access) on the target box.
    """
    noBandUsrTickets = g.settings['behaviour']['behaviour_tickets'][
        'protect_banned_user_tickets']['value']
    if not noBandUsrTickets or issuer.banned == 0:
        boxPath = richTicket['metadata']['box_path']
        request._onErrorUrl = url_for(
            'lsView',
            lsPathString='/'.join(boxPath[1:]),
        )
        box = getBoxFromPath(db, boxPath[1:], issuer)
        fileStorageDirectory = g.settings['system']['system_directories'][
            'fs_directory']['value']
        if userHasPermission(db, issuer, box.permissions, 'w'):
            # ticket is completely valid: here the form
            # handling (get/post) occurs
            uploadsLeft = (None if richTicket['ticket'].multiplicity is None
                           else (richTicket['ticket'].multiplicity -
                                 richTicket['ticket'].times_redeemed))
            form = UploadMultipleFilesForm()
            if form.validate_on_submit():
                uploadedFiles = [
                    uf for uf in request.files.getlist('files')
                    if uf.filename != ''
                ]
                filesdescription = form.filesdescription.data
                # are there too many files?
                if uploadsLeft is None or len(uploadedFiles) <= uploadsLeft:
                    filesToUpload = [{
                        'box_id':
                        box.box_id,
                        'name':
                        secure_filename(uploadedFile.filename),
                        'description':
                        form.filesdescription.data,
                        'date':
                        datetime.datetime.now(),
                        'fileObject':
                        uploadedFile,
                    } for uploadedFile in uploadedFiles]
                    # we punch the ticket as many times as
                    # there were files provided
                    dbPunchRichTicket(
                        db,
                        richTicket,
                        numPunches=len(filesToUpload),
                    )
                    makeThumbnails = g.settings['behaviour'][
                        'behaviour_appearance']['extract_thumbnails']['value']
                    savingResult = saveAndAnalyseFilesInBox(
                        db=db,
                        files=filesToUpload,
                        parentBox=box,
                        user=issuer,
                        fileStorageDirectory=fileStorageDirectory,
                        thumbnailFormat=('thumbnail'
                                         if makeThumbnails else None),
                    )
                    flashMessage('Success', 'Info', savingResult)
                    return redirect(url_for(
                        'lsView',
                        lsPathString='',
                    ))
                else:
                    request._onErrorUrl = url_for(
                        'redeemTicketView',
                        mode='c',
                        ticketId=richTicket['ticket'].ticket_id,
                        securityCode=richTicket['ticket'].security_code,
                    )
                    raise OstracionError(
                        ('Cannot upload this many files '
                         '(maximum %i allowed)') % uploadsLeft)
            else:
                return render_template(
                    'uploadmultiplefiles.html',
                    form=form,
                    user=user,
                    breadCrumbs=[],
                    pageTitle='Upload file(s)',
                    pageSubtitle='Upload%s files%s' % (
                        ('' if uploadsLeft is None else ' up to %i' %
                         uploadsLeft),
                        ('' if richTicket['metadata'].get('message') is None
                         else ('. Message on ticket: "%s"' %
                               (richTicket['metadata']['message']))),
                    ),
                    iconUrl=makeSettingImageUrl(
                        g,
                        'app_images',
                        ('single_upload' if optionNumberLeq(1, uploadsLeft)
                         else 'multiple_upload'),
                    ),
                )
        else:
            # issuer has no write permission in box
            raise OstracionError('Ticket not acessible or expired')
    else:
        raise OstracionError('Ticket not acessible or expired')