Skip to content

Commit

Permalink
Merge pull request #1899 from ehhc/deleting_of_attachment_link
Browse files Browse the repository at this point in the history
Deleting of attachments -> fixes #1828 and fixes #740
  • Loading branch information
Rokt33r committed May 15, 2018
2 parents 55b8488 + ffc3fb7 commit 0bdcfa6
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
3 changes: 3 additions & 0 deletions browser/components/CodeEditor.js
Expand Up @@ -36,6 +36,9 @@ export default class CodeEditor extends React.Component {
el = el.parentNode
}
this.props.onBlur != null && this.props.onBlur(e)

const {storageKey, noteKey} = this.props
attachmentManagement.deleteAttachmentsNotPresentInNote(this.editor.getValue(), storageKey, noteKey)
}
this.pasteHandler = (editor, e) => this.handlePaste(editor, e)
this.loadStyleHandler = (e) => {
Expand Down
46 changes: 45 additions & 1 deletion browser/main/lib/dataApi/attachmentManagement.js
Expand Up @@ -157,7 +157,7 @@ function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem
/**
* @description Returns all attachment paths of the given markdown
* @param {String} markdownContent content in which the attachment paths should be found
* @returns {String[]} Array of the relativ paths (starting with :storage) of the attachments of the given markdown
* @returns {String[]} Array of the relative paths (starting with :storage) of the attachments of the given markdown
*/
function getAttachmentsInContent (markdownContent) {
const preparedInput = markdownContent.replace(new RegExp(mdurl.encode(path.sep), 'g'), path.sep)
Expand Down Expand Up @@ -190,6 +190,49 @@ function removeStorageAndNoteReferences (input, noteKey) {
return input.replace(new RegExp(mdurl.encode(path.sep), 'g'), path.sep).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + noteKey, 'g'), DESTINATION_FOLDER)
}

/**
* @description Deletes all attachments stored in the attachment folder of the give not that are not referenced in the markdownContent
* @param markdownContent Content of the note. All unreferenced notes will be deleted
* @param storageKey StorageKey of the current note. Is used to determine the belonging attachment folder.
* @param noteKey NoteKey of the current note. Is used to determine the belonging attachment folder.
*/
function deleteAttachmentsNotPresentInNote (markdownContent, storageKey, noteKey) {
const targetStorage = findStorage.findStorage(storageKey)
const attachmentFolder = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
const attachmentsInNote = getAttachmentsInContent(markdownContent)
const attachmentsInNoteOnlyFileNames = []
if (attachmentsInNote) {
for (let i = 0; i < attachmentsInNote.length; i++) {
attachmentsInNoteOnlyFileNames.push(attachmentsInNote[i].replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + noteKey + escapeStringRegexp(path.sep), 'g'), ''))
}
}

if (fs.existsSync(attachmentFolder)) {
fs.readdir(attachmentFolder, (err, files) => {
if (err) {
console.error("Error reading directory '" + attachmentFolder + "'. Error:")
console.error(err)
return
}
files.forEach(file => {
if (!attachmentsInNoteOnlyFileNames.includes(file)) {
const absolutePathOfFile = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey, file)
fs.unlink(absolutePathOfFile, (err) => {
if (err) {
console.error("Could not delete '%s'", absolutePathOfFile)
console.error(err)
return
}
console.info("File '" + absolutePathOfFile + "' deleted because it was not included in the content of the note")
})
}
})
})
} else {
console.info("Attachment folder ('" + attachmentFolder + "') did not exist..")
}
}

module.exports = {
copyAttachment,
fixLocalURLS,
Expand All @@ -199,6 +242,7 @@ module.exports = {
getAttachmentsInContent,
getAbsolutePathsOfAttachmentsInContent,
removeStorageAndNoteReferences,
deleteAttachmentsNotPresentInNote,
STORAGE_FOLDER_PLACEHOLDER,
DESTINATION_FOLDER
}
52 changes: 52 additions & 0 deletions tests/dataApi/attachmentManagement.test.js
Expand Up @@ -260,3 +260,55 @@ it('should remove the all ":storage" and noteKey references', function () {
const actual = systemUnderTest.removeStorageAndNoteReferences(testInput, noteKey)
expect(actual).toEqual(expectedOutput)
})

it('should test that deleteAttachmentsNotPresentInNote deletes all unreferenced attachments ', function () {
const dummyStorage = {path: 'dummyStoragePath'}
const noteKey = 'noteKey'
const storageKey = 'storageKey'
const markdownContent = ''
const dummyFilesInFolder = ['file1.txt', 'file2.pdf', 'file3.jpg']
const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey)

findStorage.findStorage = jest.fn(() => dummyStorage)
fs.existsSync = jest.fn(() => true)
fs.readdir = jest.fn((paht, callback) => callback(undefined, dummyFilesInFolder))
fs.unlink = jest.fn()

systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey)
expect(fs.existsSync).toHaveBeenLastCalledWith(attachmentFolderPath)
expect(fs.readdir).toHaveBeenCalledTimes(1)
expect(fs.readdir.mock.calls[0][0]).toBe(attachmentFolderPath)

expect(fs.unlink).toHaveBeenCalledTimes(dummyFilesInFolder.length)
const fsUnlinkCallArguments = []
for (let i = 0; i < dummyFilesInFolder.length; i++) {
fsUnlinkCallArguments.push(fs.unlink.mock.calls[i][0])
}

dummyFilesInFolder.forEach(function (file) {
expect(fsUnlinkCallArguments.includes(path.join(attachmentFolderPath, file))).toBe(true)
})
})

it('should test that deleteAttachmentsNotPresentInNote does not delete referenced attachments', function () {
const dummyStorage = {path: 'dummyStoragePath'}
const noteKey = 'noteKey'
const storageKey = 'storageKey'
const dummyFilesInFolder = ['file1.txt', 'file2.pdf', 'file3.jpg']
const markdownContent = systemUnderTest.generateAttachmentMarkdown('fileLabel', path.join(systemUnderTest.STORAGE_FOLDER_PLACEHOLDER, noteKey, dummyFilesInFolder[0]), false)
const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey)

findStorage.findStorage = jest.fn(() => dummyStorage)
fs.existsSync = jest.fn(() => true)
fs.readdir = jest.fn((paht, callback) => callback(undefined, dummyFilesInFolder))
fs.unlink = jest.fn()

systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey)

expect(fs.unlink).toHaveBeenCalledTimes(dummyFilesInFolder.length - 1)
const fsUnlinkCallArguments = []
for (let i = 0; i < dummyFilesInFolder.length - 1; i++) {
fsUnlinkCallArguments.push(fs.unlink.mock.calls[i][0])
}
expect(fsUnlinkCallArguments.includes(path.join(attachmentFolderPath, dummyFilesInFolder[0]))).toBe(false)
})

0 comments on commit 0bdcfa6

Please sign in to comment.