1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-27 20:29:45 +02:00

Compare commits

...

49 Commits

Author SHA1 Message Date
Laurent Cozic
cf565d1563 Android release v1.0.129 2018-06-27 22:02:15 +01:00
Laurent Cozic
6b425cf543 Electron release v1.0.104 2018-06-27 22:00:38 +01:00
Laurent Cozic
6188e7a0fa Updated translations 2018-06-27 22:00:02 +01:00
Laurent Cozic
310afb0ad6 Electron: Resolves #612: Allow duplicating a note 2018-06-27 21:45:31 +01:00
Laurent Cozic
7d7e1e1637 Electron: Resolves #647: Allow specifying text editor path and arguments in setting 2018-06-27 21:34:41 +01:00
Laurent Cozic
424c8a2723 Clipper: Support 'author' property 2018-06-27 20:14:20 +01:00
Laurent Cozic
187fb1b85d Electron: Resolves #619: Context menu to cut, copy and paste. Also added menu to copy link in web view 2018-06-26 00:52:46 +01:00
Laurent Cozic
595fd7a9aa All: Resolves #644: Added support for .markdown extension when importing files 2018-06-26 00:07:53 +01:00
Laurent Cozic
0027cb9036 All: Fixes #646: Mentioned that TLS settings must be saved before checking sync config 2018-06-25 23:54:28 +01:00
Laurent Cozic
db6878b978 Electron: Fixes #639: Make sure text wraps when printing or exporting as PDF 2018-06-25 23:32:23 +01:00
Laurent Cozic
1c78722573 CLI: Upgraded Turndown plugins 2018-06-25 18:19:27 +01:00
Laurent Cozic
fea83e28c4 All: Optimised encryption and decryption of items so that it doesn't freeze the UI, especially on mobile 2018-06-25 18:14:57 +01:00
Laurent Cozic
84adf64271 Electron: Set PDF default file name 2018-06-22 18:36:15 +00:00
Laurent Cozic
74e2b0d15d Electron: Fixes #634: Press ESC to dismiss dialog in non-English languages 2018-06-22 18:31:55 +00:00
Laurent Cozic
df302206dd Electron: Allow HTML in Markdown documents in a secure way 2018-06-22 18:18:15 +00:00
Laurent Cozic
6d8941c005 Update website 2018-06-21 22:14:47 +01:00
Laurent Cozic
971b20062f Electron release v1.0.103 2018-06-21 19:13:10 +01:00
Laurent Cozic
936f334b61 Electron: Remove 'New notebook' shortcut 2018-06-21 19:12:08 +01:00
Laurent Cozic
7e3a290939 Merge pull request #628 from Abijeet/master
Adds a shortcut to insert the date and time.
2018-06-21 19:09:36 +01:00
Laurent Cozic
c6466a780e Android release v1.0.128 2018-06-21 19:08:59 +01:00
Laurent Cozic
43774ad3fb Electron release v1.0.102 2018-06-21 19:05:58 +01:00
Laurent Cozic
b3ba5b7747 All: Revert #554 to try to fix #624: WebDAV error when syncing with SeaFile 2018-06-21 19:00:20 +01:00
Laurent Cozic
599f4ccef4 Electron: Fixes #626: Auto-completion for indented items 2018-06-21 18:53:42 +01:00
Laurent Cozic
a67600d264 Updated Chinese and French translation 2018-06-21 18:04:10 +01:00
Laurent Cozic
ebf4c89ef0 All: Fixes #343, Fixes #191: Added options to specify custom TLS certificates 2018-06-20 01:18:58 +01:00
Laurent Cozic
aa7da784fc All: Fixes #343, Fixes #191: Added options to ignore TLS cert errors to allow self-signed certificates on desktop and CLI 2018-06-20 00:28:50 +01:00
Laurent Cozic
617ed42d8c Update website 2018-06-18 19:08:13 +01:00
Laurent Cozic
5848e7d90d Doc: Fixed image 2018-06-18 19:08:07 +01:00
Abijeet Patro
01d032261c Merge branch 'master' into master 2018-06-18 23:32:10 +05:30
Laurent Cozic
54d06646aa Update website 2018-06-18 19:01:52 +01:00
Laurent Cozic
81da46035a Doc: Fixed image 2018-06-18 19:01:42 +01:00
Laurent Cozic
74d0f75802 Update website 2018-06-18 19:00:07 +01:00
Laurent Cozic
f25a352dcb Doc: Mentioned Web Clipper 2018-06-18 18:59:54 +01:00
Laurent Cozic
21ef8da45f Clipper: Completed doc and simplified logic to get clipper port to make it easier to use by third-party 2018-06-18 18:54:48 +01:00
Laurent Cozic
1f3a1c49df Electron: Resolves #611: Allow opening and editing note in external editor 2018-06-18 18:56:07 +00:00
Laurent Cozic
a8b58aaec3 All: Fixes #632: Handle restricted_content error in Dropbox 2018-06-18 08:47:51 +01:00
Laurent Cozic
44f9b35d93 Update website 2018-06-17 19:35:25 +01:00
Laurent Cozic
711af9beed Electron release v1.0.101 2018-06-17 19:12:28 +01:00
Laurent Cozic
971339ca9a Electron: Fixes #630: PDF export in context menu 2018-06-17 17:12:28 +01:00
Laurent Cozic
f5a72ffbaf All: Fixes #593: Resource should not be auto-deleted if they've never been linked to any note 2018-06-17 16:59:06 +01:00
Laurent Cozic
cf4331c5af Electron: Fixes #623: Improved handling of text selection and fixed infinite loop 2018-06-17 02:44:37 +01:00
Abijeet
07b85388fc Adds a shortcut to insert the date and time.
Closes #521
2018-06-16 20:46:27 +05:30
Laurent Cozic
553b086ba2 Update website 2018-06-15 19:56:17 +01:00
Laurent Cozic
ff89537899 Update website 2018-06-15 19:28:58 +01:00
Laurent Cozic
f20792889a Removed Twitter and FB share buttons 2018-06-15 19:28:35 +01:00
Laurent Cozic
ee22a7ff73 Update website 2018-06-15 18:23:23 +01:00
Laurent Cozic
4fc4353859 Update translation 2018-06-15 18:23:15 +01:00
Laurent Cozic
f4f9e25e6b Update website 2018-06-15 18:14:55 +01:00
Laurent Cozic
e54f9934b5 Fixed Catalan flag 2018-06-15 18:14:31 +01:00
119 changed files with 3813 additions and 1910 deletions

View File

@@ -638,6 +638,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Cerca a totes les notes"
@@ -766,12 +772,15 @@ msgstr "Comprova la configuració de la sincronització"
msgid "Notes and settings are stored in: %s"
msgstr "Les notes i la configuració es desen a: %s"
msgid "Save"
msgstr "Desa"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr "Tramet"
msgid "Save"
msgstr "Desa"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -910,6 +919,13 @@ msgstr "Establiu la contrasenya"
msgid "Add or remove tags"
msgstr "Afegeix o suprimeix etiquetes"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Copia"
msgid "Switch between note and to-do type"
msgstr "Alterna entre el tipus nota i tasques pendents"
@@ -944,6 +960,9 @@ msgstr "Anomena i desa..."
msgid "Copy path to clipboard"
msgstr "Copia el camí al porta-retalls"
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Missatge o enllaç no suportat: %s"
@@ -962,6 +981,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -999,6 +1021,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "S'està cancel·lant..."
msgid "to-do"
msgstr "Tasques pendents"
@@ -1207,16 +1236,6 @@ msgstr "No es pot copiar la nota al bloc de notes «%s»"
msgid "Cannot move note to \"%s\" notebook"
msgstr "No es pot moure la nota al bloc de notes «%s»"
msgid "Text editor"
msgstr "Editor de text"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"L'editor que s'usarà per a obrir una nota. Si no s'indica cap, intentarà "
"detectar automàticament l'editor predeterminat."
msgid "Language"
msgstr "Llengua"
@@ -1300,6 +1319,18 @@ msgstr "%d hora"
msgid "%d hours"
msgstr "%d hores"
#, fuzzy
msgid "Text editor command"
msgstr "Editor de text"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"L'editor que s'usarà per a obrir una nota. Si no s'indica cap, intentarà "
"detectar automàticament l'editor predeterminat."
msgid "Show advanced options"
msgstr "Mostra les opcions avançades"
@@ -1341,6 +1372,19 @@ msgstr "Nom d'usuari WebDAV"
msgid "WebDAV password"
msgstr "Contrasenya de WebDAV"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "El valor de l'opció no és vàlid: «%s». Els valors possibles són: %s."

View File

@@ -620,6 +620,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Hledat ve všech poznámkách"
@@ -741,12 +747,15 @@ msgstr "Zkontrolujte nastavení synchronizace"
msgid "Notes and settings are stored in: %s"
msgstr "Poznámky a nastavení uloženo v: %s"
msgid "Save"
msgstr "Uložit"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr "Uložit"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -882,6 +891,13 @@ msgstr "Nastavit heslo"
msgid "Add or remove tags"
msgstr "Přidat či odebrat tagy"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Kopírovat"
msgid "Switch between note and to-do type"
msgstr "Přepnout mezi poznámkou a to-do"
@@ -915,6 +931,9 @@ msgstr "Uložit jako..."
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Nepodporovaný link či zpráva: %s"
@@ -931,6 +950,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -968,6 +990,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Zastavuji..."
msgid "to-do"
msgstr "to-do"
@@ -1181,16 +1210,6 @@ msgstr "Poznámku \"%s\" nelze zkopírovat do zápisníku"
msgid "Cannot move note to \"%s\" notebook"
msgstr "Poznámku nelze přesunout do zápisníku \"%s\""
msgid "Text editor"
msgstr "Textový editor"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"Editor ve kterém budou otvírány poznámky. Pokud není specifikováno, aplikace "
"se pokusí o autodetekci defaultního editoru."
msgid "Language"
msgstr "Jazyk"
@@ -1275,6 +1294,18 @@ msgstr "%d hodina"
msgid "%d hours"
msgstr "%d hodin"
#, fuzzy
msgid "Text editor command"
msgstr "Textový editor"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"Editor ve kterém budou otvírány poznámky. Pokud není specifikováno, aplikace "
"se pokusí o autodetekci defaultního editoru."
msgid "Show advanced options"
msgstr "Ukázat pokročilé volby"
@@ -1316,6 +1347,19 @@ msgstr "WebDAV uživatelské jméno"
msgid "WebDAV password"
msgstr "WebDAV heslo"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Neplatná hodnota: \"%s\". Přípustné hodnoty jsou: %s."

View File

@@ -625,6 +625,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Søg i alle noter"
@@ -746,12 +752,15 @@ msgstr "Check synkroniserings Indstillinger"
msgid "Notes and settings are stored in: %s"
msgstr "Noter og indstillinger er gemt i: %s"
msgid "Save"
msgstr "Gem"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr "Gem"
msgid "Save"
msgstr "Gem"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -891,6 +900,13 @@ msgstr "Indstil kodeord"
msgid "Add or remove tags"
msgstr "Tilføj eller slet mærker"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Kopier"
msgid "Switch between note and to-do type"
msgstr "Skift mellem note- og opgave type"
@@ -924,6 +940,9 @@ msgstr "Gem som..."
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Ugyldigt- eller ulovligt link eller besked: %s"
@@ -940,6 +959,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -977,6 +999,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Annullerer..."
msgid "to-do"
msgstr "opgave"
@@ -1190,16 +1219,6 @@ msgstr "Kan ikke kopiere note til \"%s\" notesbog"
msgid "Cannot move note to \"%s\" notebook"
msgstr "Kan ikke flytte note til \"%s\" notesbog"
msgid "Text editor"
msgstr "Tekst editor"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"Editor der bruges til at åbne note. Hvis ingen valgt/udfyldt, vil auto-"
"funktion søge efter standard editor."
msgid "Language"
msgstr "Sprog"
@@ -1284,6 +1303,18 @@ msgstr "%d time"
msgid "%d hours"
msgstr "%d timer"
#, fuzzy
msgid "Text editor command"
msgstr "Tekst editor"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"Editor der bruges til at åbne note. Hvis ingen valgt/udfyldt, vil auto-"
"funktion søge efter standard editor."
msgid "Show advanced options"
msgstr "Vis avancerede indstillinger"
@@ -1325,6 +1356,19 @@ msgstr "WebDAV brugernavn"
msgid "WebDAV password"
msgstr "WebDAV kodeord"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Ulovlig værdi: \"%s\". Mulige valg er: %s."

View File

@@ -649,6 +649,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Alle Notizen durchsuchen"
@@ -779,12 +785,15 @@ msgstr "Überprüfen der Synchronisationseinstellungen"
msgid "Notes and settings are stored in: %s"
msgstr "Notizen und Einstellungen werden gespeichert in: %s"
msgid "Save"
msgstr "Speichern"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr "Absenden"
msgid "Save"
msgstr "Speichern"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -923,6 +932,13 @@ msgstr "Setze ein Passwort"
msgid "Add or remove tags"
msgstr "Markierungen hinzufügen oder entfernen"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Kopieren"
msgid "Switch between note and to-do type"
msgstr "Zwischen Notiz und To-Do Typ wechseln"
@@ -959,6 +975,9 @@ msgstr "Sichern unter..."
msgid "Copy path to clipboard"
msgstr "Pfad in Zwischenablage kopieren"
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Nicht unterstützter Link oder Nachricht: %s"
@@ -977,6 +996,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -1014,6 +1036,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Abbrechen..."
msgid "to-do"
msgstr "To-Do"
@@ -1227,17 +1256,6 @@ msgstr "Kann Notiz nicht zu Notizbuch \"%s\" kopieren"
msgid "Cannot move note to \"%s\" notebook"
msgstr "Kann Notiz nicht zu Notizbuch \"%s\" verschieben"
msgid "Text editor"
msgstr "Textverarbeitungsprogramm"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"Das Textverarbeitungsprogramm, mit dem Notizen geöffnet werden. Wenn keines "
"ausgewählt wurde, wird Joplin versuchen das standard-"
"Textverarbeitungsprogramm zu erkennen."
msgid "Language"
msgstr "Sprache"
@@ -1321,6 +1339,19 @@ msgstr "%d Stunde"
msgid "%d hours"
msgstr "%d Stunden"
#, fuzzy
msgid "Text editor command"
msgstr "Textverarbeitungsprogramm"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"Das Textverarbeitungsprogramm, mit dem Notizen geöffnet werden. Wenn keines "
"ausgewählt wurde, wird Joplin versuchen das standard-"
"Textverarbeitungsprogramm zu erkennen."
msgid "Show advanced options"
msgstr "Erweiterte Optionen anzeigen"
@@ -1363,6 +1394,19 @@ msgstr "WebDAV Benutzername"
msgid "WebDAV password"
msgstr "WebDAV Passwort"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Ungültiger Optionswert: \"%s\". Mögliche Werte sind: %s."

View File

@@ -558,6 +558,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr ""
@@ -679,12 +685,15 @@ msgstr ""
msgid "Notes and settings are stored in: %s"
msgstr ""
msgid "Save"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr ""
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -807,6 +816,13 @@ msgstr ""
msgid "Add or remove tags"
msgstr ""
msgid "Duplicate"
msgstr ""
#, javascript-format
msgid "%s - Copy"
msgstr ""
msgid "Switch between note and to-do type"
msgstr ""
@@ -839,6 +855,9 @@ msgstr ""
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr ""
@@ -855,6 +874,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -892,6 +914,12 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
msgid "Watching..."
msgstr ""
msgid "to-do"
msgstr ""
@@ -1092,14 +1120,6 @@ msgstr ""
msgid "Cannot move note to \"%s\" notebook"
msgstr ""
msgid "Text editor"
msgstr ""
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
msgid "Language"
msgstr ""
@@ -1180,6 +1200,14 @@ msgstr ""
msgid "%d hours"
msgstr ""
msgid "Text editor command"
msgstr ""
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
msgid "Show advanced options"
msgstr ""
@@ -1217,6 +1245,19 @@ msgstr ""
msgid "WebDAV password"
msgstr ""
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr ""

View File

@@ -634,6 +634,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Buscar en todas las notas"
@@ -763,12 +769,15 @@ msgstr "Comprobar sincronización"
msgid "Notes and settings are stored in: %s"
msgstr "Las notas y los ajustes se guardan en: %s"
msgid "Save"
msgstr "Guardar"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr "Aceptar"
msgid "Save"
msgstr "Guardar"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -906,6 +915,13 @@ msgstr "Establecer la contraseña"
msgid "Add or remove tags"
msgstr "Añadir o borrar etiquetas"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Copiar"
msgid "Switch between note and to-do type"
msgstr "Cambiar entre nota y lista de tareas"
@@ -938,6 +954,9 @@ msgstr "Guardar como..."
msgid "Copy path to clipboard"
msgstr "Copiar la ruta en el portapapeles"
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Enlace o mensaje no soportado: %s"
@@ -956,6 +975,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -993,6 +1015,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Cancelando..."
msgid "to-do"
msgstr "lista de tareas"
@@ -1205,16 +1234,6 @@ msgstr "No se ha podido copiar la nota a la libreta «%s»"
msgid "Cannot move note to \"%s\" notebook"
msgstr "No se ha podido mover la nota a la libreta «%s»"
msgid "Text editor"
msgstr "Editor de texto"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"El editor que se usará para abrir una nota. Se intentará auto-detectar el "
"editor predeterminado si no se proporciona ninguno."
msgid "Language"
msgstr "Idioma"
@@ -1298,6 +1317,18 @@ msgstr "%d hora"
msgid "%d hours"
msgstr "%d horas"
#, fuzzy
msgid "Text editor command"
msgstr "Editor de texto"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"El editor que se usará para abrir una nota. Se intentará auto-detectar el "
"editor predeterminado si no se proporciona ninguno."
msgid "Show advanced options"
msgstr "Mostrar opciones avanzadas"
@@ -1340,6 +1371,19 @@ msgstr "Usuario de WebDAV"
msgid "WebDAV password"
msgstr "Contraseña de WebDAV"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Opción inválida: «%s». Los valores posibles son: %s."

View File

