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 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 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 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 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 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 saveAndAnalyseFilesInBox(db, files, parentBox, user, thumbnailFormat, fileStorageDirectory, pastActionVerbForm='uploaded'): """ Save files and enrich them with type/mimetype, unless something fails - this handles overwrites (file-on-file) and blockades (file has same name as box) """ # # checking for name clashes with boxes if any( isBoxNameUnderParentBox(db, parentBox, fName) for fName in (fObj['name'] for fObj in files)): raise OstracionError('Files cannot have the name of existing boxes') else: if not userHasPermission(db, user, parentBox.permissions, 'w'): raise OstracionError('User has no write permission on this box') else: userName = user.username fsDeletionQueue = [] numReplacements = 0 # for file in files: newFile = File(**recursivelyMergeDictionaries( {k: v for k, v in file.items() if k != 'fileObject'}, defaultMap={ 'creator_username': userName, 'icon_file_id_username': userName, 'metadata_username': userName, 'editor_username': userName, 'textual_mode': 'plain', }, )) # are we overwriting a file? if isFileNameUnderParentBox(db, parentBox, newFile.name): # delete the old file entry # AND mark the file and its thumbnail # (if any) for later deletion prevFile = getFileFromParent( db, parentBox, newFile.name, user, ) fsDeletionQueue += deleteFile( db, parentBox, prevFile, user, fileStorageDirectory=fileStorageDirectory, skipCommit=True, ) numReplacements += 1 # filePath = fileIdToPath( newFile.file_id, fileStorageDirectory=fileStorageDirectory, ) file['fileObject'].save(filePath) # fileProperties = determineFileProperties(filePath) newFile.mime_type = fileProperties['file_mime_type'] newFile.type = fileProperties['file_type'] newFile.size = fileProperties['file_size'] # if (thumbnailFormat is not None and isImageMimeType(newFile.mime_type)): # thumbnail preparation fileThumbnailId, fileThumbnailMimeType = makeFileThumbnail( newFile.file_id, newFile.mime_type, thumbnailFormat=thumbnailFormat, fileStorageDirectory=fileStorageDirectory, ) if fileThumbnailId is not None: newFile.icon_file_id = fileThumbnailId newFile.icon_mime_type = fileThumbnailMimeType # makeFileInParent(db, parentBox=parentBox, newFile=newFile) flushFsDeleteQueue(fsDeletionQueue) db.commit() return '%i file%s %s successfully%s.' % ( len(files), '' if len(files) == 1 else 's', pastActionVerbForm, '' if numReplacements == 0 else (' (%i replaced)' % numReplacements))
def _traverseForAccountDeletion(db, parentBox, username, user, path, fileStorageDirectory): """ Traverse the tree (one call per box, recursively+iteratively) and collect a delete queue while updating items found in various ways (deletions, icon resets, metadata resets, forced renames). """ fsDeleteQueue = [] for file in getFilesFromBox(db, parentBox): if (file.creator_username == username or file.editor_username == username): fsDeleteQueue += deleteFile( db, parentBox, file, None, fileStorageDirectory=fileStorageDirectory, skipCommit=True, accountDeletionInProgress=True, ) else: if file.icon_file_id_username == username: fsDeleteQueue += updateFileThumbnail( db, file, None, None, user, fileStorageDirectory=fileStorageDirectory, accountDeletionInProgress=True, skipCommit=True, ) iconedFile = getFileFromParent(db, parentBox, file.name, None) else: iconedFile = file # "iconed" as in "icon-wise fixed" if iconedFile.metadata_username == username: newFileName = findFirstAvailableObjectNameInBox( db, parentBox, prefix='REDACTED_FILE_', suffix='', ) newDescription = 'File name redacted upon account deletion' newFile = File(**recursivelyMergeDictionaries( { 'name': newFileName, 'description': newDescription, 'metadata_username': '', }, defaultMap=iconedFile.asDict(), )) updateFile( db, path, iconedFile.name, newFile, None, accountDeletionInProgress=True, skipCommit=True, ) # for link in getLinksFromBox(db, parentBox): if link.creator_username == username: fsDeleteQueue += deleteLink( db, parentBox, link, None, fileStorageDirectory=fileStorageDirectory, skipCommit=True, accountDeletionInProgress=True, ) else: if link.icon_file_id_username == username: fsDeleteQueue += updateLinkThumbnail( db, link, None, None, user, fileStorageDirectory=fileStorageDirectory, accountDeletionInProgress=True, skipCommit=True, ) iconedLink = getLinkFromParent(db, parentBox, link.name, None) else: iconedLink = link # "iconed" as in "icon-wise fixed" if iconedLink.metadata_username == username: newLinkName = findFirstAvailableObjectNameInBox( db, parentBox, prefix='REDACTED_LINK_', suffix='', ) newDescription = 'Link data redacted upon account deletion' newLink = Link(**recursivelyMergeDictionaries( { 'name': newLinkName, 'description': newDescription, 'target': '#', 'metadata_username': '', }, defaultMap=iconedLink.asDict(), )) updateLink( db, path, iconedLink.name, newLink, None, accountDeletionInProgress=True, skipCommit=True, ) # for box in getBoxesFromParent( db, parentBox, None, accountDeletionInProgress=True): if box is not None and box.box_name != '': fsDeleteQueue += _traverseForAccountDeletion( db, box, username, user, path + [box.box_name], fileStorageDirectory=fileStorageDirectory, ) if box.creator_username == username and _boxHasNoChildren(db, box): fsDeleteQueue += deleteBox( db, box, parentBox, None, fileStorageDirectory=fileStorageDirectory, accountDeletionInProgress=True, skipCommit=True, ) else: if (box.icon_file_id_username == username or box.creator_username == username): fsDeleteQueue += updateBoxThumbnail( db, box, None, None, user, fileStorageDirectory=fileStorageDirectory, accountDeletionInProgress=True, skipCommit=True, ) iconedBox = getBoxFromPath( db, path + [box.box_name], None, accountDeletionInProgress=True, ) else: iconedBox = box # if (iconedBox.metadata_username == username or iconedBox.creator_username == username): # newBoxName = findFirstAvailableObjectNameInBox( db, parentBox, prefix='REDACTED_BOX_', suffix='', ) newDescription = 'Box name redacted upon account deletion' mdNewBoxContrib = { 'box_name': newBoxName, 'description': newDescription, 'title': newBoxName, 'metadata_username': '', } # if iconedBox.creator_username == username: cNewBoxContrib = { 'creator_username': '', } else: cNewBoxContrib = {} # newBox = Box(**recursivelyMergeDictionaries( recursivelyMergeDictionaries( mdNewBoxContrib, defaultMap=cNewBoxContrib, ), defaultMap=iconedBox.asDict(), )) updateBox( db, path + [box.box_name], newBox, None, accountDeletionInProgress=True, skipCommit=True, ) # return fsDeleteQueue
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 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, )
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 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 setIconView(mode, itemPathString=''): """ Route to set/replace the thumbnail of various items.""" if (mode == 'au' and not g.settings['behaviour']['behaviour_admin_powers'] ['admin_is_god']['value']): request._onErrorUrl = url_for('adminHomeUsersView') raise OstracionError('This feature is turned off in the configuration') else: user = g.user form = UploadIconForm() db = dbGetDatabase() tempFileDirectory = g.settings['system']['system_directories'][ 'temp_directory']['value'] fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] if mode == 'b': boxPath = splitPathString(itemPathString) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBox = None thisItem = getBoxFromPath(db, boxPath, user) itemName = thisItem.getName() pageFeatures = { 'breadCrumbs': makeBreadCrumbs( splitPathString(itemPathString), g, appendedItems=[{ 'kind': 'link', 'target': None, 'name': 'Icon', }], ), } elif mode == 'f': fullPath = splitPathString(itemPathString) boxPath, fileName = fullPath[:-1], fullPath[-1] request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBox = getBoxFromPath(db, boxPath, user) thisItem = getFileFromParent(db, parentBox, fileName, user) itemName = thisItem.getName() pageFeatures = { 'breadCrumbs': makeBreadCrumbs( splitPathString(itemPathString)[:-1], g, appendedItems=[{ 'kind': 'file', 'target': thisItem, }, { 'kind': 'link', 'target': None, 'name': 'Icon', }], ), } elif mode == 'l': fullPath = splitPathString(itemPathString) boxPath, linkName = fullPath[:-1], fullPath[-1] request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) parentBox = getBoxFromPath(db, boxPath, user) thisItem = getLinkFromParent(db, parentBox, linkName, user) itemName = thisItem.getName() pageFeatures = { 'breadCrumbs': makeBreadCrumbs( splitPathString(itemPathString)[:-1], g, appendedItems=[{ 'kind': 'external_link', 'target': thisItem, }, { 'kind': 'link', 'target': None, 'name': 'Icon', }], ), } elif mode == 'u': pageFeatures = prepareTaskPageFeatures( userProfilePageDescriptor, ['root', 'icon'], g, overrides={ 'pageTitle': None, 'pageSubtitle': None, 'iconUrl': None, }, ) thisItem = user itemName = thisItem.getName() parentBox = None elif mode == 'au': pageFeatures = prepareTaskPageFeatures( adminPageDescriptor, ['root', 'users'], g, appendedItems=[{ 'kind': 'link', 'link': False, 'target': None, 'name': 'Icon', }], overrides={ 'pageTitle': None, 'pageSubtitle': None, 'iconUrl': None, }, ) if userIsAdmin(db, user): thisItem = dbGetUser(db, itemPathString) itemName = thisItem.getName() parentBox = None else: raise OstracionError('Insufficient permissions') elif mode == 's': pageFeatures = prepareTaskPageFeatures( adminPageDescriptor, ['root', 'settings', 'images'], g, appendedItems=[{ 'kind': 'link', 'link': False, 'target': None, 'name': 'Set image', }], overrides={ 'pageTitle': None, 'pageSubtitle': None, 'iconUrl': None, }, ) if userIsAdmin(db, user): settingGroupId, settingId = itemPathString.split('/') thisItem = g.settings['image'][settingGroupId][settingId] itemName = thisItem['setting'].getName() parentBox = None else: raise OstracionError('Insufficient permissions') else: raise RuntimeError('Unknown mode encountered') # if form.validate_on_submit(): # storageSuccess = storeFileAsThumbnail( db, fileToSave=form.file.data, mode=mode, thumbnailFormat=determineThumbnailFormatByModeAndTarget( db, mode, thisItem, ), targetItem=thisItem, parentBox=parentBox, user=user, tempFileDirectory=tempFileDirectory, fileStorageDirectory=fileStorageDirectory, ) if not storageSuccess: raise OstracionError('Could not set the icon') # if mode in {'f', 'b', 'l'}: return redirect( url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1] if mode == 'b' else boxPath[1:]), )) elif mode == 'u': return redirect(url_for('userProfileView', )) elif mode == 'au': return redirect(url_for('adminHomeUsersView', )) elif mode == 's': return redirect(url_for('adminHomeSettingsImagesView', )) else: raise RuntimeError('Unknown mode encountered') else: # titleMap = { 'f': 'Set File Icon', 'l': 'Set Link Icon', 'b': 'Set Box Icon', 'u': 'Set User Icon', 'au': 'Set User Icon (as admin)', 's': 'Set Application Image', } modeNameMap = { 'f': 'for file', 'l': 'for link', 'b': 'for box', 'u': 'for user', 'au': '(as admin) for user', 's': 'for setting', } finalPageFeatures = recursivelyMergeDictionaries( { 'pageTitle': titleMap[mode], 'pageSubtitle': 'Upload an image file %s "%s"' % ( modeNameMap[mode], itemName, ), }, defaultMap=pageFeatures, ) if mode == 'u': finalPageFeatures['iconUrl'] = url_for( 'userThumbnailView', dummyId='%s_' % thisItem.icon_file_id, username=thisItem.username, ) elif mode == 'au': finalPageFeatures['iconUrl'] = url_for( 'userThumbnailView', dummyId='%s_' % thisItem.icon_file_id, username=thisItem.username, ) elif mode == 's': finalPageFeatures['iconUrl'] = makeSettingImageUrl( g, settingGroupId, settingId, ) elif mode == 'b': finalPageFeatures['iconUrl'] = url_for( 'boxThumbnailView', dummyId=thisItem.icon_file_id + '_', boxPathString='/'.join(boxPath[1:]), ) elif mode == 'f': finalPageFeatures['iconUrl'] = url_for( 'fileThumbnailView', dummyId=thisItem.icon_file_id + '_', fsPathString='/'.join(boxPath[1:] + [thisItem.name]), ) elif mode == 'l': finalPageFeatures['iconUrl'] = url_for( 'linkThumbnailView', dummyId=thisItem.icon_file_id + '_', fsPathString='/'.join(boxPath[1:] + [thisItem.name]), ) # return render_template( 'uploadicon.html', form=form, user=user, mode=mode, itemPathString=itemPathString, **finalPageFeatures, )
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 _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 unsetIconView(mode, itemPathString=''): """Route for explicit removal of thumbnail, if any, to various items.""" if (mode == 'au' and not g.settings['behaviour']['behaviour_admin_powers'] ['admin_is_god']['value']): request._onErrorUrl = url_for('adminHomeUsersView') raise OstracionError('This feature is turned off in the configuration') else: user = g.user form = UploadIconForm() db = dbGetDatabase() # tempFileDirectory = g.settings['system']['system_directories'][ 'temp_directory']['value'] fileStorageDirectory = g.settings['system']['system_directories'][ 'fs_directory']['value'] # if mode == 'b': modeName = 'box' boxPath = splitPathString(itemPathString) thisItem = getBoxFromPath(db, boxPath, user) parentBox = None elif mode == 'f': modeName = 'file' fullPath = splitPathString(itemPathString) boxPath, fileName = fullPath[:-1], fullPath[-1] parentBox = getBoxFromPath(db, boxPath, user) thisItem = getFileFromParent(db, parentBox, fileName, user) elif mode == 'l': modeName = 'link' fullPath = splitPathString(itemPathString) boxPath, linkName = fullPath[:-1], fullPath[-1] parentBox = getBoxFromPath(db, boxPath, user) thisItem = getLinkFromParent(db, parentBox, linkName, user) elif mode == 'u': modeName = 'user' parentBox = None thisItem = user elif mode == 'au': modeName = 'adminuser' if userIsAdmin(db, user): thisItem = dbGetUser(db, itemPathString) parentBox = None else: raise OstracionError('Insufficient permissions') elif mode == 's': modeName = 'settingIcon' if userIsAdmin(db, user): settingGroupId, settingId = itemPathString.split('/') thisItem = g.settings['image'][settingGroupId][settingId] parentBox = None else: raise OstracionError('Insufficient permissions') else: raise RuntimeError('Unknown mode encountered') # storageSuccess = storeFileAsThumbnail( db, fileToSave=None, mode=mode, thumbnailFormat=determineThumbnailFormatByModeAndTarget( db, mode, thisItem, ), targetItem=thisItem, parentBox=parentBox, user=user, tempFileDirectory=tempFileDirectory, fileStorageDirectory=fileStorageDirectory, ) if not storageSuccess: raise OstracionError('Could not unset the icon') # if mode in {'f', 'b', 'l'}: return redirect( url_for( 'lsView', lsPathString='/'.join(boxPath[1:-1] if mode == 'b' else boxPath[1:]), )) elif mode == 'u': return redirect(url_for('userProfileView', )) elif mode == 'au': return redirect(url_for('adminHomeUsersView', )) elif mode == 's': return redirect(url_for('adminHomeSettingsImagesView', )) else: raise RuntimeError('Unknown mode encountered')