1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-13 22:12:50 +02:00

Merge branch 'master' into clipper_html_mode

This commit is contained in:
Laurent Cozic
2019-07-21 00:24:25 +01:00
29 changed files with 651 additions and 69 deletions

4
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,4 @@
👉 Please follow one of these issue templates:
- https://github.com/laurent22/joplin/issues/new/choose
Note: to keep the backlog clean and actionable, issues may be immediately closed if they do not follow one of the above issue templates.

43
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,43 @@
---
name: "🐛 Bug Report"
about: Report a reproducible bug or regression in Joplin.
title: ''
labels: 'bug'
---
<!--
Please provide a clear and concise description of what the bug is. (In the section Steps To Reproduce.)
Include screenshots if needed.
Please test using the latest Joplin release to make sure your issue has not already been fixed.
-->
## Environment
Joplin version:
Platform:
OS specifcs:
<!--
Platform can be one of: macOS, Linux, Windows, Android, iOS, terminal (or a combination)
OS specifcs: e.g. OS version, Linux distribution, Android/iOS version, ...
-->
## Steps To Reproduce
1.
2.
<!--
Issues without reproduction steps are likely to stall.
-->
Describe what you expected to happen:
## Logfile
<!--
Please attach a debug log. Issues without a debug log are likely to stall.
For information on how to collect a log file: https://joplinapp.org/debugging/
-->

View File

@@ -0,0 +1,33 @@
---
name: Feature request
about: Suggest a feature for Joplin.
title: '[Feature request] '
labels: 'feature request'
---
<!--
Please search open issues first - many features have already been requested!
-->
## Has it been discussed in the forum? Link to topic.
<!--
Feature requests should be discussed in the forum first. https://discourse.joplinapp.org
Please provide a link to the topic.
-->
## Is your feature request related to a problem? Please describe.
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
## Describe the solution you'd like
<!-- A clear and concise description of what you want to happen. -->
## Describe alternatives you've considered
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
## Additional context
<!-- Add any other context or screenshots about the feature request here. -->