@@ -633,6 +633,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Bilatu ohar guztietan"
@@ -757,12 +763,15 @@ msgstr "Sinkronizazioa utzi"
msgid "Notes and settings are stored in: %s"
msgstr "Oharrak eta ezarpenak hemen daude gordeta: %s"
msgid "Save"
msgstr "Gorde"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr "Gorde"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -901,6 +910,13 @@ msgstr "Ezarri pasahitza"
msgid "Add or remove tags"
msgstr "Gehitu edo ezabatu etiketak"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Kopiatu"
msgid "Switch between note and to-do type"
msgstr "Aldatu oharra eta zeregin eren artean."
@@ -934,6 +950,9 @@ msgstr "Gorde aldaketak"
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Esteka edo mezu ez dago onartua: %s"
@@ -950,6 +969,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -987,6 +1009,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Bertan behera uzten..."
#, fuzzy
msgid "to-do"
msgstr "Zeregin berria"
@@ -1206,16 +1235,6 @@ msgstr "Ezin kopia daiteke oharra \"%s\" koadernora"
msgid "Cannot move note to \"%s\" notebook"
msgstr "Ezin eraman daiteke oharra \"%s\" koadernora"
msgid "Text editor"
msgstr "Testu editorea"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"Editorea erabiliko da oharra zabaltzeko. Ez badago zehaztutakorik lehenetsia "
"igartzen ahaleginduko da."
msgid "Language"
msgstr "Hizkuntza"
@@ -1302,6 +1321,18 @@ msgstr "% ordua"
msgid "%d hours"
msgstr "% orduak"
#, fuzzy
msgid "Text editor command"
msgstr "Testu editorea"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"Editorea erabiliko da oharra zabaltzeko. Ez badago zehaztutakorik lehenetsia "
"igartzen ahaleginduko da."
msgid "Show advanced options"
msgstr "Erakutsi aukera aurreratuak"
@@ -1346,6 +1377,19 @@ msgstr "Nextcloud erabiltzaile-izena"
msgid "WebDAV password"
msgstr "Ezarri pasahitza"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Balio aukera baliogabea: \"%s\". Litezkeen balioak: %s."

View File

@@ -628,10 +628,16 @@ msgid "Paste"
msgstr "Coller"
msgid "Bold"
msgstr ""
msgstr "Gras"
msgid "Italic"
msgstr ""
msgstr "Italique"
msgid "Insert Date Time"
msgstr "Insérer la date et l'heure"
msgid "Edit in external editor"
msgstr "Ouvrir dans un éditeur externe"
msgid "Search in all the notes"
msgstr "Chercher dans toutes les notes"
@@ -710,7 +716,7 @@ msgstr "Le service du Web Clipper est activé et démarrera automatiquement."
#, javascript-format
msgid "Status: Started on port %d"
msgstr "État : Commencé sur le port %d"
msgstr "État : Démarré sur le port %d"
#, javascript-format
msgid "Status: %s"
@@ -729,8 +735,8 @@ msgid ""
"Joplin Web Clipper allows saving web pages and screenshots from your browser "
"to Joplin."
msgstr ""
"Le Web Clipper permet de sauver des pages web et des captures d'écran depuis "
"votre navigateur vers Joplin."
"Le Web Clipper permet d'enregistrer des pages web et des captures d'écran "
"depuis votre navigateur vers Joplin."
msgid "In order to use the web clipper, you need to do the following:"
msgstr "Pour utiliser le Web Clipper, veuillez suivre ces instructions :"
@@ -762,12 +768,15 @@ msgstr "Vérifier config synchronisation"
msgid "Notes and settings are stored in: %s"
msgstr "Les notes et paramètres se trouve dans : %s"
msgid "Save"
msgstr "Enregistrer"
msgid "Apply"
msgstr "Appliquer"
msgid "Submit"
msgstr "Envoyer"
msgid "Save"
msgstr "Enregistrer"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -909,6 +918,13 @@ msgstr "Définir le mot de passe"
msgid "Add or remove tags"
msgstr "Gérer les étiquettes"
msgid "Duplicate"
msgstr "Dupliquer"
#, javascript-format
msgid "%s - Copy"
msgstr "%s - Copie"
msgid "Switch between note and to-do type"
msgstr "Alterner entre note et tâche"
@@ -934,9 +950,9 @@ msgstr ""
msgid "Open..."
msgstr "Ouvrir..."
#, fuzzy, javascript-format
#, javascript-format
msgid "This file could not be opened: %s"
msgstr "Ce carnet n'a pas pu être sauvegardé : %s"
msgstr "Ce fichier n'a pas pu être ouvert : %s"
msgid "Save as..."
msgstr "Enregistrer sous..."
@@ -944,6 +960,9 @@ msgstr "Enregistrer sous..."
msgid "Copy path to clipboard"
msgstr "Copier le chemin"
msgid "Copy Link Address"
msgstr "Copier l'adresse du lien"
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Lien ou message non géré : %s"
@@ -957,13 +976,16 @@ msgstr ""
"l'éditeur et éditer cette note."
msgid "strong text"
msgstr ""
msgstr "texte en gras"
msgid "emphasized text"
msgstr ""
msgstr "texte en italique"
msgid "List item"
msgstr "Élément de liste"
msgid "Insert Hyperlink"
msgstr ""
msgstr "Insérer lien"
msgid "Attach file"
msgstr "Attacher un fichier"
@@ -979,25 +1001,31 @@ msgid "In: %s"
msgstr "Dans : %s"
msgid "Hyperlink"
msgstr ""
msgstr "Lien"
msgid "Code"
msgstr ""
msgstr "Code"
msgid "Numbered List"
msgstr ""
msgstr "Liste numérotée"
msgid "Bulleted List"
msgstr ""
msgstr "Liste à puces"
msgid "Checkbox"
msgstr ""
msgstr "Case à cocher"
msgid "Heading"
msgstr ""
msgstr "Titre"
msgid "Horizontal Rule"
msgstr ""
msgstr "Règle horizontale"
msgid "Click to stop external editing"
msgstr "Cliquez pour arrêter l'édition externe"
msgid "Watching..."
msgstr "En cours..."
msgid "to-do"
msgstr "tâche"
@@ -1175,7 +1203,7 @@ msgid "Conflicts"
msgstr "Conflits"
msgid "Cannot move notebook to this location"
msgstr "Impossible de déplacer le carnet vers le carnet \"%s\""
msgstr "Impossible de déplacer le carnet à cet endroit"
#, javascript-format
msgid "A notebook with this title already exists: \"%s\""
@@ -1189,10 +1217,10 @@ msgid "title"
msgstr "titre"
msgid "updated date"
msgstr "Date modification"
msgstr "date de modification"
msgid "created date"
msgstr "Date création"
msgstr "date de création"
msgid "Untitled"
msgstr "Sans titre"
@@ -1208,16 +1236,6 @@ msgstr "Impossible de copier la note vers le carnet \"%s\""
msgid "Cannot move note to \"%s\" notebook"
msgstr "Impossible de déplacer la note vers le carnet \"%s\""
msgid "Text editor"
msgstr "Éditeur de texte"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"L'éditeur de texte pour ouvrir et modifier les notes. Si aucun n'est "
"spécifié, il sera détecté automatiquement."
msgid "Language"
msgstr "Langue"
@@ -1264,7 +1282,7 @@ msgid "When creating a new note:"
msgstr "Lors de la création d'une note :"
msgid "Show tray icon"
msgstr "Afficher icône dans la zone de notifications"
msgstr "Afficher l'icône dans la zone de notifications"
msgid "Note: Does not work in all desktop environments."
msgstr "Note : Ne fonctionne pas dans tous les environnements de bureau."
@@ -1301,6 +1319,16 @@ msgstr "%d heure"
msgid "%d hours"
msgstr "%d heures"
msgid "Text editor command"
msgstr "Commande de l'éditeur de texte"
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"La commande de l'éditeur de texte (peut inclure des options) pour ouvrir et "
"modifier les notes. Si non-spécifiée, elle sera détectée automatiquement."
msgid "Show advanced options"
msgstr "Montrer les options avancées"
@@ -1343,6 +1371,24 @@ msgstr "WebDAV : Nom utilisateur"
msgid "WebDAV password"
msgstr "WebDAV : Mot de passe"
msgid "Custom TLS certificates"
msgstr "Certificats TLS personnalisés"
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
"Liste séparée par des virgules contenant les chemins des répertoires "
"contenants des certificats, ou les chemins de certificats individuels. Par "
"exemple : /my/cert_dir, /other/custom.pem. Remarquez que si vous changez les "
"paramètres TLS, vous devez enregistrer vos changements avant de cliquer sur "
"\"Vérifier config synchronisation\"."
msgid "Ignore TLS certificate errors"
msgstr "Ignorer les erreurs de certificats TLS"
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Option invalide: \"%s\". Les valeurs possibles sont : %s."
@@ -1475,9 +1521,9 @@ msgstr "Confirmer"
msgid "Cancel synchronisation"
msgstr "Annuler synchronisation"
#, fuzzy, javascript-format
#, javascript-format
msgid "Decrypting items: %d/%d"
msgstr "Téléchargés : %d/%d."
msgstr "Décryptage des objets : %d/%d"
msgid "New tags:"
msgstr "Nouvelles étiquettes :"

View File

@@ -625,6 +625,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Buscar en todas as notas"
@@ -746,12 +752,15 @@ msgstr "Comprobar a configuración da sincronización"
msgid "Notes and settings are stored in: %s"
msgstr "As notas e axustes gardáronse en: %s"
msgid "Save"
msgstr "Gardar"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr "Gardar"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -888,6 +897,13 @@ msgstr "Estabelecer un contrasinal"
msgid "Add or remove tags"
msgstr "Engadir ou eliminar etiquetas"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Copiar"
msgid "Switch between note and to-do type"
msgstr "Cambiar entre notas e tarefas"
@@ -921,6 +937,9 @@ msgstr "Gardar como…"
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Ligazón ou mensaxe incompatíbeis: %s"
@@ -939,6 +958,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -976,6 +998,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Cancelando..."
msgid "to-do"
msgstr "tarefas pendentes"
@@ -1189,16 +1218,6 @@ msgstr "Non é posíbel copiar a nota ao caderno «%s»"
msgid "Cannot move note to \"%s\" notebook"
msgstr "Non é posíbel mover a nota ao caderno «%s»"
msgid "Text editor"
msgstr "Editor de texto"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"Editor a usar para abrir unha nota. Se non se indica ningún tentará detectar "
"automaticamente o editor predeterminado."
msgid "Language"
msgstr "Idioma"
@@ -1283,6 +1302,18 @@ msgstr "%d hora"
msgid "%d hours"
msgstr "%d horas"
#, fuzzy
msgid "Text editor command"
msgstr "Editor de texto"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"Editor a usar para abrir unha nota. Se non se indica ningún tentará detectar "
"automaticamente o editor predeterminado."
msgid "Show advanced options"
msgstr "Mostrar opcións avanzadas"
@@ -1324,6 +1355,19 @@ msgstr "Usuario de WebDAV"
msgid "WebDAV password"
msgstr "Contrasinal do WebDAV"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Valor incorrecto de opción: «%s». Os valores posíbeis son: %s."

View File

@@ -631,6 +631,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Pretraži u svim bilješkama"
@@ -756,12 +762,15 @@ msgstr "Prekini sinkronizaciju"
msgid "Notes and settings are stored in: %s"
msgstr "Bilješke i postavke su pohranjene u: %s"
msgid "Save"
msgstr "Spremi"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr "Spremi"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -889,6 +898,13 @@ msgstr ""
msgid "Add or remove tags"
msgstr "Dodaj ili makni oznake"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Kopiraj"
msgid "Switch between note and to-do type"
msgstr "Zamijeni bilješku i zadatak"
@@ -922,6 +938,9 @@ msgstr "Spremi promjene"
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Nepodržana poveznica ili poruka: %s"
@@ -938,6 +957,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -975,6 +997,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Prekidam..."
#, fuzzy
msgid "to-do"
msgstr "Novi zadatak"
@@ -1189,16 +1218,6 @@ msgstr "Ne mogu kopirati bilješku u bilježnicu %s"
msgid "Cannot move note to \"%s\" notebook"
msgstr "Ne mogu premjestiti bilješku u bilježnicu %s"
msgid "Text editor"
msgstr "Uređivač teksta"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"Program za uređivanje koji će biti korišten za uređivanje bilješki. Ako ni "
"jedan nije odabran, pokušati će se sa default programom."
msgid "Language"
msgstr "Jezik"
@@ -1285,6 +1304,18 @@ msgstr "%d sat"
msgid "%d hours"
msgstr "%d sati"
#, fuzzy
msgid "Text editor command"
msgstr "Uređivač teksta"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"Program za uređivanje koji će biti korišten za uređivanje bilješki. Ako ni "
"jedan nije odabran, pokušati će se sa default programom."
msgid "Show advanced options"
msgstr "Prikaži napredne opcije"
@@ -1324,6 +1355,19 @@ msgstr ""
msgid "WebDAV password"
msgstr ""
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Nevažeća vrijednost: \"%s\". Moguće vrijednosti su: %s."

View File

@@ -616,6 +616,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Cerca in tutte le note"
@@ -741,12 +747,15 @@ msgstr "Cancella la sincronizzazione"
msgid "Notes and settings are stored in: %s"
msgstr ""
msgid "Save"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr ""
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -874,6 +883,13 @@ msgstr ""
msgid "Add or remove tags"
msgstr "Aggiungi o rimuovi etichetta"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Copia"
msgid "Switch between note and to-do type"
msgstr "Passa da un tipo di nota a un elenco di attività"
@@ -908,6 +924,9 @@ msgstr "Salva i cambiamenti"
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Collegamento o messaggio non supportato: %s"
@@ -924,6 +943,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -961,6 +983,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Cancellazione..."
#, fuzzy
msgid "to-do"
msgstr "Nuova attività"
@@ -1178,16 +1207,6 @@ msgstr "Non posso copiare la nota nel blocco note \"%s\""
msgid "Cannot move note to \"%s\" notebook"
msgstr "Non posso spostare la nota nel blocco note \"%s\""
msgid "Text editor"
msgstr "Editor di testo"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"L'editor che sarà usato per aprire la nota. Se nessun editor è specificato "
"si cercherà di individuare automaticamente l'editor predefinito."
msgid "Language"
msgstr "Linguaggio"
@@ -1274,6 +1293,18 @@ msgstr "%d ora"
msgid "%d hours"
msgstr "%d ore"
#, fuzzy
msgid "Text editor command"
msgstr "Editor di testo"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"L'editor che sarà usato per aprire la nota. Se nessun editor è specificato "
"si cercherà di individuare automaticamente l'editor predefinito."
msgid "Show advanced options"
msgstr "Mostra opzioni avanzate"
@@ -1313,6 +1344,19 @@ msgstr ""
msgid "WebDAV password"
msgstr ""
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Oprione non valida: \"%s\". I valori possibili sono: %s."

View File

@@ -618,6 +618,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "すべてのノートを検索"
@@ -742,12 +748,15 @@ msgstr "同期の中止"
msgid "Notes and settings are stored in: %s"
msgstr "ノートと設定は、%sに保存されます。"
msgid "Save"
msgstr "保存"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr "保存"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -879,6 +888,13 @@ msgstr ""
msgid "Add or remove tags"
msgstr "タグの追加・削除"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "コピー"
msgid "Switch between note and to-do type"
msgstr "ノートとToDoを切り替え"
@@ -912,6 +928,9 @@ msgstr "変更を保存"
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr ""
@@ -928,6 +947,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -965,6 +987,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "中止中..."
#, fuzzy
msgid "to-do"
msgstr "新しいToDo"
@@ -1183,16 +1212,6 @@ msgstr "ノートをノートブック \"%s\"にコピーできませんでし
msgid "Cannot move note to \"%s\" notebook"
msgstr "ノートをノートブック \"%s\"に移動できませんでした。"
msgid "Text editor"
msgstr "テキストエディタ"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"ノートを開くために使用されるエディタです。特に指定がなければ、デフォルトのエ"
"ディタの検出を試みます。"
msgid "Language"
msgstr "言語"
@@ -1279,6 +1298,18 @@ msgstr "%d 時間"
msgid "%d hours"
msgstr "%d 時間"
#, fuzzy
msgid "Text editor command"
msgstr "テキストエディタ"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"ノートを開くために使用されるエディタです。特に指定がなければ、デフォルトのエ"
"ディタの検出を試みます。"
msgid "Show advanced options"
msgstr "詳細な設定の表示"
@@ -1318,6 +1349,19 @@ msgstr ""
msgid "WebDAV password"
msgstr ""
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "無効な設定値: \"%s\"。有効な値は: %sです。"

View File

@@ -558,6 +558,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr ""
@@ -679,12 +685,15 @@ msgstr ""
msgid "Notes and settings are stored in: %s"
msgstr ""
msgid "Save"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr ""
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -807,6 +816,13 @@ msgstr ""
msgid "Add or remove tags"
msgstr ""
msgid "Duplicate"
msgstr ""
#, javascript-format
msgid "%s - Copy"
msgstr ""
msgid "Switch between note and to-do type"
msgstr ""
@@ -839,6 +855,9 @@ msgstr ""
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr ""
@@ -855,6 +874,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -892,6 +914,12 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
msgid "Watching..."
msgstr ""
msgid "to-do"
msgstr ""
@@ -1092,14 +1120,6 @@ msgstr ""
msgid "Cannot move note to \"%s\" notebook"
msgstr ""
msgid "Text editor"
msgstr ""
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
msgid "Language"
msgstr ""
@@ -1180,6 +1200,14 @@ msgstr ""
msgid "%d hours"
msgstr ""
msgid "Text editor command"
msgstr ""
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
msgid "Show advanced options"
msgstr ""
@@ -1217,6 +1245,19 @@ msgstr ""
msgid "WebDAV password"
msgstr ""
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr ""

View File

