Exemplo n.º 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:]),
    ))
Exemplo n.º 2
0
def logoutView():
    """Logout view."""
    if g.user is not None and g.user.is_authenticated:
        flashMessage(
            'Success',
            'Logged out',
            'farewell, %s.' % g.user.fullname,
        )
        logout_user()
    return redirect(url_for('indexView'))
Exemplo n.º 3
0
def exceptionHandler(error):
    """ Generic catcher for in-route errors.
        Handles all exceptions, in particular through the
        use of the 'exceptions' module they end up as flashed information.

        Preferentially redirects to request._onErrorUrl if it is set,
        otherwise falls back to "index".

        NOTE: in development this is simply re-raising the error
        for the debugger.
    """
    if app.config.get('DEVELOPMENT', False):
        raise error
    else:
        flashableError = exceptionToFlashable(error)
        flashMessage(**flashableError)
        if hasattr(request, '_onErrorUrl'):
            return redirect(request._onErrorUrl)
        else:
            return redirect(url_for('lsView'))
Exemplo n.º 4
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)
Exemplo n.º 5
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])
Exemplo n.º 6
0
def createUserUponTicket(db, user, issuer, richTicket, urlRoot):
    """User-creation-ticket route."""
    ticketsByformerAdminsAreProtected = g.settings['behaviour'][
        'behaviour_tickets']['protect_nonadmin_user_password_tickets']['value']
    appLongName = g.settings['behaviour']['behaviour_appearance'][
        'application_long_name']['value']
    if not ticketsByformerAdminsAreProtected or userIsAdmin(db, issuer):
        form = generateNewUserForm(g.settings)
        if form.validate_on_submit():
            userDict = {
                'username': form.username.data,
                'fullname': form.fullname.data,
                'email': form.email.data,
                'password': form.password.data,
            }
            if ('username' in richTicket['metadata']
                    and richTicket['metadata']['username'] !=
                    userDict['username']):
                raise RuntimeError(
                    'Detected attempt to escape username set by ticket issuer')
            else:
                # the usename must not exist (relevant if left free in ticket)
                if isUsername(db, userDict['username'], user):
                    if 'username' in richTicket['metadata']:
                        request._onErrorUrl = url_for('lsView')
                    else:
                        request._onErrorUrl = url_for(
                            'redeemTicketView',
                            mode='u',
                            ticketId=ticketId,
                            securityCode=securityCode,
                        )
                    raise OstracionError('Username "%s" exists already' %
                                         userDict['username'])
                else:
                    # we proceed with the user creation proper
                    newUser = User(
                        icon_file_id='',
                        icon_file_id_username=userDict['username'],
                        banned=0,
                        terms_accepted='0',
                        terms_accepted_version='',
                        **userDict,
                    )
                    try:
                        dbPunchRichTicket(db, richTicket, skipCommit=True)
                        dbCreateUser(db, newUser, user)
                        #
                        flashMessage(
                            'Info',
                            'Success',
                            'User created. You can now log in to %s' %
                            (appLongName),
                        )
                        return redirect(url_for('loginView'))
                    except Exception as e:
                        db.rollback()
                        raise e
        else:
            form.username.data = applyDefault(
                form.username.data,
                richTicket['metadata'].get('username', ''),
            )
            form.fullname.data = applyDefault(
                form.fullname.data,
                richTicket['metadata'].get('fullname', ''),
            )
            form.email.data = applyDefault(
                form.email.data,
                richTicket['metadata'].get('email', ''),
            )
            return render_template(
                'newuser.html',
                form=form,
                user=user,
                pageTitle='Create user',
                pageSubtitle='Create your user account on %s%s' % (
                    appLongName,
                    ('' if richTicket['metadata'].get('message') is None else
                     '. Message on ticket: "%s"' %
                     (richTicket['metadata']['message'])),
                ),
                iconUrl=makeSettingImageUrl(g, 'user_images', 'user_icon'),
                lockUsernameField='username' in richTicket['metadata'],
            )
    else:
        raise OstracionError('Ticket not acessible or expired')
        return redirect(url_for('lsView'))