29
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,29 @@
---
name: "🤔 Questions and Help"
about: The issue tracker is not for questions. Please ask questions on https://discourse.joplinapp.org/.
title: 'Question: '
labels: 'question'
---
🚨 The issue tracker is not for questions. 🚨
As it happens, support requests that are created as issues are likely to be closed. We want to make sure you are able to find the help you seek.
## Questions and Help
Please read the [documentation](https://joplinapp.org/) and [FAQ](https://joplinapp.org/faq/) first.
### https://discourse.joplinapp.org/
If you have still questions related to Joplin, please open a topic in the [forum](https://discourse.joplinapp.org/).
You can use your GitHub credentials to login to the forum.
## Links
- Documentation: https://joplinapp.org
- FAQ: https://joplinapp.org/faq/
- Forum: https://discourse.joplinapp.org
- How to enable end-to-end encryption: https://joplinapp.org/e2ee/
- API documentation: https://joplinapp.org/api/
- How to enable debug mode: https://joplinapp.org/debugging/

25
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 90
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "good first issue"
- "essential"
- "essential-reviewed"
- "help wanted"
- "nice to have"
- "upstream"
- "backlog"
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs.
You may also label this issue as "backlog" and I will leave it open.
Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.
only: issues

View File

@@ -1985,7 +1985,7 @@ msgstr ""
#, javascript-format #, javascript-format
msgid "Links with protocol \"%s\" are not supported" msgid "Links with protocol \"%s\" are not supported"
msgstr "" msgstr "Verweise mit dem Protokoll \"%s\" sind nicht unterstützt"
#, javascript-format #, javascript-format
msgid "Unsupported image type: %s" msgid "Unsupported image type: %s"

View File

@@ -26,11 +26,13 @@ const ResourceService = require('lib/services/ResourceService');
const ClipperServer = require('lib/ClipperServer'); const ClipperServer = require('lib/ClipperServer');
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher'); const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
const { bridge } = require('electron').remote.require('./bridge'); const { bridge } = require('electron').remote.require('./bridge');
const { shell } = require('electron');
const Menu = bridge().Menu; const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem; const MenuItem = bridge().MenuItem;
const PluginManager = require('lib/services/PluginManager'); const PluginManager = require('lib/services/PluginManager');
const RevisionService = require('lib/services/RevisionService'); const RevisionService = require('lib/services/RevisionService');
const MigrationService = require('lib/services/MigrationService'); const MigrationService = require('lib/services/MigrationService');
const TemplateUtils = require('lib/TemplateUtils');
const pluginClasses = [ const pluginClasses = [
require('./plugins/GotoAnything.min'), require('./plugins/GotoAnything.min'),
@@ -209,7 +211,7 @@ class Application extends BaseApplication {
// The bridge runs within the main process, with its own instance of locale.js // 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')); bridge().setLocale(Setting.value('locale'));
this.refreshMenu(); await this.refreshMenu();
} }
if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'showTrayIcon' || action.type == 'SETTING_UPDATE_ALL') { if (action.type == 'SETTING_UPDATE_ONE' && action.key == 'showTrayIcon' || action.type == 'SETTING_UPDATE_ALL') {
@@ -246,10 +248,10 @@ class Application extends BaseApplication {
return result; return result;
} }
refreshMenu() { async refreshMenu() {
const screen = this.lastMenuScreen_; const screen = this.lastMenuScreen_;
this.lastMenuScreen_ = null; this.lastMenuScreen_ = null;
this.updateMenu(screen); await this.updateMenu(screen);
} }
focusElement_(target) { focusElement_(target) {
@@ -260,7 +262,7 @@ class Application extends BaseApplication {
}); });
} }
updateMenu(screen) { async updateMenu(screen) {
if (this.lastMenuScreen_ === screen) return; if (this.lastMenuScreen_ === screen) return;
const sortNoteFolderItems = (type) => { const sortNoteFolderItems = (type) => {
@@ -328,6 +330,7 @@ class Application extends BaseApplication {
const exportItems = []; const exportItems = [];
const preferencesItems = []; const preferencesItems = [];
const toolsItemsFirst = []; const toolsItemsFirst = [];
const templateItems = [];
const ioService = new InteropService(); const ioService = new InteropService();
const ioModules = ioService.modules(); const ioModules = ioService.modules();
for (let i = 0; i < ioModules.length; i++) { for (let i = 0; i < ioModules.length; i++) {
@@ -504,6 +507,57 @@ class Application extends BaseApplication {
screens: ['Main'], screens: ['Main'],
}); });
const templateDirExists = await shim.fsDriver().exists(Setting.value('templateDir'));
templateItems.push({
label: _('Create note from template'),
visible: templateDirExists,
click: () => {
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'selectTemplate',
noteType: 'note',
});
}
}, {
label: _('Create to-do from template'),
visible: templateDirExists,
click: () => {
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'selectTemplate',
noteType: 'todo',
});
}
}, {
label: _('Insert template'),
visible: templateDirExists,
accelerator: 'CommandOrControl+Alt+I',
click: () => {
this.dispatch({
type: 'WINDOW_COMMAND',
name: 'selectTemplate',
});
}
}, {
label: _('Open template directory'),
click: () => {
const templateDir = Setting.value('templateDir');
if (!templateDirExists) shim.fsDriver().mkdir(templateDir);
shell.openItem(templateDir);
}
}, {
label: _('Refresh templates'),
click: async () => {
const templates = await TemplateUtils.loadTemplates(Setting.value('templateDir'));
this.store().dispatch({
type: 'TEMPLATE_UPDATE_ALL',
templates: templates
});
}
});
const toolsItems = toolsItemsFirst.concat(preferencesItems); const toolsItems = toolsItemsFirst.concat(preferencesItems);
function _checkForUpdates(ctx) { function _checkForUpdates(ctx) {
@@ -563,6 +617,13 @@ class Application extends BaseApplication {
shim.isMac() ? noItem : newNotebookItem, { shim.isMac() ? noItem : newNotebookItem, {
type: 'separator', type: 'separator',
visible: shim.isMac() ? false : true visible: shim.isMac() ? false : true
}, {
label: _('Templates'),
visible: shim.isMac() ? false : true,
submenu: templateItems,
}, {
type: 'separator',
visible: shim.isMac() ? false : true
}, { }, {
label: _('Import'), label: _('Import'),
visible: shim.isMac() ? false : true, visible: shim.isMac() ? false : true,
@@ -615,6 +676,11 @@ class Application extends BaseApplication {
selector: 'performClose:', selector: 'performClose:',
}, { }, {
type: 'separator', type: 'separator',
}, {
label: _('Templates'),
submenu: templateItems,
}, {
type: 'separator',
}, { }, {
label: _('Import'), label: _('Import'),
submenu: importItems, submenu: importItems,
@@ -1081,6 +1147,13 @@ class Application extends BaseApplication {
css: cssString css: cssString
}); });
const templates = await TemplateUtils.loadTemplates(Setting.value('templateDir'));
this.store().dispatch({
type: 'TEMPLATE_UPDATE_ALL',
templates: templates
});
// Note: Auto-update currently doesn't work in Linux: it downloads the update // Note: Auto-update currently doesn't work in Linux: it downloads the update
// but then doesn't install it on exit. // but then doesn't install it on exit.
if (shim.isWindows() || shim.isMac()) { if (shim.isWindows() || shim.isMac()) {

View File

@@ -74,12 +74,13 @@ class MainScreenComponent extends React.Component {
async doCommand(command) { async doCommand(command) {
if (!command) return; if (!command) return;
const createNewNote = async (title, isTodo) => { const createNewNote = async (template, isTodo) => {
const folderId = Setting.value('activeFolderId'); const folderId = Setting.value('activeFolderId');
if (!folderId) return; if (!folderId) return;
const newNote = { const newNote = {
parent_id: folderId, parent_id: folderId,
template: template,
is_todo: isTodo ? 1 : 0, is_todo: isTodo ? 1 : 0,
}; };
@@ -272,6 +273,30 @@ class MainScreenComponent extends React.Component {
eventManager.emit('alarmChange', { noteId: note.id }); eventManager.emit('alarmChange', { noteId: note.id });
} }
this.setState({ promptOptions: null });
}
},
});
} else if (command.name === 'selectTemplate') {
this.setState({
promptOptions: {
label: _('Template file:'),
inputType: 'dropdown',
value: this.props.templates[0], // Need to start with some value
autocomplete: this.props.templates,
onClose: async (answer) => {
if (answer) {
if (command.noteType === 'note' || command.noteType === 'todo') {
createNewNote(answer.value, command.noteType === 'todo');
} else {
this.props.dispatch({
type: 'WINDOW_COMMAND',
name: 'insertTemplate',
value: answer.value,
});
}
}
this.setState({ promptOptions: null }); this.setState({ promptOptions: null });
} }
}, },
@@ -523,6 +548,7 @@ const mapStateToProps = (state) => {
selectedNoteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null, selectedNoteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null,
plugins: state.plugins, plugins: state.plugins,
noteDevToolsVisible: state.noteDevToolsVisible, noteDevToolsVisible: state.noteDevToolsVisible,
templates: state.templates,
}; };
}; };

View File

@@ -40,6 +40,7 @@ const DecryptionWorker = require('lib/services/DecryptionWorker');
const ModelCache = require('lib/services/ModelCache'); const ModelCache = require('lib/services/ModelCache');
const NoteTextViewer = require('./NoteTextViewer.min'); const NoteTextViewer = require('./NoteTextViewer.min');
const NoteRevisionViewer = require('./NoteRevisionViewer.min'); const NoteRevisionViewer = require('./NoteRevisionViewer.min');
const TemplateUtils = require('lib/TemplateUtils');
require('brace/mode/markdown'); require('brace/mode/markdown');
// https://ace.c9.io/build/kitchen-sink.html // https://ace.c9.io/build/kitchen-sink.html
@@ -470,14 +471,18 @@ class NoteTextComponent extends React.Component {
const stateNoteId = this.state.note ? this.state.note.id : null; const stateNoteId = this.state.note ? this.state.note.id : null;
let noteId = null; let noteId = null;
let note = null; let note = null;
let newNote = null;
let loadingNewNote = true; let loadingNewNote = true;
let parentFolder = null; let parentFolder = null;
let noteTags = []; let noteTags = [];
let scrollPercent = 0; let scrollPercent = 0;
if (props.newNote) { if (props.newNote) {
note = Object.assign({}, props.newNote); // assign new note and prevent body from being null
note = Object.assign({}, props.newNote, {body: ''});
this.lastLoadedNoteId_ = null; this.lastLoadedNoteId_ = null;
if (note.template)
note.body = TemplateUtils.render(note.template);
} else { } else {
noteId = props.noteId; noteId = props.noteId;
@@ -1024,6 +1029,8 @@ class NoteTextComponent extends React.Component {
fn = this.commandShowLocalSearch; fn = this.commandShowLocalSearch;
} else if (command.name === 'textCode') { } else if (command.name === 'textCode') {
fn = this.commandTextCode; fn = this.commandTextCode;
} else if (command.name === 'insertTemplate') {
fn = () => { return this.commandTemplate(command.value); };
} }
} }
@@ -1361,6 +1368,10 @@ class NoteTextComponent extends React.Component {
this.wrapSelectionWithStrings('`', '`'); this.wrapSelectionWithStrings('`', '`');
} }
commandTemplate(value) {
this.wrapSelectionWithStrings(TemplateUtils.render(value));
}
addListItem(string1, string2 = '', defaultText = '') { addListItem(string1, string2 = '', defaultText = '') {
const currentLine = this.selectionRangeCurrentLine(); const currentLine = this.selectionRangeCurrentLine();
let newLine = '\n' let newLine = '\n'
@@ -1936,6 +1947,7 @@ const mapStateToProps = (state) => {
customCss: state.customCss, customCss: state.customCss,
lastEditorScrollPercents: state.lastEditorScrollPercents, lastEditorScrollPercents: state.lastEditorScrollPercents,
historyNotes: state.historyNotes, historyNotes: state.historyNotes,
templates: state.templates,
}; };
}; };

View File