@@ -635,6 +635,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Zoek in alle notities"
@@ -759,12 +765,15 @@ msgstr "Annuleer synchronisatie"
msgid "Notes and settings are stored in: %s"
msgstr "Notities en instellingen zijn opgeslaan in %s"
msgid "Save"
msgstr "Sla op"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr "Sla op"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -903,6 +912,13 @@ msgstr "Stel wachtwoord in"
msgid "Add or remove tags"
msgstr "Voeg tag toe of verwijder tag"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Kopieer"
msgid "Switch between note and to-do type"
msgstr "Wissel tussen notitie en to-do type"
@@ -938,6 +954,9 @@ msgstr "Sla wijzigingen op"
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Link of bericht \"%s\" wordt niet ondersteund"
@@ -954,6 +973,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -991,6 +1013,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Annuleren..."
#, fuzzy
msgid "to-do"
msgstr "Nieuwe to-do"
@@ -1209,17 +1238,6 @@ msgstr "Kan notitie niet naar notitieboek \"%s\" kopiëren."
msgid "Cannot move note to \"%s\" notebook"
msgstr "Kan notitie niet naar notitieboek \"%s\" verplaatsen."
msgid "Text editor"
msgstr "Tekst editor"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"De editor die zal gebruikt worden bij het openen van een notitie. Als er "
"geen meegegeven wordt, zal het programma de standaard editor proberen te "
"detecteren. "
msgid "Language"
msgstr "Taal"
@@ -1305,6 +1323,19 @@ msgstr "%d uur"
msgid "%d hours"
msgstr "%d uren"
#, fuzzy
msgid "Text editor command"
msgstr "Tekst editor"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"De editor die zal gebruikt worden bij het openen van een notitie. Als er "
"geen meegegeven wordt, zal het programma de standaard editor proberen te "
"detecteren. "
msgid "Show advanced options"
msgstr "Toon geavanceerde opties"
@@ -1346,6 +1377,19 @@ msgstr ""
msgid "WebDAV password"
msgstr "Stel wachtwoord in"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Ongeldige optie: \"%s\". Geldige waarden zijn: %s."

View File

@@ -632,6 +632,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Pesquisar em todas as notas"
@@ -753,12 +759,15 @@ msgstr "Verificar a configuração da sincronização"
msgid "Notes and settings are stored in: %s"
msgstr "Notas e configurações estão armazenadas em: %s"
msgid "Save"
msgstr "Salvar"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr "Enviar"
msgid "Save"
msgstr "Salvar"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -898,6 +907,13 @@ msgstr "Configurar a senha"
msgid "Add or remove tags"
msgstr "Adicionar ou remover tags"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Copiar"
msgid "Switch between note and to-do type"
msgstr "Alternar entre os tipos Nota e Tarefa"
@@ -931,6 +947,9 @@ msgstr "Salvar como..."
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Link ou mensagem não suportada: %s"
@@ -949,6 +968,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -986,6 +1008,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Cancelando..."
msgid "to-do"
msgstr "tarefa"
@@ -1202,16 +1231,6 @@ msgstr "Não é possível copiar a nota para o caderno \"%s\""
msgid "Cannot move note to \"%s\" notebook"
msgstr "Não é possível mover a nota para o caderno \"%s\""
msgid "Text editor"
msgstr "Editor de texto"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"O editor que será usado para abrir uma nota. Se nenhum for indicado, ele "
"tentará detectar automaticamente o editor padrão."
msgid "Language"
msgstr "Idioma"
@@ -1296,6 +1315,18 @@ msgstr "%d hora"
msgid "%d hours"
msgstr "%d horas"
#, fuzzy
msgid "Text editor command"
msgstr "Editor de texto"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"O editor que será usado para abrir uma nota. Se nenhum for indicado, ele "
"tentará detectar automaticamente o editor padrão."
msgid "Show advanced options"
msgstr "Mostrar opções avançadas"
@@ -1337,6 +1368,19 @@ msgstr "Usuário do WebDAV"
msgid "WebDAV password"
msgstr "Senha do WebDAV"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Valor da opção inválida: \"%s\". Os valores possíveis são: %s."

View File

@@ -632,6 +632,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Поиск во всех заметках"
@@ -753,12 +759,15 @@ msgstr "Проверить настройки синхронизации"
msgid "Notes and settings are stored in: %s"
msgstr "Заметки и настройки сохранены в: %s"
msgid "Save"
msgstr "Сохранить"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr "Сохранить"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -898,6 +907,13 @@ msgstr "Установить пароль"
msgid "Add or remove tags"
msgstr "Добавить или удалить теги"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Копировать"
msgid "Switch between note and to-do type"
msgstr "Переключить тип между заметкой и задачей"
@@ -931,6 +947,9 @@ msgstr "Сохранить как..."
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Неподдерживаемая ссыка или сообщение: %s"
@@ -949,6 +968,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -986,6 +1008,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "Отмена..."
msgid "to-do"
msgstr "задача"
@@ -1199,16 +1228,6 @@ msgstr "Не удалось скопировать заметку в блокн
msgid "Cannot move note to \"%s\" notebook"
msgstr "Не удалось переместить заметку в блокнот «%s»"
msgid "Text editor"
msgstr "Текстовый редактор"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"Редактор, в котором будут открываться заметки. Если не задан, будет "
"произведена попытка автоматического определения редактора по умолчанию."
msgid "Language"
msgstr "Язык"
@@ -1293,6 +1312,18 @@ msgstr "%d час"
msgid "%d hours"
msgstr "%d часов"
#, fuzzy
msgid "Text editor command"
msgstr "Текстовый редактор"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"Редактор, в котором будут открываться заметки. Если не задан, будет "
"произведена попытка автоматического определения редактора по умолчанию."
msgid "Show advanced options"
msgstr "Показывать расширенные настройки"
@@ -1334,6 +1365,19 @@ msgstr "Имя пользователя WebDAV"
msgid "WebDAV password"
msgstr "Пароль WebDAV"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Неверное значение параметра: «%s». Доступные значения: %s."

View File

@@ -631,6 +631,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "Išči znotraj vseh zabeležk"
@@ -752,12 +758,15 @@ msgstr "Preveri nastavitve sinhronizacije"
msgid "Notes and settings are stored in: %s"
msgstr "Zabeležke in nastavitve so shranjene v: %s"
msgid "Save"
msgstr "Shrani"
msgid "Apply"
msgstr ""
msgid "Submit"
msgstr ""
msgid "Save"
msgstr "Shrani"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -898,6 +907,13 @@ msgstr "Nastavi geslo"
msgid "Add or remove tags"
msgstr "Dodaj ali odstrani oznake"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "Kopiraj"
msgid "Switch between note and to-do type"
msgstr "Menjaj med zabeležko in seznamom opravil"
@@ -935,6 +951,9 @@ msgstr "Shrani kot..."
msgid "Copy path to clipboard"
msgstr ""
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr "Nepodprta povezava ali sporočilo: %s"
@@ -953,6 +972,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -990,6 +1012,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "V preklicu..."
msgid "to-do"
msgstr "seznam opravil"
@@ -1203,16 +1232,6 @@ msgstr "Ni moč kopirati zabeležke v \"%s\" beležnico"
msgid "Cannot move note to \"%s\" notebook"
msgstr "Ni moč premakniti zabeležke v \"%s\" beležnico"
msgid "Text editor"
msgstr "Urejevalnik besedila"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
"Urejevalnik, ki bo uporabljen za odpiranje zabeležk. Če noben ni izbran, bo "
"avtomatsko zaznan privzeti urejevalnik."
msgid "Language"
msgstr "Jezik"
@@ -1297,6 +1316,18 @@ msgstr "Števil ur: %d"
msgid "%d hours"
msgstr "Število ur: %d"
#, fuzzy
msgid "Text editor command"
msgstr "Urejevalnik besedila"
#, fuzzy
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
"Urejevalnik, ki bo uporabljen za odpiranje zabeležk. Če noben ni izbran, bo "
"avtomatsko zaznan privzeti urejevalnik."
msgid "Show advanced options"
msgstr "Pokaži napredne možnosti"
@@ -1338,6 +1369,19 @@ msgstr "WebDAV uporabniško ime"
msgid "WebDAV password"
msgstr "WebDAV geslo"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Neveljavna vrednost: \"%s\". Možne vrednosti so : %s."

File diff suppressed because it is too large Load Diff

View File

@@ -567,6 +567,12 @@ msgstr ""
msgid "Italic"
msgstr ""
msgid "Insert Date Time"
msgstr ""
msgid "Edit in external editor"
msgstr ""
msgid "Search in all the notes"
msgstr "在所有記事中搜尋"
@@ -688,13 +694,16 @@ msgstr "檢測同步設置"
msgid "Notes and settings are stored in: %s"
msgstr ""
msgid "Save"
msgstr "儲存"
msgid "Apply"
msgstr ""
#, fuzzy
msgid "Submit"
msgstr "提交 / 送出"
msgid "Save"
msgstr "儲存"
msgid ""
"Disabling encryption means *all* your notes and attachments are going to be "
"re-synchronised and sent unencrypted to the sync target. Do you wish to "
@@ -818,6 +827,13 @@ msgstr "設置密碼"
msgid "Add or remove tags"
msgstr "新增或移除標籤"
msgid "Duplicate"
msgstr ""
#, fuzzy, javascript-format
msgid "%s - Copy"
msgstr "複製"
msgid "Switch between note and to-do type"
msgstr ""
@@ -850,6 +866,9 @@ msgstr "另存為..."
msgid "Copy path to clipboard"
msgstr "複製路徑到剪貼板"
msgid "Copy Link Address"
msgstr ""
#, javascript-format
msgid "Unsupported link or message: %s"
msgstr ""
@@ -866,6 +885,9 @@ msgstr ""
msgid "emphasized text"
msgstr ""
msgid "List item"
msgstr ""
msgid "Insert Hyperlink"
msgstr ""
@@ -903,6 +925,13 @@ msgstr ""
msgid "Horizontal Rule"
msgstr ""
msgid "Click to stop external editing"
msgstr ""
#, fuzzy
msgid "Watching..."
msgstr "正在取消中..."
msgid "to-do"
msgstr "待辦事項"
@@ -1106,14 +1135,6 @@ msgstr "無法複製此記事到 \"%s\" 記事本"
msgid "Cannot move note to \"%s\" notebook"
msgstr "無法移動此記事到 \"%s\" 記事本"
msgid "Text editor"
msgstr "文字編輯器"
msgid ""
"The editor that will be used to open a note. If none is provided it will try "
"to auto-detect the default editor."
msgstr ""
msgid "Language"
msgstr "語言"
@@ -1196,6 +1217,15 @@ msgstr "%d 小時"
msgid "%d hours"
msgstr "%d 小時"
#, fuzzy
msgid "Text editor command"
msgstr "文字編輯器"
msgid ""
"The editor command (may include arguments) that will be used to open a note. "
"If none is provided it will try to auto-detect the default editor."
msgstr ""
msgid "Show advanced options"
msgstr "顯示進階選項"
@@ -1235,6 +1265,19 @@ msgstr "WebDAV 用戶名稱"
msgid "WebDAV password"
msgstr "WebDAV 密碼"
msgid "Custom TLS certificates"
msgstr ""
msgid ""
"Comma-separated list of paths to directories to load the certificates from, "
"or path to individual cert files. For example: /my/cert_dir, /other/custom."
"pem. Note that if you make changes to the TLS settings, you must save your "
"changes before clicking on \"Check synchronisation configuration\"."
msgstr ""
msgid "Ignore TLS certificate errors"
msgstr ""
#, javascript-format
msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "不正確選項值: \"%s\"。可能的值為: %s。"

View File

@@ -10,9 +10,9 @@
"integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4="
},
"acorn": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz",
"integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ=="
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
"integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ=="
},
"acorn-globals": {
"version": "4.1.0",
@@ -316,9 +316,9 @@
"integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs="
},
"cssstyle": {
"version": "0.2.37",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz",
"integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=",
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.3.1.tgz",
"integrity": "sha512-tNvaxM5blOnxanyxI6panOsnfiyLRj3HV4qjqqS45WPNS1usdYWRUQjqTEEELK73lpeP/1KoIGYUwrBn/VcECA==",
"requires": {
"cssom": "0.3.x"
}
@@ -468,9 +468,9 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"escodegen": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz",
"integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==",
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.10.0.tgz",
"integrity": "sha512-fjUOf8johsv23WuIKdNQU4P9t9jhQ4Qzx6pC2uW890OloK3Zs1ZAoCNpg/2larNF501jLl3UNy0kIRcF6VI22g==",
"requires": {
"esprima": "^3.1.3",
"estraverse": "^4.2.0",
@@ -904,17 +904,17 @@
"dev": true
},
"joplin-turndown": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.3.tgz",
"integrity": "sha512-WbAXje8wq4/ZLNtPDUFBEtG5zKEbz7Wth5N3vB4Nw7k+PUs3mMF49LVEPP7Kc6H4Ui671qdjpSShvdsmiLY2gA==",
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.7.tgz",
"integrity": "sha512-dOUzNg0nt1Sgz5LO/6CvZmgGLk3q0BCelZ+CDTb/UOBn7reqUl7R0B4yAfiqDtO8p0/wzY/N87RzXeEawbPvFA==",
"requires": {
"jsdom": "^11.9.0"
}
},
"joplin-turndown-plugin-gfm": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/joplin-turndown-plugin-gfm/-/joplin-turndown-plugin-gfm-1.0.2.tgz",
"integrity": "sha512-GRXmjHFrEyUnXOYzOZvUGGtKxPm5LuK98+73ZADqQYdGzMWp/o8Qx22YYAeIBsOV2WtVsRxe2IpUGBG4foSRyQ=="
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/joplin-turndown-plugin-gfm/-/joplin-turndown-plugin-gfm-1.0.7.tgz",
"integrity": "sha512-z0SveNcchtWwglkO7SgvDzPnVHYk1WumD0QRcWvUchIihqXwDVlve3G8AHkIhM69LY1YdC0HCZJlSMp2spBe/g=="
},
"jpeg-js": {
"version": "0.1.2",
@@ -933,22 +933,22 @@
"optional": true
},
"jsdom": {
"version": "11.10.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.10.0.tgz",
"integrity": "sha512-x5No5FpJgBg3j5aBwA8ka6eGuS5IxbC8FOkmyccKvObtFT0bDMict/LOxINZsZGZSfGdNomLZ/qRV9Bpq/GIBA==",
"version": "11.11.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.11.0.tgz",
"integrity": "sha512-ou1VyfjwsSuWkudGxb03FotDajxAto6USAlmMZjE2lc0jCznt7sBWkhfRBRaWwbnmDqdMSTKTLT5d9sBFkkM7A==",
"requires": {
"abab": "^1.0.4",
"acorn": "^5.3.0",
"acorn-globals": "^4.1.0",
"array-equal": "^1.0.0",
"cssom": ">= 0.3.2 < 0.4.0",
"cssstyle": ">= 0.2.37 < 0.3.0",
"cssstyle": ">= 0.3.1 < 0.4.0",
"data-urls": "^1.0.0",
"domexception": "^1.0.0",
"escodegen": "^1.9.0",
"html-encoding-sniffer": "^1.0.2",
"left-pad": "^1.2.0",
"nwmatcher": "^1.4.3",
"nwsapi": "^2.0.0",
"parse5": "4.0.0",
"pn": "^1.1.0",
"request": "^2.83.0",
@@ -960,7 +960,7 @@
"webidl-conversions": "^4.0.2",
"whatwg-encoding": "^1.0.3",
"whatwg-mimetype": "^2.1.0",
"whatwg-url": "^6.4.0",
"whatwg-url": "^6.4.1",
"ws": "^4.0.0",
"xml-name-validator": "^3.0.0"
}
@@ -1223,10 +1223,10 @@
"pify": "^3.0.0"
}
},
"nwmatcher": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz",
"integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ=="
"nwsapi": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.4.tgz",
"integrity": "sha512-Zt6HRR6RcJkuj5/N9zeE7FN6YitRW//hK2wTOwX274IBphbY3Zf5+yn5mZ9v/SzAOTMjQNxZf9KkmPLWn0cV4g=="
},
"oauth-sign": {
"version": "0.8.2",
@@ -2471,9 +2471,9 @@
},
"dependencies": {
"punycode": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz",
"integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0="
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
}
}
},
@@ -2606,9 +2606,9 @@
"integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew=="
},
"whatwg-url": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.4.1.tgz",
"integrity": "sha512-FwygsxsXx27x6XXuExA/ox3Ktwcbf+OAvrKmLulotDAiO1Q6ixchPFaHYsis2zZBZSJTR0+dR+JVtf7MlbqZjw==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
"integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
"requires": {
"lodash.sortby": "^4.7.0",
"tr46": "^1.0.1",

View File

@@ -38,8 +38,8 @@
"html-entities": "^1.2.1",
"html-minifier": "^3.5.15",
"image-type": "^3.0.0",
"joplin-turndown": "^4.0.3",
"joplin-turndown-plugin-gfm": "^1.0.2",
"joplin-turndown": "^4.0.7",
"joplin-turndown-plugin-gfm": "^1.0.7",
"jssha": "^2.3.0",
"levenshtein": "^1.0.5",
"lodash": "^4.17.4",

View File

@@ -96,4 +96,14 @@ describe('services_ResourceService', function() {
expect(!!(await Resource.load(resource1.id))).toBe(true);
}));
it('should not delete a resource that has never been associated with any note, because it probably means the resource came via sync, and associated note has not arrived yet', asyncTest(async () => {
const service = new ResourceService();
const resource = await shim.createResourceFromPath(__dirname + '/../tests/support/photo.jpg');
await service.indexNoteResources();
await service.deleteOrphanResources(0);
expect((await Resource.all()).length).toBe(1);
}));
});

View File

