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,
    )
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,
    )
Exemple #3
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')
Exemple #4
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])
Exemple #5
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,
        )