@@ -6,6 +6,7 @@ const { themeStyle } = require('../theme.js');
const { time } = require('lib/time-utils.js'); const { time } = require('lib/time-utils.js');
const Datetime = require('react-datetime'); const Datetime = require('react-datetime');
const CreatableSelect = require('react-select/lib/Creatable').default; const CreatableSelect = require('react-select/lib/Creatable').default;
const Select = require('react-select').default;
const makeAnimated = require('react-select/lib/animated').default; const makeAnimated = require('react-select/lib/animated').default;
class PromptDialog extends React.Component { class PromptDialog extends React.Component {
@@ -101,7 +102,7 @@ class PromptDialog extends React.Component {
borderColor: theme.dividerColor, borderColor: theme.dividerColor,
}; };
this.styles_.tagList = { this.styles_.select = {
control: (provided) => (Object.assign(provided, { control: (provided) => (Object.assign(provided, {
minWidth: width * 0.2, minWidth: width * 0.2,
maxWidth: width * 0.5, maxWidth: width * 0.5,
@@ -115,6 +116,10 @@ class PromptDialog extends React.Component {
fontFamily: theme.fontFamily, fontFamily: theme.fontFamily,
backgroundColor: theme.backgroundColor, backgroundColor: theme.backgroundColor,
})), })),
option: (provided) => (Object.assign(provided, {
color: theme.color,
fontFamily: theme.fontFamily,
})),
multiValueLabel: (provided) => (Object.assign(provided, { multiValueLabel: (provided) => (Object.assign(provided, {
fontFamily: theme.fontFamily, fontFamily: theme.fontFamily,
})), })),
@@ -123,14 +128,22 @@ class PromptDialog extends React.Component {
})), })),
}; };
this.styles_.tagListTheme = (tagTheme) => (Object.assign(tagTheme, { this.styles_.selectTheme = (tagTheme) => (Object.assign(tagTheme, {
borderRadius: 2, borderRadius: 2,
colors: Object.assign(tagTheme.colors, { colors: Object.assign(tagTheme.colors, {
primary: theme.raisedBackgroundColor, primary: theme.raisedBackgroundColor,
primary25: theme.raisedBackgroundColor, primary25: theme.raisedBackgroundColor,
neutral0: theme.backgroundColor, neutral0: theme.backgroundColor,
neutral5: theme.backgroundColor,
neutral10: theme.raisedBackgroundColor, neutral10: theme.raisedBackgroundColor,
neutral20: theme.raisedBackgroundColor,
neutral30: theme.raisedBackgroundColor,
neutral40: theme.color,
neutral50: theme.color,
neutral60: theme.color,
neutral70: theme.color,
neutral80: theme.color, neutral80: theme.color,
neutral90: theme.color,
danger: theme.backgroundColor, danger: theme.backgroundColor,
dangerLight: theme.colorError2, dangerLight: theme.colorError2,
}), }),
@@ -179,14 +192,19 @@ class PromptDialog extends React.Component {
this.setState({ answer: momentObject }); this.setState({ answer: momentObject });
} }
const onTagsChange = (newTags) => { const onSelectChange = (newValue) => {
this.setState({ answer: newTags }); this.setState({ answer: newValue });
this.focusInput_ = true; this.focusInput_ = true;
} }
const onKeyDown = (event) => { const onKeyDown = (event) => {
if (event.key === 'Enter' && this.props.inputType !== 'tags') { if (event.key === 'Enter') {
if (this.props.inputType !== 'tags' && this.props.inputType !== 'dropdown') {
onClose(true); onClose(true);
} else if (this.answerInput_.current && !this.answerInput_.current.state.menuIsOpen) {
// The menu will be open if the user is selecting a new item
onClose(true);
}
} else if (event.key === 'Escape') { } else if (event.key === 'Escape') {
onClose(false); onClose(false);
} }
@@ -206,8 +224,8 @@ class PromptDialog extends React.Component {
/> />
} else if (this.props.inputType === 'tags') { } else if (this.props.inputType === 'tags') {
inputComp = <CreatableSelect inputComp = <CreatableSelect
styles={styles.tagList} styles={styles.select}
theme={styles.tagListTheme} theme={styles.selectTheme}
ref={this.answerInput_} ref={this.answerInput_}
value={this.state.answer} value={this.state.answer}
placeholder="" placeholder=""
@@ -216,7 +234,20 @@ class PromptDialog extends React.Component {
isClearable={false} isClearable={false}
backspaceRemovesValue={true} backspaceRemovesValue={true}
options={this.props.autocomplete} options={this.props.autocomplete}
onChange={onTagsChange} onChange={onSelectChange}
onKeyDown={(event) => onKeyDown(event)}
/>
} else if (this.props.inputType === 'dropdown') {
inputComp = <Select
styles={styles.select}
theme={styles.selectTheme}
ref={this.answerInput_}
components={makeAnimated()}
value={this.props.answer}
defaultValue={this.props.defaultValue}
isClearable={false}
options={this.props.autocomplete}
onChange={onSelectChange}
onKeyDown={(event) => onKeyDown(event)} onKeyDown={(event) => onKeyDown(event)}
/> />
} else { } else {

View File

@@ -4519,6 +4519,11 @@
} }
} }
}, },
"mustache": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-3.0.1.tgz",
"integrity": "sha512-jFI/4UVRsRYdUbuDTKT7KzfOp7FiD5WzYmmwNwXyUVypC0xjoTL78Fqc0jHUPIvvGD+6DQSPHIt1NE7D1ArsqA=="
},
"nan": { "nan": {
"version": "2.10.0", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",

View File

@@ -111,21 +111,22 @@
"mark.js": "^8.11.1", "mark.js": "^8.11.1",
"markdown-it": "^8.4.1", "markdown-it": "^8.4.1",
"markdown-it-abbr": "^1.0.4", "markdown-it-abbr": "^1.0.4",
"markdown-it-anchor": "^5.0.2", "markdown-it-anchor": "^5.2.4",
"markdown-it-deflist": "^2.0.3", "markdown-it-deflist": "^2.0.3",
"markdown-it-emoji": "^1.4.0", "markdown-it-emoji": "^1.4.0",
"markdown-it-footnote": "^3.0.1", "markdown-it-footnote": "^3.0.2",
"markdown-it-ins": "^2.0.0", "markdown-it-ins": "^2.0.0",
"markdown-it-katex": "^2.0.3", "markdown-it-katex": "^2.0.3",
"markdown-it-mark": "^2.0.0", "markdown-it-mark": "^2.0.0",
"markdown-it-multimd-table": "^3.2.0", "markdown-it-multimd-table": "^3.2.0",
"markdown-it-sub": "^1.0.0", "markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0", "markdown-it-sup": "^1.0.0",
"markdown-it-toc-done-right": "^4.0.0", "markdown-it-toc-done-right": "^4.0.2",
"md5": "^2.2.1", "md5": "^2.2.1",
"mime": "^2.3.1", "mime": "^2.3.1",
"moment": "^2.22.2", "moment": "^2.22.2",
"multiparty": "^4.2.1", "multiparty": "^4.2.1",
"mustache": "^3.0.1",
"node-fetch": "^1.7.3", "node-fetch": "^1.7.3",
"node-notifier": "^5.2.1", "node-notifier": "^5.2.1",
"promise": "^8.0.1", "promise": "^8.0.1",

View File

@@ -47,6 +47,18 @@ table td, table th {
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
background: rgba(100, 100, 100, 0.7); background: rgba(100, 100, 100, 0.7);
} }
.fade_out {
-webkit-transition: 0.15s;
transition: 0.15s;
opacity: 0;
}
.fade_in {
-webkit-transition: 0.3s;
transition: 0.3s;
opacity: 1;
}
/* By default, the Ice Editor displays invalid characters, such as non-breaking spaces /* By default, the Ice Editor displays invalid characters, such as non-breaking spaces
as red boxes, but since those are actually valid characters and common in imported as red boxes, but since those are actually valid characters and common in imported
Evernote data, we hide them here. */ Evernote data, we hide them here. */

View File

@@ -71,7 +71,7 @@ if [[ ! -e ~/.joplin/VERSION ]] || [[ $(< ~/.joplin/VERSION) != "$version" ]]; t
# Create icon for Gnome # Create icon for Gnome
echo 'Create Desktop icon.' echo 'Create Desktop icon.'
if [[ $desktop =~ .*gnome.*|.*kde.*|.*xfce.*|.*mate.*|.*lxqt.*|.*unity.* ]] if [[ $desktop =~ .*gnome.*|.*kde.*|.*xfce.*|.*mate.*|.*lxqt.*|.*unity.*|.*X-Cinnamon.* ]]
then then
: "${TMPDIR:=/tmp}" : "${TMPDIR:=/tmp}"
# This command extracts to squashfs-root by default and can't be changed... # This command extracts to squashfs-root by default and can't be changed...

View File

@@ -28,7 +28,7 @@ Linux | <a href='https://github.com/laurent22/joplin/releases/download/
Operating System | Download | Alt. Download 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://joplinapp.org/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.279/joplin-v1.0.279.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://joplinapp.org/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.281/joplin-v1.0.281.apk)
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://joplinapp.org/images/BadgeIOS.png'/></a> | - iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://joplinapp.org/images/BadgeIOS.png'/></a> | -
## Terminal application ## Terminal application
@@ -316,6 +316,25 @@ It is generally recommended to enter the notes as Markdown as it makes the notes
Rendered markdown can be customized by placing a userstyle file in the profile directory `~/.config/joplin-desktop/userstyle.css` (This path might be different on your device - check at the top of the Config screen for the exact path). This file supports standard CSS syntax. Joplin ***must*** be restarted for the new css to be applied, please ensure that Joplin is not closing to the tray, but is actually exiting. Note that this file is used only when display the notes, **not when printing or exporting to PDF**. This is because printing has a lot more restrictions (for example, printing white text over a black background is usually not wanted), so special rules are applied to make it look good when printing, and a userstyle.css would interfer with that. Rendered markdown can be customized by placing a userstyle file in the profile directory `~/.config/joplin-desktop/userstyle.css` (This path might be different on your device - check at the top of the Config screen for the exact path). This file supports standard CSS syntax. Joplin ***must*** be restarted for the new css to be applied, please ensure that Joplin is not closing to the tray, but is actually exiting. Note that this file is used only when display the notes, **not when printing or exporting to PDF**. This is because printing has a lot more restrictions (for example, printing white text over a black background is usually not wanted), so special rules are applied to make it look good when printing, and a userstyle.css would interfer with that.
## New Note Templates
Templates can be used for new notes by creating a templates folder in `~/.config/joplin-desktop/` and placing markdown template files into it. For example creating the file `hours.md` in the directory `~/.config/joplin-desktop/templates/` with the contents:
```markdown
Date: {{date}}
Hours:
Details:
```
When creating a new note you will now be prompted to insert a template that contains the above text (and {{date}} replaced with today's date). Templates can also be inserted from the menu (File->Templates).
The currently supported template variables are:
| Variable | Description | Example |
| {{date}} | Today's date formatted based on the settings format | 2019-01-01 |
| {{time}} | Current time formatted based on the settings format | 13:00 |
| {{datetime}} | Current date and time formatted based on the settings format | 01/01/19 1:00 PM |
| {{#custom_datetime}} | Current date and/or time formatted based on a supplied string (using [moment.js](https://momentjs.com/) formatting) | {{#custom_datetime}}M d{{/custom_datetime}} |
# Searching # Searching
Joplin implements the SQLite Full Text Search (FTS4) extension. It means the content of all the notes is indexed in real time and search queries return results very fast. Both [Simple FTS Queries](https://www.sqlite.org/fts3.html#simple_fts_queries) and [Full-Text Index Queries](https://www.sqlite.org/fts3.html#full_text_index_queries) are supported. See below for the list of supported queries: Joplin implements the SQLite Full Text Search (FTS4) extension. It means the content of all the notes is indexed in real time and search queries return results very fast. Both [Simple FTS Queries](https://www.sqlite.org/fts3.html#simple_fts_queries) and [Full-Text Index Queries](https://www.sqlite.org/fts3.html#full_text_index_queries) are supported. See below for the list of supported queries:
@@ -401,6 +420,33 @@ Current translations:
![](https://joplinapp.org/images/flags/country-4x3/kr.png) | 한국말 | [ko](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ko.po) | | 87% ![](https://joplinapp.org/images/flags/country-4x3/kr.png) | 한국말 | [ko](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ko.po) | | 87%
<!-- LOCALE-TABLE-AUTO-GENERATED --> <!-- LOCALE-TABLE-AUTO-GENERATED -->
# Contributors
<!-- CONTRIBUTORS-TABLE-AUTO-GENERATED -->
| | | | | |
| :---: | :---: | :---: | :---: | :---: |
| <img width="50" src="https://avatars0.githubusercontent.com/u/1285584?v=4"/></br>[laurent22](https://api.github.com/users/laurent22) | <img width="50" src="https://avatars3.githubusercontent.com/u/223439?v=4"/></br>[tessus](https://api.github.com/users/tessus) | <img width="50" src="https://avatars3.githubusercontent.com/u/2179547?v=4"/></br>[CalebJohn](https://api.github.com/users/CalebJohn) | <img width="50" src="https://avatars3.githubusercontent.com/u/4553672?v=4"/></br>[tanrax](https://api.github.com/users/tanrax) | <img width="50" src="https://avatars0.githubusercontent.com/u/8701534?v=4"/></br>[rtmkrlv](https://api.github.com/users/rtmkrlv) |
| <img width="50" src="https://avatars3.githubusercontent.com/u/10997189?v=4"/></br>[fmrtn](https://api.github.com/users/fmrtn) | <img width="50" src="https://avatars3.githubusercontent.com/u/16101778?v=4"/></br>[gabcoh](https://api.github.com/users/gabcoh) | <img width="50" src="https://avatars2.githubusercontent.com/u/1685517?v=4"/></br>[Abijeet](https://api.github.com/users/Abijeet) | <img width="50" src="https://avatars2.githubusercontent.com/u/6557454?v=4"/></br>[innocuo](https://api.github.com/users/innocuo) | <img width="50" src="https://avatars3.githubusercontent.com/u/10927304?v=4"/></br>[matsest](https://api.github.com/users/matsest) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/5365582?v=4"/></br>[marcosvega91](https://api.github.com/users/marcosvega91) | <img width="50" src="https://avatars3.githubusercontent.com/u/37639389?v=4"/></br>[petrz12](https://api.github.com/users/petrz12) | <img width="50" src="https://avatars3.githubusercontent.com/u/208212?v=4"/></br>[foxmask](https://api.github.com/users/foxmask) | <img width="50" src="https://avatars1.githubusercontent.com/u/4237724?v=4"/></br>[alexdevero](https://api.github.com/users/alexdevero) | <img width="50" src="https://avatars0.githubusercontent.com/u/3194829?v=4"/></br>[moltenform](https://api.github.com/users/moltenform) |
| <img width="50" src="https://avatars0.githubusercontent.com/u/5199995?v=4"/></br>[zuphilip](https://api.github.com/users/zuphilip) | <img width="50" src="https://avatars0.githubusercontent.com/u/6319051?v=4"/></br>[abonte](https://api.github.com/users/abonte) | <img width="50" src="https://avatars2.githubusercontent.com/u/31567272?v=4"/></br>[0ndrey](https://api.github.com/users/0ndrey) | <img width="50" src="https://avatars1.githubusercontent.com/u/6734573?v=4"/></br>[stweil](https://api.github.com/users/stweil) | <img width="50" src="https://avatars3.githubusercontent.com/u/32770029?v=4"/></br>[bradmcl](https://api.github.com/users/bradmcl) |
| <img width="50" src="https://avatars0.githubusercontent.com/u/226708?v=4"/></br>[RaphaelKimmig](https://api.github.com/users/RaphaelKimmig) | <img width="50" src="https://avatars0.githubusercontent.com/u/17768566?v=4"/></br>[RenatoXSR](https://api.github.com/users/RenatoXSR) | <img width="50" src="https://avatars1.githubusercontent.com/u/36303913?v=4"/></br>[sensor-freak](https://api.github.com/users/sensor-freak) | <img width="50" src="https://avatars2.githubusercontent.com/u/4245227?v=4"/></br>[zblesk](https://api.github.com/users/zblesk) | <img width="50" src="https://avatars2.githubusercontent.com/u/560571?v=4"/></br>[chrisb86](https://api.github.com/users/chrisb86) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/4168339?v=4"/></br>[solariz](https://api.github.com/users/solariz) | <img width="50" src="https://avatars0.githubusercontent.com/u/390889?v=4"/></br>[mmahmoudian](https://api.github.com/users/mmahmoudian) | <img width="50" src="https://avatars1.githubusercontent.com/u/25288?v=4"/></br>[maicki](https://api.github.com/users/maicki) | <img width="50" src="https://avatars3.githubusercontent.com/u/2136373?v=4"/></br>[mjjzf](https://api.github.com/users/mjjzf) | <img width="50" src="https://avatars0.githubusercontent.com/u/2486806?v=4"/></br>[sebastienjust](https://api.github.com/users/sebastienjust) |
| <img width="50" src="https://avatars1.githubusercontent.com/u/4079047?v=4"/></br>[Zorbeyd](https://api.github.com/users/Zorbeyd) | <img width="50" src="https://avatars0.githubusercontent.com/u/35413451?v=4"/></br>[chenlhlinux](https://api.github.com/users/chenlhlinux) | <img width="50" src="https://avatars0.githubusercontent.com/u/17399340?v=4"/></br>[pf-siedler](https://api.github.com/users/pf-siedler) | <img width="50" src="https://avatars1.githubusercontent.com/u/17232523?v=4"/></br>[ruuti](https://api.github.com/users/ruuti) | <img width="50" src="https://avatars2.githubusercontent.com/u/23638148?v=4"/></br>[s1nceri7y](https://api.github.com/users/s1nceri7y) |
| <img width="50" src="https://avatars1.githubusercontent.com/u/22592201?v=4"/></br>[tfinnberg](https://api.github.com/users/tfinnberg) | <img width="50" src="https://avatars1.githubusercontent.com/u/7471938?v=4"/></br>[ShuiHuo](https://api.github.com/users/ShuiHuo) | <img width="50" src="https://avatars2.githubusercontent.com/u/11596277?v=4"/></br>[ikunya](https://api.github.com/users/ikunya) | <img width="50" src="https://avatars1.githubusercontent.com/u/498326?v=4"/></br>[Shaxine](https://api.github.com/users/Shaxine) | <img width="50" src="https://avatars2.githubusercontent.com/u/7034200?v=4"/></br>[bimlas](https://api.github.com/users/bimlas) |
| <img width="50" src="https://avatars0.githubusercontent.com/u/105843?v=4"/></br>[chaifeng](https://api.github.com/users/chaifeng) | <img width="50" src="https://avatars2.githubusercontent.com/u/549349?v=4"/></br>[charles-e](https://api.github.com/users/charles-e) | <img width="50" src="https://avatars3.githubusercontent.com/u/1686759?v=4"/></br>[chrmoritz](https://api.github.com/users/chrmoritz) | <img width="50" src="https://avatars0.githubusercontent.com/u/5131923?v=4"/></br>[donbowman](https://api.github.com/users/donbowman) | <img width="50" src="https://avatars2.githubusercontent.com/u/47756?v=4"/></br>[dflock](https://api.github.com/users/dflock) |
| <img width="50" src="https://avatars3.githubusercontent.com/u/1962738?v=4"/></br>[einverne](https://api.github.com/users/einverne) | <img width="50" src="https://avatars0.githubusercontent.com/u/628474?v=4"/></br>[Atalanttore](https://api.github.com/users/Atalanttore) | <img width="50" src="https://avatars1.githubusercontent.com/u/16492558?v=4"/></br>[eodeluga](https://api.github.com/users/eodeluga) | <img width="50" src="https://avatars2.githubusercontent.com/u/1714374?v=4"/></br>[FleischKarussel](https://api.github.com/users/FleischKarussel) | <img width="50" src="https://avatars0.githubusercontent.com/u/6190183?v=4"/></br>[gmag11](https://api.github.com/users/gmag11) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/2257024?v=4"/></br>[gusbemacbe](https://api.github.com/users/gusbemacbe) | <img width="50" src="https://avatars0.githubusercontent.com/u/18524580?v=4"/></br>[Fvbor](https://api.github.com/users/Fvbor) | <img width="50" src="https://avatars3.githubusercontent.com/u/3379379?v=4"/></br>[sczhg](https://api.github.com/users/sczhg) | <img width="50" src="https://avatars1.githubusercontent.com/u/1716229?v=4"/></br>[Vistaus](https://api.github.com/users/Vistaus) | <img width="50" src="https://avatars3.githubusercontent.com/u/11466782?v=4"/></br>[jacobherrington](https://api.github.com/users/jacobherrington) |
| <img width="50" src="https://avatars1.githubusercontent.com/u/29855366?v=4"/></br>[jcgerhard](https://api.github.com/users/jcgerhard) | <img width="50" src="https://avatars1.githubusercontent.com/u/4995433?v=4"/></br>[jaredcrowe](https://api.github.com/users/jaredcrowe) | <img width="50" src="https://avatars2.githubusercontent.com/u/339645?v=4"/></br>[jmontane](https://api.github.com/users/jmontane) | <img width="50" src="https://avatars0.githubusercontent.com/u/163555?v=4"/></br>[JoelRSimpson](https://api.github.com/users/JoelRSimpson) | <img width="50" src="https://avatars1.githubusercontent.com/u/23194385?v=4"/></br>[jony0008](https://api.github.com/users/jony0008) |
| <img width="50" src="https://avatars1.githubusercontent.com/u/6048003?v=4"/></br>[joybinchen](https://api.github.com/users/joybinchen) | <img width="50" src="https://avatars1.githubusercontent.com/u/1560189?v=4"/></br>[y-usuzumi](https://api.github.com/users/y-usuzumi) | <img width="50" src="https://avatars0.githubusercontent.com/u/1660460?v=4"/></br>[xuhcc](https://api.github.com/users/xuhcc) | <img width="50" src="https://avatars3.githubusercontent.com/u/7824233?v=4"/></br>[kklas](https://api.github.com/users/kklas) | <img width="50" src="https://avatars2.githubusercontent.com/u/2599210?v=4"/></br>[lboullo0](https://api.github.com/users/lboullo0) |
| <img width="50" src="https://avatars1.githubusercontent.com/u/1562062?v=4"/></br>[dbinary](https://api.github.com/users/dbinary) | <img width="50" src="https://avatars3.githubusercontent.com/u/5699725?v=4"/></br>[mvonmaltitz](https://api.github.com/users/mvonmaltitz) | <img width="50" src="https://avatars3.githubusercontent.com/u/5788516?v=4"/></br>[Marmo](https://api.github.com/users/Marmo) | <img width="50" src="https://avatars2.githubusercontent.com/u/12831489?v=4"/></br>[mgroth0](https://api.github.com/users/mgroth0) | <img width="50" src="https://avatars0.githubusercontent.com/u/51273874?v=4"/></br>[MichipX](https://api.github.com/users/MichipX) |
| <img width="50" src="https://avatars3.githubusercontent.com/u/9076687?v=4"/></br>[NJannasch](https://api.github.com/users/NJannasch) | <img width="50" src="https://avatars2.githubusercontent.com/u/12369770?v=4"/></br>[Ouvill](https://api.github.com/users/Ouvill) | <img width="50" src="https://avatars3.githubusercontent.com/u/43815417?v=4"/></br>[shorty2380](https://api.github.com/users/shorty2380) | <img width="50" src="https://avatars3.githubusercontent.com/u/6306608?v=4"/></br>[Diadlo](https://api.github.com/users/Diadlo) | <img width="50" src="https://avatars0.githubusercontent.com/u/8766773?v=4"/></br>[Cogitri](https://api.github.com/users/Cogitri) |
| <img width="50" src="https://avatars0.githubusercontent.com/u/559346?v=4"/></br>[metbril](https://api.github.com/users/metbril) | <img width="50" src="https://avatars1.githubusercontent.com/u/744655?v=4"/></br>[ruzaq](https://api.github.com/users/ruzaq) | <img width="50" src="https://avatars2.githubusercontent.com/u/505011?v=4"/></br>[kcrt](https://api.github.com/users/kcrt) | <img width="50" src="https://avatars1.githubusercontent.com/u/538584?v=4"/></br>[xissy](https://api.github.com/users/xissy) | <img width="50" src="https://avatars3.githubusercontent.com/u/466122?v=4"/></br>[Tekki](https://api.github.com/users/Tekki) |
| <img width="50" src="https://avatars0.githubusercontent.com/u/8731922?v=4"/></br>[tbroadley](https://api.github.com/users/tbroadley) | <img width="50" src="https://avatars1.githubusercontent.com/u/114300?v=4"/></br>[Kriechi](https://api.github.com/users/Kriechi) | <img width="50" src="https://avatars0.githubusercontent.com/u/3457339?v=4"/></br>[tkilaker](https://api.github.com/users/tkilaker) | <img width="50" src="https://avatars1.githubusercontent.com/u/4201229?v=4"/></br>[tcyrus](https://api.github.com/users/tcyrus) | <img width="50" src="https://avatars2.githubusercontent.com/u/834914?v=4"/></br>[tobias-grasse](https://api.github.com/users/tobias-grasse) |
| <img width="50" src="https://avatars3.githubusercontent.com/u/6691273?v=4"/></br>[strobeltobias](https://api.github.com/users/strobeltobias) | <img width="50" src="https://avatars2.githubusercontent.com/u/11031696?v=4"/></br>[ymitsos](https://api.github.com/users/ymitsos) | <img width="50" src="https://avatars3.githubusercontent.com/u/28493662?v=4"/></br>[cdorin93](https://api.github.com/users/cdorin93) | <img width="50" src="https://avatars3.githubusercontent.com/u/30935096?v=4"/></br>[cybertramp](https://api.github.com/users/cybertramp) | <img width="50" src="https://avatars3.githubusercontent.com/u/9694906?v=4"/></br>[delta-emil](https://api.github.com/users/delta-emil) |
| <img width="50" src="https://avatars0.githubusercontent.com/u/926263?v=4"/></br>[doc75](https://api.github.com/users/doc75) | <img width="50" src="https://avatars2.githubusercontent.com/u/2903013?v=4"/></br>[ebayer](https://api.github.com/users/ebayer) | <img width="50" src="https://avatars0.githubusercontent.com/u/14201321?v=4"/></br>[rasperepodvipodvert](https://api.github.com/users/rasperepodvipodvert) | <img width="50" src="https://avatars1.githubusercontent.com/u/11388094?v=4"/></br>[hydrandt](https://api.github.com/users/hydrandt) | <img width="50" src="https://avatars1.githubusercontent.com/u/42961947?v=4"/></br>[pensierocrea](https://api.github.com/users/pensierocrea) |
| <img width="50" src="https://avatars3.githubusercontent.com/u/10206967?v=4"/></br>[rhtenhove](https://api.github.com/users/rhtenhove) | <img width="50" src="https://avatars3.githubusercontent.com/u/14062932?v=4"/></br>[simonsan](https://api.github.com/users/simonsan) | <img width="50" src="https://avatars0.githubusercontent.com/u/10956653?v=4"/></br>[tcassaert](https://api.github.com/users/tcassaert) | <img width="50" src="https://avatars0.githubusercontent.com/u/2216902?v=4"/></br>[xcffl](https://api.github.com/users/xcffl) | |
<!-- CONTRIBUTORS-TABLE-AUTO-GENERATED -->
# Known bugs # Known bugs
- Resources larger than 10 MB are not currently supported on mobile. They will crash the application so it is recommended not to attach such resources at the moment. The issue is being looked at. - Resources larger than 10 MB are not currently supported on mobile. They will crash the application so it is recommended not to attach such resources at the moment. The issue is being looked at.

View File

@@ -94,8 +94,8 @@ android {
applicationId "net.cozic.joplin" applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097515 versionCode 2097517
versionName "1.0.279" versionName "1.0.281"
ndk { ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64" abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
} }

View File

@@ -546,6 +546,7 @@ class BaseApplication {
Setting.setConstant('env', initArgs.env); Setting.setConstant('env', initArgs.env);
Setting.setConstant('profileDir', profileDir); Setting.setConstant('profileDir', profileDir);
Setting.setConstant('templateDir', profileDir + '/templates');
Setting.setConstant('resourceDirName', resourceDirName); Setting.setConstant('resourceDirName', resourceDirName);
Setting.setConstant('resourceDir', resourceDir); Setting.setConstant('resourceDir', resourceDir);
Setting.setConstant('tempDir', tempDir); Setting.setConstant('tempDir', tempDir);

View File

@@ -0,0 +1,59 @@
const fs = require('fs-extra');
const { shim } = require('lib/shim.js');
const { time } = require('lib/time-utils.js');
const Mustache = require('mustache');
const TemplateUtils = {};
// new template variables can be added here
// If there are too many, this should be moved to a new file
const view = {
date: time.formatMsToLocal(new Date().getTime(), time.dateFormat()),
time: time.formatMsToLocal(new Date().getTime(), time.timeFormat()),
datetime: time.formatMsToLocal(new Date().getTime()),
custom_datetime: () => { return (text, render) => {
return render(time.formatMsToLocal(new Date().getTime(), text));
}},
}
// Mustache escapes strings (including /) with the html code by default
// This isn't useful for markdown so it's disabled
Mustache.escape = (text) => { return text; }
TemplateUtils.render = function(input) {
return Mustache.render(input, view);
}
TemplateUtils.loadTemplates = async function(filePath) {
let templates = [];
let files = [];
if (await shim.fsDriver().exists(filePath)) {
try {
files = await shim.fsDriver().readDirStats(filePath);
} catch (error) {
let msg = error.message ? error.message : '';
msg = 'Could not read template names from ' + filePath + '\n' + msg;
error.message = msg;
throw error;
}
files.forEach(async (file) => {
if (file.path.endsWith('.md')) {
try {
let fileString = await shim.fsDriver().readFile(filePath + '/' + file.path, 'utf-8');
templates.push({label: file.path, value: fileString});
} catch (error) {
let msg = error.message ? error.message : '';
msg = 'Could not load template ' + file.path + '\n' + msg;
error.message = msg;
throw error;
}
}
});
}
return templates;
}
module.exports = TemplateUtils;

View File

@@ -613,8 +613,8 @@ class NoteScreenComponent extends BaseScreenComponent {
const output = []; const output = [];
const createdDateString = time.unixMsToLocalDateTime(note.user_created_time); const createdDateString = time.formatMsToLocal(note.user_created_time);
const updatedDateString = time.unixMsToLocalDateTime(note.user_updated_time); const updatedDateString = time.formatMsToLocal(note.user_updated_time);
output.push({ title: _('Created: %s', createdDateString) }); output.push({ title: _('Created: %s', createdDateString) });
output.push({ title: _('Updated: %s', updatedDateString) }); output.push({ title: _('Updated: %s', updatedDateString) });

View File

@@ -648,6 +648,7 @@ Setting.constants_ = {
resourceDirName: '', resourceDirName: '',
resourceDir: '', resourceDir: '',
profileDir: '', profileDir: '',
templateDir: '',
tempDir: '', tempDir: '',
openDevTools: false, openDevTools: false,
} }

View File

@@ -33,6 +33,7 @@ const defaultState = {
hasDisabledSyncItems: false, hasDisabledSyncItems: false,
newNote: null, newNote: null,
customCss: '', customCss: '',
templates: [],
collapsedFolderIds: [], collapsedFolderIds: [],
clipperServer: { clipperServer: {
startState: 'idle', startState: 'idle',
@@ -714,6 +715,12 @@ const reducer = (state = defaultState, action) => {
newState.customCss = action.css; newState.customCss = action.css;
break; break;
case 'TEMPLATE_UPDATE_ALL':
newState = Object.assign({}, state);
newState.templates = action.templates;
break;
case 'SET_NOTE_TAGS': case 'SET_NOTE_TAGS':
newState = Object.assign({}, state); newState = Object.assign({}, state);
newState.selectedNoteTags = action.items; newState.selectedNoteTags = action.items;

View File

@@ -23,17 +23,17 @@
"katex": "^0.10.0", "katex": "^0.10.0",
"markdown-it": "^8.4.0", "markdown-it": "^8.4.0",
"markdown-it-abbr": "^1.0.4", "markdown-it-abbr": "^1.0.4",
"markdown-it-anchor": "^5.0.2", "markdown-it-anchor": "^5.2.4",
"markdown-it-deflist": "^2.0.3", "markdown-it-deflist": "^2.0.3",
"markdown-it-emoji": "^1.4.0", "markdown-it-emoji": "^1.4.0",
"markdown-it-footnote": "^3.0.1", "markdown-it-footnote": "^3.0.2",
"markdown-it-ins": "^2.0.0", "markdown-it-ins": "^2.0.0",
"markdown-it-katex": "^2.0.3", "markdown-it-katex": "^2.0.3",
"markdown-it-mark": "^2.0.0", "markdown-it-mark": "^2.0.0",
"markdown-it-multimd-table": "^3.2.0", "markdown-it-multimd-table": "^3.2.0",
"markdown-it-sub": "^1.0.0", "markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0", "markdown-it-sup": "^1.0.0",
"markdown-it-toc-done-right": "^4.0.0", "markdown-it-toc-done-right": "^4.0.2",
"md5": "^2.2.1", "md5": "^2.2.1",
"moment": "^2.18.1", "moment": "^2.18.1",
"prop-types": "^15.6.0", "prop-types": "^15.6.0",

View File

@@ -22,7 +22,7 @@ const cliLocalesDir = cliDir + '/locales';
const rnDir = rootDir + '/ReactNativeClient'; const rnDir = rootDir + '/ReactNativeClient';
const electronDir = rootDir + '/ElectronClient/app'; const electronDir = rootDir + '/ElectronClient/app';
const { execCommand, isMac } = require('./tool-utils.js'); const { execCommand, isMac, insertContentIntoFile } = require('./tool-utils.js');
const { countryDisplayName, countryCodeOnly } = require('lib/locale.js'); const { countryDisplayName, countryCodeOnly } = require('lib/locale.js');
function parsePoFile(filePath) { function parsePoFile(filePath) {
@@ -203,16 +203,12 @@ function translationStatusToMdTable(status) {
} }
async function updateReadmeWithStats(stats) { async function updateReadmeWithStats(stats) {
const mdTableMarkerOpen = '<!-- LOCALE-TABLE-AUTO-GENERATED -->\n'; await insertContentIntoFile(
const mdTableMarkerClose = '\n<!-- LOCALE-TABLE-AUTO-GENERATED -->'; rootDir + '/README.md',
let mdTable = translationStatusToMdTable(stats); '<!-- LOCALE-TABLE-AUTO-GENERATED -->\n',
mdTable = mdTableMarkerOpen + mdTable + mdTableMarkerClose; '\n<!-- LOCALE-TABLE-AUTO-GENERATED -->',
translationStatusToMdTable(stats)
let content = await fs.readFile(rootDir + '/README.md', 'utf-8'); );
// [^]* matches any character including new lines
const regex = new RegExp(mdTableMarkerOpen + '[^]*?' + mdTableMarkerClose);
content = content.replace(regex, mdTable);
await fs.writeFile(rootDir + '/README.md', content);
} }
async function main() { async function main() {

View File

@@ -75,6 +75,15 @@
"hoek": "4.x.x" "hoek": "4.x.x"
} }
}, },
"camel-case": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
"integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
"requires": {
"no-case": "^2.2.0",
"upper-case": "^1.1.1"
}
},
"camelcase": { "camelcase": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
@@ -85,6 +94,14 @@
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
}, },
"clean-css": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
"integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
"requires": {
"source-map": "~0.6.0"
}
},
"cliui": { "cliui": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
@@ -113,6 +130,11 @@
"delayed-stream": "~1.0.0" "delayed-stream": "~1.0.0"
} }
}, },
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
},
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -323,11 +345,30 @@
"sntp": "2.x.x" "sntp": "2.x.x"
} }
}, },
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"hoek": { "hoek": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA=="
}, },
"html-minifier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
"integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
"requires": {
"camel-case": "^3.0.0",
"clean-css": "^4.2.1",
"commander": "^2.19.0",
"he": "^1.2.0",
"param-case": "^2.1.1",
"relateurl": "^0.2.7",
"uglify-js": "^3.5.1"
}
},
"http-signature": { "http-signature": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -438,6 +479,11 @@
"path-exists": "^3.0.0" "path-exists": "^3.0.0"
} }
}, },
"lower-case": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
},
"map-age-cleaner": { "map-age-cleaner": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
@@ -501,6 +547,14 @@
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
}, },
"no-case": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
"integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
"requires": {
"lower-case": "^1.1.1"
}
},
"node-fetch": { "node-fetch": {
"version": "1.7.3", "version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
@@ -558,7 +612,7 @@
}, },
"p-is-promise": { "p-is-promise": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
"integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4="
}, },
"p-limit": { "p-limit": {
@@ -582,6 +636,14 @@
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
"integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ=="
}, },
"param-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
"integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
"requires": {
"no-case": "^2.2.0"
}
},
"path-exists": { "path-exists": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
@@ -621,6 +683,11 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
}, },
"relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
},
"request": { "request": {
"version": "2.85.0", "version": "2.85.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz",
@@ -701,6 +768,11 @@
"hoek": "4.x.x" "hoek": "4.x.x"
} }
}, },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"sprintf-js": { "sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -780,11 +852,25 @@
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz",
"integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg==" "integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg=="
}, },
"uglify-js": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz",
"integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==",
"requires": {
"commander": "~2.20.0",
"source-map": "~0.6.1"
}
},
"universalify": { "universalify": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
}, },
"upper-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
},
"uri-template": { "uri-template": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/uri-template/-/uri-template-1.0.1.tgz", "resolved": "https://registry.npmjs.org/uri-template/-/uri-template-1.0.1.tgz",
@@ -823,7 +909,7 @@
}, },
"wrap-ansi": { "wrap-ansi": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"requires": { "requires": {
"string-width": "^1.0.1", "string-width": "^1.0.1",
@@ -855,7 +941,7 @@
}, },
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"

View File

@@ -12,6 +12,7 @@
"app-module-path": "^2.2.0", "app-module-path": "^2.2.0",
"fs-extra": "^4.0.3", "fs-extra": "^4.0.3",
"gettext-parser": "^1.3.0", "gettext-parser": "^1.3.0",
"html-minifier": "^4.0.0",
"markdown-it": "^8.4.1", "markdown-it": "^8.4.1",
"mustache": "^2.3.0", "mustache": "^2.3.0",
"node-fetch": "^1.7.3", "node-fetch": "^1.7.3",

View File

@@ -151,4 +151,13 @@ toolUtils.isMac = () => {
return process && process.platform === 'darwin'; return process && process.platform === 'darwin';
} }
toolUtils.insertContentIntoFile = async function (filePath, markerOpen, markerClose, contentToInsert) {
const fs = require('fs-extra');
let content = await fs.readFile(filePath, 'utf-8');
// [^]* matches any character including new lines
const regex = new RegExp(markerOpen + '[^]*?' + markerClose);
content = content.replace(regex, markerOpen + contentToInsert + markerClose);
await fs.writeFile(filePath, content);
}
module.exports = toolUtils; module.exports = toolUtils;