@@ -1,166 +1,20 @@
const reservedPorts = [1024, 1027, 1028, 1029, 1058, 1059, 1080, 1085, 1098, 1099, 1109, 1119, 1167, 1194, 1198, 1214, 1220, 1234, 1241, 1270, 1293, 1311, 1314, 1337, 1341, 1344,
1352, 1360, 1414, 1417, 1418, 1419, 1420, 1431, 1433, 1434, 1492, 1494, 1500, 1501, 1503, 1512, 1513, 1521, 1524, 1527, 1533, 1547, 1550, 1581, 1582, 1583, 1589, 1604, 1626, 1627,
1628, 1629, 1645, 1646, 1666, 1677, 1688, 1701, 1707, 1716, 1719, 1720, 1723, 1755, 1761, 1783, 1801, 1812, 1813, 1863, 1880, 1883, 1900, 1935, 1967, 1970, 1972, 1984, 1985, 1998,
2000, 2010, 2033, 2049, 2056, 2080, 2082, 2083, 2086, 2087, 2095, 2096, 2100, 2101, 2102, 2103, 2104, 2123, 2142, 2152, 2159, 2181, 2195, 2196, 2210, 2211, 2221, 2222, 2261, 2262,
2266, 2305, 2351, 2368, 2369, 2370, 2372, 2375, 2376, 2377, 2379, 2380, 2399, 2401, 2404, 2424, 2427, 2447, 2480, 2483, 2484, 2535, 2541, 2546, 2593, 2598, 2599, 2638, 2710, 2727,
2809, 2811, 2827, 2944, 2945, 2947, 2948, 2949, 2967, 3000, 3004, 3020, 3050, 3052, 3074, 3101, 3128, 3225, 3233, 3260, 3268, 3269, 3283, 3290, 3305, 3306, 3313, 3316, 3323, 3332,
3333, 3351, 3386, 3389, 3396, 3412, 3423, 3424, 3455, 3478, 3479, 3480, 3483, 3493, 3516, 3527, 3535, 3544, 3632, 3645, 3659, 3667, 3689, 3690, 3702, 3724, 3725, 3768, 3784, 3785,
3799, 3804, 3825, 3826, 3830, 3835, 3856, 3868, 3872, 3880, 3900, 3960, 3962, 3978, 3979, 3999, 4000, 4001, 4018, 4035, 4045, 4050, 4069, 4089, 4090, 4093, 4096, 4105, 4111, 4116,
4125, 4172, 4190, 4198, 4201, 4222, 4226, 4242, 4243, 4244, 4303, 4321, 4352, 4444, 4486, 4488, 4500, 4502, 4505, 4534, 4560, 4567, 4569, 4604, 4605, 4610, 4662, 4664, 4672, 4711,
4713, 4728, 4730, 4739, 4747, 4750, 4753, 4840, 4843, 4847, 4848, 4894, 4949, 4950, 5000, 5001, 5002, 5003, 5004, 5005, 5010, 5011, 5031, 5037, 5048, 5050, 5051, 5060, 5061, 5062,
5064, 5065, 5070, 5084, 5085, 5093, 5099, 5104, 5121, 5124, 5125, 5150, 5151, 5154, 5190, 5201, 5222, 5223, 5228, 5242, 5243, 5246, 5247, 5269, 5280, 5281, 5298, 5310, 5349, 5351,
5353, 5355, 5357, 5358, 5394, 5402, 5405, 5412, 5413, 5417, 5421, 5432, 5433, 5445, 5480, 5481, 5495, 5498, 5499, 5500, 5501, 5517, 5550, 5554, 5555, 5556, 5568, 5601, 5631, 5632,
5656, 5666, 5667, 5670, 5672, 5678, 5683, 5701, 5718, 5719, 5722, 5723, 5724, 5741, 5742, 5800, 5900, 5931, 5938, 5984, 5985, 5986, 5988, 6000, 6001, 6002, 6003, 6004, 6005, 6006,
6007, 6008, 6009, 6010, 6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022, 6023, 6024, 6025, 6026, 6027, 6028, 6029, 6030, 6031, 6032, 6033, 6034, 6035, 6036,
6037, 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, 6048, 6049, 6050, 6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, 6062, 6063, 6086, 6100, 6101,
6110, 6111, 6112, 6113, 6136, 6159, 6200, 6201, 6225, 6227, 6240, 6244, 6255, 6257, 6260, 6262, 6343, 6346, 6347, 6350, 6379, 6389, 6432, 6436, 6437, 6444, 6445, 6463, 6502, 6513,
6514, 6515, 6543, 6556, 6560, 6566, 6571, 6600, 6601, 6602, 6619, 6622, 6653, 6660, 6665, 6679, 6690, 6697, 6699, 6715, 6771, 6783, 6789, 6869, 6881, 6888, 6889, 6891, 6901, 6902,
6969, 6970, 7000, 7001, 7002, 7005, 7006, 7010, 7022, 7023, 7025, 7047, 7070, 7133, 7144, 7145, 7171, 7262, 7272, 7306, 7307, 7312, 7396, 7400, 7401, 7402, 7471, 7473, 7474, 7478,
7542, 7547, 7575, 7624, 7631, 7634, 7652, 7655, 7656, 7670, 7687, 7707, 7717, 7777, 7831, 7880, 7890, 7915, 7935, 7946, 7990, 8000, 8005, 8006, 8007, 8008, 8009, 8042, 8069, 8070,
8074, 8075, 8080, 8088, 8089, 8090, 8091, 8092, 8111, 8112, 8116, 8118, 8123, 8139, 8140, 8172, 8184, 8194, 8200, 8222, 8243, 8245, 8280, 8281, 8291, 8303, 8332, 8333, 8337, 8384,
8388, 8443, 8444, 8484, 8500, 8530, 8531, 8580, 8629, 8642, 8691, 8767, 8834, 8840, 8880, 8883, 8887, 8888, 8889, 8983, 8997, 8998, 8999, 9000, 9001, 9002, 9006, 9030, 9042, 9043,
9050, 9060, 9080, 9090, 9091, 9092, 9100, 9101, 9102, 9103, 9119, 9150, 9191, 9199, 9200, 9217, 9293, 9300, 9303, 9306, 9309, 9312, 9332, 9333, 9339, 9389, 9418, 9419, 9420, 9421,
9422, 9425, 9443, 9535, 9536, 9600, 9675, 9676, 9695, 9785, 9800, 9875, 9898, 9899, 9981, 9982, 9987, 9993, 9997, 9999, 10000, 10001, 10009, 10010, 10024, 10025, 10042, 10050, 10051,
10080, 10110, 10172, 10200, 10201, 10212, 10308, 10480, 10505, 10514, 10823, 10891, 10933, 11001, 11111, 11112, 11211, 11214, 11215, 11235, 11311, 11371, 11753, 12012, 12013, 12035,
12043, 12046, 12201, 12222, 12223, 12345, 12443, 12489, 12975, 13000, 13008, 13075, 13720, 13721, 13724, 13782, 13783, 13785, 13786, 14550, 14567, 15000, 15345, 15441, 15567, 15672,
16000, 16080, 16200, 16225, 16250, 16261, 16300, 16384, 16385, 16386, 16387, 16393, 16394, 16395, 16396, 16397, 16398, 16399, 16400, 16401, 16402, 16403, 16404, 16405, 16406, 16407,
16408, 16409, 16410, 16411, 16412, 16413, 16414, 16415, 16416, 16417, 16418, 16419, 16420, 16421, 16422, 16423, 16424, 16425, 16426, 16427, 16428, 16429, 16430, 16431, 16432, 16433,
16434, 16435, 16436, 16437, 16438, 16439, 16440, 16441, 16442, 16443, 16444, 16445, 16446, 16447, 16448, 16449, 16450, 16451, 16452, 16453, 16454, 16455, 16456, 16457, 16458, 16459,
16460, 16461, 16462, 16463, 16464, 16465, 16466, 16467, 16468, 16469, 16470, 16471, 16472, 16482, 16567, 17011, 17500, 18091, 18092, 18104, 18200, 18201, 18206, 18300, 18301, 18306,
18333, 18400, 18401, 18505, 18506, 18605, 18606, 19000, 19001, 19132, 19150, 19226, 19294, 19295, 19302, 19812, 19813, 19814, 19999, 20000, 20560, 20595, 20808, 21025, 22000, 22136,
22222, 23073, 23399, 23513, 24441, 24444, 24465, 24554, 24800, 24842, 25565, 25575, 25826, 26000, 26900, 27000, 27016, 27017, 27031, 27036, 27037, 27374, 27500, 27888, 27901, 27950,
27960, 28001, 28015, 28770, 28785, 28852, 28910, 28960, 29000, 29070, 29900, 29920, 30564, 31337, 31416, 31438, 31457, 32137, 32400, 32764, 32887, 32976, 33434, 33848, 34000, 34197,
35357, 37008, 40000, 43110, 43594, 44405, 44818, 47001, 47808, 49151];
// From https://github.com/coverslide/node-alea
const AleaModule = function () {
// importState to sync generator states
Alea.importState = function(i){
var random = new Alea();
random.importState(i);
return random;
function randomClipperPort(state, env) {
const startPorts = {
prod: 41184,
dev: 27583,
};
return Alea;
const startPort = env === 'prod' ? startPorts.prod : startPorts.dev;
function Alea() {
return (function(args) {
// Johannes Baagøe <baagoe@baagoe.com>, 2010
var s0 = 0;
var s1 = 0;
var s2 = 0;
var c = 1;
if (args.length === 0) {
args = [+new Date()];
}
var mash = Mash();
s0 = mash(' ');
s1 = mash(' ');
s2 = mash(' ');
for (var i = 0; i < args.length; i++) {
s0 -= mash(args[i]);
if (s0 < 0) {
s0 += 1;
}
s1 -= mash(args[i]);
if (s1 < 0) {
s1 += 1;
}
s2 -= mash(args[i]);
if (s2 < 0) {
s2 += 1;
}
}
mash = null;
var random = function() {
var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
s0 = s1;
s1 = s2;
return s2 = t - (c = t | 0);
};
random.uint32 = function() {
return random() * 0x100000000; // 2^32
};
random.fract53 = function() {
return random() +
(random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53
};
random.version = 'Alea 0.9';
random.args = args;
// my own additions to sync state between two generators
random.exportState = function(){
return [s0, s1, s2, c];
};
random.importState = function(i){
s0 = +i[0] || 0;
s1 = +i[1] || 0;
s2 = +i[2] || 0;
c = +i[3] || 0;
};
return random;
} (Array.prototype.slice.call(arguments)));
if (!state) {
state = { offset: 0 };
} else {
state.offset++;
}
function Mash() {
var n = 0xefc8249d;
state.port = startPort + state.offset;
var mash = function(data) {
data = data.toString();
for (var i = 0; i < data.length; i++) {
n += data.charCodeAt(i);
var h = 0.02519603282416938 * n;
n = h >>> 0;
h -= n;
h *= n;
n = h >>> 0;
h -= n;
n += h * 0x100000000; // 2^32
}
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
};
mash.version = 'Mash 0.9';
return mash;
}
return state;
}
const Alea = AleaModule()
function findClipperPort(state, mode = 'prod') {
const seed = mode === 'prod' ? 1867 : 2001;
const minPort = 1024
const maxPort = 49151
let prng = null;
if (!state) {
prng = new Alea(seed)
state = { prng: prng }
} else {
prng = state.prng;
}
const randomPort = () => {
return minPort + Math.floor(prng() * ((maxPort + 1) - minPort));
}
let port = null;
for (let i = 0; i < maxPort; i++) {
port = randomPort();
if (reservedPorts.indexOf(port) < 0) break;
}
if (!port) throw new Error('Cannot find a non-reserved port');
state.port = port;
return state;
}
module.exports = findClipperPort;
module.exports = randomClipperPort;

View File

@@ -60,6 +60,12 @@ class ElectronAppWrapper {
// Fix: https://github.com/electron-userland/electron-builder/issues/2269
if (shim.isLinux()) windowOptions.icon = __dirname + '/build/icons/128x128.png';
require('electron-context-menu')({
shouldShowMenu: (event, params) => {
return params.isEditable;
},
});
this.win_ = new BrowserWindow(windowOptions)
this.win_.loadURL(url.format({

View File

@@ -11,7 +11,7 @@ class InteropServiceHelper {
if (module.target === 'file') {
path = bridge().showSaveDialog({
filters: [{ name: module.description, extensions: [module.fileExtension]}]
filters: [{ name: module.description, extensions: module.fileExtension}]
});
} else {
path = bridge().showOpenDialog({

View File

@@ -41,6 +41,7 @@ const appDefaultState = Object.assign({}, defaultState, {
noteVisiblePanes: ['editor', 'viewer'],
sidebarVisibility: true,
windowContentSize: bridge().windowContentSize(),
watchedNoteFiles: [],
});
class Application extends BaseApplication {
@@ -142,6 +143,33 @@ class Application extends BaseApplication {
newState.sidebarVisibility = action.visibility;
break;
case 'NOTE_FILE_WATCHER_ADD':
if (newState.watchedNoteFiles.indexOf(action.id) < 0) {
newState = Object.assign({}, state);
const watchedNoteFiles = newState.watchedNoteFiles.slice();
watchedNoteFiles.push(action.id);
newState.watchedNoteFiles = watchedNoteFiles;
}
break;
case 'NOTE_FILE_WATCHER_REMOVE':
newState = Object.assign({}, state);
const idx = newState.watchedNoteFiles.indexOf(action.id);
if (idx >= 0) {
const watchedNoteFiles = newState.watchedNoteFiles.slice();
watchedNoteFiles.splice(idx, 1);
newState.watchedNoteFiles = watchedNoteFiles;
}
break;
case 'NOTE_FILE_WATCHER_CLEAR':
newState = Object.assign({}, state);
newState.watchedNoteFiles = [];
break;
}
} catch (error) {
error.message = 'In reducer: ' + error.message + ' Action: ' + JSON.stringify(action);
@@ -155,7 +183,7 @@ class Application extends BaseApplication {
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'locale' || action.type == 'SETTING_UPDATE_ALL') {
setLocale(Setting.value('locale'));
// The bridge runs within the main process, with its own instance of locale.js
// so it needs to be set too here.
// so it needs to be set too here.
bridge().setLocale(Setting.value('locale'));
this.refreshMenu();
}
@@ -257,7 +285,7 @@ class Application extends BaseApplication {
if (moduleSource === 'file') {
path = bridge().showOpenDialog({
filters: [{ name: module.description, extensions: [module.fileExtension]}]
filters: [{ name: module.description, extensions: module.fileExtensions}]
});
} else {
path = bridge().showOpenDialog({
@@ -334,7 +362,6 @@ class Application extends BaseApplication {
}
}, {
label: _('New notebook'),
accelerator: 'CommandOrControl+B',
screens: ['Main'],
click: () => {
this.dispatch({
@@ -414,9 +441,30 @@ class Application extends BaseApplication {
name: 'textItalic',
});
},
}, {
label: _('Insert Date Time'),
screens: ['Main'],
visible: false,
accelerator: 'CommandOrControl+Shift+T',
click: () => {
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'insertDateTime',
});
},
}, {
type: 'separator',
screens: ['Main'],
}, {
label: _('Edit in external editor'),
screens: ['Main'],
accelerator: 'CommandOrControl+E',
click: () => {
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'commandStartExternalEditing',
});
},
}, {
label: _('Search in all the notes'),
screens: ['Main'],

View File

@@ -87,6 +87,7 @@ class Bridge {
const result = this.showMessageBox_(this.window(), {
type: 'question',
message: message,
cancelId: 1,
buttons: [_('OK'), _('Cancel')],
});
return result === 0;

View File

@@ -63,6 +63,7 @@ class ConfigScreenComponent extends React.Component {
color: theme.colorFaded,
marginTop: 5,
fontStyle: 'italic',
maxWidth: '70em',
});
const updateSettingValue = (key, value) => {
@@ -148,6 +149,10 @@ class ConfigScreenComponent extends React.Component {
return output;
}
onApplyClick() {
shared.saveSettings(this);
}
onSaveClick() {
shared.saveSettings(this);
this.props.dispatch({ type: 'NAV_BACK' });
@@ -203,8 +208,9 @@ class ConfigScreenComponent extends React.Component {
{_('Notes and settings are stored in: %s', pathUtils.toSystemSlashes(Setting.value('profileDir'), process.platform))}
</div>
{ settingComps }
<button onClick={() => {this.onSaveClick()}} style={buttonStyle}>{_('Save')}</button>
<button onClick={() => {this.onSaveClick()}} style={buttonStyle}>{_('OK')}</button>
<button onClick={() => {this.onCancelClick()}} style={buttonStyle}>{_('Cancel')}</button>
<button onClick={() => {this.onApplyClick()}} style={buttonStyle}>{_('Apply')}</button>
</div>
</div>
);

View File

@@ -59,7 +59,7 @@ class ImportScreenComponent extends React.Component {
async doImport() {
const filePath = this.props.filePath;
const folderTitle = await Folder.findUniqueFolderTitle(filename(filePath));
const folderTitle = await Folder.findUniqueItemTitle(filename(filePath));
const messages = this.state.messages.slice();
this.addMessage('start', _('New notebook "%s" will be created and file "%s" will be imported into it', folderTitle, basename(filePath)));

View File

@@ -88,6 +88,15 @@ class NoteListComponent extends React.Component {
});
}}));
menu.append(new MenuItem({label: _('Duplicate'), click: async () => {
for (let i = 0; i < noteIds.length; i++) {
const note = await Note.load(noteIds[i]);
await Note.duplicate(noteIds[i], {
uniqueTitle: _('%s - Copy', note.title),
});
}
}}));
menu.append(new MenuItem({label: _('Switch between note and to-do type'), click: async () => {
for (let i = 0; i < noteIds.length; i++) {
const note = await Note.load(noteIds[i]);
@@ -119,6 +128,13 @@ class NoteListComponent extends React.Component {
}}));
}
exportMenu.append(new MenuItem({ label: 'PDF - ' + _('PDF File') , click: () => {
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: 'exportPdf',
});
}}));
const exportMenuItem = new MenuItem({label: _('Export'), submenu: exportMenu});
menu.append(exportMenuItem);

View File

@@ -20,13 +20,15 @@ const MenuItem = bridge().MenuItem;
const { shim } = require('lib/shim.js');
const eventManager = require('../eventManager');
const fs = require('fs-extra');
const {clipboard} = require('electron')
const md5 = require('md5');
const mimeUtils = require('lib/mime-utils.js').mime;
const ArrayUtils = require('lib/ArrayUtils');
const urlUtils = require('lib/urlUtils');
const dialogs = require('./dialogs');
const markdownUtils = require('lib/markdownUtils');
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
const { toSystemSlashes, safeFilename } = require('lib/path-utils');
const { clipboard } = require('electron');
require('brace/mode/markdown');
// https://ace.c9.io/build/kitchen-sink.html
@@ -66,6 +68,7 @@ class NoteTextComponent extends React.Component {
this.restoreScrollTop_ = null;
this.lastSetHtml_ = '';
this.lastSetMarkers_ = [];
this.selectionRange_ = null;
// Complicated but reliable method to get editor content height
// https://github.com/ajaxorg/ace/issues/2046
@@ -127,11 +130,12 @@ class NoteTextComponent extends React.Component {
}
const updateSelectionRange = () => {
const ranges = this.rawEditor().getSelection().getAllRanges();
if (!ranges || !ranges.length || !this.state.note) {
this.setState({ selectionRange: null });
this.selectionRange_ = null;
} else {
this.setState({ selectionRange: ranges[0] });
this.selectionRange_ = ranges[0];
}
}
@@ -142,20 +146,32 @@ class NoteTextComponent extends React.Component {
this.aceEditor_focus = (event) => {
updateSelectionRange();
}
this.externalEditWatcher_noteChange = (event) => {
if (!this.state.note || !this.state.note.id) return;
if (event.id === this.state.note.id) {
this.reloadNote(this.props);
}
}
}
// Note:
// - What's called "cursor position" is expressed as { row: x, column: y } and is how Ace Editor get/set the cursor position
// - A "range" defines a selection with a start and end cusor position, expressed as { start: <CursorPos>, end: <CursorPos> }
// - A "text offset" below is the absolute position of the cursor in the string, as would be used in the indexOf() function.
// The functions below are used to convert between the different types.
rangeToTextOffsets(range, body) {
return {
start: this.cursorPositionToTextOffsets(range.start, body),
end: this.cursorPositionToTextOffsets(range.end, body),
start: this.cursorPositionToTextOffset(range.start, body),
end: this.cursorPositionToTextOffset(range.end, body),
};
}
cursorPosition() {
return this.cursorPositionToTextOffsets(this.editor_.editor.getCursorPosition(), this.state.note.body);
currentTextOffset() {
return this.cursorPositionToTextOffset(this.editor_.editor.getCursorPosition(), this.state.note.body);
}
cursorPositionToTextOffsets(cursorPos, body) {
cursorPositionToTextOffset(cursorPos, body) {
if (!this.editor_ || !this.editor_.editor || !this.state.note || !this.state.note.body) return 0;
const noteLines = body.split('\n');
@@ -172,7 +188,25 @@ class NoteTextComponent extends React.Component {
}
}
return pos;
return pos;
}
textOffsetToCursorPosition(offset, body) {
const lines = body.split('\n');
let row = 0;
let currentOffset = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (currentOffset + line.length >= offset) {
return {
row: row,
column: offset - currentOffset,
}
}
row++;
currentOffset += line.length + 1;
}
}
mdToHtml() {
@@ -219,6 +253,8 @@ class NoteTextComponent extends React.Component {
eventManager.removeListener('alarmChange', this.onAlarmChange_);
eventManager.removeListener('noteTypeToggle', this.onNoteTypeToggle_);
eventManager.removeListener('todoToggle', this.onTodoToggle_);
this.destroyExternalEditWatcher();
}
async saveIfNeeded(saveIfNewNote = false) {
@@ -230,6 +266,8 @@ class NoteTextComponent extends React.Component {
if (!shared.isModified(this)) return;
}
await shared.saveNoteButton_press(this);
this.externalEditWatcherUpdateNoteFile(this.state.note);
}
async saveOneProperty(name, value) {
@@ -267,6 +305,7 @@ class NoteTextComponent extends React.Component {
if (props.newNote) {
note = Object.assign({}, props.newNote);
this.lastLoadedNoteId_ = null;
this.externalEditWatcherStopWatchingAll();
} else {
noteId = props.noteId;
loadingNewNote = stateNoteId !== noteId;
@@ -292,6 +331,8 @@ class NoteTextComponent extends React.Component {
// Scroll back to top when loading new note
if (loadingNewNote) {
this.externalEditWatcherStopWatchingAll();
this.editorMaxScrollTop_ = 0;
// HACK: To go around a bug in Ace editor, we first set the scroll position to 1
@@ -426,7 +467,9 @@ class NoteTextComponent extends React.Component {
const menu = new Menu()
if (itemType === "image" || itemType === "link") {
console.info(itemType);
if (itemType === "image" || itemType === "resource") {
const resource = await Resource.load(arg0.resourceId);
const resourcePath = Resource.fullPath(resource);
@@ -444,10 +487,16 @@ class NoteTextComponent extends React.Component {
}}));
menu.append(new MenuItem({label: _('Copy path to clipboard'), click: async () => {
const { clipboard } = require('electron');
const { toSystemSlashes } = require('lib/path-utils.js');
clipboard.writeText(toSystemSlashes(resourcePath));
}}));
} else if (itemType === "text") {
menu.append(new MenuItem({label: _('Copy'), click: async () => {
clipboard.writeText(arg0.textToCopy);
}}));
} else if (itemType === "link") {
menu.append(new MenuItem({label: _('Copy Link Address'), click: async () => {
clipboard.writeText(arg0.textToCopy);
}}));
} else {
reg.logger().error('Unhandled item type: ' + itemType);
return;
@@ -557,8 +606,8 @@ class NoteTextComponent extends React.Component {
const letters = ['F', 'T', 'P', 'Q', 'L', ','];
for (let i = 0; i < letters.length; i++) {
const l = letters[i];
cancelledKeys.push('Ctrl+' + l);
cancelledKeys.push('Command+' + l);
cancelledKeys.push('Ctrl+' + l);
cancelledKeys.push('Command+' + l);
}
for (let i = 0; i < cancelledKeys.length; i++) {
@@ -575,6 +624,18 @@ class NoteTextComponent extends React.Component {
document.querySelector('#note-editor').addEventListener('paste', this.onEditorPaste_, true);
document.querySelector('#note-editor').addEventListener('keydown', this.onEditorKeyDown_);
const lineLeftSpaces = function(line) {
let output = '';
for (let i = 0; i < line.length; i++) {
if ([' ', '\t'].indexOf(line[i]) >= 0) {
output += line[i];
} else {
break;
}
}
return output;
}
// Disable Markdown auto-completion (eg. auto-adding a dash after a line with a dash.
// https://github.com/ajaxorg/ace/issues/2754
const that = this; // The "this" within the function below refers to something else
@@ -582,12 +643,15 @@ class NoteTextComponent extends React.Component {
const ls = that.state.lastKeys;
if (ls.length >= 2 && ls[ls.length - 1] === 'Enter' && ls[ls.length - 2] === 'Enter') return this.$getIndent(line);
if (line.indexOf('- [ ] ') === 0 || line.indexOf('- [x] ') === 0 || line.indexOf('- [X] ') === 0) return '- [ ] ';
if (line.indexOf('- ') === 0) return '- ';
if (line.indexOf('* ') === 0) return '* ';
const leftSpaces = lineLeftSpaces(line);
const lineNoLeftSpaces = line.trimLeft();
const bulletNumber = markdownUtils.olLineNumber(line);
if (bulletNumber) return (bulletNumber + 1) + '. ';
if (lineNoLeftSpaces.indexOf('- [ ] ') === 0 || lineNoLeftSpaces.indexOf('- [x] ') === 0 || lineNoLeftSpaces.indexOf('- [X] ') === 0) return leftSpaces + '- [ ] ';
if (lineNoLeftSpaces.indexOf('- ') === 0) return leftSpaces + '- ';
if (lineNoLeftSpaces.indexOf('* ') === 0) return leftSpaces + '* ';
const bulletNumber = markdownUtils.olLineNumber(lineNoLeftSpaces);
if (bulletNumber) return leftSpaces + (bulletNumber + 1) + '. ';
return this.$getIndent(line);
};
@@ -672,13 +736,14 @@ class NoteTextComponent extends React.Component {
}
async doCommand(command) {
if (!command) return;
if (!command || !this.state.note) return;
let commandProcessed = true;
if (command.name === 'exportPdf' && this.webview_) {
const path = bridge().showSaveDialog({
filters: [{ name: _('PDF File'), extensions: ['pdf']}]
filters: [{ name: _('PDF File'), extensions: ['pdf']}],
defaultPath: safeFilename(this.state.note.title),
});
if (path) {
@@ -696,6 +761,10 @@ class NoteTextComponent extends React.Component {
this.commandTextBold();
} else if (command.name === 'textItalic') {
this.commandTextItalic();
} else if (command.name === 'insertDateTime' ) {
this.commandDateTime();
} else if (command.name === 'commandStartExternalEditing') {
this.commandStartExternalEditing();
} else {
commandProcessed = false;
}
@@ -719,7 +788,7 @@ class NoteTextComponent extends React.Component {
await this.saveIfNeeded(true);
let note = await Note.load(this.state.note.id);
const position = this.cursorPosition();
const position = this.currentTextOffset();
for (let i = 0; i < filePaths.length; i++) {
const filePath = filePaths[i];
@@ -750,6 +819,44 @@ class NoteTextComponent extends React.Component {
});
}
externalEditWatcher() {
if (!this.externalEditWatcher_) {
this.externalEditWatcher_ = new ExternalEditWatcher((action) => { return this.props.dispatch(action) });
this.externalEditWatcher_.setLogger(reg.logger());
this.externalEditWatcher_.on('noteChange', this.externalEditWatcher_noteChange);
}
return this.externalEditWatcher_;
}
externalEditWatcherUpdateNoteFile(note) {
if (this.externalEditWatcher_) this.externalEditWatcher().updateNoteFile(note);
}
externalEditWatcherStopWatchingAll() {
if (this.externalEditWatcher_) this.externalEditWatcher().stopWatchingAll();
}
destroyExternalEditWatcher() {
if (!this.externalEditWatcher_) return;
this.externalEditWatcher_.off('noteChange', this.externalEditWatcher_noteChange);
this.externalEditWatcher_.stopWatchingAll();
this.externalEditWatcher_ = null;
}
async commandStartExternalEditing() {
try {
await this.externalEditWatcher().openAndWatch(this.state.note);
} catch (error) {
bridge().showErrorMessageBox(_('Error opening note in editor: %s', error.message));
}
}
async commandStopExternalEditing() {
this.externalEditWatcherStopWatchingAll();
}
async commandSetTags() {
await this.saveIfNeeded(true);
@@ -781,21 +888,21 @@ class NoteTextComponent extends React.Component {
}
selectionRangePreviousLine() {
if (!this.state.selectionRange) return '';
const row = this.state.selectionRange.start.row;
if (!this.selectionRange_) return '';
const row = this.selectionRange_.start.row;
return this.lineAtRow(row - 1);
}
selectionRangeCurrentLine() {
if (!this.state.selectionRange) return '';
const row = this.state.selectionRange.start.row;
if (!this.selectionRange_) return '';
const row = this.selectionRange_.start.row;
return this.lineAtRow(row);
}
wrapSelectionWithStrings(string1, string2 = '', defaultText = '') {
if (!this.rawEditor() || !this.state.note) return;
const selection = this.state.selectionRange ? this.rangeToTextOffsets(this.state.selectionRange, this.state.note.body) : null;
const selection = this.selectionRange_ ? this.rangeToTextOffsets(this.selectionRange_, this.state.note.body) : null;
let newBody = this.state.note.body;
@@ -805,7 +912,7 @@ class NoteTextComponent extends React.Component {
const s3 = this.state.note.body.substr(selection.end);
newBody = s1 + string1 + s2 + string2 + s3;
const r = this.state.selectionRange;
const r = this.selectionRange_;
const newRange = {
start: { row: r.start.row, column: r.start.column + string1.length},
@@ -813,27 +920,27 @@ class NoteTextComponent extends React.Component {
};
this.updateEditorWithDelay((editor) => {
const range = this.state.selectionRange;
const range = this.selectionRange_;
range.setStart(newRange.start.row, newRange.start.column);
range.setEnd(newRange.end.row, newRange.end.column);
editor.getSession().getSelection().setSelectionRange(range, false);
editor.focus();
});
} else {
const cursorPos = this.cursorPosition();
const s1 = this.state.note.body.substr(0, cursorPos);
const s2 = this.state.note.body.substr(cursorPos);
const textOffset = this.currentTextOffset();
const s1 = this.state.note.body.substr(0, textOffset);
const s2 = this.state.note.body.substr(textOffset);
newBody = s1 + string1 + defaultText + string2 + s2;
const r = this.state.selectionRange;
const newRange = !r ? null : {
start: { row: r.start.row, column: r.start.column + string1.length },
end: { row: r.end.row, column: r.start.column + string1.length + defaultText.length },
const p = this.textOffsetToCursorPosition(textOffset + string1.length, newBody);
const newRange = {
start: { row: p.row, column: p.column },
end: { row: p.row, column: p.column + defaultText.length },
};
this.updateEditorWithDelay((editor) => {
if (defaultText && newRange) {
const range = this.state.selectionRange;
const range = this.selectionRange_;
range.setStart(newRange.start.row, newRange.start.column);
range.setEnd(newRange.end.row, newRange.end.column);
editor.getSession().getSelection().setSelectionRange(range, false);
@@ -848,6 +955,7 @@ class NoteTextComponent extends React.Component {
shared.noteComponent_change(this, 'body', newBody);
this.scheduleHtmlUpdate();
this.scheduleSave();
}
commandTextBold() {
@@ -858,30 +966,34 @@ class NoteTextComponent extends React.Component {
this.wrapSelectionWithStrings('*', '*', _('emphasized text'));
}
commandDateTime() {
this.wrapSelectionWithStrings(time.formatMsToLocal(new Date().getTime()));
}
commandTextCode() {
this.wrapSelectionWithStrings('`', '`');
}
addListItem(item) {
addListItem(string1, string2 = '', defaultText = '') {
const currentLine = this.selectionRangeCurrentLine();
let newLine = '\n'
if (!currentLine) newLine = '';
this.wrapSelectionWithStrings(newLine + item);
this.wrapSelectionWithStrings(newLine + string1, string2, defaultText);
}
commandTextCheckbox() {
this.addListItem('- [ ] ');
this.addListItem('- [ ] ', '', _('List item'));
}
commandTextListUl() {
this.addListItem('- ');
this.addListItem('- ', '', _('List item'));
}
commandTextListOl() {
let bulletNumber = markdownUtils.olLineNumber(this.selectionRangeCurrentLine());
if (!bulletNumber) bulletNumber = markdownUtils.olLineNumber(this.selectionRangePreviousLine());
if (!bulletNumber) bulletNumber = 0;
this.addListItem((bulletNumber + 1) + '. ');
this.addListItem((bulletNumber + 1) + '. ', '', _('List item'));
}
commandTextHeading() {
@@ -950,7 +1062,7 @@ class NoteTextComponent extends React.Component {
tooltip: _('Hyperlink'),
iconName: 'fa-link',
onClick: () => { return this.commandTextLink(); },
});
});
toolbarItems.push({
tooltip: _('Code'),
@@ -998,10 +1110,31 @@ class NoteTextComponent extends React.Component {
onClick: () => { return this.commandTextHorizontalRule(); },
});
toolbarItems.push({
tooltip: _('Insert Date Time'),
iconName: 'fa-calendar-plus-o',
onClick: () => { return this.commandDateTime(); },
});
toolbarItems.push({
type: 'separator',
});
if (note && this.props.watchedNoteFiles.indexOf(note.id) >= 0) {
toolbarItems.push({
tooltip: _('Click to stop external editing'),
title: _('Watching...'),
iconName: 'fa-external-link',
onClick: () => { return this.commandStopExternalEditing(); },
});
} else {
toolbarItems.push({
tooltip: _('Edit in external editor'),
iconName: 'fa-external-link',
onClick: () => { return this.commandStartExternalEditing(); },
});
}
toolbarItems.push({
tooltip: _('Tags'),
iconName: 'fa-tags',
@@ -1163,7 +1296,7 @@ class NoteTextComponent extends React.Component {
const viewer = <webview
style={viewerStyle}
nodeintegration="1"
preload="gui/note-viewer/preload.js"
src="gui/note-viewer/index.html"
ref={(elem) => { this.webview_ref(elem); } }
/>
@@ -1214,7 +1347,7 @@ class NoteTextComponent extends React.Component {
editorProps={{$blockScrolling: true}}
// This is buggy (gets outside the container)
highlightActiveLine={false}
highlightActiveLine={false}
/>
return (
@@ -1247,6 +1380,7 @@ const mapStateToProps = (state) => {
notesParentType: state.notesParentType,
searches: state.searches,
selectedSearchId: state.selectedSearchId,
watchedNoteFiles: state.watchedNoteFiles,
};
};

View File

@@ -34,7 +34,6 @@
<div id="content" ondragstart="return false;" ondrop="return false;"></div>
<script>
const { ipcRenderer } = require('electron');
const contentElement = document.getElementById('content');
// ----------------------------------------------------------------------
@@ -230,9 +229,23 @@
if (element && element.getAttribute('data-resource-id')) {
ipcRenderer.sendToHost('contextMenu', {
type: element.getAttribute('src') ? 'image' : 'link',
type: element.getAttribute('src') ? 'image' : 'resource',
resourceId: element.getAttribute('data-resource-id'),
});
} else {
const selectedText = window.getSelection().toString();
if (selectedText) {
ipcRenderer.sendToHost('contextMenu', {
type: 'text',
textToCopy: selectedText,
});
} else if (event.target.getAttribute('href')) {
ipcRenderer.sendToHost('contextMenu', {
type: 'link',
textToCopy: event.target.getAttribute('href'),
});
}
}
});

View File

@@ -0,0 +1,4 @@
// Define here Electron objects that need to be accessed from the WebView
// https://github.com/electron/electron/blob/master/docs/tutorial/security.md#2-disable-nodejs-integration-for-remote-content
window.ipcRenderer = require('electron').ipcRenderer;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "1.0.100",
"version": "1.0.104",
"description": "Joplin for Desktop",
"main": "main.js",
"scripts": {
@@ -79,6 +79,7 @@
"app-module-path": "^2.2.0",
"async-mutex": "^0.1.3",
"base-64": "^0.1.0",
"chokidar": "^2.0.3",
"compare-versions": "^3.2.1",
"electron-context-menu": "^0.9.1",
"electron-is-dev": "^0.3.0",
@@ -121,6 +122,7 @@
"sqlite3": "^3.1.13",
"string-padding": "^1.0.2",
"string-to-stream": "^1.1.1",
"syswide-cas": "^5.1.0",
"tar": "^4.4.4",
"tcp-port-used": "^0.1.2",
"url-parse": "^1.4.1",

View File

@@ -6,9 +6,9 @@ Joplin is a free, open source note taking and to-do application, which can handl
Notes exported from Evernote via .enex files [can be imported](#importing) into Joplin, including the formatted content (which is converted to Markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.). Plain Markdown files can also be imported.
The notes can be [synchronised](#synchronisation) with various cloud services including [Nextcloud](https://nextcloud.com/), Dropbox, OneDrive or the file system (for example with a network directory). When synchronising the notes, notebooks, tags and other metadata are saved to plain text files which can be easily inspected, backed up and moved around.
The notes can be [synchronised](#synchronisation) with various cloud services including [Nextcloud](https://nextcloud.com/), Dropbox, OneDrive, WebDAV or the file system (for example with a network directory). When synchronising the notes, notebooks, tags and other metadata are saved to plain text files which can be easily inspected, backed up and moved around.
The UI of the terminal client is built on top of the great [terminal-kit](https://github.com/cronvel/terminal-kit) library, the desktop client using [Electron](https://electronjs.org/), and the Android client front end is done using [React Native](https://facebook.github.io/react-native/).
The application is available for Windows, Linux, macOS, Android and iOS. A [Web Clipper](https://github.com/laurent22/joplin/blob/master/readme/clipper.md), to save web pages and screenshots from your browser, is also available for Firefox and Chrome.
<div class="top-screenshot"><img src="https://joplin.cozic.net/images/AllClients.jpg" style="max-width: 100%; max-height: 35em;"></div>
@@ -20,9 +20,9 @@ Three types of applications are available: for the **desktop** (Windows, macOS a
Operating System | Download | Alternative
-----------------|--------|-------------------
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.100/Joplin-Setup-1.0.100.exe'><img alt='Get it on Windows' height="40px" src='https://joplin.cozic.net/images/BadgeWindows.png'/></a> | or Get the <a href='https://github.com/laurent22/joplin/releases/download/v1.0.100/JoplinPortable.exe'>Portable version</a><br>(to run from a USB key, etc.)
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.100/Joplin-1.0.100.dmg'><img alt='Get it on macOS' height="40px" src='https://joplin.cozic.net/images/BadgeMacOS.png'/></a> |
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.100/Joplin-1.0.100-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://joplin.cozic.net/images/BadgeLinux.png'/></a> | An Arch Linux package<br>[is also available](#terminal-application).
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.103/Joplin-Setup-1.0.103.exe'><img alt='Get it on Windows' height="40px" src='https://joplin.cozic.net/images/BadgeWindows.png'/></a> | or Get the <a href='https://github.com/laurent22/joplin/releases/download/v1.0.103/JoplinPortable.exe'>Portable version</a><br>(to run from a USB key, etc.)
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.103/Joplin-1.0.103.dmg'><img alt='Get it on macOS' height="40px" src='https://joplin.cozic.net/images/BadgeMacOS.png'/></a> |
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.103/Joplin-1.0.103-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://joplin.cozic.net/images/BadgeLinux.png'/></a> | An Arch Linux package<br>[is also available](#terminal-application).
The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
@@ -30,7 +30,7 @@ The [portable application](https://en.wikipedia.org/wiki/Portable_application) a
Operating System | Download | Alt. Download
-----------------|----------|----------------
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.127/joplin-v1.0.127.apk)
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.129/joplin-v1.0.129.apk)
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://joplin.cozic.net/images/BadgeIOS.png'/></a> | -
## Terminal application
@@ -45,6 +45,10 @@ To start it, type `joplin`.
For usage information, please refer to the full [Joplin Terminal Application Documentation](https://joplin.cozic.net/terminal).
## Web Clipper
The Web Clipper is a browser extension that allows you to save web pages and screenshots from your browser. For more information on how to install and use it, see the [Web Clipper Help Page](https://github.com/laurent22/joplin/blob/master/readme/clipper.md).
<!-- TOC -->
# Table of contents
@@ -53,6 +57,7 @@ For usage information, please refer to the full [Joplin Terminal Application Doc
- [Desktop application](https://github.com/laurent22/joplin/blob/master/readme/desktop.md)
- [Mobile applications](https://github.com/laurent22/joplin/blob/master/readme/mobile.md)
- [Terminal application](https://github.com/laurent22/joplin/blob/master/readme/terminal.md)
- [Web Clipper](https://github.com/laurent22/joplin/blob/master/readme/clipper.md)
- Support
@@ -72,6 +77,7 @@ For usage information, please refer to the full [Joplin Terminal Application Doc
# Features
- Desktop, mobile and terminal applications.
- [Web Clipper](https://github.com/laurent22/joplin/blob/master/readme/clipper.md) for Firefox and Chrome.
- End To End Encryption (E2EE)
- Synchronisation with various services, including NextCloud, Dropbox, WebDAV and OneDrive.
- Import Enex files (Evernote export format) and Markdown files.
@@ -158,11 +164,12 @@ On the **terminal application**, to initiate the synchronisation process, type `
Select the "WebDAV" synchronisation target and follow the same instructions as for Nextcloud above.
WebDAV-compatible services that are known to work with Joplin.
WebDAV-compatible services that are known to work with Joplin:
- [Box.com](https://www.box.com/)
- [DriveHQ](https://www.drivehq.com)
- [HiDrive](https://www.strato.fr/stockage-en-ligne/) from Strato. [Setup help](https://github.com/laurent22/joplin/issues/309)
- [Nginx WebDAV Module](https://nginx.org/en/docs/http/ngx_http_dav_module.html)
- [OwnCloud](https://owncloud.org/)
- [Seafile](https://www.seafile.com/)
- [Stack](https://www.transip.nl/stack/)
@@ -273,7 +280,7 @@ Please see the [donation page](https://joplin.cozic.net/donate/) for information
- For general discussion about Joplin, user support, software development questions, and to discuss new features, go to the [Joplin Forum](https://discourse.joplin.cozic.net/). It is possible to login with your GitHub account.
- Also see here for information about [the latest releases and general news](https://discourse.joplin.cozic.net/c/news).
- For bug reports and feature requests, go to the [GitHub Issue Tracker](https://github.com/laurent22/joplin/issues).
- The latest news are often posted [on this Twitter account](https://twitter.com/laurent2233).
- The latest news are posted [on the Patreon page](https://www.patreon.com/joplin).
# Contributing
@@ -297,24 +304,24 @@ Current translations:
<!-- LOCALE-TABLE-AUTO-GENERATED -->
&nbsp; | Language | Po File | Last translator | Percent done
---|---|---|---|---
![](https://joplin.cozic.net/images/flags/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 68%
![](https://joplin.cozic.net/images/flags/country-4x3/.png) | Catalan | [ca](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ca.po) | jmontane, 2018 | 96%
![](https://joplin.cozic.net/images/flags/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić (trbuhom@net.hr) | 55%
![](https://joplin.cozic.net/images/flags/country-4x3/cz.png) | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 85%
![](https://joplin.cozic.net/images/flags/country-4x3/dk.png) | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | Morten Juhl-Johansen Zölde-Fejér (mjjzf@syntaktisk. | 87%
![](https://joplin.cozic.net/images/flags/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Philipp Zumstein (zuphilip@gmail.com) | 95%
![](https://joplin.cozic.net/images/flags/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 65%
![](https://joplin.cozic.net/images/flags/es/catalonia.png) | Catalan | [ca](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ca.po) | jmontane, 2018 | 93%
![](https://joplin.cozic.net/images/flags/country-4x3/hr.png) | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić (trbuhom@net.hr) | 53%
![](https://joplin.cozic.net/images/flags/country-4x3/cz.png) | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 82%
![](https://joplin.cozic.net/images/flags/country-4x3/dk.png) | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | Morten Juhl-Johansen Zölde-Fejér (mjjzf@syntaktisk. | 84%
![](https://joplin.cozic.net/images/flags/country-4x3/de.png) | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Philipp Zumstein (zuphilip@gmail.com) | 92%
![](https://joplin.cozic.net/images/flags/country-4x3/gb.png) | English | [en_GB](https://github.com/laurent22/joplin/blob/master/CliClient/locales/en_GB.po) | | 100%
![](https://joplin.cozic.net/images/flags/country-4x3/es.png) | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín (f@mrtn.es) | 95%
![](https://joplin.cozic.net/images/flags/country-4x3/fr.png) | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 96%
![](https://joplin.cozic.net/images/flags/country-4x3/es.png) | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans (marcoslansgarza@gmail.com) | 86%
![](https://joplin.cozic.net/images/flags/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 57%
![](https://joplin.cozic.net/images/flags/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 68%
![](https://joplin.cozic.net/images/flags/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 88%
![](https://joplin.cozic.net/images/flags/country-4x3/si.png) | Slovenian | [sl_SI](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sl_SI.po) | | 85%
![](https://joplin.cozic.net/images/flags/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov (artyom.karlov@gmail.com) | 85%
![](https://joplin.cozic.net/images/flags/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 82%
![](https://joplin.cozic.net/images/flags/country-4x3/tw.png) | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_TW.po) | penguinsam (samliu@gmail.com) | 73%
![](https://joplin.cozic.net/images/flags/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 55%
![](https://joplin.cozic.net/images/flags/country-4x3/es.png) | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín (f@mrtn.es) | 92%
![](https://joplin.cozic.net/images/flags/country-4x3/fr.png) | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 100%
![](https://joplin.cozic.net/images/flags/es/galicia.png) | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans (marcoslansgarza@gmail.com) | 83%
![](https://joplin.cozic.net/images/flags/country-4x3/it.png) | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 55%
![](https://joplin.cozic.net/images/flags/country-4x3/be.png) | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 66%
![](https://joplin.cozic.net/images/flags/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 85%
![](https://joplin.cozic.net/images/flags/country-4x3/si.png) | Slovenian | [sl_SI](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sl_SI.po) | | 82%
![](https://joplin.cozic.net/images/flags/country-4x3/ru.png) | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov (artyom.karlov@gmail.com) | 82%
![](https://joplin.cozic.net/images/flags/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 96%
![](https://joplin.cozic.net/images/flags/country-4x3/tw.png) | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_TW.po) | penguinsam (samliu@gmail.com) | 71%
![](https://joplin.cozic.net/images/flags/country-4x3/jp.png) | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | | 53%
<!-- LOCALE-TABLE-AUTO-GENERATED -->
# Known bugs

View File

@@ -90,8 +90,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion 16
targetSdkVersion 26
versionCode 2097305
versionName "1.0.127"
versionCode 2097307
versionName "1.0.129"
ndk {
abiFilters "armeabi-v7a", "x86"
}

View File

@@ -24,6 +24,7 @@ const os = require('os');
const fs = require('fs-extra');
const JoplinError = require('lib/JoplinError');
const EventEmitter = require('events');
const syswidecas = require('syswide-cas');
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js');
const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js');
@@ -309,6 +310,20 @@ class BaseApplication {
time.setTimeFormat(Setting.value('timeFormat'));
}
if ((action.type == 'SETTING_UPDATE_ONE' && action.key == 'net.ignoreTlsErrors') || (action.type == 'SETTING_UPDATE_ALL')) {
// https://stackoverflow.com/questions/20082893/unable-to-verify-leaf-signature
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = Setting.value('net.ignoreTlsErrors') ? '0' : '1';
}
if ((action.type == 'SETTING_UPDATE_ONE' && action.key == 'net.customCertificates') || (action.type == 'SETTING_UPDATE_ALL')) {
const caPaths = Setting.value('net.customCertificates').split(',');
for (let i = 0; i < caPaths.length; i++) {
const f = caPaths[i].trim();
if (!f) continue;
syswidecas.addCAs(f);
}
}
if ((action.type == 'SETTING_UPDATE_ONE' && (action.key.indexOf('encryption.') === 0)) || (action.type == 'SETTING_UPDATE_ALL')) {
if (this.hasGui()) {
await EncryptionService.instance().loadMasterKeysFromSettings();
@@ -429,6 +444,8 @@ class BaseApplication {
Setting.setConstant('resourceDir', resourceDir);
Setting.setConstant('tempDir', tempDir);
await shim.fsDriver().remove(tempDir);
await fs.mkdirp(profileDir, 0o755);
await fs.mkdirp(resourceDir, 0o755);
await fs.mkdirp(tempDir, 0o755);

View File

@@ -94,6 +94,7 @@ class ClipperServer {
}
if (requestNote.source_url) output.source_url = requestNote.source_url;
if (requestNote.author) output.author = requestNote.author;
return output;
}

View File

@@ -216,7 +216,7 @@ class MdToHtml {
if (isInlineCode) {
openTag = null;
} else if (tag && t.type.indexOf('html_inline') >= 0) {
} else if (tag && (t.type.indexOf('html_inline') >= 0 || t.type.indexOf('html_block') >= 0)) {
openTag = null;
} else if (tag && t.type.indexOf('_open') >= 0) {
openTag = tag;
@@ -277,7 +277,7 @@ class MdToHtml {
if (t.type === 'image') {
if (tokenContent) attrs.push(['title', tokenContent]);
output.push(this.renderImage_(attrs, options));
} else if (t.type === 'html_inline') {
} else if (t.type === 'html_inline' || t.type === 'html_block') {
output.push(t.content);
} else if (t.type === 'softbreak') {
output.push('<br/>');
@@ -392,7 +392,7 @@ class MdToHtml {
const md = new MarkdownIt({
breaks: true,
linkify: true,
html: false, // For security, HTML tags are not supported - https://github.com/laurent22/joplin/issues/500
html: true,
});
// This is currently used only so that the $expression$ and $$\nexpression\n$$ blocks are translated
@@ -558,6 +558,10 @@ class MdToHtml {
left: -0.02em;
color: ` + style.htmlColor + `;
}
pre {
white-space: pre-wrap;
}
}
`;

View File

@@ -53,7 +53,7 @@ class SyncTargetWebDAV extends BaseSyncTarget {
try {
const result = await fileApi.stat('');
if (!result) throw new Error('WebDAV directory not found: ' + options.path);
if (!result) throw new Error('WebDAV directory not found: ' + options.path());
output.ok = true;
} catch (error) {
output.errorMessage = error.message;

View File

@@ -133,6 +133,8 @@ class FileApiDriverDropbox {
} catch (error) {
if (this.hasErrorCode_(error, 'not_found')) {
return null;
} else if (this.hasErrorCode_(error, 'restricted_content')) {
throw new JoplinError('Cannot download because content is restricted by Dropbox', 'rejectedByTarget');
} else {
throw error;
}

View File

@@ -43,8 +43,10 @@ class FileApiDriverWebDav {
const propStat = this.api().arrayFromJson(resource, ['d:propstat']);
if (!Array.isArray(propStat)) throw new Error('Invalid WebDAV resource format: ' + JSON.stringify(resource));
const httpStatusLine = this.api().stringFromJson(resource, ['d:propstat',0,'d:status', 0]);
if ( typeof httpStatusLine === 'string' && httpStatusLine.indexOf('404') >= 0 ) throw new JoplinError(resource, 404);
// Disabled for now to try to fix this: https://github.com/laurent22/joplin/issues/624
//
// const httpStatusLine = this.api().stringFromJson(resource, ['d:propstat',0,'d:status', 0]);
// if ( typeof httpStatusLine === 'string' && httpStatusLine.indexOf('404') >= 0 ) throw new JoplinError(resource, 404);
const resourceTypes = this.api().resourcePropByName(resource, 'array', 'd:resourcetype');
let isDir = false;

View File

@@ -48,7 +48,8 @@ class FsDriverNode extends FsDriverBase {
// same as rm -rf
async remove(path) {
try {
return await fs.remove(path);
const r = await fs.remove(path);
return r;
} catch (error) {
throw this.fsErrorToJsError_(error, path);
}

View File

@@ -29,6 +29,19 @@ class BaseItem extends BaseModel {
throw new Error('Invalid class name: ' + className);
}
static async findUniqueItemTitle(title) {
let counter = 1;
let titleToTry = title;
while (true) {
const item = await this.loadByField('title', titleToTry);
if (!item) return titleToTry;
titleToTry = title + ' (' + counter + ')';
counter++;
if (counter >= 100) titleToTry = title + ' (' + ((new Date()).getTime()) + ')';
if (counter >= 1000) throw new Error('Cannot find unique title');
}
}
// Need to dynamically load the classes like this to avoid circular dependencies
static getClass(name) {
for (let i = 0; i < BaseItem.syncItemDefinitions_.length; i++) {

View File

@@ -33,19 +33,6 @@ class Folder extends BaseItem {
}
}
static async findUniqueFolderTitle(title) {
let counter = 1;
let titleToTry = title;
while (true) {
const folder = await this.loadByField('title', titleToTry);
if (!folder) return titleToTry;
titleToTry = title + ' (' + counter + ')';
counter++;
if (counter >= 100) titleToTry = title + ' (' + ((new Date()).getTime()) + ')';
if (counter >= 1000) throw new Error('Cannot find unique title');
}
}
static noteIds(parentId) {
return this.db().selectAll('SELECT id FROM notes WHERE is_conflict = 0 AND parent_id = ?', [parentId]).then((rows) => {
let output = [];

View File

@@ -420,6 +420,7 @@ class Note extends BaseItem {
static async duplicate(noteId, options = null) {
const changes = options && options.changes;
const uniqueTitle = options && options.uniqueTitle;
const originalNote = await Note.load(noteId);
if (!originalNote) throw new Error('Unknown note: ' + noteId);
@@ -432,6 +433,11 @@ class Note extends BaseItem {
newNote[n] = changes[n];
}
if (uniqueTitle) {
const title = await Note.findUniqueItemTitle(uniqueTitle);
newNote.title = title;
}
return this.save(newNote);
}

View File

@@ -1,5 +1,10 @@
const BaseModel = require('lib/BaseModel.js');
// - If is_associated = 1, note_resources indicates which note_id is currently associated with the given resource_id
// - If is_associated = 0, note_resources indicates which note_id *was* associated with the given resource_id
// - last_seen_time tells the last time that reosurce was associated with this note.
// - If last_seen_time is 0, it means the resource has never been associated with any note.
class NoteResource extends BaseModel {
static tableName() {
@@ -39,7 +44,7 @@ class NoteResource extends BaseModel {
const queries = [];
for (let i = 0; i < missingResources.length; i++) {
const id = missingResources[i].id;
queries.push({ sql: 'INSERT INTO note_resources (note_id, resource_id, is_associated, last_seen_time) VALUES (?, ?, ?, ?)', params: ["", id, 0, Date.now()] });
queries.push({ sql: 'INSERT INTO note_resources (note_id, resource_id, is_associated, last_seen_time) VALUES (?, ?, ?, ?)', params: ["", id, 0, 0] });
}
await this.db().transactionExecBatch(queries);
}
@@ -57,6 +62,7 @@ class NoteResource extends BaseModel {
GROUP BY resource_id
HAVING sum(is_associated) <= 0
AND last_seen_time < ?
AND last_seen_time != 0
`, [cutOffTime]);
return output.map(r => r.resource_id);
}

View File

@@ -73,7 +73,12 @@ class Resource extends BaseItem {
}
try {
await this.encryptionService().decryptFile(encryptedPath, plainTextPath);
// const stat = await this.fsDriver().stat(encryptedPath);
await this.encryptionService().decryptFile(encryptedPath, plainTextPath, {
// onProgress: (progress) => {
// console.info('Decryption: ', progress.doneSize / stat.size);
// },
});
} catch (error) {
if (error.code === 'invalidIdentifier') {
// As the identifier is invalid it most likely means that this is not encrypted data
@@ -108,7 +113,12 @@ class Resource extends BaseItem {
if (resource.encryption_blob_encrypted) return { path: encryptedPath, resource: resource };
try {
await this.encryptionService().encryptFile(plainTextPath, encryptedPath);
// const stat = await this.fsDriver().stat(plainTextPath);
await this.encryptionService().encryptFile(plainTextPath, encryptedPath, {
// onProgress: (progress) => {
// console.info(progress.doneSize / stat.size);
// },
});
} catch (error) {
if (error.code === 'ENOENT') throw new JoplinError('File not found:' + error.toString(), 'fileNotFound');
throw error;

View File

@@ -32,7 +32,6 @@ class Setting extends BaseModel {
this.metadata_ = {
'activeFolderId': { value: '', type: Setting.TYPE_STRING, public: false },
'firstStart': { value: true, type: Setting.TYPE_BOOL, public: false },
'editor': { value: '', type: Setting.TYPE_STRING, public: true, appTypes: ['cli'], label: () => _('Text editor'), description: () => _('The editor that will be used to open a note. If none is provided it will try to auto-detect the default editor.') },
'locale': { value: defaultLocale(), type: Setting.TYPE_STRING, isEnum: true, public: true, label: () => _('Language'), options: () => {
return ObjectUtils.sortByValue(supportedLocalesToLanguages());
}},
@@ -116,6 +115,7 @@ class Setting extends BaseModel {
}},
'noteVisiblePanes': { value: ['editor', 'viewer'], type: Setting.TYPE_ARRAY, public: false, appTypes: ['desktop'] },
'sidebarVisibility': { value: true, type: Setting.TYPE_BOOL, public: false, appTypes: ['desktop'] },
'editor': { value: '', type: Setting.TYPE_STRING, public: true, appTypes: ['cli', 'desktop'], label: () => _('Text editor command'), description: () => _('The editor command (may include arguments) that will be used to open a note. If none is provided it will try to auto-detect the default editor.') },
'showAdvancedOptions': { value: false, type: Setting.TYPE_BOOL, public: true, appTypes: ['mobile' ], label: () => _('Show advanced options') },
'sync.target': { value: SyncTargetRegistry.nameToId('dropbox'), type: Setting.TYPE_INT, isEnum: true, public: true, label: () => _('Synchronisation target'), description: (appType) => { return appType !== 'cli' ? null : _('The target to synchonise to. Each sync target may have additional parameters which are named as `sync.NUM.NAME` (all documented below).') }, options: () => {
return SyncTargetRegistry.idAndLabelPlainObject();
@@ -149,6 +149,9 @@ class Setting extends BaseModel {
'sync.5.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.6.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.7.context': { value: '', type: Setting.TYPE_STRING, public: false },
'net.customCertificates': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return [SyncTargetRegistry.nameToId('nextcloud'), SyncTargetRegistry.nameToId('webdav')].indexOf(settings['sync.target']) >= 0 }, public: true, appTypes: ['desktop', 'cli'], label: () => _('Custom TLS certificates'), description: () => _('Comma-separated list of paths to directories to load the certificates from, or path to individual cert files. For example: /my/cert_dir, /other/custom.pem. Note that if you make changes to the TLS settings, you must save your changes before clicking on "Check synchronisation configuration".') },
'net.ignoreTlsErrors': { value: false, type: Setting.TYPE_BOOL, show: (settings) => { return [SyncTargetRegistry.nameToId('nextcloud'), SyncTargetRegistry.nameToId('webdav')].indexOf(settings['sync.target']) >= 0 }, public: true, appTypes: ['desktop', 'cli'], label: () => _('Ignore TLS certificate errors') },
};
return this.metadata_;

View File

@@ -1,164 +1,18 @@
const reservedPorts = [1024, 1027, 1028, 1029, 1058, 1059, 1080, 1085, 1098, 1099, 1109, 1119, 1167, 1194, 1198, 1214, 1220, 1234, 1241, 1270, 1293, 1311, 1314, 1337, 1341, 1344,
1352, 1360, 1414, 1417, 1418, 1419, 1420, 1431, 1433, 1434, 1492, 1494, 1500, 1501, 1503, 1512, 1513, 1521, 1524, 1527, 1533, 1547, 1550, 1581, 1582, 1583, 1589, 1604, 1626, 1627,
1628, 1629, 1645, 1646, 1666, 1677, 1688, 1701, 1707, 1716, 1719, 1720, 1723, 1755, 1761, 1783, 1801, 1812, 1813, 1863, 1880, 1883, 1900, 1935, 1967, 1970, 1972, 1984, 1985, 1998,
2000, 2010, 2033, 2049, 2056, 2080, 2082, 2083, 2086, 2087, 2095, 2096, 2100, 2101, 2102, 2103, 2104, 2123, 2142, 2152, 2159, 2181, 2195, 2196, 2210, 2211, 2221, 2222, 2261, 2262,
2266, 2305, 2351, 2368, 2369, 2370, 2372, 2375, 2376, 2377, 2379, 2380, 2399, 2401, 2404, 2424, 2427, 2447, 2480, 2483, 2484, 2535, 2541, 2546, 2593, 2598, 2599, 2638, 2710, 2727,
2809, 2811, 2827, 2944, 2945, 2947, 2948, 2949, 2967, 3000, 3004, 3020, 3050, 3052, 3074, 3101, 3128, 3225, 3233, 3260, 3268, 3269, 3283, 3290, 3305, 3306, 3313, 3316, 3323, 3332,
3333, 3351, 3386, 3389, 3396, 3412, 3423, 3424, 3455, 3478, 3479, 3480, 3483, 3493, 3516, 3527, 3535, 3544, 3632, 3645, 3659, 3667, 3689, 3690, 3702, 3724, 3725, 3768, 3784, 3785,
3799, 3804, 3825, 3826, 3830, 3835, 3856, 3868, 3872, 3880, 3900, 3960, 3962, 3978, 3979, 3999, 4000, 4001, 4018, 4035, 4045, 4050, 4069, 4089, 4090, 4093, 4096, 4105, 4111, 4116,
4125, 4172, 4190, 4198, 4201, 4222, 4226, 4242, 4243, 4244, 4303, 4321, 4352, 4444, 4486, 4488, 4500, 4502, 4505, 4534, 4560, 4567, 4569, 4604, 4605, 4610, 4662, 4664, 4672, 4711,
4713, 4728, 4730, 4739, 4747, 4750, 4753, 4840, 4843, 4847, 4848, 4894, 4949, 4950, 5000, 5001, 5002, 5003, 5004, 5005, 5010, 5011, 5031, 5037, 5048, 5050, 5051, 5060, 5061, 5062,
5064, 5065, 5070, 5084, 5085, 5093, 5099, 5104, 5121, 5124, 5125, 5150, 5151, 5154, 5190, 5201, 5222, 5223, 5228, 5242, 5243, 5246, 5247, 5269, 5280, 5281, 5298, 5310, 5349, 5351,
5353, 5355, 5357, 5358, 5394, 5402, 5405, 5412, 5413, 5417, 5421, 5432, 5433, 5445, 5480, 5481, 5495, 5498, 5499, 5500, 5501, 5517, 5550, 5554, 5555, 5556, 5568, 5601, 5631, 5632,
5656, 5666, 5667, 5670, 5672, 5678, 5683, 5701, 5718, 5719, 5722, 5723, 5724, 5741, 5742, 5800, 5900, 5931, 5938, 5984, 5985, 5986, 5988, 6000, 6001, 6002, 6003, 6004, 6005, 6006,
6007, 6008, 6009, 6010, 6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022, 6023, 6024, 6025, 6026, 6027, 6028, 6029, 6030, 6031, 6032, 6033, 6034, 6035, 6036,
6037, 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, 6048, 6049, 6050, 6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, 6062, 6063, 6086, 6100, 6101,
6110, 6111, 6112, 6113, 6136, 6159, 6200, 6201, 6225, 6227, 6240, 6244, 6255, 6257, 6260, 6262, 6343, 6346, 6347, 6350, 6379, 6389, 6432, 6436, 6437, 6444, 6445, 6463, 6502, 6513,
6514, 6515, 6543, 6556, 6560, 6566, 6571, 6600, 6601, 6602, 6619, 6622, 6653, 6660, 6665, 6679, 6690, 6697, 6699, 6715, 6771, 6783, 6789, 6869, 6881, 6888, 6889, 6891, 6901, 6902,
6969, 6970, 7000, 7001, 7002, 7005, 7006, 7010, 7022, 7023, 7025, 7047, 7070, 7133, 7144, 7145, 7171, 7262, 7272, 7306, 7307, 7312, 7396, 7400, 7401, 7402, 7471, 7473, 7474, 7478,
7542, 7547, 7575, 7624, 7631, 7634, 7652, 7655, 7656, 7670, 7687, 7707, 7717, 7777, 7831, 7880, 7890, 7915, 7935, 7946, 7990, 8000, 8005, 8006, 8007, 8008, 8009, 8042, 8069, 8070,
8074, 8075, 8080, 8088, 8089, 8090, 8091, 8092, 8111, 8112, 8116, 8118, 8123, 8139, 8140, 8172, 8184, 8194, 8200, 8222, 8243, 8245, 8280, 8281, 8291, 8303, 8332, 8333, 8337, 8384,
8388, 8443, 8444, 8484, 8500, 8530, 8531, 8580, 8629, 8642, 8691, 8767, 8834, 8840, 8880, 8883, 8887, 8888, 8889, 8983, 8997, 8998, 8999, 9000, 9001, 9002, 9006, 9030, 9042, 9043,
9050, 9060, 9080, 9090, 9091, 9092, 9100, 9101, 9102, 9103, 9119, 9150, 9191, 9199, 9200, 9217, 9293, 9300, 9303, 9306, 9309, 9312, 9332, 9333, 9339, 9389, 9418, 9419, 9420, 9421,
9422, 9425, 9443, 9535, 9536, 9600, 9675, 9676, 9695, 9785, 9800, 9875, 9898, 9899, 9981, 9982, 9987, 9993, 9997, 9999, 10000, 10001, 10009, 10010, 10024, 10025, 10042, 10050, 10051,
10080, 10110, 10172, 10200, 10201, 10212, 10308, 10480, 10505, 10514, 10823, 10891, 10933, 11001, 11111, 11112, 11211, 11214, 11215, 11235, 11311, 11371, 11753, 12012, 12013, 12035,
12043, 12046, 12201, 12222, 12223, 12345, 12443, 12489, 12975, 13000, 13008, 13075, 13720, 13721, 13724, 13782, 13783, 13785, 13786, 14550, 14567, 15000, 15345, 15441, 15567, 15672,
16000, 16080, 16200, 16225, 16250, 16261, 16300, 16384, 16385, 16386, 16387, 16393, 16394, 16395, 16396, 16397, 16398, 16399, 16400, 16401, 16402, 16403, 16404, 16405, 16406, 16407,
16408, 16409, 16410, 16411, 16412, 16413, 16414, 16415, 16416, 16417, 16418, 16419, 16420, 16421, 16422, 16423, 16424, 16425, 16426, 16427, 16428, 16429, 16430, 16431, 16432, 16433,
16434, 16435, 16436, 16437, 16438, 16439, 16440, 16441, 16442, 16443, 16444, 16445, 16446, 16447, 16448, 16449, 16450, 16451, 16452, 16453, 16454, 16455, 16456, 16457, 16458, 16459,
16460, 16461, 16462, 16463, 16464, 16465, 16466, 16467, 16468, 16469, 16470, 16471, 16472, 16482, 16567, 17011, 17500, 18091, 18092, 18104, 18200, 18201, 18206, 18300, 18301, 18306,
18333, 18400, 18401, 18505, 18506, 18605, 18606, 19000, 19001, 19132, 19150, 19226, 19294, 19295, 19302, 19812, 19813, 19814, 19999, 20000, 20560, 20595, 20808, 21025, 22000, 22136,
22222, 23073, 23399, 23513, 24441, 24444, 24465, 24554, 24800, 24842, 25565, 25575, 25826, 26000, 26900, 27000, 27016, 27017, 27031, 27036, 27037, 27374, 27500, 27888, 27901, 27950,
27960, 28001, 28015, 28770, 28785, 28852, 28910, 28960, 29000, 29070, 29900, 29920, 30564, 31337, 31416, 31438, 31457, 32137, 32400, 32764, 32887, 32976, 33434, 33848, 34000, 34197,
35357, 37008, 40000, 43110, 43594, 44405, 44818, 47001, 47808, 49151];
// From https://github.com/coverslide/node-alea
const AleaModule = function () {
// importState to sync generator states
Alea.importState = function(i){
var random = new Alea();
random.importState(i);
return random;
};
return Alea;
function Alea() {
return (function(args) {
// Johannes Baagøe <baagoe@baagoe.com>, 2010
var s0 = 0;
var s1 = 0;
var s2 = 0;
var c = 1;
if (args.length === 0) {
args = [+new Date()];
}
var mash = Mash();
s0 = mash(' ');
s1 = mash(' ');
s2 = mash(' ');
for (var i = 0; i < args.length; i++) {
s0 -= mash(args[i]);
if (s0 < 0) {
s0 += 1;
}
s1 -= mash(args[i]);
if (s1 < 0) {
s1 += 1;
}
s2 -= mash(args[i]);
if (s2 < 0) {
s2 += 1;
}
}
mash = null;
var random = function() {
var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
s0 = s1;
s1 = s2;
return s2 = t - (c = t | 0);
};
random.uint32 = function() {
return random() * 0x100000000; // 2^32
};
random.fract53 = function() {
return random() +
(random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53
};
random.version = 'Alea 0.9';
random.args = args;
// my own additions to sync state between two generators
random.exportState = function(){
return [s0, s1, s2, c];
};
random.importState = function(i){
s0 = +i[0] || 0;
s1 = +i[1] || 0;
s2 = +i[2] || 0;
c = +i[3] || 0;
};
return random;
} (Array.prototype.slice.call(arguments)));
}
function Mash() {
var n = 0xefc8249d;
var mash = function(data) {
data = data.toString();
for (var i = 0; i < data.length; i++) {
n += data.charCodeAt(i);
var h = 0.02519603282416938 * n;
n = h >>> 0;
h -= n;
h *= n;
n = h >>> 0;
h -= n;
n += h * 0x100000000; // 2^32
}
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
};
mash.version = 'Mash 0.9';
return mash;
}
}
const Alea = AleaModule()
function randomClipperPort(state, env) {
const seed = env === 'prod' ? 1867 : 2001;
const startPorts = {
prod: 41184,
dev: 27583,
};
const minPort = 1024
const maxPort = 49151
const startPort = env === 'prod' ? startPorts.prod : startPorts.dev;
let prng = null;
if (!state) {
prng = new Alea(seed)
state = { prng: prng }
state = { offset: 0 };
} else {
prng = state.prng;
state.offset++;
}
const randomPort = () => {
return minPort + Math.floor(prng() * ((maxPort + 1) - minPort));
}
let port = null;
for (let i = 0; i < maxPort; i++) {
port = randomPort();
if (reservedPorts.indexOf(port) < 0) break;
}
if (!port) throw new Error('Cannot find a non-reserved port');
state.port = port;
state.port = startPort + state.offset;
return state;
}

View File

@@ -14,9 +14,20 @@ function hexPad(s, length) {
class EncryptionService {
constructor() {
// Note: 1 MB is very slow with Node and probably even worse on mobile. 50 KB seems to work well
// and doesn't produce too much overhead in terms of headers.
this.chunkSize_ = 50000;
// Note: 1 MB is very slow with Node and probably even worse on mobile.
//
// On mobile the time it takes to decrypt increases exponentially for some reason, so it's important
// to have a relatively small size so as not to freeze the app. For example, on Android 7.1 simulator
// with 4.1 GB RAM, it takes this much to decrypt a block;
//
// 50KB => 1000 ms
// 25KB => 250ms
// 10KB => 200ms
// 5KB => 10ms
//
// So making the block 10 times smaller make it 100 times faster! So for now using 5KB. This can be
// changed easily since the chunk size is incorporated into the encrypted data.
this.chunkSize_ = 5000;
this.loadedMasterKeys_ = {};
this.activeMasterKeyId_ = null;
this.defaultEncryptionMethod_ = EncryptionService.METHOD_SJCL;
@@ -299,7 +310,9 @@ class EncryptionService {
throw new Error('Unknown decryption method: ' + method);
}
async encryptAbstract_(source, destination) {
async encryptAbstract_(source, destination, options = null) {
if (!options) options = {};
const method = this.defaultEncryptionMethod();
const masterKeyId = this.activeMasterKeyId();
const masterKeyPlainText = this.loadedMasterKey(masterKeyId);
@@ -311,17 +324,29 @@ class EncryptionService {
await destination.append(this.encodeHeader_(header));
let doneSize = 0;
while (true) {
const block = await source.read(this.chunkSize_);
if (!block) break;
doneSize += this.chunkSize_;
if (options.onProgress) options.onProgress({ doneSize: doneSize });
// Wait for a frame so that the app remains responsive in mobile.
// https://corbt.com/posts/2015/12/22/breaking-up-heavy-processing-in-react-native.html
await shim.waitForFrame();
const encrypted = await this.encrypt(method, masterKeyPlainText, block);
await destination.append(padLeft(encrypted.length.toString(16), 6, '0'));
await destination.append(encrypted);
}
}
async decryptAbstract_(source, destination) {
async decryptAbstract_(source, destination, options = null) {
if (!options) options = {};
const identifier = await source.read(5);
if (!this.isValidHeaderIdentifier(identifier)) throw new JoplinError('Invalid encryption identifier. Data is not actually encrypted? ID was: ' + identifier, 'invalidIdentifier');
const mdSizeHex = await source.read(6);
@@ -331,6 +356,8 @@ class EncryptionService {
const header = this.decodeHeader_(identifier + mdSizeHex + md);
const masterKeyPlainText = this.loadedMasterKey(header.masterKeyId);
let doneSize = 0;
while (true) {
const lengthHex = await source.read(6);
if (!lengthHex) break;
@@ -338,6 +365,11 @@ class EncryptionService {
const length = parseInt(lengthHex, 16);
if (!length) continue; // Weird but could be not completely invalid (block of size 0) so continue decrypting
doneSize += length;
if (options.onProgress) options.onProgress({ doneSize: doneSize });
await shim.waitForFrame();
const block = await source.read(length);
const plainText = await this.decrypt(header.encryptionMethod, masterKeyPlainText, block);
@@ -395,21 +427,21 @@ class EncryptionService {
};
}
async encryptString(plainText) {
async encryptString(plainText, options = null) {
const source = this.stringReader_(plainText);
const destination = this.stringWriter_();
await this.encryptAbstract_(source, destination);
await this.encryptAbstract_(source, destination, options);
return destination.result();
}
async decryptString(cipherText) {
async decryptString(cipherText, options = null) {
const source = this.stringReader_(cipherText);
const destination = this.stringWriter_();
await this.decryptAbstract_(source, destination);
await this.decryptAbstract_(source, destination, options);
return destination.data.join('');
}
async encryptFile(srcPath, destPath) {
async encryptFile(srcPath, destPath, options = null) {
let source = await this.fileReader_(srcPath, 'base64');
let destination = await this.fileWriter_(destPath, 'ascii');
@@ -422,7 +454,7 @@ class EncryptionService {
try {
await this.fsDriver().unlink(destPath);
await this.encryptAbstract_(source, destination);
await this.encryptAbstract_(source, destination, options);
} catch (error) {
cleanUp();
await this.fsDriver().unlink(destPath);
@@ -432,7 +464,7 @@ class EncryptionService {
await cleanUp();
}
async decryptFile(srcPath, destPath) {
async decryptFile(srcPath, destPath, options = null) {
let source = await this.fileReader_(srcPath, 'ascii');
let destination = await this.fileWriter_(destPath, 'base64');
@@ -445,7 +477,7 @@ class EncryptionService {
try {
await this.fsDriver().unlink(destPath);
await this.decryptAbstract_(source, destination);
await this.decryptAbstract_(source, destination, options);
} catch (error) {
cleanUp();
await this.fsDriver().unlink(destPath);

View File

@@ -0,0 +1,237 @@
const { Logger } = require('lib/logger.js');
const Note = require('lib/models/Note');
const Setting = require('lib/models/Setting');
const { shim } = require('lib/shim');
const chokidar = require('chokidar');
const EventEmitter = require('events');
const { splitCommandString } = require('lib/string-utils');
const spawn = require('child_process').spawn;
class ExternalEditWatcher {
constructor(dispatch = null) {
this.logger_ = new Logger();
this.dispatch_ = dispatch ? dispatch : (action) => {};
this.watcher_ = null;
this.eventEmitter_ = new EventEmitter();
this.skipNextChangeEvent_ = {};
}
on(eventName, callback) {
return this.eventEmitter_.on(eventName, callback);
}
off(eventName, callback) {
return this.eventEmitter_.removeListener(eventName, callback);
}
setLogger(l) {
this.logger_ = l;
}
logger() {
return this.logger_;
}
dispatch(action) {
this.dispatch_(action);
}
watch(fileToWatch) {
if (!this.watcher_) {
this.watcher_ = chokidar.watch(fileToWatch);
this.watcher_.on('all', async (event, path) => {
this.logger().debug('ExternalEditWatcher: Event: ' + event + ': ' + path);
if (event === 'unlink') {
this.watcher_.unwatch(path);
} else if (event === 'change') {
const id = Note.pathToId(path);
if (!this.skipNextChangeEvent_[id]) {
const note = await Note.load(id);
const noteContent = await shim.fsDriver().readFile(path, 'utf-8');
const updatedNote = await Note.unserializeForEdit(noteContent);
updatedNote.id = id;
await Note.save(updatedNote);
this.eventEmitter_.emit('noteChange', { id: updatedNote.id });
}
this.skipNextChangeEvent_ = {};
} else if (event === 'error') {
this.logger().error('ExternalEditWatcher:');
this.logger().error(error)
}
});
} else {
this.watcher_.add(fileToWatch);
}
return this.watcher_;
}
static instance() {
if (this.instance_) return this.instance_;
this.instance_ = new ExternalEditWatcher();
return this.instance_;
}
noteFilePath(note) {
return Setting.value('tempDir') + '/' + note.id + '.md';
}
watchedFiles() {
if (!this.watcher_) return [];
const output = [];
const watchedPaths = this.watcher_.getWatched();
for (let dirName in watchedPaths) {
if (!watchedPaths.hasOwnProperty(dirName)) continue;
for (let i = 0; i < watchedPaths[dirName].length; i++) {
const f = watchedPaths[dirName][i];
output.push(Setting.value('tempDir') + '/' + f);
}
}
return output;
}
noteIsWatched(note) {
if (!this.watcher_) return false;
const noteFilename = Note.systemPath(note);
const watchedPaths = this.watcher_.getWatched();
for (let dirName in watchedPaths) {
if (!watchedPaths.hasOwnProperty(dirName)) continue;
for (let i = 0; i < watchedPaths[dirName].length; i++) {
const f = watchedPaths[dirName][i];
if (f === noteFilename) return true;
}
}
return false;
}
textEditorCommand() {
const editorCommand = Setting.value('editor');
if (!editorCommand) return null;
const s = splitCommandString(editorCommand);
const path = s.splice(0, 1);
if (!path.length) throw new Error('Invalid editor command: ' + editorCommand);
return {
path: path[0],
args: s,
};
}
async spawnCommand(path, args, options) {
return new Promise((resolve, reject) => {
const subProcess = spawn(path, args, options);
const iid = setInterval(() => {
if (subProcess && subProcess.pid) {
this.logger().debug('Started editor with PID ' + subProcess.pid);
clearInterval(iid);
resolve();
}
}, 100);
subProcess.on('error', (error) => {
clearInterval(iid);
reject(error);
});
});
}
async openAndWatch(note) {
if (!note || !note.id) {
this.logger().warn('ExternalEditWatcher: Cannot open note: ', note);
return;
}
const filePath = await this.writeNoteToFile_(note);
this.watch(filePath);
const cmd = this.textEditorCommand();
if (!cmd) {
bridge().openExternal('file://' + filePath);
} else {
cmd.args.push(filePath);
await this.spawnCommand(cmd.path, cmd.args, { detached: true });
}
this.dispatch({
type: 'NOTE_FILE_WATCHER_ADD',
id: note.id,
});
this.logger().info('ExternalEditWatcher: Started watching ' + filePath);
}
async stopWatching(note) {
if (!note || !note.id) return;
const filePath = this.noteFilePath(note);
if (this.watcher_) this.watcher_.unwatch(filePath);
await shim.fsDriver().remove(filePath);
this.dispatch({
type: 'NOTE_FILE_WATCHER_REMOVE',
id: note.id,
});
this.logger().info('ExternalEditWatcher: Stopped watching ' + filePath);
}
async stopWatchingAll() {
const filePaths = this.watchedFiles();
for (let i = 0; i < filePaths.length; i++) {
await shim.fsDriver().remove(filePaths[i]);
}
if (this.watcher_) this.watcher_.close();
this.watcher_ = null;
this.logger().info('ExternalEditWatcher: Stopped watching all files');
this.dispatch({
type: 'NOTE_FILE_WATCHER_CLEAR',
});
}
async updateNoteFile(note) {
if (!this.noteIsWatched(note)) return;
if (!note || !note.id) {
this.logger().warn('ExternalEditWatcher: Cannot update note file: ', note);
return;
}
this.logger().debug('ExternalEditWatcher: Update note file: ' + note.id);
// When the note file is updated programmatically, we skip the next change event to
// avoid update loops. We only want to listen to file changes made by the user.
this.skipNextChangeEvent_[note.id] = true;
this.writeNoteToFile_(note);
}
async writeNoteToFile_(note) {
if (!note || !note.id) {
this.logger().warn('ExternalEditWatcher: Cannot update note file: ', note);
return;
}
const filePath = this.noteFilePath(note);
const noteContent = await Note.serializeForEdit(note);
await shim.fsDriver().writeFile(filePath, noteContent, 'utf-8');
return filePath;
}
}
module.exports = ExternalEditWatcher;

View File

@@ -27,12 +27,12 @@ class InteropService {
let importModules = [
{
format: 'jex',
fileExtension: 'jex',
fileExtensions: ['jex'],
sources: ['file'],
description: _('Joplin Export File'),
}, {
format: 'md',
fileExtension: 'md',
fileExtensions: ['md', 'markdown'],
sources: ['file', 'directory'],
isNoteArchive: false, // Tells whether the file can contain multiple notes (eg. Enex or Jex format)
description: _('Markdown'),
@@ -42,7 +42,7 @@ class InteropService {
description: _('Joplin Export Directory'),
}, {
format: 'enex',
fileExtension: 'enex',
fileExtensions: ['enex'],
sources: ['file'],
description: _('Evernote Export File'),
},
@@ -51,7 +51,7 @@ class InteropService {
let exportModules = [
{
format: 'jex',
fileExtension: 'jex',
fileExtensions: ['jex'],
target: 'file',
description: _('Joplin Export File'),
}, {
@@ -108,7 +108,9 @@ class InteropService {
const module = this.moduleByFormat_(type, format);
if (!module) throw new Error(_('Cannot load "%s" module for format "%s"', type, format));
const ModuleClass = require(module.path);
return new ModuleClass();
const output = new ModuleClass();
output.setMetadata(module);
return output;
}
moduleByFileExtension_(type, ext) {
@@ -119,7 +121,7 @@ class InteropService {
for (let i = 0; i < modules.length; i++) {
const m = modules[i];
if (type !== m.type) continue;
if (m.fileExtension === ext) return m;
if (m.fileExtensions.indexOf(ext) >= 0) return m;
}
return null;

View File

@@ -5,6 +5,14 @@ class InteropService_Exporter_Base {
async processResource(resource, filePath) {}
async close() {}
setMetadata(md) {
this.metadata_ = md;
}
metadata() {
return this.metadata_;
}
async temporaryDirectory_(createIt) {
const md5 = require('md5');
const tempDir = require('os').tmpdir() + '/' + md5(Math.random() + Date.now());

View File

@@ -1,5 +1,13 @@
class InteropService_Importer_Base {
setMetadata(md) {
this.metadata_ = md;
}
metadata() {
return this.metadata_;
}
async init(sourcePath, options) {
this.sourcePath_ = sourcePath;
this.options_ = options;

View File

@@ -23,7 +23,7 @@ class InteropService_Importer_Enex extends InteropService_Importer_Base {
let folder = this.options_.destinationFolder;
if (!folder) {
const folderTitle = await Folder.findUniqueFolderTitle(filename(this.sourcePath_));
const folderTitle = await Folder.findUniqueItemTitle(filename(this.sourcePath_));
folder = await Folder.save({ title: folderTitle });
}

View File

@@ -21,18 +21,20 @@ class InteropService_Importer_Md extends InteropService_Importer_Base {
async exec(result) {
let parentFolderId = null;
const supportedFileExtension = this.metadata().fileExtensions;
const filePaths = [];
if (await shim.fsDriver().isDirectory(this.sourcePath_)) {
const stats = await shim.fsDriver().readDirStats(this.sourcePath_);
for (let i = 0; i < stats.length; i++) {
const stat = stats[i];
if (fileExtension(stat.path).toLowerCase() === 'md') {
if (supportedFileExtension.indexOf(fileExtension(stat.path).toLowerCase()) >= 0) {
filePaths.push(this.sourcePath_ + '/' + stat.path);
}
}
if (!this.options_.destinationFolder) {
const folderTitle = await Folder.findUniqueFolderTitle(basename(rtrimSlashes(this.sourcePath_)));
const folderTitle = await Folder.findUniqueItemTitle(basename(rtrimSlashes(this.sourcePath_)));
const folder = await Folder.save({ title: folderTitle });
parentFolderId = folder.id;
} else {

View File

@@ -51,7 +51,7 @@ class InteropService_Importer_Raw extends InteropService_Importer_Base {
let defaultFolder_ = null;
const defaultFolder = async () => {
if (defaultFolder_) return defaultFolder_;
const folderTitle = await Folder.findUniqueFolderTitle(this.options_.defaultFolderTitle ? this.options_.defaultFolderTitle : 'Imported');
const folderTitle = await Folder.findUniqueItemTitle(this.options_.defaultFolderTitle ? this.options_.defaultFolderTitle : 'Imported');
defaultFolder_ = await Folder.save({ title: folderTitle });
return defaultFolder_;
}
@@ -100,7 +100,7 @@ class InteropService_Importer_Raw extends InteropService_Importer_Base {
if (!itemIdMap[item.id]) itemIdMap[item.id] = uuid.create();
item.id = itemIdMap[item.id];
item.title = await Folder.findUniqueFolderTitle(item.title);
item.title = await Folder.findUniqueItemTitle(item.title);
if (item.parent_id) {
await setFolderToImportTo(item.parent_id);

View File

@@ -300,6 +300,8 @@ function shimInit() {
bridge().openExternal(url)
}
shim.waitForFrame = () => {}
}
module.exports = { shimInit };

View File

@@ -123,6 +123,12 @@ function shimInit() {
shim.openUrl = (url) => {
Linking.openURL(url);
}
shim.waitForFrame = () => {
return new Promise(function(resolve, reject) {
requestAnimationFrame(function() { resolve(); });
});
}
}
module.exports = { shimInit };

View File

@@ -139,5 +139,6 @@ shim.attachFileToNote = async (note, filePath) => {}
shim.imageFromDataUrl = async function(imageDataUrl, filePath, options = null) { throw new Error('Not implemented') }
shim.Buffer = null;
shim.openUrl = () => { throw new Error('Not implemented'); }
shim.waitForFrame = () => { throw new Error('Not implemented'); }
module.exports = { shim };

View File

@@ -266,7 +266,17 @@ class Synchronizer {
// could be done using the file timestamp and the potentially unnecessary content loading could be skipped.
// OneDrive does not appear to have accurate timestamps as lastModifiedDateTime would occasionally be
// a few seconds ahead of what it was set with setTimestamp()
remoteContent = await this.api().get(path);
try {
remoteContent = await this.api().get(path);
} catch (error) {
if (error.code === 'rejectedByTarget') {
this.progressReport_.errors.push(error);
this.logger().warn('Rejected by target: ' + path + ': ' + error.message);
continue;
} else {
throw error;
}
}
if (!remoteContent) throw new Error("Got metadata for path but could not fetch content: " + path);
remoteContent = await BaseItem.unserialize(remoteContent);
@@ -485,25 +495,36 @@ class Synchronizer {
let local = await BaseItem.loadItemByPath(path);
let ItemClass = null;
let content = null;
if (!local) {
if (remote.isDeleted !== true) {
action = "createLocal";
reason = "remote exists but local does not";
content = await loadContent();
ItemClass = content ? BaseItem.itemClass(content) : null;
}
} else {
ItemClass = BaseItem.itemClass(local);
local = ItemClass.filter(local);
if (remote.isDeleted) {
action = "deleteLocal";
reason = "remote has been deleted";
} else {
content = await loadContent();
if (content && content.updated_time > local.updated_time) {
action = "updateLocal";
reason = "remote is more recent than local";
try {
if (!local) {
if (remote.isDeleted !== true) {
action = "createLocal";
reason = "remote exists but local does not";
content = await loadContent();
ItemClass = content ? BaseItem.itemClass(content) : null;
}
} else {
ItemClass = BaseItem.itemClass(local);
local = ItemClass.filter(local);
if (remote.isDeleted) {
action = "deleteLocal";
reason = "remote has been deleted";
} else {
content = await loadContent();
if (content && content.updated_time > local.updated_time) {
action = "updateLocal";
reason = "remote is more recent than local";
}
}
}
} catch (error) {
if (error.code === 'rejectedByTarget') {
this.progressReport_.errors.push(error);
this.logger().warn('Rejected by target: ' + path + ': ' + error.message);
action = null;
} else {
throw error;
}
}
@@ -536,7 +557,17 @@ class Synchronizer {
if (content.type_ == BaseModel.TYPE_RESOURCE && action == "createLocal") {
let localResourceContentPath = Resource.fullPath(content);
let remoteResourceContentPath = this.resourceDirName_ + "/" + content.id;
await this.api().get(remoteResourceContentPath, { path: localResourceContentPath, target: "file" });
try {
await this.api().get(remoteResourceContentPath, { path: localResourceContentPath, target: "file" });
} catch (error) {
if (error.code === 'rejectedByTarget') {
this.progressReport_.errors.push(error);
this.logger().warn('Rejected by target: ' + path + ': ' + error.message);
continue;
} else {
throw error;
}
}
}
await ItemClass.save(content, options);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -155,11 +155,10 @@ async function translationStatus(isDefault, poFile) {
}
function flagImageUrl(locale) {
if (locale === 'eu') {
return 'https://joplin.cozic.net/images/flags/es/basque_country.png';
} else {
return 'https://joplin.cozic.net/images/flags/country-4x3/' + countryCodeOnly(locale).toLowerCase() + '.png'
}
if (locale === 'eu') return 'https://joplin.cozic.net/images/flags/es/basque_country.png';
if (locale === 'gl_ES') return 'https://joplin.cozic.net/images/flags/es/galicia.png';
if (locale === 'ca') return 'https://joplin.cozic.net/images/flags/es/catalonia.png';
return 'https://joplin.cozic.net/images/flags/country-4x3/' + countryCodeOnly(locale).toLowerCase() + '.png'
}
function poFileUrl(locale) {

Some files were not shown because too many files have changed in this diff Show More