Exemplo n.º 7
0
def changePasswordUponTicket(db, user, issuer, richTicket, urlRoot):
    """Change-password-ticket route."""
    ticketsByformerAdminsAreProtected = g.settings['behaviour'][
        'behaviour_tickets']['protect_nonadmin_user_password_tickets']['value']
    applicationLongName = g.settings['behaviour']['behaviour_appearance'][
        'application_long_name']['value']
    if not ticketsByformerAdminsAreProtected or userIsAdmin(db, issuer):
        form = generateTicketChangePasswordForm(g.settings)
        if form.validate_on_submit():
            changeeUser = dbGetUser(db, richTicket['metadata']['username'])
            if form.username.data == changeeUser.username:
                #
                newPassword = form.newpassword.data
                minPasswordLength = g.settings['behaviour'][
                    'behaviour_security']['password_min_length']['value']
                if len(form.newpassword.data) < minPasswordLength:
                    print('richTicket ', richTicket)
                    flashMessage(
                        'Info',
                        'Please retry',
                        'New password too short (min. %i characters)' %
                        (minPasswordLength, ),
                    )
                    return redirect(
                        url_for(
                            'redeemTicketView',
                            mode='p',
                            ticketId=richTicket['ticket'].ticket_id,
                            securityCode=richTicket['ticket'].security_code,
                        ))
                else:
                    newUser = User(
                        **({(k if k != 'passwordhash' else 'password'): (
                            v if k != 'passwordhash' else form.newpassword.data
                        )
                            for k, v in changeeUser.asDict().items()}))
                    try:
                        dbPunchRichTicket(db, richTicket, skipCommit=True)
                        dbUpdateUser(db, newUser, user, skipCommit=True)
                        db.commit()
                        # flash a message
                        flashMessage(
                            'Success',
                            'Success',
                            'Password changed. You can now log in to %s' %
                            (applicationLongName, ),
                        )
                        # go to login
                        return redirect(url_for('loginView'))
                    except Exception as e:
                        db.rollback()
                        raise e
            else:
                raise OstracionError('Wrong username provided')
        else:
            pageSubtitle = ('Complete the process by entering your '
                            'new password twice%s') % (
                                '' if richTicket['metadata'].get('message') is
                                None else '. Message on ticket: "%s"' %
                                (richTicket['metadata'].get('message'), ))
            return render_template(
                'userchangepassword.html',
                user=user,
                form=form,
                showOldPasswordField=False,
                mode='ticket',
                breadCrumbs=None,
                pageTitle='Change password',
                pageSubtitle=pageSubtitle,
                iconUrl=makeSettingImageUrl(g, 'admin_images',
                                            'change_password_ticket'),
                backToUrl=url_for('lsView'),
            )
    else:
        raise OstracionError('Ticket not acessible or expired')
Exemplo n.º 8
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')
Exemplo n.º 9
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')
Exemplo n.º 10
0
def makeTextFileView(fsPathString=''):
    """Generate-new-text-file route."""
    user = g.user
    form = MakeTextFileForm()
    db = dbGetDatabase()
    boxPath = splitPathString(fsPathString)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    parentBoxPath = boxPath
    parentBox = getBoxFromPath(db, parentBoxPath, user)
    if form.validate_on_submit():
        fileName = secure_filename(form.filename.data)
        savableFile = SavableTextFile('')
        filesToUpload = [
            {
                'box_id': parentBox.box_id,
                'name': fileName,
                'description': form.filedescription.data,
                'date': datetime.datetime.now(),
                'fileObject': savableFile,
            }
        ]
        makeThumbnails = g.settings['behaviour']['behaviour_appearance'][
            'extract_thumbnails']['value']
        savingResult = saveAndAnalyseFilesInBox(
            db=db,
            files=filesToUpload,
            parentBox=parentBox,
            user=user,
            fileStorageDirectory=fileStorageDirectory,
            thumbnailFormat='thumbnail' if makeThumbnails else None,
            pastActionVerbForm='created',
        )
        flashMessage('Success', 'Info', savingResult)
        return redirect(url_for(
            'editTextFileView',
            fsPathString='/'.join(parentBoxPath[1:] + [fileName]),
        ))
    else:
        pathBCrumbs = makeBreadCrumbs(
            parentBoxPath,
            g,
            appendedItems=[{
                'kind': 'link',
                'target': None,
                'name': 'New text',
            }],
        )
        return render_template(
            'maketextfile.html',
            form=form,
            user=user,
            breadCrumbs=pathBCrumbs,
            iconUrl=pickFileThumbnail('text/plain'),
            pageTitle='New text',
            pageSubtitle='Create a new text file in "%s"' % (
                describeBoxTitle(parentBox)
            ),
        )
