Example #1
0
def _boxHasNoChildren(db, box):
    """ Is it true that this box has no children?
        (regardless of visibility-to-this-user issues).
    """
    return (len(list(getFilesFromBox(db, box))) == 0 and
            len(list(
                getBoxesFromParent(
                    db,
                    box,
                    None,
                    accountDeletionInProgress=True
                )
            )) == 0)
Example #2
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,
    )
Example #3
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))
Example #4
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')
Example #5
0
def lsView(lsPathString=''):
    """LS-of-a-box route, additionally with 'tasks' if viewing root."""
    user = g.user
    lsPath = splitPathString(lsPathString)
    db = dbGetDatabase()
    thisBox = getBoxFromPath(db, lsPath, user)
    boxPath = lsPath[1:]
    #
    showBoxPermissionToAll = g.settings['behaviour']['behaviour_permissions'][
        'show_permission']['value']
    #
    if thisBox is not None:
        request._onErrorUrl = url_for('lsView')
        if thisBox.box_id == '':
            tasks = prepareRootTasks(db, g, user)
        else:
            tasks = []
        boxes = [
            {
                'box': box,
                'path': boxPath + [box.box_name],
                'info': prepareBoxInfo(db, box),
                'actions': prepareBoxActions(
                    db,
                    box,
                    boxPath + [box.box_name],
                    thisBox,
                    user
                ),
            }
            for box in sorted(
                (
                    b
                    for b in getBoxesFromParent(db, thisBox, user)
                    if b is not None
                ),
                key=lambda b: (b.box_name.lower(), b.box_name),
            )
            if box.box_id != ''
        ]
        files = [
            {
                'file': file,
                'path': boxPath + [file.name],
                'nice_size': formatBytesSize(file.size),
                'info': prepareFileInfo(db, file),
                'actions': prepareFileActions(
                    db,
                    file,
                    boxPath + [file.name],
                    thisBox,
                    user
                ),
            }
            for file in sorted(
                getFilesFromBox(db, thisBox),
                key=lambda f: (f.name.lower(), f.name),
            )
        ]
        links = [
            {
                'link': link,
                'path': boxPath + [link.name],
                'info': prepareLinkInfo(db, link),
                'actions': prepareLinkActions(
                    db,
                    link,
                    boxPath + [link.name],
                    thisBox,
                    user
                ),
            }
            for link in sorted(
                getLinksFromBox(db, thisBox),
                key=lambda l: (l.name.lower(), l.name),
            )
        ]
        #
        pathBCrumbs = makeBreadCrumbs(lsPath, g)
        boxNiceName = transformIfNotEmpty(thisBox.box_name)
        if thisBox.box_id == '':
            boxTitle, boxDescription = describeRootBoxCaptions(g)
        else:
            boxTitle = describeBoxTitle(thisBox)
            boxDescription = thisBox.description
        #
        boxActions = prepareBoxHeaderActions(
            db,
            thisBox,
            boxPath,
            user,
            g.settings,
            discardedActions=(
                set()
                if len(files) > 0
                else {'gallery_view', 'issue_gallery_ticket'}
            ),
        )
        showAdminToolsLink = userIsAdmin(db, user)
        if showBoxPermissionToAll or userIsAdmin(db, user):
            thisBoxPermissionAlgebra = calculateBoxPermissionAlgebra(
                thisBox.permissionHistory,
                thisBox.permissions,
            )
            #
            roleKeyToRoleMap = {
                r.roleKey(): r
                for r in dbGetAllRoles(db, user)
                if r.can_box != 0
            }
            structuredPermissionInfo = reformatBoxPermissionAlgebraIntoLists(
                thisBoxPermissionAlgebra,
                roleKeyToRoleMap,
            )
            #
            permissionInfo = {
                'powers': structuredPermissionInfo,
                'assignments': {
                    'edit_url': (
                        url_for(
                            'adminLsPermissionsView',
                            lsPathString='/'.join(boxPath),
                        )
                        if showAdminToolsLink
                        else None
                    ),
                    'native': [
                        {
                            'role_class': brp.role_class,
                            'role_id': brp.role_id,
                            'r': brp.r,
                            'w': brp.w,
                            'c': brp.c,
                            'role': roleKeyToRoleMap[(
                                brp.role_class,
                                brp.role_id,
                            )],
                        }
                        for brp in sorted(thisBox.listPermissions('native'))
                    ],
                    'inherited': [
                        {
                            'role_class': brp.role_class,
                            'role_id': brp.role_id,
                            'r': brp.r,
                            'w': brp.w,
                            'c': brp.c,
                            'role': roleKeyToRoleMap[(
                                brp.role_class,
                                brp.role_id,
                            )],
                        }
                        for brp in sorted(thisBox.listPermissions('inherited'))
                    ],
                },
            }
        else:
            permissionInfo = None
        #
        if len(boxes) + len(files) + len(links) > 0:
            foundCounts = [
                (len(boxes), 'box', 'boxes'),
                (len(files), 'file', 'files'),
                (len(links), 'link', 'links'),
            ]
            foundParts = pickSingularPluralSentences(
                foundCounts,
                keepZeroes=False,
            )
            boxChildrenCounter = colloquialJoinClauses(foundParts)
        else:
            boxChildrenCounter = 'no contents'
        #
        return render_template(
            'ls.html',
            user=user,
            thisBox=thisBox,
            boxTitle=boxTitle,
            boxNiceName=boxNiceName,
            boxDescription=boxDescription,
            pageTitle=boxNiceName,
            boxChildrenCounter=boxChildrenCounter,
            boxActions=boxActions,
            boxPath=boxPath,
            permissionInfo=permissionInfo,
            breadCrumbs=pathBCrumbs,
            tasks=tasks,
            boxes=boxes,
            files=files,
            links=links,
        )
    else:
        request._onErrorUrl = url_for(
            'lsView',
            lsPathString='/'.join(lsPath[1:-1]),
        )
        raise OstracionWarning(
            'Cannot access the specified box "%s"' % lsPath[-1]
        )
