def _boxHasNoChildren(db, box): """ Is it true that this box has no children? (regardless of visibility-to-this-user issues). """ return (len(list(getFilesFromBox(db, box))) == 0 and len(list( getBoxesFromParent( db, box, None, accountDeletionInProgress=True ) )) == 0)
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 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 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 lsView(lsPathString=''): """LS-of-a-box route, additionally with 'tasks' if viewing root.""" user = g.user lsPath = splitPathString(lsPathString) db = dbGetDatabase() thisBox = getBoxFromPath(db, lsPath, user) boxPath = lsPath[1:] # showBoxPermissionToAll = g.settings['behaviour']['behaviour_permissions'][ 'show_permission']['value'] # if thisBox is not None: request._onErrorUrl = url_for('lsView') if thisBox.box_id == '': tasks = prepareRootTasks(db, g, user) else: tasks = [] boxes = [ { 'box': box, 'path': boxPath + [box.box_name], 'info': prepareBoxInfo(db, box), 'actions': prepareBoxActions( db, box, boxPath + [box.box_name], thisBox, user ), } for box in sorted( ( b for b in getBoxesFromParent(db, thisBox, user) if b is not None ), key=lambda b: (b.box_name.lower(), b.box_name), ) if box.box_id != '' ] files = [ { 'file': file, 'path': boxPath + [file.name], 'nice_size': formatBytesSize(file.size), 'info': prepareFileInfo(db, file), 'actions': prepareFileActions( db, file, boxPath + [file.name], thisBox, user ), } for file in sorted( getFilesFromBox(db, thisBox), key=lambda f: (f.name.lower(), f.name), ) ] links = [ { 'link': link, 'path': boxPath + [link.name], 'info': prepareLinkInfo(db, link), 'actions': prepareLinkActions( db, link, boxPath + [link.name], thisBox, user ), } for link in sorted( getLinksFromBox(db, thisBox), key=lambda l: (l.name.lower(), l.name), ) ] # pathBCrumbs = makeBreadCrumbs(lsPath, g) boxNiceName = transformIfNotEmpty(thisBox.box_name) if thisBox.box_id == '': boxTitle, boxDescription = describeRootBoxCaptions(g) else: boxTitle = describeBoxTitle(thisBox) boxDescription = thisBox.description # boxActions = prepareBoxHeaderActions( db, thisBox, boxPath, user, g.settings, discardedActions=( set() if len(files) > 0 else {'gallery_view', 'issue_gallery_ticket'} ), ) showAdminToolsLink = userIsAdmin(db, user) if showBoxPermissionToAll or userIsAdmin(db, user): thisBoxPermissionAlgebra = calculateBoxPermissionAlgebra( thisBox.permissionHistory, thisBox.permissions, ) # roleKeyToRoleMap = { r.roleKey(): r for r in dbGetAllRoles(db, user) if r.can_box != 0 } structuredPermissionInfo = reformatBoxPermissionAlgebraIntoLists( thisBoxPermissionAlgebra, roleKeyToRoleMap, ) # permissionInfo = { 'powers': structuredPermissionInfo, 'assignments': { 'edit_url': ( url_for( 'adminLsPermissionsView', lsPathString='/'.join(boxPath), ) if showAdminToolsLink else None ), 'native': [ { 'role_class': brp.role_class, 'role_id': brp.role_id, 'r': brp.r, 'w': brp.w, 'c': brp.c, 'role': roleKeyToRoleMap[( brp.role_class, brp.role_id, )], } for brp in sorted(thisBox.listPermissions('native')) ], 'inherited': [ { 'role_class': brp.role_class, 'role_id': brp.role_id, 'r': brp.r, 'w': brp.w, 'c': brp.c, 'role': roleKeyToRoleMap[( brp.role_class, brp.role_id, )], } for brp in sorted(thisBox.listPermissions('inherited')) ], }, } else: permissionInfo = None # if len(boxes) + len(files) + len(links) > 0: foundCounts = [ (len(boxes), 'box', 'boxes'), (len(files), 'file', 'files'), (len(links), 'link', 'links'), ] foundParts = pickSingularPluralSentences( foundCounts, keepZeroes=False, ) boxChildrenCounter = colloquialJoinClauses(foundParts) else: boxChildrenCounter = 'no contents' # return render_template( 'ls.html', user=user, thisBox=thisBox, boxTitle=boxTitle, boxNiceName=boxNiceName, boxDescription=boxDescription, pageTitle=boxNiceName, boxChildrenCounter=boxChildrenCounter, boxActions=boxActions, boxPath=boxPath, permissionInfo=permissionInfo, breadCrumbs=pathBCrumbs, tasks=tasks, boxes=boxes, files=files, links=links, ) else: request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(lsPath[1:-1]), ) raise OstracionWarning( 'Cannot access the specified box "%s"' % lsPath[-1] )
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