Exemplo n.º 11
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,
        )
Exemplo n.º 12
0
def uploadMultipleFilesView(fsPathString=''):
    """Upload-several-files (to a box) route."""
    user = g.user
    form = UploadMultipleFilesForm()
    db = dbGetDatabase()
    boxPath = splitPathString(fsPathString)
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    parentBoxPath = boxPath
    parentBox = getBoxFromPath(db, parentBoxPath, user)
    #
    if form.validate_on_submit():
        uploadedFiles = [
            uf
            for uf in request.files.getlist('files')
            if uf.filename != ''
        ]
        #
        filesToUpload = [
            {
                'box_id': parentBox.box_id,
                'name': secure_filename(uploadedFile.filename),
                'description': form.filesdescription.data,
                'date': datetime.datetime.now(),
                'fileObject': uploadedFile,
            }
            for uploadedFile in uploadedFiles
        ]
        makeThumbnails = g.settings['behaviour']['behaviour_appearance'][
            'extract_thumbnails']['value']
        savingResult = saveAndAnalyseFilesInBox(
            db=db,
            files=filesToUpload,
            parentBox=parentBox,
            user=user,
            fileStorageDirectory=fileStorageDirectory,
            thumbnailFormat='thumbnail' if makeThumbnails else None,
        )
        flashMessage('Success', 'Info', savingResult)
        return redirect(url_for(
            'lsView',
            lsPathString='/'.join(parentBoxPath[1:]),
        ))
    else:
        pathBCrumbs = makeBreadCrumbs(
            parentBoxPath,
            g,
            appendedItems=[{
                'kind': 'link',
                'target': None,
                'name': 'Multiple upload',
            }],
        )
        return render_template(
            'uploadmultiplefiles.html',
            form=form,
            user=user,
            breadCrumbs=pathBCrumbs,
            pageTitle='Upload new files',
            pageSubtitle='Upload new files to box "%s"' % (
                describeBoxTitle(parentBox)
            ),
            iconUrl=makeSettingImageUrl(g, 'app_images', 'multiple_upload')
        )
Exemplo n.º 13
0
def editTextFileView(fsPathString=''):
    """Edit-text-file route."""
    user = g.user
    db = dbGetDatabase()
    lsPath = splitPathString(fsPathString)
    boxPath, fileName = lsPath[:-1], lsPath[-1]
    request._onErrorUrl = url_for(
        'lsView',
        lsPathString='/'.join(boxPath[1:]),
    )
    fileStorageDirectory = g.settings['system']['system_directories'][
        'fs_directory']['value']
    parentBox = getBoxFromPath(db, boxPath, user)
    file = getFileFromParent(db, parentBox, fileName, user)
    if file is None:
        raise OstracionError('File not found.')
    else:
        fileActions = prepareFileActions(
            db,
            file,
            boxPath[1:] + [file.name],
            parentBox,
            user,
            discardedActions={'text_edit'},
        )
        fileInfo = prepareFileInfo(db, file)
        form = EditTextFileForm()
        form.textformat.choices = [
            (mId, mDesc['title'])
            for mId, mDesc in sorted(
                textFileViewingModes.items(),
                key=lambda kv: kv[1]['index'],
            )
        ]
        ##
        if form.validate_on_submit():
            newcontents = form.filecontents.data
            filePath = fileIdToPath(
                file.file_id,
                fileStorageDirectory=fileStorageDirectory,
            )
            with open(filePath, 'w') as openFile:
                openFile.write('%s' % newcontents)
            # file properties
            fileProperties = determineFileProperties(filePath)
            newFile = File(**file.asDict())
            newFile.mime_type = fileProperties['file_mime_type']
            newFile.type = fileProperties['file_type']
            newFile.size = fileProperties['file_size']
            newFile.textual_mode = form.textformat.data
            newFile.editor_username = user.username
            updateFile(db, boxPath, file.name, newFile, user)
            #
            flashMessage('Info', 'Info', 'File "%s" saved.' % file.name)
            return redirect(
                url_for(
                    'lsView',
                    lsPathString='/'.join(boxPath[1:]),
                )
            )
        else:
            form.filecontents.data = applyDefault(
                form.filecontents.data,
                open(
                    fileIdToPath(
                        file.file_id,
                        fileStorageDirectory=fileStorageDirectory,
                    )
                ).read(),
            )
            form.textformat.data = applyDefault(
                form.textformat.data,
                file.textual_mode,
                additionalNulls=['None'],
            )
            pathBCrumbs = makeBreadCrumbs(
                boxPath,
                g,
                appendedItems=[
                    {
                        'kind': 'file',
                        'target': file,
                    },
                    {
                        'kind': 'link',
                        'target': None,
                        'name': 'Edit text',
                    }
                ],
            )
            return render_template(
                'edittextfile.html',
                user=user,
                form=form,
                fileActions=fileActions,
                fileInfo=fileInfo,
                pageTitle='"%s" file edit' % file.name,
                pageSubtitle='Click "Save" to commit the changes',
                breadCrumbs=pathBCrumbs,
            )
