Merge branch 'staging' into newShare

pull/1/head
yflory 5 years ago
commit 2807ae0fb0

@ -1,3 +1,62 @@
# UplandMoa (3.20.0)
## Goals
We've held off on deploying any major features while we work towards deploying some documentation we've been busy organizing. This release features a wide range of minor features intended to address a number of github issues and frequent causes of support tickets.
## Update notes
This release features a modification to the recommended Content Security Policy headers as demonstrated in `./cryptpad/docs/example.nginx.conf`. CryptPad will work without making this change, however, we highly recommend updating your instance's nginx.conf as it will mitigate a variety of potential security vulnerabilities.
Otherwise, we've introduced a new client-side dependency (_Mathjax_) and changed some server-side code that will require a server restart.
To update from 3.19.1 to 3.20.0:
1. Apply the recommended changes to your `nginx.conf`
2. Stop your server
3. Get the latest platform code with git
4. Install client-side dependencies with `bower update`
5. Reload nginx to apply the updated CSP headers
6. Restart the CryptPad API server
## Features
* As noted above, this release features a change to the Content Security Policy headers which define the types of code that can be loaded in a given context. More specifically, we've addressed a number of CKEditor's quirks which required us to set a more lax security policy for the rich text editor. With these changes in place the only remaining exceptions to our general policy are applied for the sake of our OnlyOffice integration, though we hope to address its quirks soon as well.
* On the topic of the rich text editor, we also moved the _print_ action from the CKEditor toolbar to the _File_ menu to be more consistent with our other apps.
* The Kanban board that we use to organize our own team has become rather large and complex due to a wealth of long-term ideas and a large number of tags. We started to notice some performance issues as a result, and have begun looking into some optimizations to improve its scalability. As a start, we avoid applying changes whenever the Kanban's tab is not visible.
* We finally decided to file off one of the platform's rough edges which had been confusing curious users for some time. Every registered user is identified by a randomly-generated cryptographic key (the _Public Signing Key_ found on your settings page). These identifiers are used to allocate additional storage space via our premium accounts, and we occasionally require them for other support issues like deleting accounts or debugging server issues. Unfortunately, because we occasionally receive emails asking for help with _other administrators instances_ these keys were formatted along with the host domain in the form of a URL. As such, it was very tempting to open them in the browser even though there was no functionality corresponding to the URL. We've updated all the code that parses these keys and introduced a new format which is clearly _not a URL_, so hopefully we'll get fewer messages asking us why they _don't work_.
* We've made a number of small improvements to the common functionality in our code and slide editors:
* We've merged and built upon a pull request which implemented two new extensions to our markdown renderer for _Mathjax_ and _Markmap_. This introduces support for embedding formatted equations and markdown-based mind maps. Since these depend on new client-side code which would otherwise increase page loading time we've also implemented support for lazily loading extensions on demand, so you'll only load the extra code if the current document requires it.
* The _slide_ editor now throttles slide redraws so that updates are only applied after 400ms of inactivity rather than on every character update.
* We've made a number of small style tweaks for blockquotes, tables, and embedded media in rendered markdown.
* Lastly, we've made a large number of improvements to user and team drives:
* Search results now include shared folders with matching names and have been made _sortable_ like the rest of the drive.
* Inserting media in a document via the _Insert_ menu now updates its access time, which causes it to show up in the _Recent pads_ category of your drive.
* Shared folders now support access lists. To apply an access list to a shared folder that you own you may right-click the shared folder in your drive, choose _Access_, then click the _List_ tab of the resulting dialog. Enabling its access list will restrict access to its owners and any other contacts that you or other owners add to its list. Note, this access applies to the folder itself (who can view it or add to its directory), its access list will not be applied recursively to all the elements contained within which might be contained in other shared folders or other users drives.
* In the interest of removing jargon from the platform we've started to change text from "Delete from the server" to "Destroy". We plan to make more changes like this on an ongoing basis as we notice them.
* We've made a significant change to the way that _owned files_ are treated in the user and team drives. Previously, files that you owned were implicitly deleted from the server whenever you removed them from your drive. This seemed sensible when we first introduced the concept of ownership, however, now that a variety of assets can have multiple owners it is clearly less appropriate. Rather than require users to first remove themselves as a co-owner before removing an asset from their drive in order to allow other owners to continue accessing it we now offer two distinct _Remove_ and _Destroy_ actions. _Remove_ will simply take it out of your drive so that it will no longer count against your storage limit, while _Destroy_ will cause it to stop existing _for everyone_. To clarify the two actions we've associated them with a _trash bin_ and _paper shredder_ icon, respectively.
## Bug fixes
* Remote changes in the Kanban app removed pending text in new cards, effectively making it impossible (and very frustrating) to create new cards while anyone else was editing existing content or submitting their own new cards.
* Dropping an image directly into a spreadsheet no longer puts the UI into an unrecoverable state, though we still don't support image drop. To insert images, use the "Insert" menu. This was actually fixed in our 3.19.1 release, but it wasn't documented in the release notes.
* When a user attempted to open an automatically expiring document which had passed its expiration date they were shown a general message indicating that the document had been deleted even when they had sufficient information to know that it had been marked for expiration. We now display a message indicating the more likely cause of its deletion.
* We've spent some time working on the usability of comments in our rich text app:
* When a user started adding a first comment to a document then canceled their action it was possible for the document to get stuck in an odd layout. This extra space allocated towards comments now correctly collapses as intended when there are no comments, pending or otherwise.
* The comments UI is now completely disabled whenever the document is in read-only mode, whether due to disconnection or insufficient permissions.
* The _comment_ button in the app toolbar now toggles on and off to indicate the eligibility of the current selection as a new comment.
* We've fixed a number of issues with teams:
* Users no longer send themselves a notification when they remove themself as an owner of a pad from within the _Teams_ UI.
* The _worker_ process which is responsible for managing account rights now correctly upgrades and downgrades its internal state when its role within a team is changed by a remote user instead of requiring a complete worker reload.
* The worker does not delete credentials to access a team when it finds that its id is not in the team's roster, since this could be triggered accidentally by some unrelated server bugs that responded incorrectly to a request for the team roster's history.
* We've fixed a number of issues in our code and slide editors:
* The "Language" dropdown selectors in the "Theme" menu used to show "Language (Markdown)" when the page was first loaded, however, changing the setting to another language would drop the annotation and instead show only "Markdown". Now the annotation is preserved as intended.
* A recent update to our stylesheets introduced a regression in the buttons of our "print options" dialog.
* While polishing up the PRs which introduced the _Mathjax_ and _Markmap_ support we noticed that the client-side cache which is used to prevent unnecessary redraws of embedded media was causing only one instance of an element to be rendered when the same source was embedded in multiple sections of a document.
* The "File export" dialog featured a similar regression in the style of its buttons which has been addressed.
* We fixed a minor bug in our 3.19.0 release in which unregistered users (who do not have a "mailbox") tried to send a notification to themselves.
* We've added an additional check to the process for changing your account password in which we make sure that we are not overwriting another account with the same username and password.
# Thylacine's revenge (3.19.1)
Our upcoming 3.20.0 release is planned for July 7th, 2020, but we are once again releasing a minor version featuring some nice bug fixes and usability improvements which are ready to be deployed now. In case you missed [our announcement](https://social.weho.st/@cryptpad/104360490068671089) we are phasing out our usage of the `master` and basing our releases on the `main` branch. For best results we recommend explicitly checking out code by its tag.

@ -62,7 +62,7 @@ define([
var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ?
'/imprint.html' : AppConfig.imprint);
Pages.versionString = "CryptPad v3.19.1 (Thylacine's revenge)";
Pages.versionString = "CryptPad v3.20.0 (UplandMoa)";
// used for the about menu
Pages.imprintLink = AppConfig.imprint ? footLink(imprintUrl, 'imprint') : undefined;

@ -317,6 +317,7 @@
padding: 0 10px;
border: 0;
color: lighten(@colortheme_sidebar-left-fg, 40%);
height: auto;
}
& > span.cp-app-drive-element-row {
overflow: hidden;
@ -575,6 +576,7 @@
border-radius: 0;
border: 1px solid #ddd;
font-size: 14px;
height: auto;
}
.cp-app-drive-element-state {
position: absolute;
@ -638,6 +640,7 @@
padding: 0 4px;
flex: 1;
min-width: 0;
height: auto;
}
&> span {

2
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "cryptpad",
"version": "3.19.1",
"version": "3.20.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "3.19.1",
"version": "3.20.0",
"license": "AGPL-3.0+",
"repository": {
"type": "git",

@ -105,6 +105,12 @@
.markdown_preformatted-code;
.markdown_gfm-table(black);
table {
margin-bottom: 1rem;
}
media-tag > * {
margin-bottom: 1rem;
}
}
.cp-splitter {

@ -30,7 +30,6 @@ define([
var Mermaid = {
__stubbed: true,
init: function () {
var args = Util.slice(arguments);
require([
'mermaid',
'css!/code/mermaid-new.css'
@ -44,7 +43,6 @@ define([
});
}
Mermaid.init.call(args);
pluginLoaded.fire();
});
}
@ -88,7 +86,7 @@ define([
};
var sfCommon;
var fixMathjaxClickables = function ($svg) {
var fixMarkmapClickables = function ($svg) {
// find all links in the tree and do the following for each one
var onClick = function (e) {
e.preventDefault();
@ -132,7 +130,7 @@ define([
var data = MarkMapTransform.transform($el[0].getAttribute("markmap-source"));
$el[0].innerHTML = "<svg width='100%' height='600'/>";
Markmap.markmap($el[0].firstChild, data);
fixMathjaxClickables($el);
fixMarkmapClickables($el);
};
var highlighter = function () {
@ -191,17 +189,14 @@ define([
var mediaMap = {};
var defaultCode = renderer.code;
renderer.code = function (code, language) {
if (language === 'mermaid' && code.match(/^(graph|pie|gantt|sequenceDiagram|classDiagram|gitGraph)/)) {
return '<pre class="mermaid" data-plugin="mermaid">'+Util.fixHTML(code)+'</pre>';
} else if (language === 'markmap') {
return '<pre class="markmap" data-plugin="markmap">'+Util.fixHTML(code)+'</pre>';
} else if (language === 'mathjax') {
var svg = Mathjax.tex2svg(code, {display: true});
if (!svg) {
return defaultCode.apply(renderer, arguments);
}
return '<pre class="mathjax">'+ svg.innerHTML.replace(/xlink:href/g, "href") +'</pre>';
return '<pre class="mathjax" data-plugin="mathjax">'+Util.fixHTML(code)+'</pre>';
} else {
return defaultCode.apply(renderer, arguments);
}
@ -443,6 +438,58 @@ define([
}
};
plugins.mathjax = {
name: 'mathjax',
attr: 'mathjax-source',
render: function renderMathjax ($el) {
var el = $el[0];
if (!el) { return; }
var code = el.getAttribute("mathjax-source");
var svg = Mathjax.tex2svg(code, {display: true});
if (!svg) { return; }
svg.innerHTML = svg.innerHTML.replace(/xlink:href/g, "href");
var wrapper = document.createElement('span');
wrapper.innerHTML = svg.innerHTML;
el.innerHTML = wrapper.outerHTML;
}
};
var getAvailableCachedElement = function ($content, cache, src) {
var cached = cache[src];
if (!Array.isArray(cached)) { return; }
var root = $content[0];
var l = cached.length;
for (var i = 0; i < l; i++) {
if (!root.contains(cached[i])) {
return cached[i];
}
}
};
var cacheRenderedElement = function (cache, src, el) {
if (Array.isArray(cache[src])) {
cache[src].push(el);
} else {
cache[src] = [ el ];
}
};
// remove elements from the cache that are not embedded in the dom
var clearUnusedCacheEntries = function ($content, plugins) {
var root = $content[0];
Object.keys(plugins).forEach(function (name) {
var plugin = plugins[name];
var cache = plugin.cache;
Object.keys(cache).forEach(function (key) {
var list = cache[key];
if (!Array.isArray(list)) { return; }
cache[key] = list.filter(function (el) {
return root.contains(el);
});
});
});
};
DiffMd.apply = function (newHtml, $content, common) {
if (!sfCommon) { sfCommon = common; }
@ -514,7 +561,7 @@ define([
} else if (el.childNodes.length === 1 && el.childNodes[0].nodeType !== 3) {
// otherwise, confirm that the content of the rendered chart is not a text node
// and keep a copy of it
plugin.cache[src] = el.childNodes[0];
cacheRenderedElement(plugin.cache, src, el.childNodes[0]);
}
});
@ -550,7 +597,8 @@ define([
// Find initial position
// If the element is a mermaid or markmap svg, get the corresponding attribute
// If the element is supported by one of our plugin types
// (mermaid, mathjax, or markmap) get the corresponding attribute
var isSvg = $mt.is('pre[data-plugin]');
var plugin = isSvg && plugins[$mt.attr('data-plugin')];
@ -652,7 +700,7 @@ define([
if (target) { target.scrollIntoView(); }
});
// loop over mermaid elements in the rendered content
// loop over plugin elements in the rendered content
$content.find('pre[data-plugin]').each(function (index, el) {
var plugin = plugins[el.getAttribute('data-plugin')];
if (!plugin) { return; }
@ -674,7 +722,7 @@ define([
// of those in the markdown source.
var src = plugin.source[index];
el.setAttribute(plugin.attr, src);
var cached = plugin.cache[src];
var cached = getAvailableCachedElement($content, plugin.cache, src);
// check if you had cached a pre-rendered instance of the supplied source
if (typeof(cached) !== 'object') {
@ -694,6 +742,8 @@ define([
el.setAttribute('data-processed', true);
});
}
clearUnusedCacheEntries($content, plugins);
// recover the previous scroll position to avoid jank
$parent.scrollTop(scrollTop);
};

@ -2905,10 +2905,12 @@ define([
var el = useId ? _el : root[_el];
var sfId = (el && el.root && el.key) ? el.root[el.key] : el;
if (folder && el && manager.isSharedFolder(sfId)) {
var title = manager.getSharedFolderData(sfId).title || el;
var sfData = manager.getSharedFolderData(sfId);
var title = sfData.title || sfData.lastTitle || el;
return String(title).toLowerCase();
} else if (folder) {
return String((el && el.key) || el).toLowerCase();
console.log(el);
return String((el && el.key) || _el).toLowerCase();
}
var data = manager.getFileData(el);
if (!data) { return ''; }
@ -2929,6 +2931,7 @@ define([
}
props[uid] = getProp(k);
});
if (folder) { console.error(useId, props); }
keys.sort(function(a, b) {
var _a = props[(a && a.uid) || a];
var _b = props[(b && b.uid) || b];

@ -898,7 +898,7 @@ define([
}
}
if (owned) {
var deleteOwned = h('button.btn.btn-danger-alt', Messages.fc_delete_owned);
var deleteOwned = h('button.btn.btn-danger-alt', [h('i.cptools.cptools-destroy'), Messages.fc_delete_owned]);
var spinner = UI.makeSpinner();
UI.confirmButton(deleteOwned, {
classes: 'btn-danger'
@ -909,6 +909,7 @@ define([
channel: data.channel
}, function (err, obj) {
spinner.done();
UI.findCancelButton().click();
if (err || (obj && obj.error)) { UI.warn(Messages.error); }
});
});

@ -2054,6 +2054,10 @@ define([
var addSharedFolderHandler = function () {
store.sharedFolders = {};
store.handleSharedFolder = function (id, rt) {
if (!rt) {
delete store.sharedFolders[id];
return;
}
store.sharedFolders[id] = rt;
if (store.driveEvents) {
registerProxyEvents(rt.proxy, id);

@ -211,6 +211,9 @@ define([
// We can only hide it
sf.teams.forEach(function (obj) {
obj.store.manager.deprecateProxy(obj.id, secret.channel);
if (obj.store.handleSharedFolder) {
obj.store.handleSharedFolder(obj.id, null);
}
});
} catch (e) {}
delete allSharedFolders[secret.channel];
@ -252,9 +255,14 @@ define([
if (!sf) { return; }
var clients = sf.teams;
if (!Array.isArray(clients)) { return; }
// Remove the shared folder from the client's store and
// remove the client/team from our list
var idx;
clients.some(function (obj, i) {
if (obj.store.id === teamId) {
if (obj.store.handleSharedFolder) {
obj.store.handleSharedFolder(obj.id, null);
}
idx = i;
return true;
}

@ -164,6 +164,10 @@ define([
var handleSharedFolder = function (ctx, id, sfId, rt) {
var t = ctx.teams[id];
if (!t) { return; }
if (!rt) {
delete t.sharedFolders[sfId];
return;
}
t.sharedFolders[sfId] = rt;
registerChangeEvents(ctx, t, rt.proxy, sfId);
};

@ -52,6 +52,10 @@ define([
// Password may have changed
var deprecateProxy = function (Env, id, channel) {
if (Env.folders[id] && Env.folders[id].deleting) {
// Folder is being deleted by its owner, don't deprecate it
return;
}
if (Env.user.userObject.readOnly) {
// In a read-only team, we can't deprecate a shared folder
// Use a empty object with a deprecated flag...
@ -823,19 +827,38 @@ define([
var data = uo.isFile(el) ? uo.getFileData(el) : getSharedFolderData(Env, el);
chan = data.channel;
}
// If the pad was a shared folder, delete it too and leave it
var fId;
Object.keys(Env.user.proxy[UserObject.SHARED_FOLDERS] || {}).some(function (id) {
var sfData = Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {};
if (sfData.channel === chan) {
fId = Number(id);
Env.folders[id].deleting = true;
return true;
}
});
Env.removeOwnedChannel(chan, function (obj) {
// If the error is that the file is already removed, nothing to
// report, it's a normal behavior (pad expired probably)
if (obj && obj.error && obj.error.code !== "ENOENT") {
// RPC may not be responding
// Send a report that can be handled manually
if (fId && Env.folders[fId] && Env.folders[fId].deleting) {
delete Env.folders[fId].deleting;
}
console.error(obj.error, chan);
Feedback.send('ERROR_DELETING_OWNED_PAD=' + chan + '|' + obj.error, true);
return void cb();
}
// No error: delete the pads and all its copies from our drive and shared folders
// No error: delete the pad and all its copies from our drive and shared folders
var ids = _findChannels(Env, [chan]);
// If the pad was a shared folder, delete it too and leave it
if (fId) {
ids.push(fId);
}
ids.forEach(function (id) {
var paths = findFile(Env, id);
var _resolved = _resolvePaths(Env, paths);
@ -869,6 +892,10 @@ define([
}).nThen(function () {
// Remove deleted pads from the drive
_delete(Env, { resolved: toDelete }, cb);
// If we were using the access modal, send a refresh command
if (data.channel) {
Env.Store.refreshDriveUI();
}
});
};
@ -926,9 +953,8 @@ define([
if (!resolved.id) {
var el = Env.user.userObject.find(resolved.path);
if (Env.user.userObject.isSharedFolder(el) && Env.folders[el]) {
var oldName = Env.folders[el].proxy.metadata.title;
Env.folders[el].proxy.metadata.title = data.newName;
Env.user.proxy[UserObject.SHARED_FOLDERS][el].lastTitle = oldName;
Env.user.proxy[UserObject.SHARED_FOLDERS][el].lastTitle = data.newName;
return void cb();
}
}

@ -233,7 +233,7 @@
"poll_removeOption": "Bist du sicher, dass du diese Option entfernen möchtest?",
"poll_removeUser": "Bist du sicher, dass du diesen Nutzer entfernen möchtest?",
"poll_titleHint": "Titel",
"poll_descriptionHint": "Beschreibe deine Abstimmung und publiziere sie mit der Schaltfläche ✓ (Veröffentlichen), wenn du fertig bist. Bei der Beschreibung kann Markdown-Syntax verwendet werden und du kannst Medien-Elemente von deinem CryptDrive einbetten. Jeder, der den Link kennt, kann die Beschreibung ändern. Dies wird aber nicht empfohlen.",
"poll_descriptionHint": "Beschreibe deine Abstimmung und veröffentliche sie mit der Schaltfläche ✓ (Veröffentlichen).\nBei der Beschreibung kann Markdown-Syntax verwendet werden und du kannst Medien-Elemente von deinem CryptDrive einbetten.\nJeder, der den Link kennt, kann die Beschreibung ändern. Dies wird aber nicht empfohlen.",
"poll_remove": "Entfernen",
"poll_edit": "Bearbeiten",
"poll_locked": "Gesperrt",
@ -327,9 +327,9 @@
"fm_openParent": "Im Ordner zeigen",
"fm_noname": "Dokument ohne Titel",
"fm_emptyTrashDialog": "Soll der Papierkorb wirklich geleert werden?",
"fm_removeSeveralPermanentlyDialog": "Bist du sicher, dass du diese {0} Elemente endgültig aus deinem CryptDrive entfernen möchtest?",
"fm_removeSeveralPermanentlyDialog": "Bist du sicher, dass du diese {0} Elemente aus deinem CryptDrive entfernen möchtest? Sie verbleiben in den CryptDrives anderer Benutzer, die sie gespeichert haben.",
"fm_removePermanentlyNote": "Wenn du fortfährst, werden deine eigenen Pads vom Server entfernt.",
"fm_removePermanentlyDialog": "Bist du sicher, dass du dieses Element endgültig aus deinem CryptDrive entfernen möchtest?",
"fm_removePermanentlyDialog": "Bist du sicher, dass du dieses Element aus deinem CryptDrive entfernen möchtest? Es verbleibt in den CryptDrives anderer Benutzer, die es gespeichert haben.",
"fm_removeSeveralDialog": "Bist du sicher, dass du diese {0} Elemente in den Papierkorb verschieben möchtest?",
"fm_removeDialog": "Bist du sicher, dass du {0} in den Papierkorb verschieben möchtest?",
"fm_deleteOwnedPad": "Bist du sicher, dass du dieses Pad endgültig vom Server löschen möchtest?",
@ -373,9 +373,9 @@
"fc_open": "Öffnen",
"fc_open_ro": "Öffnen (schreibgeschützt)",
"fc_delete": "In den Papierkorb verschieben",
"fc_delete_owned": "Vom Server löschen",
"fc_delete_owned": "Zerstören",
"fc_restore": "Wiederherstellen",
"fc_remove": "Aus deinem CryptDrive entfernen",
"fc_remove": "Entfernen",
"fc_remove_sharedfolder": "Entfernen",
"fc_empty": "Den Papierkorb leeren",
"fc_prop": "Eigenschaften",
@ -605,7 +605,7 @@
"blog": "Blog",
"topbar_whatIsCryptpad": "Was ist CryptPad",
"whatis_title": "Was ist CryptPad",
"whatis_collaboration": "Effektive und und leichte Zusammenarbeit",
"whatis_collaboration": "Effektive und leichte Zusammenarbeit",
"whatis_collaboration_p1": "Mit CryptPad kannst du kollaborative Dokumente erstellen, um Notizen und Ideen gemeinsam zu bearbeiten. Wenn du dich registrierst und einloggst, bekommst du die Möglichkeit, Dateien hochzuladen und Ordner einzurichten, um alle deine Dokumente zu organisieren. Als registrierter Nutzer erhältst du kostenlos 50 MB Speicherplatz.",
"whatis_collaboration_p2": "Du kannst den Zugang zu einem CryptPad-Dokument teilen, indem du einfach den entsprechenden Link teilst. Du kannst auch einen <em>schreibgeschützten</em> Zugang erstellen, um die Ergebnisse deiner Arbeit zu teilen, während du sie noch bearbeitest.",
"whatis_collaboration_p3": "Du kannst Rich-Text Dokumente mit dem <a href=\"http://ckeditor.com/\">CKEditor</a> erstellen. Außerdem kannst du Markdown-Dokumente erstellen, die in Echtzeit formatiert angezeigt werden, während du tippst. Du kannst auch die Umfrage-Anwendung verwenden, um Termine unter mehrere Teilnehmern zu abzustimmen.",
@ -720,7 +720,7 @@
},
"register": {
"q": "Weisst der Server mehr über mich, wenn ich registriere?",
"a": "Wir verlangen nicht Deine Emailadresse und der Server kennt Benutzername und Passwort auch dann nicht, wenn du dich registrierst. Statt dessen generiert das Registrierungs- und Anmeldeformular ein Schlüsselpaar mit deiner Eingabe. Nur der öffentliche Schlüssel dieses Schlüsselpaars wird zum Server geschickt. Mit diesem öffentlichen Schlüssel könenn wir z.B. die Menge der Daten, die du benutzt, kontrollieren, denn jeder Benutzer hat eine beschränkte Quota.<br><br> Wir benutzen die <em>Rückmeldung</em>s-Funktion, um den Server zu informieren, dass jemand mit deiner IP ein Konto registriert hat. Damit können wir messen, wie viele Benutzer CryptPad Konten registrieren und aus welchen Regionen. Somit können wir erfahren, welche Sprache besseren Support braucht.<br><br> Wenn Du registrierst, erstellst Du einen öffentlichen Schlüssel, der benutzt wird, um den Server zu informieren, dass er Dokumente auch dann nicht löschen sollte, wenn sie nicht aktiv benutzt werden. Diese Information zeigt dem Server, wie Du CryptPad benutzt und dieses System erlaubt uns, die Dokumente zu löschen, wofür sich keiner mehr interessiert."
"a": "Wir verlangen deine E-Mail-Adresse nicht, und der Server kennt Benutzername und Passwort auch dann nicht, wenn du dich registrierst. Statt dessen generiert das Registrierungs- und Anmeldeformular ein Schlüsselpaar aus deiner Eingabe. Nur der öffentliche Schlüssel dieses Schlüsselpaars wird zum Server geschickt. Mit diesem öffentlichen Schlüssel können wir z.B. die Menge der Daten, die du benutzt, kontrollieren, denn jeder Benutzer hat eine beschränkte Quota.<br><br>Wir benutzen die <em>Rückmeldung</em>s-Funktion, um den Server zu informieren, dass jemand mit deiner IP ein Konto registriert hat. Damit können wir messen, wie viele Benutzer CryptPad Konten registrieren und aus welchen Regionen. Somit können wir erfahren, welche Sprache besser unterstützt werden sollte.<br><br>Registrierte Benutzer informieren den Server, dass er Dokumente im CryptDrive auch dann nicht wegen Inaktivität löschen sollte, wenn sie nicht aktiv benutzt werden."
},
"other": {
"q": "Was können andere Benutzer über mich erfahren?",
@ -747,7 +747,7 @@
},
"compromised": {
"q": "Liefert mir CryptPad einen Schutz, wenn mein Gerät kompromittiert wird?",
"a": "Für den Fall, dass dein Gerät gestohlen wird, ermöglicht CryptPad, das Ausloggen aller Geräte zu erzwingen - außer dem, das du gerade verwendest. Gehe dazu zur Seite mit deinen <strong>Einstellungen</strong> und klicke auf <strong>Überall ausloggen</strong>. Alle anderen Geräte, die mit diesem Konto verbunden sind, werden dann ausgeloggt. Alle früher verbundenen Geräte werden ausgeloggt, sobald sie CryptPad besuchen.<br><br> Die beschriebene Funktion ist derzeit im Browser implementiert und nicht im Server. Somit schützt sie nicht vor staatlichen Akteuren. Aber sie sollte ausreichend sein, wenn du nach Verwendung eines öffentlichen Computers vergessen hast dich auszuloggen."
"a": "Für den Fall, dass dein Gerät gestohlen wird, ermöglicht CryptPad, das Ausloggen aller Geräte zu erzwingen - außer dem, das du gerade verwendest. Gehe dazu zur Seite mit deinen <strong>Einstellungen</strong> und klicke auf <strong>Überall ausloggen</strong>. Alle anderen Geräte, die mit diesem Konto verbunden sind, werden dann ausgeloggt. Alle früher verbundenen Geräte werden ausgeloggt, sobald sie CryptPad besuchen.<br><br> Die beschriebene Funktion ist derzeit im Browser implementiert und nicht im Server. Somit schützt sie nicht vor staatlichen Akteuren. Aber sie sollte ausreichend sein, wenn du nach Verwendung eines öffentlichen Computers vergessen hast dich auszuloggen."
},
"crypto": {
"q": "Welche Kryptografie benutzt ihr?",
@ -770,7 +770,7 @@
},
"remove": {
"q": "Ich habe ein Dokument aus meinem CryptDrive gelöscht, aber der Inhalt ist noch verfügbar. Wie kann ich es entfernen?",
"a": "Nur <em>eigene Pads</em>, die im Februar 2018 eingeführt wurden, können gelöscht werden und zwar nur von deren Eigentümer (der Benutzer, der das Dokument ursprünglich erstellt hat). Wenn du nicht der Eigentümer des Pads bist, musst du den Eigentümer bitten, dass er dieses für dich löscht. Bei Pads, deren Eigentümer du bist, kannst du <strong>auf das Pad in deinem CryptDrive rechtsklicken</em> und <strong>Vom Server löschen</strong> wählen."
"a": "Nur <em>eigene Pads</em>, die im Februar 2018 eingeführt wurden, können gelöscht werden und zwar nur von deren <em>Eigentümer</em> (der Benutzer, der das Dokument ursprünglich erstellt hat). Wenn du nicht der Eigentümer des Pads bist, musst du den Eigentümer bitten, dass er dieses für dich löscht. Bei Pads, deren Eigentümer du bist, kannst du <strong>auf das Pad in deinem CryptDrive rechtsklicken</strong> und <strong>Vom Server löschen</strong> wählen."
},
"forget": {
"q": "Was passiert, wenn ich mein Passwort vergesse?",
@ -797,7 +797,7 @@
"title": "Andere Fragen",
"pay": {
"q": "Wieso soll ich zahlen, wenn so viele Funktionen sowieso kostenfrei sind?",
"a": "Wir geben Unterstützern zusätzlichen Speicherplatz sowie die Möglichkeit, die Speicherplatzbegrenzung ihrer Kontakte zu erhöhen (<a href='https://accounts.cryptpad.fr/#/faq' target='_blank'>erfahre mehr</a>).<br><br>Über diese diese kurzfristigen Vorteile hinaus kannst du, wenn du ein Premiumangebot annimmst, die aktive Weiterentwicklung von CryptPad fördern. Das beinhaltet, Fehler zu beseitigen, neue Funktionen zu umzusetzen und Installationen von CryptPad auf eigenen Servern zu erleichtern. Zusätzlich hilfst du, anderen Anbietern zu beweisen, dass Leute datenschutzfreundliche Technologien unterstützen. Wir hoffen, dass Geschäftsmodelle, die auf dem Verkauf von Benutzerdaten basieren, letztendlich der Vergangenheit angehören werden.<br><br>Außerdem glauben wir, dass es gut ist, die Funktionen von CryptPad kostenfrei anzubieten. Denn jeder verdient persönlichen Datenschutz und nicht nur Personen mit hohem Einkommen. Durch deine Unterstützung hilfst du uns, zu ermöglichen, dass auch Menschen mit geringerem Einkommen diese grundlegenden Funktionen genießen können, ohne dass ein Preisetikett daran klebt."
"a": "Wir geben Unterstützern zusätzlichen Speicherplatz sowie die Möglichkeit, die Speicherplatzbegrenzung ihrer Kontakte zu erhöhen (<a href='https://accounts.cryptpad.fr/#/faq' target='_blank'>erfahre mehr</a>).<br><br>Über diese kurzfristigen Vorteile hinaus kannst du, wenn du ein Premiumangebot annimmst, die aktive Weiterentwicklung von CryptPad fördern. Das beinhaltet, Fehler zu beseitigen, neue Funktionen umzusetzen und Installationen von CryptPad auf eigenen Servern zu erleichtern. Zusätzlich hilfst du, anderen Anbietern zu beweisen, dass Benutzer datenschutzfreundliche Technologien unterstützen. Wir hoffen, dass Geschäftsmodelle, die auf dem Verkauf von Benutzerdaten basieren, letztendlich der Vergangenheit angehören werden.<br><br>Außerdem glauben wir, dass es gut ist, die Funktionen von CryptPad kostenfrei anzubieten. Denn jeder verdient persönlichen Datenschutz und nicht nur Personen mit hohem Einkommen. Durch deine Unterstützung hilfst du uns, zu ermöglichen, dass auch Menschen mit geringerem Einkommen diese grundlegenden Funktionen genießen können, ohne dass ein Preisetikett daran klebt."
},
"goal": {
"q": "Was ist euer Ziel?",
@ -813,7 +813,7 @@
},
"revenue": {
"q": "Wie kann ich meine Einnahmen mit den Entwicklern teilen?",
"a": "Wenn du deine eigene Installation von CrytPad betreibst und die Einnahmen für deine bezahlten Konten mit den Entwicklern teilen möchtest, muss dein Server als Partnerservice konfiguriert werden.<br><br>In deinem CryptPad-Verzeichnis befindet sich <em>config.example.js</em>. Darin wird erklärt, wie du deinen Server dafür konfigurieren musst. Danach solltest du <a href='mailto:sales@cryptpad.fr'>sales@cryptpad.fr</a> kontaktieren, damit geprüft wird, dass dein Server richtig für HTTPS konfiguriert ist, und die Zahlungsmethoden abgesprochen werden können."
"a": "Wenn du deine eigene Installation von CrytPad betreibst und die Einnahmen für deine bezahlten Konten mit den Entwicklern teilen möchtest, muss dein Server als Partnerservice konfiguriert werden.<br><br>In deinem CryptPad-Verzeichnis befindet sich <em>config.example.js</em>. Darin wird erklärt, wie du deinen Server dafür konfigurieren musst. Danach solltest du <a href='mailto:sales@cryptpad.fr'>sales@cryptpad.fr</a> kontaktieren, damit geprüft wird, dass dein Server richtig für HTTPS konfiguriert ist, und die Zahlungsmethoden abgesprochen werden können."
}
}
},
@ -906,7 +906,7 @@
"shortcuts": "Mit den Tastenkürzeln `Strg+b`, `Strg+i` und `Strg+u` formatierst du Text fett, kursiv oder unterstrichen.",
"indent": "In nummerierten Listen oder Aufzählungen kannst du mit Tab und Umschalt+Tab den Einzug erhöhen oder reduzieren."
},
"feedback_about": "Wenn du das liest, fragst du dich wahrscheinlich, weshalb dein Browser bei der der Ausführung mancher Aktionen Anfragen an Webseiten sendet",
"feedback_about": "Wenn du das liest, fragst du dich wahrscheinlich, weshalb dein Browser bei der Ausführung mancher Aktionen Anfragen an Webseiten sendet",
"feedback_privacy": "Wir respektieren deine Datenschutz, aber gleichzeitig wollen wir, dass die Benutzung von CryptPad sehr leicht ist. Deshalb wollen wir erfahren, welche Funktion am wichtigsten für unsere Benutzer ist, indem wir diese mit einer genauen Parameterbeschreibung anfordern.",
"feedback_optout": "Wenn du das nicht möchtest, kannst du es in <a href='/settings/'>deinen Einstellungen</a> deaktivieren",
"creation_404": "Dieses Pad existiert nicht mehr. Benutze das folgende Formular, um ein neues Pad zu erstellen.",
@ -1395,5 +1395,7 @@
"oo_refresh": "Neu laden",
"support_category": "Wähle eine Kategorie",
"oo_refreshText": "Dieses Dokument wurde aktualisiert",
"support_formCategoryError": "Fehler: Kategorie ist leer"
"support_formCategoryError": "Fehler: Kategorie ist leer",
"fm_restricted": "Du kannst auf dieses Element nicht zugreifen",
"fm_emptyTrashOwned": "In deinem Papierkorb sind Dokumente gespeichert, deren Eigentümer du bist. Du kannst sie aus deinem CryptDrive <b>entfernen</b> oder für alle Benutzer <b>zerstören</b>."
}

@ -203,7 +203,7 @@ define([
$p.append($('<br>'));
$('<button>', {
title: Messages.filePickerButton,
'class': '',
'class': 'btn',
style: 'font-size: 17px',
id: 'cp-app-slide-options-bg'
}).click(function () {
@ -221,16 +221,18 @@ define([
}
});
}).text(Messages.printBackgroundButton).appendTo($p);
$p.append($('<br>'));
}
$p.append($('<br>'));
var $bgValue = $('<div>').appendTo($p);
var $bgValue = $('<div class="cp-background-selected">').appendTo($p);
var refreshValue = function () {
$bgValue.html('');
if (slideOptionsTmp.background && slideOptionsTmp.background.name) {
$bgValue.append(Messages._getKey("printBackgroundValue", [slideOptionsTmp.background.name]));
$('<button>', {
$('<span>', {
'class': 'fa fa-times',
title: Messages.printBackgroundRemove
title: Messages.printBackgroundRemove,
style: 'margin-left: 5px'
}).click(function () {
slideOptionsTmp.background = false;
refreshValue();
@ -247,7 +249,6 @@ define([
};
}
$p.append($('<br>'));
$p.append($('<br>'));
// CSS
$('<label>', {'for': 'cp-app-slide-options-css'}).text(Messages.printCSS).appendTo($p);
$p.append($('<br>'));
@ -307,8 +308,8 @@ define([
h = UI.listenForKeys(todo, todoCancel);
var $nav = $('<nav>').appendTo($div);
$('<button>', {'class': 'cancel'}).text(Messages.cancelButton).appendTo($nav).click(todoCancel);
$('<button>', {'class': 'ok'}).text(Messages.settings_save).appendTo($nav).click(todo);
$('<button>', {'class': 'btn cancel'}).text(Messages.cancelButton).appendTo($nav).click(todoCancel);
$('<button>', {'class': 'btn ok'}).text(Messages.settings_save).appendTo($nav).click(todo);
return $container;
};
@ -484,15 +485,19 @@ define([
CodeMirror.init(framework.localChange, framework._.title, framework._.toolbar);
CodeMirror.configureTheme(common);
var drawSlides = Util.throttle(function (content) {
Slide.update(content);
}, 400);
framework.onContentUpdate(function (newContent) {
CodeMirror.contentUpdate(newContent);
Slide.update(newContent.content);
drawSlides(newContent.content);
});
framework.setContentGetter(function () {
CodeMirror.removeCursors();
var content = CodeMirror.getContent();
Slide.update(content.content);
drawSlides(content.content);
return content;
});

@ -154,18 +154,22 @@ define([
updateFontSize();
};
Slide.update = function (content) {
Slide.update = function (content, force) {
updateFontSize();
if (!content) { content = ''; }
var old = Slide.content;
Slide.content = content.replace(/\n\s*\-\-\-\s*\n/g, '\n\n'+separator+'\n\n');
if (old !== Slide.content) {
if (force || old !== Slide.content) {
draw(Slide.index);
return;
}
change(Slide.lastIndex, Slide.index);
};
DiffMd.onPluginLoaded(function () {
Slide.update(Slide.content, true);
});
Slide.left = function () {
console.log('left');
Slide.lastIndex = Slide.index;

@ -257,10 +257,7 @@ define([
});
if (active === 'drive') {
APP.$rightside.addClass('cp-rightside-drive');
APP.$leftside.on('mouseover', function() {
APP.$leftside.addClass('cp-leftside-narrow');
APP.$leftside.off('mouseover');
});
APP.$leftside.addClass('cp-leftside-narrow');
} else {
APP.$rightside.removeClass('cp-rightside-drive');
APP.$leftside.removeClass('cp-leftside-narrow');

@ -491,10 +491,10 @@ define([
}, [
h('button#cp-app-whiteboard-clear.btn.btn-danger', Messages.canvas_clear), ' ',
h('div.cp-whiteboard-type', [
h('button.brush.fa.fa-paint-brush.btn-primary', {title: Messages.canvas_brush}),
h('button.move.fa.fa-arrows', {title: Messages.canvas_select}),
h('button.btn.brush.fa.fa-paint-brush.btn-primary', {title: Messages.canvas_brush}),
h('button.btn.move.fa.fa-arrows', {title: Messages.canvas_select}),
]),
h('button.fa.fa-trash#cp-app-whiteboard-delete', {
h('button.btn.fa.fa-trash#cp-app-whiteboard-delete', {
disabled: 'disabled',
title: Messages.canvas_delete
}),

Loading…
Cancel
Save