1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Desktop: Create fileURLs via drag and drop (#1653)

* enable drag and drop fileURLs

* fix windows fileURL syntax

* introduce encodeURI function

* fixed encoding issue

* use path-utils.js to deal with fileURL path conversion

* add changes as requested

* Minor rewording 'On the' -> 'In the', additional info about attaching files

* change call of toFileProtocolPath

* enable test script to check syntax for all OS-platforms
This commit is contained in:
tfinnberg 2019-07-29 12:16:47 +02:00 committed by Laurent Cozic
parent 38a51070fc
commit f7fcabbf41
5 changed files with 75 additions and 23 deletions

View File

@ -1,6 +1,6 @@
require('app-module-path').addPath(__dirname);
const { extractExecutablePath, quotePath, unquotePath, friendlySafeFilename } = require('lib/path-utils.js');
const { extractExecutablePath, quotePath, unquotePath, friendlySafeFilename, toFileProtocolPath} = require('lib/path-utils.js');
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
process.on('unhandledRejection', (reason, p) => {
@ -73,4 +73,28 @@ describe('pathUtils', function() {
done();
});
it('should create correct fileURL syntax', async (done) => {
const testCases_win32 = [
['C:\\handle\\space test', 'file:///C:/handle/space+test'],
['C:\\escapeplus\\+', 'file:///C:/escapeplus/%2B'],
['C:\\handle\\single quote\'', 'file:///C:/handle/single+quote%27'],
];
const testCases_unixlike = [
['/handle/space test', 'file:///handle/space+test'],
['/escapeplus/+', 'file:///escapeplus/%2B'],
['/handle/single quote\'', 'file:///handle/single+quote%27'],
];
for (let i = 0; i < testCases_win32.length; i++) {
const t = testCases_win32[i];
expect(toFileProtocolPath(t[0], 'win32')).toBe(t[1]);
}
for (let i = 0; i < testCases_unixlike.length; i++) {
const t = testCases_unixlike[i];
expect(toFileProtocolPath(t[0], 'linux')).toBe(t[1]);
}
done();
});
});

View File

@ -175,6 +175,7 @@ class NoteTextComponent extends React.Component {
this.onDrop_ = async (event) => {
const dt = event.dataTransfer;
const createFileURL = event.altKey;
if (dt.types.indexOf("text/x-jop-note-ids") >= 0) {
const noteIds = JSON.parse(dt.getData("text/x-jop-note-ids"));
@ -198,7 +199,7 @@ class NoteTextComponent extends React.Component {
filesToAttach.push(file.path);
}
await this.commandAttachFile(filesToAttach);
await this.commandAttachFile(filesToAttach, createFileURL);
}
const updateSelectionRange = () => {
@ -1076,7 +1077,7 @@ class NoteTextComponent extends React.Component {
});
}
async commandAttachFile(filePaths = null) {
async commandAttachFile(filePaths = null, createFileURL = false) {
if (!filePaths) {
filePaths = bridge().showOpenDialog({
properties: ['openFile', 'createDirectory', 'multiSelections'],
@ -1093,7 +1094,7 @@ class NoteTextComponent extends React.Component {
const filePath = filePaths[i];
try {
reg.logger().info('Attaching ' + filePath);
note = await shim.attachFileToNote(note, filePath, position);
note = await shim.attachFileToNote(note, filePath, position, createFileURL);
reg.logger().info('File was attached.');
this.setState({
note: Object.assign({}, note),

View File

@ -107,17 +107,17 @@ Joplin was designed as a replacement for Evernote and so can import complete Eve
To import Evernote data, first export your Evernote notebooks to ENEX files as described [here](https://help.evernote.com/hc/en-us/articles/209005557-How-to-back-up-export-and-restore-import-notes-and-notebooks). Then follow these steps:
On the **desktop application**, open File > Import > ENEX and select your file. The notes will be imported into a new separate notebook. If needed they can then be moved to a different notebook, or the notebook can be renamed, etc.
In the **desktop application**, open File > Import > ENEX and select your file. The notes will be imported into a new separate notebook. If needed they can then be moved to a different notebook, or the notebook can be renamed, etc.
On the **terminal application**, in [command-line mode](https://joplinapp.org/terminal/#command-line-mode), type `import /path/to/file.enex`. This will import the notes into a new notebook named after the filename.
In the **terminal application**, in [command-line mode](https://joplinapp.org/terminal/#command-line-mode), type `import /path/to/file.enex`. This will import the notes into a new notebook named after the filename.
## Importing from Markdown files
Joplin can import notes from plain Markdown file. You can either import a complete directory of Markdown files or individual files.
On the **desktop application**, open File > Import > MD and select your Markdown file or directory.
In the **desktop application**, open File > Import > MD and select your Markdown file or directory.
On the **terminal application**, in [command-line mode](https://joplinapp.org/terminal/#command-line-mode), type `import --format md /path/to/file.md` or `import --format md /path/to/directory/`.
In the **terminal application**, in [command-line mode](https://joplinapp.org/terminal/#command-line-mode), type `import --format md /path/to/file.md` or `import --format md /path/to/directory/`.
## Importing from other applications
@ -142,9 +142,9 @@ Currently, synchronisation is possible with Nextcloud, Dropbox (by default), One
<img src="https://joplinapp.org/images/nextcloud-logo-background.png" width="100" align="left"> <a href="https://nextcloud.com/">Nextcloud</a> is a self-hosted, private cloud solution. It can store documents, images and videos but also calendars, passwords and countless other things and can sync them to your laptop or phone. As you can host your own Nextcloud server, you own both the data on your device and infrastructure used for synchronisation. As such it is a good fit for Joplin. The platform is also well supported and with a strong community, so it is likely to be around for a while - since it's open source anyway, it is not a service that can be closed, it can exist on a server for as long as one chooses.
On the **desktop application** or **mobile application**, go to the config screen and select Nextcloud as the synchronisation target. Then input the WebDAV URL (to get it, click on Settings in the bottom left corner of the page, in Nextcloud), this is normally `https://example.com/nextcloud/remote.php/webdav/Joplin` (**make sure to create the "Joplin" directory in Nextcloud**), and set the username and password. If it does not work, please [see this explanation](https://github.com/laurent22/joplin/issues/61#issuecomment-373282608) for more details.
In the **desktop application** or **mobile application**, go to the config screen and select Nextcloud as the synchronisation target. Then input the WebDAV URL (to get it, click on Settings in the bottom left corner of the page, in Nextcloud), this is normally `https://example.com/nextcloud/remote.php/webdav/Joplin` (**make sure to create the "Joplin" directory in Nextcloud**), and set the username and password. If it does not work, please [see this explanation](https://github.com/laurent22/joplin/issues/61#issuecomment-373282608) for more details.
On the **terminal application**, you will need to set the `sync.target` config variable and all the `sync.5.path`, `sync.5.username` and `sync.5.password` config variables to, respectively the Nextcloud WebDAV URL, your username and your password. This can be done from the command line mode using:
In the **terminal application**, you will need to set the `sync.target` config variable and all the `sync.5.path`, `sync.5.username` and `sync.5.password` config variables to, respectively the Nextcloud WebDAV URL, your username and your password. This can be done from the command line mode using:
:config sync.5.path https://example.com/nextcloud/remote.php/webdav/Joplin
:config sync.5.username YOUR_USERNAME
@ -157,9 +157,9 @@ If synchronisation does not work, please consult the logs in the app profile dir
When syncing with Dropbox, Joplin creates a sub-directory in Dropbox, in `/Apps/Joplin` and read/write the notes and notebooks from it. The application does not have access to anything outside this directory.
On the **desktop application** or **mobile application**, select "Dropbox" as the synchronisation target in the config screen (it is selected by default). Then, to initiate the synchronisation process, click on the "Synchronise" button in the sidebar and follow the instructions.
In the **desktop application** or **mobile application**, select "Dropbox" as the synchronisation target in the config screen (it is selected by default). Then, to initiate the synchronisation process, click on the "Synchronise" button in the sidebar and follow the instructions.
On the **terminal application**, to initiate the synchronisation process, type `:sync`. You will be asked to follow a link to authorise the application. It is possible to also synchronise outside of the user interface by typing `joplin sync` from the terminal. This can be used to setup a cron script to synchronise at regular interval. For example, this would do it every 30 minutes:
In the **terminal application**, to initiate the synchronisation process, type `:sync`. You will be asked to follow a link to authorise the application. It is possible to also synchronise outside of the user interface by typing `joplin sync` from the terminal. This can be used to setup a cron script to synchronise at regular interval. For example, this would do it every 30 minutes:
*/30 * * * * /path/to/joplin sync
@ -186,9 +186,9 @@ WebDAV-compatible services that are known to work with Joplin:
When syncing with OneDrive, Joplin creates a sub-directory in OneDrive, in /Apps/Joplin and read/write the notes and notebooks from it. The application does not have access to anything outside this directory.
On the **desktop application** or **mobile application**, select "OneDrive" as the synchronisation target in the config screen. Then, to initiate the synchronisation process, click on the "Synchronise" button in the sidebar and follow the instructions.
In the **desktop application** or **mobile application**, select "OneDrive" as the synchronisation target in the config screen. Then, to initiate the synchronisation process, click on the "Synchronise" button in the sidebar and follow the instructions.
On the **terminal application**, to initiate the synchronisation process, type `:sync`. You will be asked to follow a link to authorise the application (simply input your Microsoft credentials - you do not need to register with OneDrive).
In the **terminal application**, to initiate the synchronisation process, type `:sync`. You will be asked to follow a link to authorise the application (simply input your Microsoft credentials - you do not need to register with OneDrive).
# Encryption
@ -212,7 +212,8 @@ Joplin notes can be opened and edited using an external editor of your choice. I
Any kind of file can be attached to a note. In Markdown, links to these files are represented as a simple ID to the attachment. In the note viewer, these files, if they are images, will be displayed or, if they are other files (PDF, text files, etc.) they will be displayed as links. Clicking on this link will open the file in the default application.
On the **desktop application**, images can be attached either by clicking on "Attach file" or by pasting (with Ctrl+V) an image directly in the editor, or by drag and dropping an image.
In the **desktop application**, files can be attached either by clicking the "Attach file" icon in the editor or via drag and drop. If you prefer to create a link to a local file instead, hold the ALT key while performing the drag and drop operation.
If the OS-clipboard contains an image you can directly paste it in the editor via Ctrl+V.
Resources that are not attached to any note will be automatically deleted after 10 days (see [rationale](https://github.com/laurent22/joplin/issues/154#issuecomment-356582366)).
@ -224,7 +225,7 @@ The way the attachments are downloaded during synchronisation can be customised
# Notifications
On the desktop and mobile apps, an alarm can be associated with any to-do. It will be triggered at the given time by displaying a notification. How the notification will be displayed depends on the operating system since each has a different way to handle this. Please see below for the requirements for the desktop applications:
In the desktop and mobile apps, an alarm can be associated with any to-do. It will be triggered at the given time by displaying a notification. How the notification will be displayed depends on the operating system since each has a different way to handle this. Please see below for the requirements for the desktop applications:
- **Windows**: >= 8. Make sure the Action Center is enabled on Windows. Task bar balloon for Windows < 8. Growl as fallback. Growl takes precedence over Windows balloons.
- **macOS**: >= 10.8 or Growl if earlier.
@ -242,7 +243,7 @@ Sub-notebooks allow organising multiple notebooks into a tree of notebooks. For
![](https://joplinapp.org/images/SubNotebooks.png)
- On the **desktop application**, to create a subnotebook, drag and drop it onto another notebook. To move it back to the root, drag and drop it on the "Notebooks" header. Currently only the desktop app can be used to organise the notebooks.
- In the **desktop application**, to create a subnotebook, drag and drop it onto another notebook. To move it back to the root, drag and drop it on the "Notebooks" header. Currently only the desktop app can be used to organise the notebooks.
- The **mobile application** supports displaying and collapsing/expanding the tree of notebooks, however it does not currently support moving the subnotebooks to different notebooks.
- The **terminal app** supports displaying the tree of subnotebooks but it does not support collapsing/expanding them or moving the subnotebooks around.

View File

@ -101,9 +101,18 @@ function friendlySafeFilename(e, maxLength = null) {
return output.substr(0, maxLength);
}
function toFileProtocolPath(path) {
const output = path.replace(/\\/g, "/");
return 'file://' + escape(output);
function toFileProtocolPath(filePathEncode, os = null) {
if (os === null) os = process.platform;
if (os === 'win32') {
filePathEncode = filePathEncode.replace(/\\/g, '/'); // replace backslash in windows pathname with slash e.g. c:\temp to c:/temp
filePathEncode = "/" + filePathEncode; // put slash in front of path to comply with windows fileURL syntax
}
filePathEncode = encodeURI(filePathEncode);
filePathEncode = filePathEncode.replace(/\+/g, '%2B'); // escape '+' with unicode
filePathEncode = filePathEncode.replace(/%20/g, '+'); // switch space (%20) with '+'. To comply with syntax used by joplin, see urldecode_(str) in MdToHtml.js
return "file://" + filePathEncode.replace(/\'/g, '%27'); // escape '(single quote) with unicode, to prevent crashing the html view
}
function toSystemSlashes(path, os = null) {

View File

@ -10,6 +10,7 @@ const Note = require('lib/models/Note.js');
const Resource = require('lib/models/Resource.js');
const urlValidator = require('valid-url');
function shimInit() {
shim.fsDriver = () => { throw new Error('Not implemented') }
shim.FileApiDriverLocal = FileApiDriverLocal;
@ -168,8 +169,16 @@ function shimInit() {
return await Resource.save(resource, { isNew: true });
}
shim.attachFileToNote = async function(note, filePath, position = null) {
const resource = await shim.createResourceFromPath(filePath);
shim.attachFileToNote = async function(note, filePath, position = null, createFileURL = false) {
const { basename } = require('path');
const { escapeLinkText } = require('lib/markdownUtils');
const { toFileProtocolPath } = require('lib/path-utils');
let resource = [];
if (!createFileURL) {
resource = await shim.createResourceFromPath(filePath);
}
const newBody = [];
@ -178,7 +187,15 @@ function shimInit() {
}
if (note.body && position) newBody.push(note.body.substr(0, position));
newBody.push(Resource.markdownTag(resource));
if (!createFileURL) {
newBody.push(Resource.markdownTag(resource));
} else {
let filename = escapeLinkText(basename(filePath)); // to get same filename as standard drag and drop
let fileURL = "[" + filename + "]("+ toFileProtocolPath(filePath) +")"
newBody.push(fileURL);
}
if (note.body) newBody.push(note.body.substr(position));
const newNote = Object.assign({}, note, {