View File

@@ -0,0 +1,108 @@
require('app-module-path').addPath(__dirname + '/../ReactNativeClient');
'use strict';
const fs = require('fs-extra');
const https = require('https');
const request = require('request');
const { fileExtension } = require('lib/path-utils.js');
const readmePath = __dirname + '/../README.md';
const { insertContentIntoFile } = require('./tool-utils.js');
const markdownUtils = require('lib/markdownUtils');
async function gitHubContributors(page) {
return new Promise((resolve, reject) => {
request.get({
url: 'https://api.github.com/repos/laurent22/joplin/contributors' + (page ? '?page=' + page : ''),
json: true,
headers: {'User-Agent': 'Joplin Readme Updater'}
}, (error, response, data) => {
if (error) {
reject(error);
} else if (response.statusCode !== 200) {
reject(new Error('Error HTTP ' + response.statusCode));
} else {
resolve(data);
}
});
});
}
function contributorTable(contributors) {
let rows = [];
let row = [];
rows.push(row);
let x = 0;
let y = 0;
let rowLength = 5;
let contributorIndex = 0;
while (contributorIndex < contributors.length) {
const c = contributors[contributorIndex];
contributorIndex++;
const cell = `<img width="50" src="${c.avatar_url}"/></br>[${c.login}](${c.url})`;
row.push(cell);
if (row.length >= rowLength) {
row = [];
rows.push(row);
}
}
while (rows[rows.length - 1].length < rowLength) rows[rows.length - 1].push('');
const header = [];
const headerLine = [];
for (let i = 0; i < rowLength; i++) {
header.push(' ');
headerLine.push(':---:');
}
const lines = [];
lines.push('| ' + header.join(' | ') + ' |');
lines.push('| ' + headerLine.join(' | ') + ' |');
for (const row of rows) {
lines.push('| ' + row.join(' | ') + ' |');
}
return lines.join('\n');
}
async function main(argv) {
let contributors = [];
let pageIndex = 0;
const doneNames = [];
while (true) {
const response = await gitHubContributors(pageIndex);
pageIndex++;
if (!response.length) break;
// Remove duplicates
const temp = [];
for (const r of response) {
if (doneNames.indexOf(r.login) >= 0) continue;
doneNames.push(r.login);
temp.push(r);
}
contributors = contributors.concat(temp);
}
const tableHtml = contributorTable(contributors);
await insertContentIntoFile(
readmePath,
'<!-- CONTRIBUTORS-TABLE-AUTO-GENERATED -->\n',
'\n<!-- CONTRIBUTORS-TABLE-AUTO-GENERATED -->',
tableHtml
);
}
main(process.argv).catch((error) => {
console.error('Fatal error', error);
process.exit(1);
});

View File

@@ -1,26 +0,0 @@
### Operating system
<!-- Please only leave those relevant to your request -->
- Windows
- macOS
- Linux
- Android
- iOS
### Application
<!-- Please only leave those relevant to your request -->
- Desktop
- Mobile
- Terminal
<!--
Please read the guide first! https://github.com/laurent22/joplin/blob/master/CONTRIBUTING.md
If you are reporting a bug, did you try enabling debug mode? https://joplinapp.org/debugging/
If so, please post any error or warning.
For general discussion, user support or to discuss feature requests, please post to the forum https://discourse.joplinapp.org/ (You can login with your GitHub account)
-->