1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-14 23:26:58 +02:00

Compare commits

..

15 Commits

Author SHA1 Message Date
Laurent Cozic
7e1b38b936 eslint 2022-01-06 20:06:33 +00:00
Laurent Cozic
a11402db6d fix ci 2022-01-06 18:48:55 +00:00
Laurent Cozic
c19d533d93 fix lock 2022-01-06 18:27:18 +00:00
Laurent Cozic
1a4ff7054a Add support for plugin task list 2022-01-06 17:46:45 +00:00
Laurent Cozic
71140a638f Minor clipper tweaks 2022-01-06 15:46:28 +00:00
Laurent Cozic
6ba0fce237 Doc: Update Joplin Cloud FAQ 2022-01-06 12:22:23 +00:00
Laurent Cozic
c033a343c1 Doc: Update Joplin Server license 2022-01-06 12:00:54 +00:00
Daeraxa
898c96a566 Tools: Fix desktop app build (#5960) 2022-01-06 10:54:10 +00:00
CandleCandle
b83fa133b2 Doc: Default S3 permissions require 'GetObject' (#5957) 2022-01-05 16:46:41 +00:00
reportxx
ec7fec8b59 All: Translation: Update sv.po (#5950) 2022-01-03 18:14:37 -05:00
Fusion future
b2fb4f2ea2 Desktop: Add "X-GNOME-SingleWindow=true" to the desktop file (#5948)
Since this commit (ebd2acd98e),
Plasma Desktop will check "X-GNOME-SingleWindow" property to determine
whether to show "Open New Window" action in the context menu of a task.

Joplin cannot launch a new instance or open a new window, so add the
property and set it to true to hide the action.
2022-01-02 21:15:08 -07:00
Laurent Cozic
c74e51a58e Tools: Apply "withRetry" technique to copyPluginAssets too to try to fix CI 2022-01-02 18:38:06 +01:00
Daeraxa
7cba4be498 Doc: update e2ee spec paths and links (#5938) 2022-01-02 12:03:07 -05:00
giorgi shengelaia
19b396f2ec Doc: fix grammar and typos (#5946) 2022-01-02 11:57:30 -05:00
Laurent
4ed7c340a0 Tools: Fixed Docker image building and make it more stable (#5942) 2022-01-02 12:48:03 +00:00
45 changed files with 6053 additions and 116 deletions

View File

@@ -52,6 +52,8 @@ packages/app-mobile/lib/rnInjectedJs/
packages/app-mobile/locales
packages/app-mobile/node_modules
packages/app-mobile/pluginAssets/
packages/electron-process-manager/dist/
packages/electron-process-manager/src/ui/
packages/fork-*
packages/htmlpack/dist/
packages/lib/assets/

View File

@@ -1,5 +1,10 @@
#!/bin/bash
# We want the script to stop as soon as an error is found. Otherwise if there's
# an error during `yarn install` for example, it's also going to throw millions
# of errors in test units, which makes debugging difficult.
set -e
# =============================================================================
# Setup environment variables
# =============================================================================

View File

@@ -60,7 +60,7 @@ Normally the **bundler** should start automatically with the application. If it
## Building the clipper
cd packages/app-clipper/popup
yarn run watch # To watch for changes
npm run watch # To watch for changes
To test the extension please refer to the relevant pages for each browser: [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension#Trying_it_out) / [Chrome](https://developer.chrome.com/docs/extensions/mv3/getstarted/). Please note that the extension in dev mode will only connect to a dev instance of the desktop app (and vice-versa).

View File

@@ -31,12 +31,12 @@ Joplin is available in multiple languages thanks to the help of its users. You c
If you want to start contributing to the project's code, please follow these guidelines before creating a pull request:
- Explain WHY you want to add this change. Explain it inside the pull request and you may link to an issue for additional information, but the PR should gives a clear overview of why you want to add this.
- Explain WHY you want to add this change. Explain it inside the pull request and you may link to an issue for additional information, but the PR should give a clear overview of why you want to add this.
- Bug fixes are always welcome. Start by reviewing the [list of bugs](https://github.com/laurent22/joplin/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
- A good way to easily start contributing is to pick and work on a [good first issue](https://github.com/laurent22/joplin/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). We try to make these issues as clear as possible and provide basic info on how the code should be changed, and if something is unclear feel free to ask for more information on the issue.
- Before adding a new feature, ask about it in the [Github Issue Tracker](https://github.com/laurent22/joplin/issues?utf8=%E2%9C%93&q=is%3Aissue) or the [Joplin Forum](https://discourse.joplinapp.org/), or check if existing discussions exist to make sure the new functionality is desired.
- **Changes that will consist in more than 50 lines of code should be discussed the [Joplin Forum](https://discourse.joplinapp.org/)**, so that you don't spend too much time implementing something that might not be accepted.
- All the applications share the same backend (database, synchronisation, settings, models, business logic, etc.) so if you change something in the backend in one app, makes sure it still work in the other apps. Usually it does, but keep this in mind.
- **Changes that will consist of more than 50 lines of code should be discussed on the [Joplin Forum](https://discourse.joplinapp.org/)**, so that you don't spend too much time implementing something that might not be accepted.
- All the applications share the same backend (database, synchronisation, settings, models, business logic, etc.) so if you change something in the backend in one app, make sure it still works in the other apps. Usually it does, but keep this in mind.
- Pull requests that make many changes using an automated tool, like for spell fixing, styling, etc. will not be accepted. An exception would be if the changes have been discussed in the forum and someone has agreed to review **and test** the pull request.
- Pull requests that make address multiple issues will most likely stall and eventually be closed. This is because we might be fine with one of the changes but not with others and untangling that kind of pull request is too much hassle both for maintainers and the person who submitted it. So most of the time someone gives up and the PR gets closed. So please keep the pull request focused on one issue.

View File

@@ -219,6 +219,7 @@ then
Type=Application
Categories=Office;
MimeType=x-scheme-handler/joplin;
X-GNOME-SingleWindow=true
EOF
# Update application icons

10
LICENSE
View File

@@ -1,9 +1,9 @@
All code in this repository is licensed under the MIT License **unless a
directory contains a LICENSE file**, in which case that LICENSE file applies to
the code in that sub-directory.
directory contains a LICENSE or LICENSE.md file**, in which case that file
applies to the code in that sub-directory.
For example, packages/fork-sax contains a ISC LICENSE file, thus all code under
the packages/fork-sax directory is licensed under ISC.
For example, packages/server contains a LICENSE.md file, thus all code under the
packages/server directory is licensed under that license.
For example, packages/app-cli does NOT contain a LICENSE file, thus all code
under that directory is licensed under the default license, which is MIT.
@@ -20,7 +20,7 @@ icons please contact the author in order to get a permission.
MIT License
Copyright (c) 2016-2021 Laurent Cozic
Copyright (c) 2016-2022 Laurent Cozic
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@@ -304,6 +304,7 @@ To add a **Bucket Policy** from the AWS S3 Web Console, navigate to the **Permis
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:PutObject"

View File

@@ -19,7 +19,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Joplin Web Clipper</title>
</head>
<body>
<noscript>

View File

@@ -9,6 +9,7 @@ const path = require('path');
const { dirname } = require('@joplin/lib/path-utils');
const fs = require('fs-extra');
const { ipcMain } = require('electron');
const { openProcessManager } = require('@joplin/electron-process-manager');
interface RendererProcessQuitReply {
canClose: boolean;
@@ -41,6 +42,10 @@ export default class ElectronAppWrapper {
this.initialCallbackUrl_ = initialCallbackUrl;
}
public openProcessManager() {
openProcessManager(require('@electron/remote/main'));
}
electronApp() {
return this.electronApp_;
}

View File

@@ -62,7 +62,7 @@ import ShareService from '@joplin/lib/services/share/ShareService';
import checkForUpdates from './checkForUpdates';
import { AppState } from './app.reducer';
import syncDebugLog from '@joplin/lib/services/synchronizer/syncDebugLog';
import eventManager from '../lib/eventManager';
import eventManager from '@joplin/lib/eventManager';
// import { runIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils';
const pluginClasses = [

View File

@@ -30,6 +30,10 @@ export class Bridge {
return !this.electronApp().electronApp().isPackaged;
}
public openProcessManager() {
this.electronApp().openProcessManager();
}
// The build directory contains additional external files that are going to
// be packaged by Electron Builder. This is for files that need to be
// accessed outside of the Electron app (for example the application icon).

View File

@@ -726,6 +726,13 @@ function useMenu(props: Props) {
});
},
},
{
id: 'help:toggleTaskList',
label: _('Open plugin task list'),
click: () => {
bridge().openProcessManager();
},
},
menuItemDic.toggleSafeMode,
menuItemDic.openProfileDirectory,

View File

@@ -134,9 +134,10 @@
"7zip-bin-win": "^2.1.1"
},
"dependencies": {
"@electron/remote": "^2.0.1",
"@electron/remote": "2.0.1",
"@fortawesome/fontawesome-free": "^5.13.0",
"@joeattardi/emoji-button": "^4.6.0",
"@joplin/electron-process-manager": "workspace:^",
"@joplin/lib": "~2.7",
"@joplin/renderer": "~2.7",
"async-mutex": "^0.1.3",

View File

@@ -1,5 +1,29 @@
const { copy, mkdirp, remove } = require('fs-extra');
const msleep = async (ms) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
};
// Same as copyApplicationAssets - probably both scripts should be merged in
// one.
const withRetry = async (fn) => {
for (let i = 0; i < 5; i++) {
try {
await fn();
return;
} catch (error) {
console.warn(`withRetry: Failed calling function - will retry (${i})`, error);
await msleep(1000 + i * 1000);
}
}
throw new Error('withRetry: Could not run function after multiple attempts');
};
async function main() {
const rootDir = `${__dirname}/..`;
@@ -12,11 +36,11 @@ async function main() {
for (const action of ['delete', 'copy']) {
for (const destDir of destDirs) {
if (action === 'delete') {
await remove(destDir);
await withRetry(() => remove(destDir));
} else {
console.info(`Copying to ${destDir}`);
await mkdirp(destDir);
await copy(sourceDir, destDir, { overwrite: true });
await withRetry(() => mkdirp(destDir));
await withRetry(() => copy(sourceDir, destDir, { overwrite: true }));
}
}
}

View File

@@ -0,0 +1,4 @@
{
"presets" : ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

View File

@@ -0,0 +1 @@
dist/

View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<content url="file://$MODULE_DIR$/../electron-process-reporter" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
<component name="MarkdownProjectSettings" wasCopied="true">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.5" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="0" emojiImages="0">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="ADMONITION_EXT" value="false" />
<option name="ATTRIBUTES_EXT" value="false" />
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="NO_TEXT_ATTRIBUTES" value="false" />
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/electron-process-manager.iml" filepath="$PROJECT_DIR$/.idea/electron-process-manager.iml" />
</modules>
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b935834e-d3d3-420f-991d-e49e232dd71b" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileColors">
<fileColor scope="Non-Project Files (Material Default)" color="2E3C43" />
<fileColor scope="Non-Project Files (Material Darker)" color="323232" />
<fileColor scope="Non-Project Files (Material Lighter)" color="eae8e8" />
<fileColor scope="Non-Project Files (Material Palenight)" color="2f2e43" />
</component>
<component name="Git.Settings">
<option name="PREVIOUS_COMMIT_AUTHORS">
<list>
<option value="Kris Dages &lt;krisdages@git.whiteboxsoftware.net&gt;" />
</list>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GitSEFilterConfiguration">
<file-type-list>
<filtered-out-file-type name="LOCAL_BRANCH" />
<filtered-out-file-type name="REMOTE_BRANCH" />
<filtered-out-file-type name="TAG" />
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
</file-type-list>
</component>
<component name="ProjectId" id="1hYhEkXEjWlOUW5DO64AadCwogW" />
<component name="ProjectViewState">
<option name="autoscrollFromSource" value="true" />
<option name="autoscrollToSource" value="true" />
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/../electron-process-reporter" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.selected.package.eslint" value="(autodetect)" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
<property name="nodejs_package_manager_path" value="yarn" />
<property name="settings.editor.selected.configurable" value="web-ide.project.structure" />
<property name="ts.external.directory.path" value="$APPLICATION_HOME_DIR$/plugins/JavaScriptLanguage/jsLanguageServicesImpl/external" />
<property name="vue.rearranger.settings.migration" value="true" />
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="b935834e-d3d3-420f-991d-e49e232dd71b" name="Default Changelist" comment="" />
<created>1600193824206</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1600193824206</updated>
<workItem from="1600193825399" duration="438000" />
<workItem from="1606177958157" duration="419000" />
<workItem from="1616558380418" duration="1481000" />
<workItem from="1627318845586" duration="3411000" />
<workItem from="1627323366882" duration="72000" />
<workItem from="1627489266598" duration="1192000" />
</task>
<task id="LOCAL-00001" summary="(fix) Add `enableRemoteModule: true` to ProcessManagerWindow webPreferences&#10;&#10;Fixes broken UI in Electron 10">
<created>1600193995342</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1600193995342</updated>
</task>
<task id="LOCAL-00002" summary="(breaking) Use `@electron/remote` instead of deprecated `remote` module&#10;&#10;Updated min electron version to 10.&#10;Bump version to 2.0.0">
<created>1627322100905</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1627322100905</updated>
</task>
<task id="LOCAL-00003" summary="Update README.md for fork">
<created>1627490446165</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1627490446165</updated>
</task>
<option name="localTasksCounter" value="4" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="(fix) Add `enableRemoteModule: true` to ProcessManagerWindow webPreferences&#10;&#10;Fixes broken UI in Electron 10" />
<MESSAGE value="(breaking) Use `@electron/remote` instead of deprecated `remote` module&#10;&#10;Updated min electron version to 10.&#10;Bump version to 2.0.0" />
<MESSAGE value="Update README.md for fork" />
<option name="LAST_COMMIT_MESSAGE" value="Update README.md for fork" />
</component>
</project>

View File

@@ -0,0 +1,92 @@
# Process Manager UI for Electron Apps
* * *
2022-01-06: Forked from https://github.com/krisdages/electron-process-manager
* * *
## Fork using @electron/remote instead of builtin remote module
* Minimum electron version is `10`
* [@electron/remote](https://github.com/electron/remote) is a peerDependency. It needs to be initialized in the main process. Follow the instructions in the link.
## Original 1.0 Readme
This package provides a process manager UI for Electron applications.
It opens a window displaying a table of every processes run by the Electron application with information (type, URL for `webContents`, memory..).
[![npm version](https://badge.fury.io/js/electron-process-manager.svg)](https://badge.fury.io/js/electron-process-manager)
![screenshot](https://github.com/getstation/electron-process-manager/raw/master/.github/screenshots/window.png)
~~:warning: For `@electron>=3.0.0, <7.x`, use version `0.7.1` of this package.
For versions `>=7.x`, use latest.~~
It can be useful to debug performance of an app with several `webview`.
It's inspired from Chrome's task manager.
## Features
- [ ] Memory reporting
- [ ] Link memory data to web-contents (for electron >=1.7.1)
- [x] Kill a process from the UI
- [x] Open developer tools for a given process
- [x] CPU metrics
- [x] Sort by columns
⚠️ Unfortunately, memory info are no longer available in Electron>=4 (see [electron/electron#16179](https://github.com/electron/electron/issues/16179))
## Installation
```bash
$ npm install electron-process-manager
```
## Usage
```js
const { openProcessManager } = require('electron-process-manager');
openProcessManager();
```
## Options
`openProcessManager` function can take options in paramters
#### options.defaultSorting
**defaultSorting.how**: `'ascending' | 'descending'`
**defaultSorting.path**:
| Field name | path |
|--------------------|----------------------------|
| Pid | 'pid' |
| WebContents Domain | 'webContents.0.URLDomain' |
| Process Type | 'webContents.0.type' |
| Private Memory | 'memory.privateBytes' |
| Shared Memory | 'memory.sharedBytes' |
| Working Set Size | 'memory.workingSetSize' |
| % CPU | 'cpu.percentCPUUsage' |
| Idle Wake Ups /s | 'cpu.idleWakeupsPerSecond' |
| WebContents Id | 'webContents.0.id' |
| WebContents Type | 'webContents.0.type' |
| WebContents URL | 'webContents.0.URL' |
example:
```js
const { openProcessManager } = require('electron-process-manager');
openProcessManager({ how: 'descending', path: 'cpu.percentCPUUsage' });
```
## Future
- Add physical memory (noted as "Memory" in Chrome's task manager)
- Add networks metrics
Pull requests welcome :)
## License
MIT License

View File

@@ -0,0 +1,37 @@
{
"name": "@joplin/electron-process-manager",
"version": "2.0.1",
"description": "Process manager UI for Electron applications - Fork with support for @electron/remote",
"main": "src/index.js",
"private": true,
"scripts": {
"build": "webpack"
},
"author": "",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.0",
"bluebird": "^3.7.1",
"bluebird-extra": "^2.0.0",
"electron": "^10.4.7",
"electron-default-menu": "1.0.1",
"filesize": "^5.0.3",
"format-number": "^3.0.0",
"object-path": "^0.11.4",
"prop-types": "^15.5.10",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"spectron": "^12.0.0",
"webpack": "^2.5.1"
},
"peerDependencies": {
"electron": ">= 10"
},
"dependencies": {
"electron-process-reporter": "^1.4.0"
}
}

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Process Manager</title>
<link rel="stylesheet" href="vendor/photon.css">
<style>
.process-table {
/*margin: 10px;*/
}
.process-table-container {
overflow-x: scroll;
flex: 2;
}
</style>
</head>
<body>
<div id="app" class="window"></div>
<script src="dist/ui-bundle.js"></script>
</body>
</html>

View File

@@ -0,0 +1,64 @@
const { EventEmitter } = require('events');
const process = require('process');
const { webContents } = require('electron');
const ProcessManagerWindow = require('./ProcessManagerWindow.js');
const defaultOptions = { defaultSorting: { path: null, how: null } };
class ProcessManager extends EventEmitter {
constructor() {
super();
// legacy
this.openProcessManager = this.open.bind(this);
}
// in case this isn't already done in the app.
//
// No longer needed because caller should setup electron/remote
//
// initializeElectronRemote() {
// return require('@electron/remote/main').initialize();
// }
// We pass the electron/remote/main instance to the manager to ensure it's
// using the same as the main application.
//
// When using a peer dependency it seems the package ends up using its own
// instance, which doesn't work.
open(electronRemote, options = defaultOptions) {
if (this.window) {
this.window.focus();
}
this.window = new ProcessManagerWindow(electronRemote);
this.window.defaultSorting = options.defaultSorting || {};
this.window.showWhenReady();
this.window.on('kill-process', pid => this.killProcess(pid));
this.window.on('open-dev-tools', webContentsId => this.openDevTools(webContentsId));
this.window.on('closed', () => this.window = null);
this.emit('open-window', this.window);
return this.window;
}
killProcess(pid) {
this.emit('will-kill-process', pid, this.window);
process.kill(pid);
this.emit('killed-process', pid, this.window);
}
openDevTools(webContentsId) {
this.emit('will-open-dev-tools', webContentsId, this.window);
const wc = webContents.fromId(webContentsId);
wc.openDevTools({ mode: 'detach' });
this.emit('did-open-dev-tools', webContentsId, this.window);
}
}
module.exports = ProcessManager;

View File

@@ -0,0 +1,78 @@
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const { onExtendedProcessMetrics } = require('electron-process-reporter');
class ProcessManagerWindow extends BrowserWindow {
constructor(electronRemote, options) {
const winOptions = Object.assign({
width: 800,
height: 300,
useContentSize: true,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInSubFrames: true,
nodeIntegrationInWorker: true,
webviewTag: true,
enableRemoteModule: true,
contextIsolation: false,
},
}, options || {});
super(winOptions);
this.options = options;
this.attachProcessReporter();
const indexHtml = `file://${path.join(__dirname, '..', 'process-manager.html')}`;
this.loadURL(indexHtml);
console.info('IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', electronRemote);
electronRemote.enable(this.webContents);
setTimeout(() => {
this.openDevTools();
}, 5000);
}
showWhenReady() {
this.once('ready-to-show', () => {
this.show();
});
}
sendStatsReport(reportData) {
if (!this.webContents) return;
this.webContents.send('process-manager:data', reportData);
}
openDevTools() {
this.webContents.openDevTools();
}
attachProcessReporter() {
this.subscription = onExtendedProcessMetrics(app)
.subscribe(report => this.sendStatsReport(report));
ipcMain.on('process-manager:kill-process', (e, pid) => {
// ignore if not for us
if (!this || this.isDestroyed()) return;
if (e.sender !== this.webContents) return;
this.emit('kill-process', pid);
});
ipcMain.on('process-manager:open-dev-tools', (e, webContentsId) => {
// ignore if not for us
if (!this || this.isDestroyed()) return;
if (e.sender !== this.webContents) return;
this.emit('open-dev-tools', webContentsId);
});
this.on('closed', () => {
if (this.subscription) this.subscription.unsubscribe();
});
}
}
module.exports = ProcessManagerWindow;

View File

@@ -0,0 +1,4 @@
const ProcessManager = require('./ProcessManager.js');
// singleton
module.exports = new ProcessManager();

View File

@@ -0,0 +1,106 @@
import React from 'react';
import { ipcRenderer } from 'electron';
import objectPath from 'object-path';
import ProcessTable from './ProcessTable';
import ToolBar from './ToolBar';
export default class ProcessManager extends React.Component {
constructor(props) {
super(props);
this.state = {
processData: null,
selectedPid: null,
sorting: {
path: null,
how: null
}
};
}
UNSAFE_componentWillMount() {
// TODO: disabled for now - the remote package would need to be passed to this script somehow.
//
// this.setState({ sorting: remote.getCurrentWindow().defaultSorting });
ipcRenderer.on('process-manager:data', (_, data) => {
this.setState({ processData: data });
})
}
canKill() {
if (!this.state.selectedPid) return false;
const pids = this.state.processData.map(p => p.pid);
// verify that select pid is in list of processes
return pids.indexOf(this.state.selectedPid) !== -1;
}
canOpenDevTool() {
return this.canKill() && this.getWebContentsIdForSelectedProcess() !== null;
}
getWebContentsIdForSelectedProcess() {
const { processData, selectedPid } = this.state;
if (!selectedPid) return null;
const process = processData.find(p => p.pid === selectedPid);
if (!process || !process.webContents || process.webContents.length === 0) return null;
return process.webContents[0].id;
}
handleKillProcess() {
const pid = this.state.selectedPid;
if (!pid) return;
ipcRenderer.send('process-manager:kill-process', pid);
}
handleOpenDevTool() {
const webContentsId = this.getWebContentsIdForSelectedProcess();
ipcRenderer.send('process-manager:open-dev-tools', webContentsId);
}
getProcessData() {
const { processData, sorting } = this.state;
if (!sorting.path || !sorting.how) return processData;
return processData.sort((p1, p2) => {
const p1Metric = objectPath.get(p1, sorting.path);
const p2Metric = objectPath.get(p2, sorting.path);
if (p1Metric === p2Metric) return 0;
const comp = p1Metric < p2Metric ? -1 : 1;
return sorting.how == 'ascending' ? comp : -comp;
});
}
render () {
const { processData } = this.state;
if (!processData) return (<span>No data</span>);
return (
<div className="window">
<header className="toolbar toolbar-header">
<ToolBar
disableKill={!this.canKill()}
onKillClick={this.handleKillProcess.bind(this)}
disabelOpenDevTool={!this.canOpenDevTool()}
onOpenDevToolClick={this.handleOpenDevTool.bind(this)}
/>
</header>
<div className="process-table-container">
<ProcessTable
processData={this.getProcessData()}
selectedPid={this.state.selectedPid}
sorting={this.state.sorting}
onSortingChange={sorting => this.setState({ sorting })}
onSelectedPidChange={pid => this.setState({ selectedPid: pid })}
/>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,75 @@
import React from 'react';
import PropTypes from 'prop-types';
import filesize from 'filesize';
import format from 'format-number';
const KB = 1024;
const formatPercentage = format({
round: 1,
padRight: 1
});
export default class ProcessRow extends React.Component {
static propTypes = {
pid: PropTypes.number,
type: PropTypes.string,
memory: PropTypes.shape({
peakWorkingSetSize: PropTypes.number,
workingSetSize: PropTypes.number
}),
cpu: PropTypes.shape({
percentCPUUsage: PropTypes.number,
idleWakeupsPerSecond: PropTypes.number
}),
webContents: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number,
type: PropTypes.string,
URL: PropTypes.string,
URLDomain: PropTypes.string
})),
selected: PropTypes.bool,
onSelect: PropTypes.func
}
render() {
const { webContents, memory } = this.props;
if (!webContents || webContents.length === 0) {
return (
<tr
className={this.props.selected ? 'selected': ''}
onClick={this.props.onSelect}
>
<td>{this.props.pid}</td>
<td></td>
<td>{this.props.type}</td>
<td>{memory ? filesize(memory.workingSetSize*KB) : 'N/A'}</td>
<td>{formatPercentage(this.props.cpu.percentCPUUsage)}</td>
<td>{this.props.cpu.idleWakeupsPerSecond}</td>
<td></td>
<td></td>
<td></td>
</tr>
)
} else {
// FIX ME: we consider we have only have 1 webContents per process
const wc = webContents[0];
return (
<tr
className={this.props.selected ? 'selected': ''}
onClick={this.props.onSelect}
>
<td>{this.props.pid}</td>
<td>{wc.URLDomain}</td>
<td>{this.props.type}</td>
<td>{memory ? filesize(memory.workingSetSize*KB) : 'N/A'}</td>
<td>{formatPercentage(this.props.cpu.percentCPUUsage)}</td>
<td>{this.props.cpu.idleWakeupsPerSecond}</td>
<td>{wc.id}</td>
<td>{wc.type}</td>
<td>{wc.URL}</td>
</tr>
)
}
}
}

View File

@@ -0,0 +1,94 @@
import React from 'react';
import PropTypes from 'prop-types';
import ProcessRow from './ProcessRow';
import ProcessTableHeader from './ProcessTableHeader';
export default class ProcessTable extends React.Component {
static propTypes = {
processData: PropTypes.arrayOf(PropTypes.object),
selectedPid: PropTypes.number,
sorting: PropTypes.PropTypes.shape({
path: PropTypes.string,
how: PropTypes.string
}),
onSortingChange: PropTypes.func,
onSelectedPidChange: PropTypes.func
}
render() {
return (
<table className="process-table table-striped">
<thead>
<tr>
<ProcessTableHeader
path='pid'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>Pid</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.URLDomain'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>WebContents Domain</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.type'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>Process Type</ProcessTableHeader>
<ProcessTableHeader
path='memory.workingSetSize'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>Working Set Size</ProcessTableHeader>
<ProcessTableHeader
path='cpu.percentCPUUsage'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>% CPU</ProcessTableHeader>
<ProcessTableHeader
path='cpu.idleWakeupsPerSecond'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>Idle Wake Ups /s</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.id'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>WebContents Id</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.type'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>WebContents Type</ProcessTableHeader>
<ProcessTableHeader
path='webContents.0.URL'
sorting={this.props.sorting}
onSortingChange={this.props.onSortingChange}
>WebContents URL</ProcessTableHeader>
</tr>
</thead>
<tbody>
{
this.props.processData.map(p =>
<ProcessRow
key={p.pid}
{...p}
onSelect={() => this.props.onSelectedPidChange(p.pid)}
selected={this.props.selectedPid === p.pid}
/>
)
}
</tbody>
</table>
)
}
}

View File

@@ -0,0 +1,59 @@
import React from 'react';
import PropTypes from 'prop-types';
export default class ProcessTableHeader extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
static propTypes = {
children: PropTypes.node,
path: PropTypes.string.isRequired,
sorting: PropTypes.PropTypes.shape({
path: PropTypes.string,
how: PropTypes.string
}),
onSortingChange: PropTypes.func
}
getSortCharacter() {
if (!this.sortHow) return (
<span>&nbsp;</span>
);
return this.sortHow == 'ascending' ? '👆' : '👇'
}
get sortHow() {
if (!this.props.sorting) return null;
if (this.props.sorting.path == this.props.path){
return this.props.sorting.how;
}
return null;
}
handleClick() {
let nextSortHow = null;
if(this.sortHow === null) {
nextSortHow = 'ascending';
} else if (this.sortHow === 'ascending') {
nextSortHow = 'descending';
} else {
nextSortHow = null;
}
this.props.onSortingChange({
path: this.props.path,
how: nextSortHow
});
}
render() {
return (
<th onClick={this.handleClick}>
{this.props.children}
&nbsp;
{this.getSortCharacter()}
</th>
)
}
}

View File

@@ -0,0 +1,35 @@
import React from 'react';
import PropTypes from 'prop-types';
export default class ToolBar extends React.Component {
static propTypes = {
onKillClick: PropTypes.func,
disableKill: PropTypes.bool,
onOpenDevToolClick: PropTypes.func,
disabelOpenDevTool: PropTypes.bool
}
render() {
return (
<div className="toolbar-actions">
<div className="btn-group">
<button
className="btn btn-default"
disabled={this.props.disableKill}
onClick={this.props.onKillClick}
>
End process
</button>
<button
className="btn btn-default"
disabled={this.props.disabelOpenDevTool}
onClick={this.props.onOpenDevToolClick}
>
Open Dev Tool
</button>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,6 @@
import React from 'react';
import { render } from 'react-dom';
import ProcessManager from './ProcessManager';
render(<ProcessManager/>, document.getElementById('app'));

View File

@@ -0,0 +1,30 @@
const Application = require('spectron').Application;
const { join } = require('path');
const assert = require('assert');
const app = new Application({
env: { TEST_PROCESS_MANAGER: 1 },
path: require(join(__dirname, '../node_modules/electron')),
args: [join(__dirname, '../example/main.js')],
});
(async () => {
try {
await app.start();
await app.client.waitUntilWindowLoaded();
await app.electron.ipcRenderer.send('open-process-manager');
// This looks to be incorrect signature for assert.
// assert(app.client.getWindowCount(), 2);
// There are 2 webviews on the index page. They are included in windowCount, so it's 4, not 2.
assert.equal(await app.client.getWindowCount(), 4);
await app.client.switchWindow(/process-manager\.html/);
await (await app.client.$('#app .process-table')).waitForDisplayed({ timeout: 60000 });
await app.stop();
} catch (error) {
console.error('Test failed', error);
if (app && app.isRunning()) {
await app.stop();
process.exit(1);
} else { process.exit(1); }
}
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
const webpack = require('webpack');
const path = require('path');
const BUILD_DIR = path.resolve(__dirname, 'dist');
const config = {
entry: path.resolve(__dirname, 'src/ui/index.js'),
// mode: 'development',
devtool: 'eval-source-map',
output: {
path: BUILD_DIR,
filename: 'ui-bundle.js',
},
module: {
loaders: [
{
test: /\.(jsx|js)?$/,
loader: 'babel-loader',
include: path.resolve(__dirname, 'src/ui'),
},
],
},
target: 'electron-renderer',
};
module.exports = config;

View File

@@ -1,7 +0,0 @@
Copyright (c) 2017-2021 Laurent Cozic
Personal Use License
Joplin Server is available for personal use only. For example you may host the software on your own server for non-commercial activity.
To obtain a license for commercial purposes, please contact us.

282
packages/server/LICENSE.md Normal file
View File

@@ -0,0 +1,282 @@
**JOPLIN SERVER PERSONAL USE LICENSE**
v.1: 21 December 2021
This Joplin Server Personal Use License (the "**License**") is a legally binding
agreement between Cozic Ltd. registered under the laws of England and Wales
(the "**Licensor**"), the owner of the server entitled ‘Joplin Server,’ the
related software applications, and services (collectively, the "**Software**")
and an individual user accessing the Software (the "**Licensee**") (each
individually a "**Party**" and collectively, the "**Parties**").
# 1. Acceptance of the License
* **1.1** By accessing or otherwise using the Software, the Licensee confirms
that the Licensee has read the License, accepts the terms of the License, and
agrees to become legally bound by the License.
* **1.2** If the Licensee is entering into the License on behalf of a legal
entity, the Licensee represents that the Licensee has the authority and the
necessary capacity to bind such entity and its affiliates to the terms of the
License.
* **1.3** If the Licensee does not have the authority specified in section 1.2
or if the Licensee does not agree with one or more provisions of the License,
the Licensee is not allowed to access the Software and the Licensee must not
accept the License.
* **1.4** By using the Software, the Licensee acknowledges, agrees, and warrants
that the Licensee:
* i. Shall comply with the terms of the License and all applicable local,
state, national and foreign laws, treaties, and regulations;
* ii. Shall provide only true, accurate, complete, and up-to-date
information; and
* iii. Has the capacity to conclude legally binding contracts with the
Licensor.
# 2. Grant of RIGHTS
* **2.1** The Licensor hereby grants the Licensee a worldwide, non-exclusive,
royalty free, and revocable license to access and use the Software in
accordance with the terms of this License and the applicable laws.
* **2.2** Subject to the terms of the License, the Licensee is entitled to
access and use the Software for non-commercial purposes for taking personal
and professional notes, sharing notes with other users of the Software, and
synchronising data.
* **2.3** Unless authorised by the Licensor in writing, the Software may be used
for personal non-commercial purposes only. The Licensee is allowed to grant
access to the Software to others for non-commercial purposes, provided that
(i) the Licensee is not a business entity, (ii) the Licensee or the third
party to which the access is granted does not use the Software to generate
profits of any kind, and (iii) the Software is used for non-commercial
purposes only. The Licensee is not allowed to: (i) use the Software for
commercial purposes and (ii) grant others the right to use the Software for a
fee or for any commercial purposes, including, without limitation, copying,
reproducing, publishing, transmitting, transferring, selling, renting,
modifying, creating derivative works from, distributing, reposting,
performing, displaying, or in any other way commercially exploiting the
Software without prior written authorisation from the Licensor. By way of
illustration, the Licensee is not allowed to:
1. Install the Software on Licensee’s infrastructure and charge others for
the use of the Software;
2. Install the Software behind a proxy and charge others for the use of the
Software;
3. Install the Software on a Licensee’s or third party’s server and provide
access to the Software to third parties (a) for a fee or (b) free of
charge, if the said third parties may use the Software for commercial
purposes;
4. Grant access to the Software to others for a fee or for any commercial
purposes;
5. Grant access to the Software to others for a fee or free of charge if
the Licensee is a legal or natural person engaged in commercial
activities; or
6. Use the Software or grant other the right to use the Software in a way
that generates income or commercial profits.
* **2.4** This License does not govern any commercial use of the Software, as
authorised by the Licensor.
* **2.5** The Software is licensed and not sold. By accepting the License, the
Licensee obtains the right to use the Software and not the ownership of the
Software.
* **2.6** The Licensor reserves any rights not expressly granted to the
Licensee under this License.
* **2.7** The Licensee is allowed to make a reasonable number of copies of the
Software, as and if necessary for the purposes set forth herein, provided that
only complete copies of the Software are made, including without limitation
all ‘read me’ files, copyright notices, and other legal notices and terms
included in the Software.
* **2.8** The Licensee is permitted to load and run the Software on any device,
network or cloud virtual machines under Licensee’s control (collectively, the
"**Devices**"), if such Devices are compatible with the Software. The Licensee
is solely responsible for assessing the compatibility of the Devices to be
used with the Software.
* **2.9** It is Licensee’s sole responsibility to verify and assess the
suitability, validity and integrity of the Software prior to using it and to
decide whether or not the Software fits for the intended use.
* **2.10** The Licensor reserves the right to grant the right to use the
Software to third parties.
* **2.11** The Licensee acknowledges and agrees that any use of the Software
that is prohibited by this License may be unlawful and may result in
Licensee’s criminal liability.
# 3. Covered Software and services
* **3.1** The License applies only to the Software as provided to the Licensee
by the Licensor. The License also applies to updates, supplements, and support
services related to the Software, or any other services provided in relation
to the Software, unless other terms and conditions have been provided thereto.
* **3.2** Any software or services that are not provided by the Licensor are
not covered by this License. Such Software and services are subject to the
terms and conditions set by the respective third party and the Licensee is
solely responsible for obtaining, agreeing to, and complying with the
respective terms and conditions at its own cost and expense.
* **3.3** The Licensor reserves the right, but is not under any obligation, to
provide paid or free-of-charge updates and technical support services with
regard to the Software, including fixing bugs and errors, and the possibility
to use new versions of the Software.
# 4. Intellectual property and ownership
* **4.1** All title and copyright in and to the Software (including, but not
limited to, any source code, images, graphics, photographs, animations, video,
audio, music, text, and applets, incorporated in the Software) are owned by
the Licensor. The Software is protected by the English copyright laws and
international treaties. The Licensee is not allowed to incorporate any portion
of the Software into other programs or compile any portion of it in
combination with other programs, or otherwise copy (except to exercise rights
granted in this License), modify, create derivative works of, distribute,
assign any rights to, or license the Software in whole or in part.
* **4.2** The Licensee is not permitted to, without obtaining prior written
authorisation from the Licensor, to use the trade names, trademarks, service
marks or product names of the Licensor, except as required for the use of the
Software.
* **4.3** The Licensor has made all efforts possible to avoid the Software
being subject to the rights of third parties, in particular that its use does
not infringe patents, copyrights or other intellectual property rights of
third parties. However, the Licensor does not guarantee that the Software is
not subject to the rights of third parties. The Licensee agrees to notify the
Licensor immediately and in writing if any third party asserts an infringement
claim against the Licensee in connection with the Software.
* **4.4** By submitting any content through the Software (the "Licensee’s
Content"), the Licensee grants the Licensor unrestricted, sub-licensable,
royalty-free, perpetual, and irrevocable rights to process the Licensee’s
Content for the purposes of providing the Licensee with the Software and
carrying out Licensor’s legitimate business interests.
* **4.5** The Licensee is not permitted to remove any copyright or other
proprietary notices and legends.
# 5. PRIVACY AND DATA PROTECTION
* **5.1** The Licensor does not have access in any manner to the Licensee’s
Content. Therefore, the Licensee is solely responsible for creating, keeping
and maintaining any backup copies of any Licensee’s Content or other
information submitted to, through, or in relation to the Software.
The Parties agree to individually comply with the applicable data protection
laws pertaining to the Software.
# 6. AVAILABILITY
* **6.1** The availability of the Software may be affected by factors, which
the Licensor cannot reasonably control, such as bandwidth problems, equipment
failure, acts and omissions of our third-party service providers, or *force
majeure* events. The Licensor takes no responsibility for the unavailability
of the Software caused by such factors.
# 7. Limitation of liability and disclaimer of warranties
* **7.1** To the extent permitted by the applicable law, the Licensor expressly
disclaims all warranties, express or implied, for the Software. The Licensor
provides the Software on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied, including, without limitation, any
warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
FITNESS FOR A PARTICULAR PURPOSE. The entire risk arising out of use or
performance of the Software remains with the Licensee.
* **7.2** In no event shall the Licensor be liable for any damages whatsoever
(including, without limitation, damages for loss of business profits, business
interruption, loss of business information, or any other pecuniary loss)
arising out of the Licensee’s Content, the use of or inability to use the
Software, even if the Licensor has been advised of the possibility of such
damages.
* **7.3** The Licensee is solely responsible for determining the
appropriateness of the Software and assumes any risks associated with
Licensee’s exercise of permissions under the License.
* **7.4** Third-party content or services are not covered by this License. The
Licensee shall ensure Licensee’s compliance with any terms set forth by the
respective third parties at its own risk, cost and expense. To the maximum
extent permitted by law, the Licensor excludes any liability for any loss or
damage resulting from the acts and omissions of such third-party service
providers.
# 8. Governing Law AND DISPUTE RESOLUTION
* 8.1 This License and any disputes arising out of or in connection with the
License and the Software shall be governed by and construed in accordance with
the laws of England and Wales. Unless otherwise provided by the applicable
law, all disputes arising out of or in connection with the License shall be
submitted to the exclusive jurisdiction of the courts in London, the United
Kingdom.
# 9. FINAL PROVISIONS
* **9.1** **Indemnification.** The Licensee shall indemnify the Licensor at
Licensee’s expense if any claims are asserted by a third party against the
Licensor by reason of Licensee’s misconduct or breach of any terms of the
License, including failed adherence by the Licensee with any applicable laws,
including, whether express or implied.
* **9.2** **Severability.** The unenforceability of any single provision of
this License shall not affect any other provision hereof. Where such a
provision is held to be unenforceable, the Parties shall use their best
endeavours to negotiate and agree upon an enforceable provision, which
achieves, to the greatest extent possible, the economic, legal and commercial
objectives of the unenforceable provision.
* **9.3** **Waiver.** A failure of either Party to enforce strictly a provision
of this License shall in no event be considered a waiver of any part of such
provision. No waiver by either Party of any breach or default by the other
party shall operate as a waiver of any succeeding breach or other default or
breach by such other Party. No waiver shall have any effect unless it is
specific, irrevocable and in writing.
* **9.4** **Term and termination.** The License shall commence upon Licensee’s
access to the Software and continue until terminated by the Licensor. Upon
termination of the License, the Licensee agrees to (i) stop all access and use
of the Software and (ii) destroy all copies of the Software and all its
component parts (if any) stored on the Devices. The provisions of the License
that, by their nature, continue and survive will survive any termination of
the License.
* **9.5** **Amendments.** The Licensor reserves the right, at its sole
discretion, to change or modify this License at any time by sending a prior
notification to the Licensee (if the contact details of the Licensee are
available to the Licensor). Any modifications to the License shall become
effective on the date indicated at the top of the amended License. By
continuing to use the Software after the date on which the modifications were
communicated, the Licensee agrees to be bound by the modified License. The
Licensor reserves the right to change or discontinue the Software and any
feature thereof with or without a prior notice to the Licensee.
* **9.6** **Entire agreement.** This License constitutes the entire
understanding between the Parties with respect to the subject matter thereof
and supersedes all prior agreements, negotiations and discussions between the
Parties relating thereto.
* **9.7** **Transfer of rights.** The Licensee is not allowed to assign
Licensee’s rights under the License. The Licensor is entitled to transfer its
rights and obligations under the License entirely or partially to a third
party by giving a prior notice to the Licensee. If the Licensee does not agree
to the transfer, the Licensee can terminate this License by ceasing to use the
Software.
* **9.8** **Contact.** For general enquiries and commercial licenses to use the
Software, please contact the Licensor directly.
***

View File

@@ -157,10 +157,4 @@ From `packages/server`, run `npm run start-dev`
# License
Copyright (c) 2017-2021 Laurent Cozic
Personal Use License
Joplin Server is available for personal use only. For example you may host the software on your own server for non-commercial activity.
To obtain a license for commercial purposes, please contact us.
See LICENSE.md in this directory

View File

@@ -15,6 +15,8 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.4.2\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
#: packages/app-mobile/components/screens/ConfigScreen.tsx:565
msgid "- Camera: to allow taking a picture and attaching it to a note."
@@ -33,8 +35,8 @@ msgid ""
"- Storage: to allow attaching files to notes and to enable filesystem "
"synchronisation."
msgstr ""
"- Lagring: för att tillåta att du bifoga filer till anteckningar och för att "
"aktivera filsystemsynkronisering."
"- Lagring: för att tillåta att du bifogar filer till anteckningar och för "
"att aktivera filsystemsynkronisering."
#: packages/lib/services/KeymapService.ts:308
#: packages/lib/services/KeymapService.ts:314
@@ -844,6 +846,8 @@ msgid ""
"Could not verify the share status of this notebook - aborting. Please try "
"again when you are connected to the internet."
msgstr ""
"Det gick inte att verifiera delningsstatusen för den här anteckningsboken - "
"avbryter. Vänligen försök igen när du är ansluten till internet."
#: packages/app-mobile/components/note-list.js:101
msgid "Create a notebook"
@@ -1536,7 +1540,7 @@ msgstr "Exportera felsökningsrapport"
#: packages/app-mobile/components/screens/ConfigScreen.tsx:528
msgid "Export profile"
msgstr "Exportera profilen"
msgstr "Exportera profil"
#: packages/app-mobile/components/screens/ConfigScreen.tsx:528
msgid "Exporting profile..."
@@ -1657,7 +1661,7 @@ msgstr ""
#: packages/lib/models/Setting.ts:568
msgid "Force path style"
msgstr ""
msgstr "Tvinga sökvägsstil"
#: packages/lib/commands/historyForward.ts:6
msgid "Forward"
@@ -1726,7 +1730,6 @@ msgid "Heading"
msgstr "Rubrik"
#: packages/server/src/services/MustacheService.ts:201
#, fuzzy
msgid "Help"
msgstr "Hjälp"
@@ -1748,7 +1751,7 @@ msgstr "Markera"
#: packages/server/src/services/MustacheService.ts:196
msgid "Home"
msgstr ""
msgstr "Hem"
#: packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts:78
msgid "Horizontal Rule"
@@ -1961,9 +1964,8 @@ msgid "Invalid option value: \"%s\". Possible values are: %s."
msgstr "Ogiltigt inställningsvärde: \"%s\". Möjliga värden är: %s."
#: packages/app-cli/app/command-e2ee.ts:46
#, fuzzy
msgid "Invalid password"
msgstr "Ogiltigt svar: %s"
msgstr "Ogiltigt lösenord"
#: packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts:38
msgid "Italic"
@@ -2192,7 +2194,7 @@ msgstr "Logga in med OneDrive"
#: packages/server/src/services/MustacheService.ts:202
msgid "Logout"
msgstr ""
msgstr "Logga ut"
#: packages/app-desktop/gui/MenuBar.tsx:705
#: packages/app-mobile/components/screens/ConfigScreen.tsx:583
@@ -2521,9 +2523,8 @@ msgid "Note&book"
msgstr "Antecknings&bok"
#: packages/lib/models/Setting.ts:2143
#, fuzzy
msgid "Notebook"
msgstr "Anteckningsböcker"
msgstr "Anteckningsbok"
#: packages/lib/models/Setting.ts:1378
msgid "Notebook list growth factor"
@@ -2549,9 +2550,8 @@ msgstr ""
#: packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderField.ts:8
#: packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderReverse.ts:9
#, fuzzy
msgid "Notes"
msgstr "Anteckning"
msgstr "Anteckningar"
#: packages/lib/models/Setting.ts:2159
msgid "Notes and settings are stored in: %s"
@@ -3034,31 +3034,27 @@ msgstr "Kör kommandona i textfilen. Det ska finnas ett kommando per rad."
#: packages/lib/SyncTargetAmazonS3.js:28
msgid "S3"
msgstr ""
msgstr "S3"
#: packages/lib/models/Setting.ts:546
#, fuzzy
msgid "S3 access key"
msgstr "AWS-nyckel"
msgstr "S3 åtkomstnyckel"
#: packages/lib/models/Setting.ts:506
#, fuzzy
msgid "S3 bucket"
msgstr "AWS S3"
msgstr "S3 bucket"
#: packages/lib/models/Setting.ts:535
msgid "S3 region"
msgstr ""
#: packages/lib/models/Setting.ts:557
#, fuzzy
msgid "S3 secret key"
msgstr "AWS-hemlighet"
msgstr "S3 hemlig nyckel"
#: packages/lib/models/Setting.ts:521
#, fuzzy
msgid "S3 URL"
msgstr "AWS S3 URL"
msgstr "S3 URL"
#: packages/app-desktop/gui/MainScreen/MainScreen.tsx:579
msgid ""
@@ -3224,9 +3220,8 @@ msgid "Show note counts"
msgstr "Visa anteckningsantal"
#: packages/lib/models/Setting.ts:866
#, fuzzy
msgid "Show sort order buttons"
msgstr "Visa anteckningsantal"
msgstr "Visa sorteringsordningsknappar"
#: packages/lib/models/Setting.ts:1042
msgid "Show tray icon"
@@ -3557,7 +3552,7 @@ msgstr "Ta ett foto"
#: packages/server/src/services/MustacheService.ts:200
msgid "Tasks"
msgstr ""
msgstr "Uppgifter"
#: packages/lib/models/Setting.ts:1228
msgid "Text editor command"
@@ -4035,9 +4030,8 @@ msgid "Toggle note list"
msgstr "Växla anteckningslista"
#: packages/app-desktop/gui/MainScreen/commands/togglePerFolderSortOrder.ts:7
#, fuzzy
msgid "Toggle own sort order"
msgstr "Växla säkert läge"
msgstr "Växla egen sorteringsordning"
#: packages/app-desktop/commands/toggleSafeMode.ts:8
msgid "Toggle safe mode"
@@ -4048,9 +4042,8 @@ msgid "Toggle sidebar"
msgstr "Växla sidofältet"
#: packages/app-desktop/gui/MainScreen/commands/toggleNotesSortOrderField.ts:7
#, fuzzy
msgid "Toggle sort order field"
msgstr "Växla säkert läge"
msgstr "Växla sorteringsordningsfält"
#: packages/app-desktop/gui/ClipperConfigScreen.min.js:40
#: packages/app-desktop/gui/ClipperConfigScreen.tsx:41
@@ -4257,7 +4250,7 @@ msgstr ""
#: packages/server/src/services/MustacheService.ts:197
msgid "Users"
msgstr ""
msgstr "Användare"
#: packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.tsx:182
msgid "Valid"
@@ -4411,11 +4404,12 @@ msgid "Your master password is needed to decrypt some of your data."
msgstr "Ditt huvudlösenord behövs för att dekryptera några av dina data."
#: packages/app-cli/app/command-sync.ts:242
#, fuzzy
msgid ""
"Your password is needed to decrypt some of your data. Type `:e2ee decrypt` "
"to set it."
msgstr "Ditt huvudlösenord behövs för att dekryptera några av dina data."
msgstr ""
"Ditt lösenord behövs för att dekryptera en del av dina data. Skriv `:e2ee "
"decrypt` för att ställa in det."
#: packages/app-mobile/components/CameraView.tsx:189
msgid "Your permission to use your camera is required."

View File

@@ -21,3 +21,7 @@ If you exceed the storage space, you will not be able to upload new notes. You m
We offer a 50% Education Discount for students and teachers. To claim it, please [contact us](mailto:support@joplincloud.com) from your university or school email address. You will then receive a URL you can use to subscribe to Joplin Cloud while benefiting from the 50% discount. This is valid for a whole year and can be renewed for as long as you are in education by contacting us again.
We may also offer bulk discounts for companies, associations and nonprofit organisations. Please [contact us](mailto:support@joplincloud.com) for more details.
## Can I get a refund for my subscription?
Unless you were accidentally charged due to an error, your subscription cannot be refunded. You can cancel it at any time however, which will stop any further charges. We also give a 14 days trial when the subscription starts so that you can evaluate the service and potentially change your mind - if you cancel during that period you will not be charged.

View File

@@ -19,7 +19,7 @@ Length | 6 chars (Hexa string)
Encryption method | 2 chars (Hexa string)
Master key ID | 32 chars (Hexa string)
See `lib/services/EncryptionService.js` for the list of available encryption methods.
See `lib/services/e2ee/EncryptionService.ts` for the list of available encryption methods.
### Data chunk
@@ -32,7 +32,7 @@ Data | ("Length" bytes) (ASCII)
## Master Keys
The master keys are used to encrypt and decrypt data. They can be generated from the Encryption Service and are saved to the database. They are themselves encrypted via a user password using a [strong encryption method](https://github.com/laurent22/joplin/blob/f21199a7f38b43d1f350ee81f84d4f335cb285b3/packages/lib/services/EncryptionService.js#L374).
The master keys are used to encrypt and decrypt data. They can be generated from the Encryption Service and are saved to the database. They are themselves encrypted via a user password using a [strong encryption method](https://github.com/laurent22/joplin/blob/b5b02d8d7bce2c07c89fef50103e1399d792b75e/packages/lib/services/e2ee/EncryptionService.ts#L373).
These encrypted master keys are transmitted with the sync data so that they can be available to each client. Each client will need to supply the user password to decrypt each key.

2432
yarn.lock

File diff suppressed because it is too large Load Diff