def updateLink( db, boxPath, prevLinkName, newLink, user, accountDeletionInProgress=False, skipCommit=False): """ Update the fields of a link object on DB.""" # # we check link_id is unchanged parentBox = getBoxFromPath( db, boxPath, user, accountDeletionInProgress=accountDeletionInProgress, ) prevLink = getLinkFromParent( db, parentBox, prevLinkName, user, accountDeletionInProgress=accountDeletionInProgress, ) if newLink.link_id != prevLink.link_id: raise RuntimeError('Link ID mismatch') else: if (not accountDeletionInProgress and not userHasPermission(db, user, parentBox.permissions, 'w')): raise OstracionError('User is not allowed to edit link') else: if not isNameUnderParentBox( db, parentBox, newLink.name, excludedIds=[('link', prevLink.link_id)]): newLinkItem = Link(**{ k: v for k, v in newLink.asDict().items() if k not in { 'dvector_name', 'dvector_description', 'dvector_title', } }) dbUpdateRecordOnTable( db, 'links', newLinkItem.asDict(), dbTablesDesc=dbSchema, ) if not skipCommit: db.commit() else: raise OstracionError('Name already exists')
def getLinksFromBox(db, box): """ Perform a 'ls' call and return all links found in a given box. """ return ( Link(**linkDict) for linkDict in dbRetrieveRecordsByKey( db, 'links', {'box_id': box.box_id}, dbTablesDesc=dbSchema, ) )
def makeLinkInParent( db, user, parentBox, date, linkName, linkTitle, linkDescription, linkTarget, linkOptions={}): """ Create a new external link object in the specified box. Return a dummy value (True upon success, but it is the errors that are raised.) """ if userHasPermission(db, user, parentBox.permissions, 'w'): if not isNameUnderParentBox(db, parentBox, linkName): userName = user.username newLink = Link( box_id=parentBox.box_id, name=linkName, title=linkTitle, description=linkDescription, icon_file_id='', date=date, creator_username=userName, icon_file_id_username=userName, icon_mime_type='', metadata_username=userName, target=linkTarget, metadata_dict=linkOptions, ) dbAddRecordToTable( db, 'links', newLink.asDict(), dbTablesDesc=dbSchema, ) db.commit() else: raise OstracionError('Name already exists') else: raise OstracionError('User has no write permission')
def getLinkFromParent( db, parentBox, linkName, user, accountDeletionInProgress=False): """ Given a box and the name of a link supposedly contained in it, return the link (or None). """ linkDict = dbRetrieveRecordByKey( db, 'links', {'name': linkName, 'box_id': parentBox.box_id}, dbTablesDesc=dbSchema, ) if linkDict is not None: return Link(**linkDict) else: return None
col for col in dbSchema[tName]['columns'] if col[0] not in {'title', 'dvector_title'} ] } }, defaultMap={tName: dbSchema[tName]}, ) for prevLinkEntryDict in dbRetrieveAllRecords( db, tName, tempSchema, ): print('<', end='') newLink = Link(**(recursivelyMergeDictionaries( {'title': prevLinkEntryDict['name']}, prevLinkEntryDict, ))).asDict() print(newLink) dbUpdateRecordOnTable( db, tName, newLink, dbTablesDesc=dbSchema, ) print('*> ', end='') # print('\n done.') elif tName == 'roles': # we add new roles, only if they do not exist at all tcontents = initialDbValues.get(tName) print(' * Refreshing')
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, )
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 explicitLoopSimilaritySearch( db, searchTerm, searchBoxes=True, searchFiles=True, searchLinks=True, useDescription=False): """ Explicit loop over files/boxes and use a 'similarity' logic to pick matching results. Same return object as 'sqliteLikeSubstringSearch'. """ searchTermDVector = textToDVector(searchTerm) if len(searchTermDVector) > 0: # if searchFiles: foundFiles = [ ffRichItem for ffRichItem in ( { 'item': File(**fileDict), 'score': _itemSimilarityMatchScore( 'file', searchTermDVector, fileDict, useDescription, ), } for fileDict in dbRetrieveAllRecords( db, 'files', dbTablesDesc=dbSchema, ) ) if ffRichItem['score'] >= similarityScoreThreshold ] else: foundFiles = [] # if searchBoxes: foundBoxes = [ fbRichItem for fbRichItem in ( { 'item': Box(**boxDict), 'score': _itemSimilarityMatchScore( 'box', searchTermDVector, boxDict, useDescription, ), } for boxDict in dbRetrieveAllRecords( db, 'boxes', dbTablesDesc=dbSchema, ) if boxDict['box_id'] != '' ) if fbRichItem['score'] >= similarityScoreThreshold ] else: foundBoxes = [] # if searchLinks: foundLinks = [ flRichItem for flRichItem in ( { 'item': Link(**linkDict), 'score': _itemSimilarityMatchScore( 'link', searchTermDVector, linkDict, useDescription, ), } for linkDict in dbRetrieveAllRecords( db, 'links', dbTablesDesc=dbSchema, ) ) if flRichItem['score'] >= similarityScoreThreshold ] else: foundLinks = [] # return { 'files': foundFiles, 'boxes': foundBoxes, 'links': foundLinks, } else: return { 'files': [], 'boxes': [], 'links': [], 'message': 'Not enough digits/letters in search term', }
def explicitLoopSubstringSearch( db, searchTerm, searchBoxes=True, searchFiles=True, searchLinks=True, caseSensitive=True, useDescription=False): """ Explicit loop over the files/boxes tables and pick the substring-matching items. Same return object as 'sqliteLikeSubstringSearch'. We explicitly browse all files and all boxes and apply the search with the options passed. Not so fast but highly extendable in the future. """ # _strippedSearchTerm = stripToAscii(searchTerm) _caseAdjustedSearchTerm = ( _strippedSearchTerm if caseSensitive else _strippedSearchTerm.lower() ) searchTokens = { itm.strip() for itm in _caseAdjustedSearchTerm.split(' ') if itm.strip() != '' } if len(searchTokens) > 0: # tokenResLens = [ (re.compile(re.escape(tk)), len(tk)) for tk in searchTokens ] # if searchFiles: foundFiles = [ ffRichItem for ffRichItem in ( { 'item': File(**fileDict), 'score': _itemSubstringMatchScore( 'file', tokenResLens, fileDict, caseSensitive, useDescription, ), } for fileDict in dbRetrieveAllRecords( db, 'files', dbTablesDesc=dbSchema, ) ) if ffRichItem['score'] > 0 ] else: foundFiles = [] # if searchBoxes: foundBoxes = [ fbRichItem for fbRichItem in ( { 'item': Box(**boxDict), 'score': _itemSubstringMatchScore( 'box', tokenResLens, boxDict, caseSensitive, useDescription, ), } for boxDict in dbRetrieveAllRecords( db, 'boxes', dbTablesDesc=dbSchema, ) if boxDict['box_id'] != '' ) if fbRichItem['score'] > 0 ] else: foundBoxes = [] # if searchLinks: foundLinks = [ flRichItem for flRichItem in ( { 'item': Link(**linkDict), 'score': _itemSubstringMatchScore( 'link', tokenResLens, linkDict, caseSensitive, useDescription, ), } for linkDict in dbRetrieveAllRecords( db, 'links', dbTablesDesc=dbSchema, ) ) if flRichItem['score'] > 0 ] else: foundLinks = [] # return { 'files': foundFiles, 'boxes': foundBoxes, 'links': foundLinks, } else: return { 'files': [], 'boxes': [], 'links': [], 'message': 'No search terms provided', }