Example #6
0
def _traverseForAccountDeletion(db, parentBox, username, user,
                                path, fileStorageDirectory):
    """ Traverse the tree (one call per box, recursively+iteratively)
        and collect a delete queue while updating items found in various
        ways (deletions, icon resets, metadata resets, forced renames).
    """
    fsDeleteQueue = []
    for file in getFilesFromBox(db, parentBox):
        if (file.creator_username == username or
                file.editor_username == username):
            fsDeleteQueue += deleteFile(
                db,
                parentBox,
                file,
                None,
                fileStorageDirectory=fileStorageDirectory,
                skipCommit=True,
                accountDeletionInProgress=True,
            )
        else:
            if file.icon_file_id_username == username:
                fsDeleteQueue += updateFileThumbnail(
                    db,
                    file,
                    None,
                    None,
                    user,
                    fileStorageDirectory=fileStorageDirectory,
                    accountDeletionInProgress=True,
                    skipCommit=True,
                )
                iconedFile = getFileFromParent(db, parentBox, file.name, None)
            else:
                iconedFile = file
            # "iconed" as in "icon-wise fixed"
            if iconedFile.metadata_username == username:
                newFileName = findFirstAvailableObjectNameInBox(
                    db,
                    parentBox,
                    prefix='REDACTED_FILE_',
                    suffix='',
                )
                newDescription = 'File name redacted upon account deletion'
                newFile = File(**recursivelyMergeDictionaries(
                    {
                        'name': newFileName,
                        'description': newDescription,
                        'metadata_username': '',
                    },
                    defaultMap=iconedFile.asDict(),
                ))
                updateFile(
                    db,
                    path,
                    iconedFile.name,
                    newFile,
                    None,
                    accountDeletionInProgress=True,
                    skipCommit=True,
                )
    #
    for link in getLinksFromBox(db, parentBox):
        if link.creator_username == username:
            fsDeleteQueue += deleteLink(
                db,
                parentBox,
                link,
                None,
                fileStorageDirectory=fileStorageDirectory,
                skipCommit=True,
                accountDeletionInProgress=True,
            )
        else:
            if link.icon_file_id_username == username:
                fsDeleteQueue += updateLinkThumbnail(
                    db,
                    link,
                    None,
                    None,
                    user,
                    fileStorageDirectory=fileStorageDirectory,
                    accountDeletionInProgress=True,
                    skipCommit=True,
                )
                iconedLink = getLinkFromParent(db, parentBox, link.name, None)
            else:
                iconedLink = link
            # "iconed" as in "icon-wise fixed"
            if iconedLink.metadata_username == username:
                newLinkName = findFirstAvailableObjectNameInBox(
                    db,
                    parentBox,
                    prefix='REDACTED_LINK_',
                    suffix='',
                )
                newDescription = 'Link data redacted upon account deletion'
                newLink = Link(**recursivelyMergeDictionaries(
                    {
                        'name': newLinkName,
                        'description': newDescription,
                        'target': '#',
                        'metadata_username': '',
                    },
                    defaultMap=iconedLink.asDict(),
                ))
                updateLink(
                    db,
                    path,
                    iconedLink.name,
                    newLink,
                    None,
                    accountDeletionInProgress=True,
                    skipCommit=True,
                )
    #
    for box in getBoxesFromParent(
            db, parentBox, None, accountDeletionInProgress=True):
        if box is not None and box.box_name != '':
            fsDeleteQueue += _traverseForAccountDeletion(
                db,
                box,
                username,
                user,
                path + [box.box_name],
                fileStorageDirectory=fileStorageDirectory,
            )
            if box.creator_username == username and _boxHasNoChildren(db, box):
                fsDeleteQueue += deleteBox(
                    db,
                    box,
                    parentBox,
                    None,
                    fileStorageDirectory=fileStorageDirectory,
                    accountDeletionInProgress=True,
                    skipCommit=True,
                )
            else:
                if (box.icon_file_id_username == username or
                        box.creator_username == username):
                    fsDeleteQueue += updateBoxThumbnail(
                        db,
                        box,
                        None,
                        None,
                        user,
                        fileStorageDirectory=fileStorageDirectory,
                        accountDeletionInProgress=True,
                        skipCommit=True,
                    )
                    iconedBox = getBoxFromPath(
                        db,
                        path + [box.box_name],
                        None,
                        accountDeletionInProgress=True,
                    )
                else:
                    iconedBox = box
                #
                if (iconedBox.metadata_username == username or
                        iconedBox.creator_username == username):
                    #
                    newBoxName = findFirstAvailableObjectNameInBox(
                        db,
                        parentBox,
                        prefix='REDACTED_BOX_',
                        suffix='',
                    )
                    newDescription = 'Box name redacted upon account deletion'
                    mdNewBoxContrib = {
                            'box_name': newBoxName,
                            'description': newDescription,
                            'title': newBoxName,
                            'metadata_username': '',
                    }
                    #
                    if iconedBox.creator_username == username:
                        cNewBoxContrib = {
                            'creator_username': '',
                        }
                    else:
                        cNewBoxContrib = {}
                    #
                    newBox = Box(**recursivelyMergeDictionaries(
                        recursivelyMergeDictionaries(
                            mdNewBoxContrib,
                            defaultMap=cNewBoxContrib,
                        ),
                        defaultMap=iconedBox.asDict(),
                    ))
                    updateBox(
                        db,
                        path + [box.box_name],
                        newBox,
                        None,
                        accountDeletionInProgress=True,
                        skipCommit=True,
                    )
    #
    return fsDeleteQueue