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 extractTopLevelTaskFromTreeDescriptor(tDesc, bgColorKey, g, overrides={}): """ Pick the top-level task item from a tree-descriptor and format it in a way fit for ls-view tasks. """ rootTask = tDesc['tasks']['root'] return recursivelyMergeDictionaries( overrides, defaultMap={ 'name': rootTask['title'], 'description': rootTask['subtitle'], 'url': url_for( rootTask['endpoint_name'][0], **rootTask['endpoint_name'][1], ), 'thumbnail': makeSettingImageUrl( g, rootTask['image_id'][0], rootTask['image_id'][1], ), 'bgcolor': g.settings['color']['task_colors'][bgColorKey]['value'], })
def dbPunchRichTicket(db, richTicket, numPunches=1, skipCommit=False): """ Punch a ticket, i.e. mark it as been used once more. We assume the ticket has been read during this database session, i.e. it reflects current DB data. """ if richTicket['redeemable']: # newTicketDict = recursivelyMergeDictionaries( { 'times_redeemed': (richTicket['ticket'].times_redeemed + numPunches), 'last_redeemed': datetime.datetime.now(), }, defaultMap=richTicket['ticket'].asDict(), ) dbUpdateRecordOnTable( db, 'tickets', newTicketDict, dbTablesDesc=dbSchema, ) # if not skipCommit: db.commit() else: if richTicket['exhausted']: raise OstracionError('Ticket is exhausted') else: raise OstracionError('Cannot punch a non-redeemable ticket')
def dbToggleBoxRolePermissionBit(db, thisBox, roleClass, roleId, bit, user, skipCommit=False): """ Toggle (t<-->f) a bit from an existing permission set (a role) for a box. """ roleKey = (roleClass, roleId) if thisBox.box_id == '' and roleKey == ('system', 'admin'): raise OstracionError('Cannot edit root box permissions for admin') elif thisBox.box_id == '' and roleKey == ('system', 'anonymous'): raise OstracionError('Cannot edit root box permissions for anonymous') else: if userIsAdmin(db, user): foundRecords = list( dbRetrieveRecordsByKey( db, 'box_role_permissions', { 'box_id': thisBox.box_id, 'role_class': roleClass, 'role_id': roleId, }, dbTablesDesc=dbSchema, )) if len(foundRecords) > 0: foundBRP = BoxRolePermission(**foundRecords[0]) lBit = bit.lower() newBRPDict = recursivelyMergeDictionaries( {lBit: 1 if not foundBRP.booleanBit(lBit) else 0}, defaultMap={ k: v for k, v in foundBRP.asDict().items() if k in {'box_id', 'role_class', 'role_id'} }, ) try: dbUpdateRecordOnTable( db, 'box_role_permissions', newBRPDict, dbTablesDesc=dbSchema, allowPartial=True, ) if not skipCommit: db.commit() except Exception as e: db.rollback() raise e else: raise OstracionError('Box role permission does not exist') else: raise OstracionError('Insufficient permissions')
def calendarMakerSetCover(coverObjPath): """Calendar cover image selection view (uses quoted coverObjPath).""" request._onErrorUrl = url_for('calendarMakerImagesView', ) coverImagePath = urllib.parse.unquote_plus(coverObjPath) response = redirect(url_for('calendarMakerImagesView')) currentCalendar = cookiesToCurrentCalendar(request.cookies) dResponse = dressResponseWithCurrentCalendar( response, recursivelyMergeDictionaries( {'cover_image_path_string': coverImagePath}, defaultMap=currentCalendar), ) return dResponse
def calendarMakerResetSettingsView(): """Reset calendar settings view.""" request._onErrorUrl = url_for('calendarMakerIndexView', ) currentCalendar = cookiesToCurrentCalendar(request.cookies) response = redirect(url_for('calendarMakerIndexView')) dResponse = dressResponseWithCurrentCalendar( response, recursivelyMergeDictionaries( { 'properties': defaultCalendarProperties(), }, defaultMap=currentCalendar, ), ) return dResponse
def calendarMakerAddImage(imageObjPath): """Calendar image-list add-image view (through quoted imageObjPath).""" request._onErrorUrl = url_for('calendarMakerImagesView', ) imagePath = urllib.parse.unquote_plus(imageObjPath) response = redirect(url_for('calendarMakerImagesView')) currentCalendar = cookiesToCurrentCalendar(request.cookies) images = currentCalendar.get('image_path_strings', []) + [imagePath] dResponse = dressResponseWithCurrentCalendar( response, recursivelyMergeDictionaries( {'image_path_strings': images}, defaultMap=currentCalendar, ), ) return dResponse
def cookiesToCurrentCalendar(cookies): """ Extract, from the passed request 'cookies', all currently stored settings for the calendar generation. """ cpCookie = cookies.get('apps_calendarmaker_current') if cpCookie is None: calFromCookie = {} else: calFromCookie = json.loads(cpCookie) # defaults defaultCal = {'properties': defaultCalendarProperties()} # return recursivelyMergeDictionaries( calFromCookie, defaultMap=defaultCal, )
def updateSettingThumbnail(db, richSetting, tId, tMT, user, fileStorageDirectory, skipCommit=False): """ Update the image in a setting of type Image. This returns the (actual) filesystem deletion queue after taking care of the DB part. The new thumbnail ID is passed to this function ('' to reset). richSetting['setting'] is the DB object, at top-level we have enrichment info (such as the default/value resolved etc) """ prevId = richSetting['setting'].value if prevId != '': delQueue = [ fileIdToPath(prevId, fileStorageDirectory=fileStorageDirectory) ] else: delQueue = [] # newSettingDict = recursivelyMergeDictionaries( { 'value': tId if tId is not None else '', 'icon_mime_type': tMT if tMT is not None else '', }, defaultMap={ k: v for k, v in richSetting['setting'].asDict().items() if k in {'value', 'icon_mime_type', 'group_id', 'id'} }, ) dbUpdateRecordOnTable( db, 'settings', newSettingDict, dbTablesDesc=dbSchema, allowPartial=True, ) # if not skipCommit: db.commit() return delQueue
def moveFile( db, file, srcBox, dstBox, user, fileStorageDirectory, skipCommit=False): """ Move a file between boxes: also check for w permission on both boxes, check that no box in dstBox yields a name clash.""" if file.box_id != srcBox.box_id: raise OstracionError('Parent mismatch between file and source box') elif srcBox.box_id == dstBox.box_id: raise OstracionError('Source and destination are the same') else: if all([ userHasPermission(db, user, srcBox.permissions, 'w'), userHasPermission(db, user, dstBox.permissions, 'w'), ]): # fileName = file.name if isNameUnderParentBox(db, dstBox, fileName): raise OstracionError( 'Destination box contains an item with same name' ) else: # now can the actual MV be performed newFile = recursivelyMergeDictionaries( {'box_id': dstBox.box_id}, defaultMap=file.asDict(), ) dbUpdateRecordOnTable( db, 'files', newFile, dbTablesDesc=dbSchema, ) # if not skipCommit: db.commit() # messages = [] return messages else: raise OstracionError('User has no write permission')
def applyReindexingToImages(req, idxMap): """ Apply an altered index-to-new-position map to the selected calendar images and build the response which cookifies the choice. Abstracts deletion and swaps among chosen calendar images. """ response = redirect(url_for('calendarMakerImagesView')) currentCalendar = cookiesToCurrentCalendar(req.cookies) prevImages = currentCalendar.get('image_path_strings', []) images = [ prevImages[idxMap.get(idx, idx)] for idx in range(len(prevImages)) if idxMap.get(idx, idx) is not None if idxMap.get(idx, idx) >= 0 if idxMap.get(idx, idx) < len(prevImages) ] dResponse = dressResponseWithCurrentCalendar( response, recursivelyMergeDictionaries({'image_path_strings': images}, defaultMap=currentCalendar), ) return dResponse
def convertRoleRelatedRecord(db, legacySchema, srcTableName, inRecord): if srcTableName == 'roles': isSystem = inRecord['system'] != 0 outRecord = { 'role_id': inRecord['role_id'], 'role_class': determineRoleClass(inRecord), # 'system' DISAPPEARS 'description': inRecord['description'], 'can_box': ( 0 if isSystem and inRecord['role_id'] == 'ticketer' else 1 ), 'can_user': ( 0 if isSystem and inRecord['role_id'] == 'anonymous' else 1 ), 'can_delete': 0 if isSystem else 1, } return outRecord elif srcTableName in {'box_role_permissions', 'user_roles'}: referenceRole = dbRetrieveRecordByKey( db, 'roles', {'role_id': inRecord['role_id']}, dbTablesDesc=legacySchema, ) outRecord = recursivelyMergeDictionaries( {'role_class': determineRoleClass(referenceRole)}, defaultMap=inRecord, ) return outRecord else: raise NotImplementedError('Cannot translate records for table "%s"' % ( srcTableName, ))
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 dbDeleteUser(db, username, user, fileStorageDirectory): """ Delete a user altogether. Return a fsDeletionQueue for deletions.""" if username != '': if not userIsAdmin(db, dbGetUser(db, username)): try: if username == user.username or userIsAdmin(db, user): fsDeleteQueue = [] # 1. cleanup of file system rootBox = getRootBox(db) fsDeleteQueue += _traverseForAccountDeletion( db, rootBox, username, user, path=[''], fileStorageDirectory=fileStorageDirectory, ) # 2. deleting user-related data around dbDeleteRecordsByKey( db, 'user_roles', {'username': username}, dbTablesDesc=dbSchema, ) deleteeUser = dbGetUser(db, username) if deleteeUser.icon_file_id != '': fsDeleteQueue.append(fileIdToPath( deleteeUser.icon_file_id, fileStorageDirectory=fileStorageDirectory, )) dbDeleteRecordsByKey( db, 'tickets', {'username': username}, dbTablesDesc=dbSchema, ) for u in dbGetAllUsers(db, user, accountDeletionInProgress=True): if u.username != username: if u.icon_file_id_username == username: if u.icon_file_id != '': fsDeleteQueue.append(fileIdToPath( u.icon_file_id, fileStorageDirectory, )) dbUpdateUser( db, User(**recursivelyMergeDictionaries( { 'icon_file_id': '', 'icon_file_id_username': '', }, defaultMap=u.asDict(), )), user, skipCommit=True, ) # 3. delete user-specific role association from boxes dbDeleteRecordsByKey( db, 'box_role_permissions', {'role_class': 'user', 'role_id': username}, dbTablesDesc=dbSchema, ) # 4. delete user-specific role dbDeleteRecordsByKey( db, 'roles', {'role_class': 'user', 'role_id': username}, dbTablesDesc=dbSchema ) # 5. finally, delete the user dbDeleteRecordsByKey( db, 'users', {'username': username}, dbTablesDesc=dbSchema ) db.commit() return fsDeleteQueue else: raise OstracionError('Insufficient permissions') except Exception as e: db.rollback() raise e else: raise OstracionError('Cannot delete an admin') else: raise OstracionError('Cannot alter system user')
'tasks': recursivelyMergeDictionaries( { 'images': { 'title': 'Images', 'subtitle': ('Configure images ' '(icons and so on)'), 'image_id': ( 'admin_images', 'admin_settings_images' ), 'endpoint_name': ( 'adminHomeSettingsImagesView', {}, ), }, 'colors': { 'title': 'Colors', 'subtitle': ('Configure colors (for box, ' 'file, task cards and so on)'), 'image_id': ( 'admin_images', 'admin_settings_colors' ), 'endpoint_name': ( 'adminHomeSettingsColorsView', {}, ), }, }, defaultMap={ genericSettingKey: { 'title': genericSettingInfo['pageTitle'], 'subtitle': genericSettingInfo['pageSubtitle'], 'image_id': ( 'admin_images', genericSettingInfo['image_id'], ), 'endpoint_name': ( 'adminHomeSettingsGenericView', {'settingType': genericSettingKey}, ), } for genericSettingKey, genericSettingInfo in ( genericSettingsPageDesc.items() ) } ),
def calendarMakerSettingsView(): """Calendar settings form view.""" user = g.user db = dbGetDatabase() request._onErrorUrl = url_for('calendarMakerIndexView', ) # currentCalendar = cookiesToCurrentCalendar(request.cookies) cProps = currentCalendar.get('properties', {}) currentYear = datetime.datetime.now().year form = CalendarMakerPropertyForm() # if form.validate_on_submit(): month0 = safeInt(form.month0.data, 1) year0 = form.year0.data month1 = safeInt(form.month1.data, 12) year1 = form.year1.data language = form.language.data startingweekday = safeInt(form.startingweekday.data, 6) response = redirect(url_for('calendarMakerIndexView')) dResponse = dressResponseWithCurrentCalendar( response, recursivelyMergeDictionaries( { 'properties': { 'month0': month0, 'year0': year0, 'month1': month1, 'year1': year1, 'language': language, 'startingweekday': startingweekday, }, }, defaultMap=currentCalendar, ), ) return dResponse else: form.month0.data = str(applyDefault(cProps.get('month0'), 1)) form.year0.data = applyDefault(cProps.get('year0'), currentYear) form.month1.data = str(applyDefault(cProps.get('month1'), 12)) form.year1.data = applyDefault(cProps.get('year1'), currentYear) form.language.data = applyDefault(cProps.get('language'), 'en') form.startingweekday.data = applyDefault( cProps.get('startingweekday'), 6, ) # pageFeatures = prepareTaskPageFeatures( appsPageDescriptor, ['root', 'calendar_maker', 'settings'], g, ) # return render_template( 'apps/calendarmaker/settings.html', user=user, bgcolor=g.settings['color']['app_colors']['calendar_maker_color'] ['value'], form=form, **pageFeatures, )
def fsLinkMetadataView(fsPathString=''): """Edit-link-metadata view, a web-form.""" user = g.user db = dbGetDatabase() form = EditLinkForm() lsPath = splitPathString(fsPathString) boxPath, prevLinkName = lsPath[:-1], lsPath[-1] parentBox = getBoxFromPath(db, boxPath, user) request._onErrorUrl = url_for( 'lsView', lsPathString='/'.join(boxPath[1:]), ) link = getLinkFromParent(db, parentBox, prevLinkName, user) if form.validate_on_submit(): newLink = Link(**recursivelyMergeDictionaries( { 'name': form.linkname.data, 'description': form.linkdescription.data, 'title': form.linktitle.data, 'target': form.linktarget.data, 'metadata_username': user.username, 'metadata_dict': { 'open_in_new_window': form.openinnewwindow.data, }, }, defaultMap={ k: v for k, v in link.asDict().items() if k not in { 'metadata', 'dvector_description', 'dvector_name', 'dvector_title' } }, )) updateLink(db, boxPath, prevLinkName, newLink, user) return redirect(url_for('lsView', lsPathString='/'.join(boxPath[1:]))) else: form.linkname.data = applyDefault(form.linkname.data, link.name) form.linkdescription.data = applyDefault(form.linkdescription.data, link.description) form.linktitle.data = applyDefault(form.linktitle.data, link.title) form.linktarget.data = applyDefault(form.linktarget.data, link.target) form.openinnewwindow.data = link.getMetadata( 'open_in_new_window', True, ) # pageFeatures = { 'breadCrumbs': makeBreadCrumbs( boxPath, g, appendedItems=[{ 'kind': 'external_link', 'target': link, }, { 'kind': 'link', 'target': None, 'name': 'Metadata', }], ), 'pageTitle': 'Edit link metadata (%s)' % link.name, 'pageSubtitle': 'Name and target are mandatory', 'iconUrl': url_for( 'linkThumbnailView', dummyId=link.icon_file_id + '_', fsPathString='/'.join(lsPath[1:]), ), } return render_template( 'editlink.html', form=form, user=user, **pageFeatures, )
dbTablesDesc=dbSchema, ) if itemDictFound is None: # new: insert print('inserting... ', end='') dbAddRecordToTable( db, tName, model.asDict(), dbTablesDesc=dbSchema, ) print('done ', end='') else: # careful override and update back mergedDict = recursivelyMergeDictionaries( {'value': itemDictFound['value']}, defaultMap=model.asDict(), ) print('updating... ', end='') dbUpdateRecordOnTable( db, tName, mergedDict, dbTablesDesc=dbSchema, ) print('done ', end='') print('#') print(' * done.') elif tName == 'links': # we may have to add a field columns = dbQueryColumns(db, tName) if 'title' not in columns:
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 dbUpdateStandardSettingDict(db, enrichedSetting, newFormValue, user, skipCommit=False): """ Update a non-image setting with a new value (taking care of deforming before the actual writing to DB). """ if userIsAdmin(db, user): deformedValue = _deformSettingValueByType( enrichedSetting['setting'].type, newFormValue, ) newSettingDict = recursivelyMergeDictionaries( { 'value': deformedValue, }, defaultMap={ k: v for k, v in enrichedSetting['setting'].asDict().items() if k in {'value', 'icon_mime_type', 'group_id', 'id'} }) # if (enrichedSetting['setting'].type == 'string' and enrichedSetting['metadata'].get('is_text_image', False)): # we handle the special case of deleting outdated # image-text that may have been produced # # this largerSettingDict is used only to determine the nextValue largerNextSettingDict = recursivelyMergeDictionaries( { 'value': deformedValue, }, defaultMap={ k: v for k, v in enrichedSetting['setting'].asDict().items() if k in {'value', 'type', 'default_value'} }) # prevValue = enrichedSetting['value'] nextValue = _getValueFromSetting(Setting(**largerNextSettingDict)) # if there is a picture-with-text to be superseded, delete it prevPath, prevTitle, prevTag = determineManagedTextImagePath( prevValue, prefix='%s/%s' % ( enrichedSetting['setting'].group_id, enrichedSetting['setting'].id, ), ) nextPath, nextTitle, nextTag = determineManagedTextImagePath( nextValue, prefix='%s/%s' % ( enrichedSetting['setting'].group_id, enrichedSetting['setting'].id, ), ) if prevTag != nextTag: prevFileName = os.path.join( prevPath, prevTitle, ) if os.path.isfile(prevFileName): # delete the old, unused file os.remove(prevFileName) # dbUpdateRecordOnTable(db, 'settings', newSettingDict, dbTablesDesc=dbSchema, allowPartial=True) if not skipCommit: db.commit() else: raise OstracionError('Insufficient permissions')
def moveBox(db, box, srcBox, dstBox, user, skipCommit=False): """ Move 'box' from being contained in 'srcBox' to being contained in 'dstBox' (with all involved permission checks). """ if box.box_id == '': # nobody can touch root raise OstracionError('Cannot act on this object') else: if box.parent_id != srcBox.box_id: # consistency in request raise RuntimeError( 'Parent mismatch between box to move and source box' ) else: if box.parent_id == dstBox.box_id: raise OstracionError( 'Source and destination box are the same' ) else: if not canDeleteBox(db, box, srcBox, user): # must be able to remove from source raise OstracionError( 'Insufficient permissions to move box away' ) else: if not all( userHasPermission( db, user, dstBox.permissions, prm ) for prm in {'w', 'c'}): # must be able to upload and create to dest raise OstracionError( 'Insufficient permissions to move boxes ' 'to the destination box' ) else: # name clash check for destination if isNameUnderParentBox(db, dstBox, box.box_name): raise OstracionError( 'An object with that name ' 'exists already' ) else: # check against box incest if isAncestorBoxOf(db, box, dstBox): raise OstracionError( 'Cannot move box to a sub-box of itself' ) else: # perform the move newBox = recursivelyMergeDictionaries( {'parent_id': dstBox.box_id}, defaultMap=box.asDict(), ) dbUpdateRecordOnTable( db, 'boxes', newBox, dbTablesDesc=dbSchema, ) # if not skipCommit: db.commit() return []
def placeFSFileInBox(db, user, fileStorageDirectory, box, filePhysicalPath, fileName, fileDescription, fileTextualMode='', fileThumbnailFormat=None): """ Given an actual file at a physical location, insert it as a Ostracion file by handling the filesystem DB as well as the actual file copy into the storage directories. """ # can user write to box? if userHasPermission(db, user, box.permissions, 'w'): # is the name available? if not isNameUnderParentBox(db, box, fileName): protoFile = { 'box_id': box.box_id, 'name': fileName, 'description': fileDescription, 'date': datetime.datetime.now(), } userName = user.username newFile = File(**recursivelyMergeDictionaries( protoFile, defaultMap={ 'creator_username': userName, 'icon_file_id_username': userName, 'metadata_username': userName, 'editor_username': userName, 'textual_mode': fileTextualMode, }, )) filePath = fileIdToPath( newFile.file_id, fileStorageDirectory=fileStorageDirectory, ) shutil.copy2(filePhysicalPath, filePath) # fileProperties = determineFileProperties(filePath) newFile.mime_type = fileProperties['file_mime_type'] newFile.type = fileProperties['file_type'] newFile.size = fileProperties['file_size'] # if (fileThumbnailFormat is not None and isImageMimeType(newFile.mime_type)): # thumbnail preparation fileThumbnailId, fileThumbnailMimeType = makeFileThumbnail( newFile.file_id, newFile.mime_type, thumbnailFormat=fileThumbnailFormat, fileStorageDirectory=fileStorageDirectory, ) if fileThumbnailId is not None: newFile.icon_file_id = fileThumbnailId newFile.icon_mime_type = fileThumbnailMimeType # makeFileInParent(db, parentBox=box, newFile=newFile) db.commit() else: raise OstracionError('Cannot create a file with that name') else: raise OstracionError('User has no write permission on box')
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 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))