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:]), ))
def fsDoMoveBoxView(quotedSrcBox, quotedDestBox=''): """ Move-box-phase-2 route: where, with source and dest boxes as quoted-plus strings, the actual moving is performed. """ user = g.user db = dbGetDatabase() # we retrieve the source box, its current parent # and the destination box srcBoxPathString = urllib.parse.unquote_plus(quotedSrcBox) srcBoxPath = splitPathString(srcBoxPathString) box = getBoxFromPath(db, srcBoxPath, user) srcBox = getBoxFromPath(db, srcBoxPath[:-1], user) dstBoxPathString = urllib.parse.unquote_plus(quotedDestBox) dstBoxPath = splitPathString(dstBoxPathString) dstBox = getBoxFromPath(db, dstBoxPath, user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(srcBoxPath[1:-1]), ) # we attempt the move moveBox( db, box, srcBox, dstBox, user, ) return redirect(url_for( 'lsView', lsPathString='/'.join(dstBoxPath[1:]), ))
def ticketGalleryFsView(ticketId, securityCode, fileName): """ View-file-within-a-ticket-generated-gallery-view route. Helper endpoint to return viewable (only viewables, there's no 'download') files in a ticket-gallery view. Must take care of punching the ticket. """ user = g.user db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] # richTicket = dbGetEnrichAndCheckTicket( db, 'g', ticketId, securityCode, request.url_root, ) if richTicket is not None: issuer = dbGetUser(db, richTicket['ticket'].username) if richTicket['redeemable']: # valid ticket. Further checks are on the way. if (not g.settings['behaviour']['behaviour_tickets'] ['protect_banned_user_tickets']['value'] or issuer.banned == 0): # boxPath = richTicket['metadata']['box_path'] request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBox = getBoxFromPath(db, boxPath[1:], issuer) if parentBox is not None: # we retrieve the file and serve it file = getFileFromParent(db, parentBox, fileName, issuer) if file is not None: dbPunchRichTicket(db, richTicket) filePhysicalPath, filePhysicalName = fileIdToSplitPath( file.file_id, fileStorageDirectory=fileStorageDirectory, ) return send_from_directory( filePhysicalPath, filePhysicalName, attachment_filename=file.name, as_attachment=True, mimetype=file.mime_type, ) else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable')
def boxThumbnailView(dummyId, boxPathString=''): """Route for access to thumbnail image files based on box path.""" user = g.user db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] if boxPathString == '': # root case return redirect(makeSettingImageUrl(g, 'app_images', 'root_box')) else: db = dbGetDatabase() boxPath = splitPathString(boxPathString) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) box = getBoxFromPath(db, boxPath, user) if (box is not None and box.icon_file_id is not None and box.icon_file_id != ''): filePhysicalPath, filePhysicalName = fileIdToSplitPath( box.icon_file_id, fileStorageDirectory=fileStorageDirectory, ) return send_from_directory( filePhysicalPath, filePhysicalName, mimetype=box.icon_mime_type, ) else: return redirect( makeSettingImageUrl( g, 'app_images', 'standard_box', ))
def fsDownloadView(fsPathString=''): """Download-file view.""" user = g.user lsPath = splitPathString(fsPathString) boxPath, fileName = lsPath[:-1], lsPath[-1] db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] parentBox = getBoxFromPath(db, boxPath, user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) if parentBox is not None: file = getFileFromParent(db, parentBox, fileName, user) if file is not None: filePhysicalPath, filePhysicalName = fileIdToSplitPath( file.file_id, fileStorageDirectory=fileStorageDirectory, ) return send_from_directory( filePhysicalPath, filePhysicalName, attachment_filename=file.name, as_attachment=True, mimetype=file.mime_type, ) else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable')
def fsDeleteFileView(fsPathString=''): """ Delete-file view. Permission checks are performed by the deleteFile filesystem call. """ user = g.user db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] lsPath = splitPathString(fsPathString) boxPath, fileName = lsPath[:-1], lsPath[-1] parentBox = getBoxFromPath(db, boxPath, user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) file = getFileFromParent(db, parentBox, fileName, user) fsDeleteQueue = deleteFile( db, parentBox, file, user, fileStorageDirectory=fileStorageDirectory, ) flushFsDeleteQueue(fsDeleteQueue) return redirect(url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ))
def fsMetadataView(fsPathString=''): """Edit-file-metadata view, a web-form.""" user = g.user db = dbGetDatabase() form = FileDataForm() lsPath = splitPathString(fsPathString) boxPath, prevFileName = lsPath[:-1], lsPath[-1] parentBox = getBoxFromPath(db, boxPath, user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) file = getFileFromParent(db, parentBox, prevFileName, user) if form.validate_on_submit(): newFile = File(**recursivelyMergeDictionaries( { 'name': form.filename.data, 'description': form.filedescription.data, 'metadata_username': user.username, }, defaultMap=file.asDict(), )) updateFile(db, boxPath, prevFileName, newFile, user) return redirect(url_for('lsView', lsPathString='/'.join(boxPath[1:]))) else: form.filename.data = applyDefault(form.filename.data, file.name) form.filedescription.data = applyDefault(form.filedescription.data, file.description) # pageFeatures = { 'breadCrumbs': makeBreadCrumbs( boxPath, g, appendedItems=[{ 'kind': 'file', 'target': file, }, { 'kind': 'link', 'target': None, 'name': 'Metadata', }], ), 'pageTitle': 'Edit file metadata (%s)' % file.name, 'pageSubtitle': 'Name is mandatory', 'iconUrl': url_for( 'fileThumbnailView', dummyId=file.icon_file_id + '_', fsPathString='/'.join(lsPath[1:]), ), } return render_template( 'filedataedit.html', form=form, user=user, **pageFeatures, )
def fileThumbnailView(dummyId, fsPathString): """Route for access to thumbnail image files based on file path.""" user = g.user lsPath = splitPathString(fsPathString) boxPath, fileName = lsPath[:-1], lsPath[-1] db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] parentBox = getBoxFromPath(db, boxPath, user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) file = getFileFromParent(db, parentBox, fileName, user) if (file is not None and file.icon_file_id is not None and file.icon_file_id != ''): filePhysicalPath, filePhysicalName = fileIdToSplitPath( file.icon_file_id, fileStorageDirectory=fileStorageDirectory, ) return send_from_directory( filePhysicalPath, filePhysicalName, mimetype=file.icon_mime_type, ) else: return redirect(pickFileThumbnail(file.mime_type))
def linkThumbnailView(dummyId, fsPathString=''): """Route for access to thumbnail image files based on link path.""" user = g.user lsPath = splitPathString(fsPathString) boxPath, linkName = lsPath[:-1], lsPath[-1] db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] parentBox = getBoxFromPath(db, boxPath, user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) link = getLinkFromParent(db, parentBox, linkName, user) if (link is not None and link.icon_file_id is not None and link.icon_file_id != ''): filePhysicalPath, filePhysicalName = fileIdToSplitPath( link.icon_file_id, fileStorageDirectory=fileStorageDirectory, ) return send_from_directory( filePhysicalPath, filePhysicalName, mimetype=link.icon_mime_type, ) else: return redirect(makeSettingImageUrl(g, 'app_images', 'external_link'))
def ticketFsDownloadView(ticketId, securityCode): """ Give-the-file-contents-based-on-ticket route. Helper endpoint to load-and-return a file upon a ticket; access to files based on a ticket. Used by both the direct-file-download or the view-file file-ticket modes. Note: punching occurs here. """ user = g.user db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] # richTicket = dbGetEnrichAndCheckTicket( db, 'f', ticketId, securityCode, request.url_root, ) issuer = (dbGetUser(db, richTicket['ticket'].username) if richTicket is not None else None) noBandUsrTickets = g.settings['behaviour']['behaviour_tickets'][ 'protect_banned_user_tickets']['value'] if (issuer is not None and (not noBandUsrTickets or issuer.banned == 0)): boxPath, fileName = ( richTicket['metadata']['path'][:-1], richTicket['metadata']['path'][-1], ) parentBox = getBoxFromPath(db, boxPath, issuer) # if parentBox is not None: file = getFileFromParent(db, parentBox, fileName, issuer) if file is not None: # return it and contextually punch the ticket dbPunchRichTicket(db, richTicket) # then we return the file as a download # (this flow assumes download is desired as opposed to view) filePhysicalPath, filePhysicalName = fileIdToSplitPath( file.file_id, fileStorageDirectory=fileStorageDirectory, ) return send_from_directory( filePhysicalPath, filePhysicalName, attachment_filename=file.name, as_attachment=True, mimetype=file.mime_type, ) else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable') else: return abort(404, 'Content unavailable')
def makeLinkView(fsPathString=''): """Generate-new-link route.""" user = g.user form = EditLinkForm() db = dbGetDatabase() boxPath = splitPathString(fsPathString) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBoxPath = boxPath parentBox = getBoxFromPath(db, parentBoxPath, user) if form.validate_on_submit(): linkName = secure_filename(form.linkname.data) linkTitle = form.linktitle.data linkDescription = form.linkdescription.data linkTarget = form.linktarget.data openInNewWindow = form.openinnewwindow.data savingResult = makeLinkInParent( db=db, user=user, parentBox=parentBox, date=datetime.now(), linkName=linkName, linkTitle=linkTitle, linkDescription=linkDescription, linkTarget=linkTarget, linkOptions={ 'open_in_new_window': openInNewWindow, }, ) return redirect(url_for( 'lsView', lsPathString=fsPathString, )) else: pathBCrumbs = makeBreadCrumbs( parentBoxPath, g, appendedItems=[{ 'kind': 'link', 'target': None, 'name': 'New link', }], ) form.openinnewwindow.data = True return render_template( 'editlink.html', form=form, user=user, breadCrumbs=pathBCrumbs, iconUrl=makeSettingImageUrl(g, 'app_images', 'external_link'), pageTitle='New link', pageSubtitle='Create a new link in "%s"' % (describeBoxTitle(parentBox)), )
def deleteBoxView(boxPathString=''): """RMBOX route.""" user = g.user db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] boxPath = splitPathString(boxPathString) box = getBoxFromPath(db, boxPath, user) parentBox = getBoxFromPath(db, boxPath[:-1], user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1]), ) # canDelete = canDeleteBox(db, box, parentBox, user) # if canDelete: if parentBox is None: # deletee box was root raise OstracionError('Cannot act on this object') else: if box is not None: fsDeleteQueue = deleteBox( db, box, parentBox, user, fileStorageDirectory=fileStorageDirectory, ) flushFsDeleteQueue(fsDeleteQueue) else: raise OstracionError('Box not accessible') else: raise OstracionError('Cannot delete this box') return redirect(url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1]), ))
def fsHybridView(fsPathString=''): """ "Hybrid" file view: if it is a textually-viewable file (such as plaintext but most importantly markdown), show it as 'view'; if it is anything else (especially an image), return it as 'download': this is to make sure embedding of images in markdown documents works as usual. """ user = g.user lsPath = splitPathString(fsPathString) boxPath, fileName = lsPath[:-1], lsPath[-1] db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] parentBox = getBoxFromPath(db, boxPath, user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) file = getFileFromParent(db, parentBox, fileName, user) isTextual = isFileTextViewable(file) if isTextual: return fsView(fsPathString=fsPathString) else: return fsDownloadView(fsPathString=fsPathString)
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))
def fsMoveFileView(quotedFilePath): """Move-file, view 1/2: select destination box.""" user = g.user db = dbGetDatabase() # first we find the file fsPathString = urllib.parse.unquote_plus(quotedFilePath) lsPath = splitPathString(fsPathString) boxPath, fileName = lsPath[:-1], lsPath[-1] parentBox = getBoxFromPath(db, boxPath, user) file = getFileFromParent(db, parentBox, fileName, user) # next we prepare the selectable destinations rootBox = getRootBox(db) def rbPredicate(richBox, idToAvoid=parentBox.box_id): return all((richBox['box'].box_id != idToAvoid, userHasPermission(db, user, richBox['box'].permissions, 'w'))) dstBoxTree = collectTreeFromBox( db, rootBox, user, admitFiles=False, fileOrBoxEnricher=lambda richBox: { 'obj_path': urllib.parse.quote_plus('/'.join(richBox['path'])), }, predicate=rbPredicate, ) # maxDepth = getMaxTreeDepth(dstBoxTree) colorShadeMap = prepareColorShadeMap( g.settings['color']['navigation_colors']['file']['value'], g.settings['color']['tree_shade_colors']['shade_treeview_pickbox'] ['value'], numShades=1 + maxDepth, ) destinationsExist = treeAny( dstBoxTree, property=lambda node: node['predicate'], ) # return render_template( 'dirtreeview.html', tree=dstBoxTree if destinationsExist else None, mode='file_move', object_quoted_path=quotedFilePath, colorShadeMap=colorShadeMap, user=user, iconUrl=makeSettingImageUrl(g, 'app_images', 'move'), pageTitle='Select file destination', pageSubtitle=(('Choose the box to which file "%s" shall ' 'be moved from "%s"') % ( file.name, describeBoxTitle(parentBox), )), actions=None, backToUrl=None if destinationsExist else url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ), breadCrumbs=makeBreadCrumbs( boxPath, g, appendedItems=[{ 'kind': 'file', 'target': file, }, { 'kind': 'link', 'target': None, 'name': 'Move file', }], ), )
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)
def makeBoxView(parentBoxPathString=''): """MKBOX-in-a-box- route.""" user = g.user form = makeMakeBoxForm('Create')() db = dbGetDatabase() parentBoxPath = splitPathString(parentBoxPathString) parentBox = getBoxFromPath(db, parentBoxPath, user) boxNiceName = describeBoxTitle(parentBox) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(parentBoxPath), ) if form.validate_on_submit(): # boxName = form.boxname.data boxDescription = form.boxdescription.data boxTitle = form.boxtitle.data # newBox = Box( box_name=boxName, title=boxTitle, description=boxDescription, icon_file_id='', nature='box', parent_id=parentBox.box_id, date=datetime.now(), creator_username=user.username, icon_file_id_username=user.username, metadata_username=user.username, ) makeBoxInParent(db, parentBox, newBox, user, skipCommit=True) if parentBox.box_id == '': # the new box is a direct child of root: # act according to the localhost-mode policy setting rootChildAnonPerms = g.settings['behaviour'][ 'behaviour_permissions'][ 'rootchild_box_anonymous_permissions']['value'] rcPermSet = { k: 1 if rootChildAnonPerms[i] != '_' else 0 for i, k in enumerate('rwc') } # the above is a bit of a hack which parses the char # of the setting 'r__', 'rwc', ... rootChildBRP = BoxRolePermission( box_id=newBox.box_id, role_class='system', role_id='anonymous', **rcPermSet, ) dbInsertBoxRolePermission( db, rootChildBRP, user, skipCommit=True, ) # db.commit() additionalLsItems = [boxName] # return redirect(url_for( 'lsView', lsPathString='/'.join(parentBoxPath[1:] + additionalLsItems), )) else: pathBCrumbs = makeBreadCrumbs( parentBoxPath, g, appendedItems=[{ 'kind': 'link', 'target': None, 'name': 'Make box', }], ) return render_template( 'makebox.html', form=form, user=user, breadCrumbs=pathBCrumbs, iconUrl=makeSettingImageUrl(g, 'app_images', 'standard_box'), pageTitle='Create box in "%s"' % boxNiceName, pageSubtitle='Name and title are mandatory', )
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')
def fsView(fsPathString=''): """View file route. If not viewable, display an error message.""" user = g.user lsPath = splitPathString(fsPathString) boxPath, fileName = lsPath[:-1], lsPath[-1] db = dbGetDatabase() fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] parentBox = getBoxFromPath(db, boxPath, user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) file = getFileFromParent(db, parentBox, fileName, user) if file is not None: fileInfo = prepareFileInfo(db, file) fileActions = prepareFileActions( db, file, boxPath[1:] + [file.name], parentBox, user, discardedActions={'view'}, ) fileContents = produceFileViewContents( db, file, mode='fsview', viewParameters={ 'boxPath': boxPath, 'fileName': fileName, }, fileStorageDirectory=fileStorageDirectory, ) # pathBCrumbs = makeBreadCrumbs( boxPath, g, appendedItems=[{ 'kind': 'file', 'target': file, }], ) # return render_template( 'fileview.html', fileActions=fileActions, fileInfo=fileInfo, filecontents=fileContents, breadCrumbs=pathBCrumbs, user=user, pageTitle='"%s" file view' % file.name, pageSubtitle='%s (%s; %s)' % ( file.description, file.type, formatBytesSize(file.size), ), downloadUrl=None, ) else: return abort(404)
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, )
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])
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, )
def retrieveFileUponTicket(db, user, issuer, richTicket, urlRoot): """ Retrieve a file using a file ticket. Retrieval fails if either: 1. user banned; 2. object not available to the issuer. """ noBandUsrTickets = g.settings['behaviour']['behaviour_tickets'][ 'protect_banned_user_tickets']['value'] if (not noBandUsrTickets or issuer.banned == 0): boxPath, fileName = ( richTicket['metadata']['path'][:-1], richTicket['metadata']['path'][-1], ) fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] parentBox = getBoxFromPath(db, boxPath, issuer) # if parentBox is not None: file = getFileFromParent(db, parentBox, fileName, issuer) if file is not None: if richTicket['metadata'].get('file_mode') == 'view': # either we offer a view-type page # (with ticketMessage and optionally view) # and decide whether to punch the ticket and how ... fileRetrievalUrl = url_for( 'ticketFsDownloadView', ticketId=richTicket['ticket'].ticket_id, securityCode=richTicket['ticket'].security_code, ) # for mode='ticketview'this will internally call # the punching endpoint 'ticketFsDownloadView' below. fileContents = produceFileViewContents( db, file, mode='ticketview', viewParameters={ 'ticketId': richTicket['ticket'].ticket_id, 'securityCode': richTicket['ticket'].security_code, }, fileStorageDirectory=fileStorageDirectory, urlRoot=urlRoot, protectBannedUserTickets=noBandUsrTickets, ) return render_template( 'fileview.html', fileActions=None, fileInfo=None, filecontents=fileContents, breadCrumbs=[], user=user, pageTitle='File access', pageSubtitle='View/download the file%s' % ('' if richTicket['metadata'].get('message') is None else ('. Message on ticket: "%s"' % (richTicket['metadata']['message']))), downloadUrl=fileRetrievalUrl, ) elif richTicket['metadata'].get('file_mode') == 'direct': # ... or we offer a direct download and punch the ticket... return ticketFsDownloadView( richTicket['ticket'].ticket_id, richTicket['ticket'].security_code, ) else: raise NotImplementedError( 'Unknown file_mode "%s" in file ticket' % (richTicket['metadata'].get('file_mode'), )) else: raise OstracionError('Ticket target unavailable') else: raise OstracionError('Ticket target unavailable') else: raise OstracionError('Ticket cannot be redeemed')
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')
def _resolveUponPermissions(db, richObject, user, mode, score): """ Verify and validate the input (rich-)box/file against permissions, and return either a similar structure or a None if permissions turn out to be blocking. ( richObject has keys "path" and "box/file", mode = 'file' / 'box' ) """ pBox = getBoxFromPath(db, richObject['path'][:-1], user) if mode == 'box': nBox = getBoxFromPath(db, richObject['path'], user) if nBox is not None and pBox is not None: return { 'path': richObject['path'][1:], 'box': nBox, 'actions': prepareBoxActions(db, nBox, richObject['path'][1:], pBox, user, prepareParentButton=True), 'info': prepareBoxInfo(db, nBox), 'parentInfo': 'Parent box: "%s"' % (describeBoxTitle(pBox), ), 'object_type': mode, 'score': score, } else: return None elif mode == 'file': if pBox is not None: nFile = getFileFromParent(db, pBox, richObject['path'][-1], user) if nFile is not None: return { 'path': richObject['path'][1:], 'file': nFile, 'actions': prepareFileActions(db, nFile, richObject['path'][1:], pBox, user, prepareParentButton=True), 'info': prepareFileInfo(db, nFile), 'nice_size': formatBytesSize(nFile.size), 'parentInfo': 'Container box: "%s"' % (describeBoxTitle(pBox), ), 'object_type': mode, 'score': score, } else: return None else: return None elif mode == 'link': if pBox is not None: nLink = getLinkFromParent(db, pBox, richObject['path'][-1], user) if nLink is not None: return { 'path': richObject['path'][1:], 'link': nLink, 'actions': prepareLinkActions(db, nLink, richObject['path'][1:], pBox, user, prepareParentButton=True), 'info': prepareLinkInfo(db, nLink), 'parentInfo': 'Container box: "%s"' % (describeBoxTitle(pBox), ), 'object_type': mode, 'score': score, } else: return None else: return None else: raise NotImplementedError('Unknown _resolveUponPermissions mode "%s"' % mode)
def metadataBoxView(boxPathString=''): """edit-metadata-of-box route.""" user = g.user db = dbGetDatabase() boxPath = splitPathString(boxPathString) box = getBoxFromPath(db, boxPath, user) parentBox = getBoxFromPath(db, boxPath[:-1], user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1]), ) # form = makeMakeBoxForm('Save')() if parentBox is None: # target box was root raise OstracionError('Cannot act on this object') else: if box is not None: # if form.validate_on_submit(): newBox = Box( **recursivelyMergeDictionaries( { 'box_name': form.boxname.data, 'title': form.boxtitle.data, 'description': form.boxdescription.data, 'metadata_username': user.username, }, defaultMap=box.asDict(), ) ) updateBox(db, boxPath, newBox, user) return redirect(url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1]), )) else: form.boxname.data = applyDefault( form.boxname.data, box.box_name, ) form.boxdescription.data = applyDefault( form.boxdescription.data, box.description, ) form.boxtitle.data = applyDefault( form.boxtitle.data, box.title, ) pathBCrumbs = makeBreadCrumbs( boxPath, g, appendedItems=[{ 'kind': 'link', 'target': None, 'name': 'Metadata', }], ) return render_template( 'makebox.html', form=form, user=user, breadCrumbs=pathBCrumbs, pageTitle='Edit box metadata', pageSubtitle='Name and title are mandatory', iconUrl=url_for( 'boxThumbnailView', dummyId=box.icon_file_id + '_', boxPathString='/'.join(boxPath[1:]), ), ) # else: raise OstracionWarning('Box not accessible') return redirect(url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1]), ))
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'))
def fsMoveBoxView(quotedSrcBox): """ Move-box-phase-1 route: where the source box is selected (and then in offering the choose-dest-box it is put in the URL for calling phase two). """ user = g.user db = dbGetDatabase() # first we find the box bxPathString = urllib.parse.unquote_plus(quotedSrcBox) boxPath = splitPathString(bxPathString) parentBox = getBoxFromPath(db, boxPath[:-1], user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1]), ) box = getBoxFromPath(db, boxPath, user) if not canDeleteBox(db, box, parentBox, user): raise OstracionWarning('Cannot act on this object') # next we prepare the selectable destinations rootBox = getRootBox(db) def rbPredicate(richBox, _box=box, _srcBox=parentBox): _dstBox = richBox['box'] if _box.parent_id != _dstBox.box_id: if all(userHasPermission(db, user, _dstBox.permissions, prm) for prm in {'w', 'c'}): if not isNameUnderParentBox(db, _dstBox, _box.box_name): if not isAncestorBoxOf(db, _box, _dstBox): return True return False dstBoxTree = collectTreeFromBox( db, rootBox, user, admitFiles=False, fileOrBoxEnricher=lambda richBox: { 'obj_path': urllib.parse.quote_plus('/'.join(richBox['path'])), }, predicate=rbPredicate, ) # maxDepth = getMaxTreeDepth(dstBoxTree) colorShadeMap = prepareColorShadeMap( g.settings['color']['navigation_colors']['box']['value'], g.settings['color']['tree_shade_colors'][ 'shade_treeview_pickbox']['value'], numShades=1 + maxDepth, ) destinationsExist = treeAny( dstBoxTree, property=lambda node: node['predicate'], ) # return render_template( 'dirtreeview.html', tree=dstBoxTree if destinationsExist else None, mode='box_move', object_quoted_path=quotedSrcBox, colorShadeMap=colorShadeMap, user=user, iconUrl=makeSettingImageUrl(g, 'app_images', 'move'), pageTitle='Select box destination', pageSubtitle=('Choose the box to which box "%s" ' 'shall be moved from "%s"') % ( describeBoxTitle(box), describeBoxTitle(parentBox), ), actions=None, backToUrl=(None if destinationsExist else url_for( 'lsView', lsPathString='/'.join(boxPath[1:]))), breadCrumbs=makeBreadCrumbs( boxPath, g, appendedItems=[ { 'kind': 'link', 'target': None, 'name': 'Move box', } ], ), )
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 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')