Exemplo n.º 14
0
def loginView():
    """Login view."""
    user = g.user
    db = dbGetDatabase()
    g._onErrorUrl = url_for('loginView')
    if user is not None and user.is_authenticated:
        return redirect(url_for('indexView'))
    form = LoginForm()
    loginProtectionSeconds = int(g.settings['behaviour']['behaviour_security']
                                 ['login_protection_seconds']['value'])
    if form.validate_on_submit():
        #
        loginWaitSeconds = secondsToWaitBeforeLogin(
            db,
            request.remote_addr,
            doWrite=True,
            loginProtectionSeconds=loginProtectionSeconds,
            hashSalt=g.settings['behaviour']['behaviour_security']
            ['ip_addr_hashing_salt']['value'],
        )
        if loginWaitSeconds <= 0:
            #
            qUser = dbGetUser(db, form.username.data)
            if qUser and qUser.checkPassword(form.password.data):
                #
                newUser = User(**qUser.asDict())
                newUser.last_login = datetime.datetime.now()
                dbUpdateUser(db, newUser, qUser)
                #
                login_user(qUser)
                #
                flashMessage(
                    'Success',
                    'Login successful',
                    'welcome, %s!' % (qUser.fullname, ),
                )
                #
                return redirect(url_for('indexView'))
            else:
                g._onErrorUrl = url_for(
                    'loginView',
                    username=form.username.data,
                )
                raise OstracionError('invalid username or password')
        else:
            raise OstracionWarning(
                ('Automated repeated login protection. '
                 'Please wait %i seconds ...') % int(loginWaitSeconds))
    else:
        requestParams = request.args
        form.username.data = applyDefault(
            form.username.data,
            requestParams.get('username', ''),
        )
        appShortName = g.settings['behaviour']['behaviour_appearance'][
            'application_short_name']['value']
        iconUrl = makeSettingImageUrl(g, 'user_images', 'login')
        return render_template(
            'login.html',
            form=form,
            user=user,
            pageTitle=loginTitle(g),
            pageSubtitle=loginSubtitle(g),
            iconUrl=iconUrl,
        )
