From dbeff4fd7de0dfdf9bd7e389d5f61fc0529d6c6c Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Fri, 1 Dec 2017 23:15:49 +0000 Subject: [PATCH] All: Allow attaching files of unknown mime type --- ReactNativeClient/lib/MdToHtml.js | 8 ++-- .../lib/components/screens/note.js | 5 ++- ReactNativeClient/lib/joplin-database.js | 8 +++- ReactNativeClient/lib/logger.js | 2 +- ReactNativeClient/lib/models/resource.js | 3 +- ReactNativeClient/lib/path-utils.js | 7 ++- ReactNativeClient/lib/shim-init-node.js | 43 +++---------------- ReactNativeClient/lib/shim-init-react.js | 20 --------- 8 files changed, 29 insertions(+), 67 deletions(-) diff --git a/ReactNativeClient/lib/MdToHtml.js b/ReactNativeClient/lib/MdToHtml.js index f0cbdd4e8..2fabcb7fb 100644 --- a/ReactNativeClient/lib/MdToHtml.js +++ b/ReactNativeClient/lib/MdToHtml.js @@ -132,7 +132,7 @@ class MdToHtml { if (isResourceUrl && !this.supportsResourceLinks_) { // In mobile, links to local resources, such as PDF, etc. currently aren't supported. // Ideally they should be opened in the user's browser. - return '[Resource not yet supported: '; //+ htmlentities(text) + ']'; + return '(Resource not yet supported: '; //+ htmlentities(text) + ']'; } else { if (isResourceUrl) { const resourceId = Resource.pathToId(href); @@ -150,7 +150,7 @@ class MdToHtml { const isResourceUrl = Resource.isResourceUrl(href); if (isResourceUrl && !this.supportsResourceLinks_) { - return ']'; + return ')'; } else { return ''; } @@ -159,6 +159,7 @@ class MdToHtml { renderTokens_(tokens, options) { let output = []; let previousToken = null; + let anchorAttrs = []; for (let i = 0; i < tokens.length; i++) { const t = tokens[i]; const nextToken = i < tokens.length ? tokens[i+1] : null; @@ -190,6 +191,7 @@ class MdToHtml { if (openTag) { if (openTag === 'a') { + anchorAttrs.push(attrs); output.push(this.renderOpenLink_(attrs, options)); } else { const attrsHtml = this.renderAttrs_(attrs); @@ -235,7 +237,7 @@ class MdToHtml { if (closeTag) { if (closeTag === 'a') { - output.push(this.renderCloseLink_(attrs, options)); + output.push(this.renderCloseLink_(anchorAttrs.pop(), options)); } else { output.push(''); } diff --git a/ReactNativeClient/lib/components/screens/note.js b/ReactNativeClient/lib/components/screens/note.js index 6e885190a..8e7eb42dd 100644 --- a/ReactNativeClient/lib/components/screens/note.js +++ b/ReactNativeClient/lib/components/screens/note.js @@ -12,7 +12,7 @@ const { BackButtonService } = require('lib/services/back-button.js'); const { BaseModel } = require('lib/base-model.js'); const { ActionButton } = require('lib/components/action-button.js'); const Icon = require('react-native-vector-icons/Ionicons').default; -const { fileExtension, basename } = require('lib/path-utils.js'); +const { fileExtension, basename, safeFileExtension } = require('lib/path-utils.js'); const mimeUtils = require('lib/mime-utils.js').mime; const { ScreenHeader } = require('lib/components/screen-header.js'); const { time } = require('lib/time-utils.js'); @@ -295,6 +295,9 @@ class NoteScreenComponent extends BaseScreenComponent { resource.id = uuid.create(); resource.mime = mimeType; resource.title = pickerResponse.fileName ? pickerResponse.fileName : _('Untitled'); + resource.file_extension = safeFileExtension(fileExtension(pickerResponse.fileName)); + + if (!resource.mime) resource.mime = 'application/octet-stream'; let targetPath = Resource.fullPath(resource); diff --git a/ReactNativeClient/lib/joplin-database.js b/ReactNativeClient/lib/joplin-database.js index 95af9a577..3497d2843 100644 --- a/ReactNativeClient/lib/joplin-database.js +++ b/ReactNativeClient/lib/joplin-database.js @@ -202,7 +202,7 @@ class JoplinDatabase extends Database { // default value and thus might cause problems. In that case, the default value // must be set in the synchronizer too. - const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6]; + const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7]; let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion); if (currentVersionIndex == existingDatabaseVersions.length - 1) return false; @@ -228,7 +228,7 @@ class JoplinDatabase extends Database { ); `; - queries.push({ sql: 'DROP TABLE deleted_items' }); + // queries.push({ sql: 'DROP TABLE deleted_items' }); queries.push({ sql: this.sqlStringToLines(newTableSql)[0] }); queries.push({ sql: "CREATE INDEX deleted_items_sync_target ON deleted_items (sync_target)" }); } @@ -259,6 +259,10 @@ class JoplinDatabase extends Database { queries.push('CREATE INDEX alarm_note_id ON alarms (note_id)'); } + if (targetVersion == 7) { + queries.push('ALTER TABLE resources ADD COLUMN file_extension TEXT NOT NULL DEFAULT ""'); + } + queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] }); await this.transactionExecBatch(queries); diff --git a/ReactNativeClient/lib/logger.js b/ReactNativeClient/lib/logger.js index 23f637776..15ba1084e 100644 --- a/ReactNativeClient/lib/logger.js +++ b/ReactNativeClient/lib/logger.js @@ -47,7 +47,7 @@ class Logger { output = object.toString(); if (object.code) output += "\nCode: " + object.code; if (object.headers) output += "\nHeader: " + JSON.stringify(object.headers); - if (object.request) output += "\nRequest: " + object.request; + if (object.request) output += "\nRequest: " + (object.request.substr ? object.request.substr(0, 1024) : ''); if (object.stack) output += "\n" + object.stack; } else { output = JSON.stringify(object); diff --git a/ReactNativeClient/lib/models/resource.js b/ReactNativeClient/lib/models/resource.js index 66465a90d..ccd91bb84 100644 --- a/ReactNativeClient/lib/models/resource.js +++ b/ReactNativeClient/lib/models/resource.js @@ -33,7 +33,8 @@ class Resource extends BaseItem { } static filename(resource) { - let extension = resource.mime ? mime.toFileExtension(resource.mime) : ''; + let extension = resource.file_extension; + if (!extension) extension = resource.mime ? mime.toFileExtension(resource.mime) : ''; extension = extension ? '.' + extension : ''; return resource.id + extension; } diff --git a/ReactNativeClient/lib/path-utils.js b/ReactNativeClient/lib/path-utils.js index 0730e77ae..30e1a82f6 100644 --- a/ReactNativeClient/lib/path-utils.js +++ b/ReactNativeClient/lib/path-utils.js @@ -35,4 +35,9 @@ function isHidden(path) { return b[0] === '.'; } -module.exports = { basename, dirname, filename, isHidden, fileExtension }; \ No newline at end of file +function safeFileExtension(e) { + if (!e || !e.replace) return ''; + return e.replace(/[^a-zA-Z0-9]/g, '') +} + +module.exports = { basename, dirname, filename, isHidden, fileExtension, safeFileExtension }; \ No newline at end of file diff --git a/ReactNativeClient/lib/shim-init-node.js b/ReactNativeClient/lib/shim-init-node.js index 6a9df1c52..cbb54e186 100644 --- a/ReactNativeClient/lib/shim-init-node.js +++ b/ReactNativeClient/lib/shim-init-node.js @@ -82,7 +82,7 @@ function shimInit() { shim.attachFileToNote = async function(note, filePath) { const { Resource } = require('lib/models/resource.js'); const { uuid } = require('lib/uuid.js'); - const { filename } = require('lib/path-utils.js'); + const { basename, fileExtension, safeFileExtension } = require('lib/path-utils.js'); const mime = require('mime/lite'); const { Note } = require('lib/models/note.js'); @@ -91,7 +91,10 @@ function shimInit() { let resource = Resource.new(); resource.id = uuid.create(); resource.mime = mime.getType(filePath); - resource.title = filename(filePath); + resource.title = basename(filePath); + resource.file_extension = safeFileExtension(fileExtension(filePath)); + + if (!resource.mime) resource.mime = 'application/octet-stream'; let targetPath = Resource.fullPath(resource); @@ -120,26 +123,6 @@ function shimInit() { return shim.fetchWithRetry(() => { return nodeFetch(url, options) }, options); - - // if (!options) options = {}; - // if (!options.timeout) options.timeout = 1000 * 120; // ms - // if (!('maxRetry' in options)) options.maxRetry = 5; - - // let retryCount = 0; - // while (true) { - // try { - // const response = await nodeFetch(url, options); - // return response; - // } catch (error) { - // if (fetchRequestCanBeRetried(error)) { - // retryCount++; - // if (retryCount > options.maxRetry) throw error; - // await time.sleep(retryCount * 3); - // } else { - // throw error; - // } - // } - // } } shim.fetchBlob = async function(url, options) { @@ -204,22 +187,6 @@ function shimInit() { }; return shim.fetchWithRetry(doFetchOperation, options); - - // let retryCount = 0; - // while (true) { - // try { - // const response = await doFetchOperation(); - // return response; - // } catch (error) { - // if (fetchRequestCanBeRetried(error)) { - // retryCount++; - // if (retryCount > options.maxRetry) throw error; - // await time.sleep(retryCount * 3); - // } else { - // throw error; - // } - // } - // } } } diff --git a/ReactNativeClient/lib/shim-init-react.js b/ReactNativeClient/lib/shim-init-react.js index 1a2b6ad90..d690d2c48 100644 --- a/ReactNativeClient/lib/shim-init-react.js +++ b/ReactNativeClient/lib/shim-init-react.js @@ -13,26 +13,6 @@ function shimInit() { return shim.fetchWithRetry(() => { return shim.nativeFetch_(url, options) }, options); - - // if (!options) options = {}; - // if (!options.timeout) options.timeout = 1000 * 120; // ms - // if (!('maxRetry' in options)) options.maxRetry = 5; - - // let retryCount = 0; - // while (true) { - // try { - // const response = await nodeFetch(url, options); - // return response; - // } catch (error) { - // if (fetchRequestCanBeRetried(error)) { - // retryCount++; - // if (retryCount > options.maxRetry) throw error; - // await time.sleep(retryCount * 3); - // } else { - // throw error; - // } - // } - // } } shim.fetchBlob = async function(url, options) {