Exemplo n.º 15
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))
Exemplo n.º 16
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')
Exemplo n.º 17
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'))
Exemplo n.º 18
0
def before_request():
    """ Preparation of globally-accessible info for all views,
        handling of special post-install situations and of banned users
        and logged-in-only setups.
    """
    db = dbGetDatabase()
    g.user = current_user
    g.settings = dbLoadAllSettings(db, g.user)
    g.applicationLogoUrl = makeSettingImageUrl(
        g,
        'ostracion_images',
        'navbar_logo',
    )
    #
    g.canPerformSearch = isUserWithinPermissionCircle(
        db,
        g.user,
        g.settings['behaviour']['search']['search_access']['value'],
    )
    g.quickFindForm = QuickFindForm()
    g.canShowTreeView = isUserWithinPermissionCircle(
        db,
        g.user,
        g.settings['behaviour']['search']['tree_view_access']['value'],
    )
    # task lists - must happen after the above (which set access flags)
    g.availableApps = selectAvailableApps(db, g.user, g)
    g.availableInfoItems = selectAvailableInfoItems(db, g.user, g)
    g.availableTools = selectAvailableTools(db, g.user, g)
    #
    if g.user.is_authenticated:
        g.user.setRoles(list(dbGetUserRoles(db, g.user)))
        if g.user.banned:
            flashMessage(
                'Warning',
                'Warning',
                ('Your user is momentarily banned. '
                 'Please continue as anonymous'),
            )
            logout_user()
    # handling of first-time post-install forced steps
    dirsSetUp = g.settings['managed']['post_install_finalisations'][
        'directories_set_up']['value']
    if (not dirsSetUp
            and request.endpoint not in endpointsWithoutAppInitRerouting):
        if g.user.is_authenticated and userIsAdmin(db, g.user):
            flashMessage(
                'Warning',
                'Action required',
                ('Please review carefully the following directory settings '
                 'to make the application function. Changing them later, '
                 'when material is uploaded, is a disruptive operation.'),
            )
            return redirect(
                url_for('adminHomeSettingsGenericView',
                        settingType='system_settings'))
        else:
            if g.user.is_authenticated:
                logout_user()
            flashMessage(
                'Warning',
                'Action required',
                ('To finalise installation/make the application function '
                 'properly, an administrator should log in and '
                 'complete the setup'),
            )
            return redirect(url_for('loginView'))
    else:
        # we care about reviewing the settings only if the dirs are OK
        if g.user.is_authenticated and userIsAdmin(db, g.user):
            if request.endpoint not in endpointsWithoutAppInitRerouting:
                settingsReviewed = g.settings['managed'][
                    'post_install_finalisations']['settings_reviewed']['value']
                if not settingsReviewed:
                    flashMessage(
                        'Warning',
                        'Action required',
                        ('Please carefully review the following application '
                         'behaviour settings before opening the service '
                         'to users (anyway, these settings can be '
                         'changed at any time).'),
                    )
                    return redirect(
                        url_for('adminHomeSettingsGenericView',
                                settingType='behaviour'))
    # we take care of terms-and-conditions acceptance here
    if request.endpoint not in endpointsWithoutTermAcceptanceBlocking:
        usersMustAbideByTOS = g.settings['terms']['terms']['terms_must_agree'][
            'value']
        anonymousMustAbideByTOS = g.settings['terms']['terms'][
            'terms_must_agree_anonymous']['value']
        currentTermVersion = g.settings['terms']['terms']['terms_version'][
            'value']
        #
        if g.user.is_authenticated:
            mustAbideByTOS = usersMustAbideByTOS
            typeOfUsers = 'Users'
            storedTermsVersion = g.user.terms_accepted_version
            storedTermsAcceptance = safeInt(
                g.user.terms_accepted,
                default=None,
            )
        else:
            mustAbideByTOS = anonymousMustAbideByTOS
            typeOfUsers = 'Visitors'
            storedTermsVersion = request.cookies.get('termsAcceptedVersion')
            storedTermsAcceptance = safeInt(
                request.cookies.get('termsAccepted'),
                default=None,
            )
        #
        if storedTermsVersion == currentTermVersion:
            thisVersionAcceptance = storedTermsAcceptance
        else:
            thisVersionAcceptance = None
        #
        if thisVersionAcceptance is None:
            canContinueTermwise = not mustAbideByTOS
        elif thisVersionAcceptance == 0:
            # explicit not-acceptance
            canContinueTermwise = False
        else:
            # positive updated acceptance
            canContinueTermwise = True
        #
        if not canContinueTermwise:
            quotedTravelToPath = urllib.parse.quote_plus(request.full_path)
            flashMessage(
                'Info',
                'Action required',
                ('%s must review the Terms and Conditions'
                 ' before accessing the site.') % typeOfUsers,
            )
            return redirect(
                url_for(
                    'termsView',
                    showAgreementButtons='y',
                    travelTo=quotedTravelToPath,
                ))

    # here the ordinary flow is resumed
    if g.settings['behaviour']['behaviour_permissions'][
            'logged_in_users_only']['value']:
        if (not g.user.is_authenticated and request.endpoint
                not in endpointsWithoutInviteOnlyBlocking):
            return redirect(url_for('loginView'))