Merge branch 'staging' into collapse-toolbar

pull/1/head
ansuz 3 years ago
commit 4e02c309bb

@ -1,3 +1,59 @@
# WIP
* Sheet export
* most exports broken by Chrome 92, mostly fixed
* we discovered that CSV export was not working in any major browser, though it's unclear why. We've disabled CSV export in the meantime
* updated translation to stop referring to Microsoft since we support OpenDocument formats
* some new browser-specific checkup tests to make it easier to detect future regressions in the APIs
* drive bug fixes
* guard against a few possible type errors
* kanban screen real-estate
* narrower 'add board' button
* 'Tools' menu to collapse the tag and view mode UI
# 4.9.0
## Goals and announcements
We allocated most of this release cycle towards a schedule of one-on-one user interviews and some broad usage studies leveraging our new Form app. The remainder of our time was spent on some minor improvements. We'll continue at a slightly slower pace of implementation for the coming weeks while we complete our scheduled interviews and take some much-needed vacations.
## Update notes
It appears our promotion of the checkup page through our recent release notes and the inclusion of a link to it from the instance admin have been moderately successful. We've observed that more instance admins are noticing and fixing some common configuration issues.
This release features some minor changes to one instance configuration test which incorrectly provided an exemption for the use of `http://localhost:3000` as an `httpUnsafeOrigin` value. This exemption was provided because this value is valid for local development. However, it suppressed errors when this configuration was used for production instances where it could cause a variety of problems. As usual, we recommend checking your instance's admin page after updating to confirm that you are passing the latest tests. Information about the checkup page is included in [our documentation](https://docs.cryptpad.fr/en/admin_guide/admin_panel.html#network).
To update from 4.8.0 to 4.9.0:
1. Stop your server
2. Get the latest code with git
3. Install the latest dependencies with `bower update` and `npm i`
4. Restart your server
5. Confirm that your instance is passing all the tests included on the `/checkup/` page
## Features
* We've added the ability to store URLs in user and team drives as requested in a private support ticket and [this issue](https://github.com/xwiki-labs/cryptpad/issues/732). Links can be shared directly with contacts. Unlike pads, links are not collaborative objects, so updating a link's name will not update the entry in another user's drive if you've already shared it with them. Links are integrated into our apps' _insert_ menu to facilitate quick insertion of links you've stored into your documents. We're interested in measuring how this functionality is used in practice so we can decide whether it's worth spending more time on it. We have added some telemetry to measure (in aggregate) how often its components are used. We anonymize IP addresses in the logs for CryptPad.fr, but as always, you can disable telemetry via your settings panel.
* Our rich text editor now supports indentation with the tab key, as per [issue #634](https://github.com/xwiki-labs/cryptpad/issues/634).
* Forms received another round of improvements to styles, workflows, and some basic survey functionality to yield more accurate results.
* Ordered lists are now shuffled for each survey participant so that their initial order has less effect on the final results.
* CSV export now uses one column for each option in polls, making them easier to read.
* Unregistered users can now add a name to their response.
* Form results are displayed automatically (when available) to those who have answered.
* Authors and auditors can now click on usernames in polls to jump directly to other answers from the same user.
* Users with very large drives might notice that their account loads slightly faster now, due to some minor optimizations in an integrity check that the client performs when loading accounts.
## Bugs
* We've added a guard against a type error that could be triggered when loading teams under certain rare conditions.
* Unregistered users' drives now show the "bread-crumb" UI for navigating between folders when viewing a shared folder in read-only mode. We've also suppressed the "Files" button for displaying the tree view which was non-functional for such users.
* A change in the format of support tickets caused tickets recently created by premium users to not be recognized as such. We've fixed the categorization in the admin panel's support ticket view.
* We've fixed a number of minor issues with forms:
* The maximum number of selectable choices for checkbox questions can no longer exceed the number of available choices.
* We guard against a type error that could occur when parsing dates.
* Forms imported from templates now have their initial title corrected.
* We've disabled the use of our indexedDB caching system for form results, since it was quietly dropping older responses when more than 100 responses had been submitted. We plan to re-enable caching for results once we've updated the eviction metric to better handle the response format.
# 4.8.0
## Goals

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 83 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.3 KiB

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360.4 94.7"><defs><style>.cls-1{fill:#4591c4}.cls-2{fill:#999}</style></defs><title>CryptPad_logo_color</title><g id="Layer_2" data-name="Layer 2"><g id="svg2"><g id="g4845"><path id="path4811" class="cls-1" d="M99.5 63.6a24.8 24.8 0 0 1-5.9-.6 8.5 8.5 0 0 1-3.8-1.9 7.1 7.1 0 0 1-2-3.4 19.4 19.4 0 0 1-.6-5.2v-10a19.4 19.4 0 0 1 .6-5.1 7.1 7.1 0 0 1 2-3.4 8.5 8.5 0 0 1 3.8-2 24.8 24.8 0 0 1 5.9-.6h22.3v6.2h-22a11.8 11.8 0 0 0-2.7.3 3.5 3.5 0 0 0-1.7.9 3.3 3.3 0 0 0-.9 1.6 11.1 11.1 0 0 0-.2 2.5v9.4a11.1 11.1 0 0 0 .2 2.5 3.3 3.3 0 0 0 .9 1.6 3.3 3.3 0 0 0 1.7.8l2.7.2h22v6.1z"/><path id="path4813" class="cls-1" d="M126.5 63.6V48.8a16 16 0 0 1 .7-4.9 7.5 7.5 0 0 1 2.1-3.3 8.9 8.9 0 0 1 3.7-1.9 21.4 21.4 0 0 1 5.5-.6h4.6v5.7h-4.4l-2.5.2a3.3 3.3 0 0 0-1.6.8 3.2 3.2 0 0 0-.8 1.6 10.1 10.1 0 0 0-.3 2.5v14.7z"/><path id="path4815" class="cls-1" d="M154.1 51.4a3.8 3.8 0 0 0 .9 3 4.6 4.6 0 0 0 3.1.8h15.7V38h6.8v25.2q0 4.3-2.1 6.2t-6.9 1.9h-10.2v-5.8h9.6a3.3 3.3 0 0 0 2.1-.6 2.7 2.7 0 0 0 .7-2.2v-2h-16.1a19 19 0 0 1-4.9-.5 7.8 7.8 0 0 1-3.3-1.7 6.5 6.5 0 0 1-1.8-2.8 12.7 12.7 0 0 1-.6-4V38h6.9z"/><path id="path4817" class="cls-1" d="M207.5 38a25.1 25.1 0 0 1 5.9.6 8.5 8.5 0 0 1 3.8 1.9 7.1 7.1 0 0 1 2 3.4 19.7 19.7 0 0 1 .6 5.2v3.4a19.4 19.4 0 0 1-.6 5.2 7.1 7.1 0 0 1-2 3.4 8.5 8.5 0 0 1-3.8 1.9 25.1 25.1 0 0 1-5.9.6h-14.3v7.6h-7V49.1a19.7 19.7 0 0 1 .6-5.2 7.1 7.1 0 0 1 2-3.4 8.6 8.6 0 0 1 3.8-1.9 25.1 25.1 0 0 1 5.9-.6zm5.3 11.3a11.7 11.7 0 0 0-.3-2.7 2.9 2.9 0 0 0-1-1.6 4 4 0 0 0-1.9-.8l-3-.2h-7.8l-2.7.2a3.5 3.5 0 0 0-1.7.8 3.2 3.2 0 0 0-.8 1.6 11.7 11.7 0 0 0-.2 2.6v8.4h13.4l3-.2a3.8 3.8 0 0 0 1.9-.7 2.9 2.9 0 0 0 1-1.6 11.9 11.9 0 0 0 .3-2.8z"/><path id="path4819" class="cls-1" d="M226.5 63.6V43.8H223V38h3.6v-7.2h7.2V38h8.1v5.7h-8.1v19.9z"/><path id="path4821" class="cls-2" d="M252.4 54.4v9.2h-7.2V31.4H271a25.7 25.7 0 0 1 5.8.5 8.4 8.4 0 0 1 3.7 1.8 6.8 6.8 0 0 1 2 3.2 17.1 17.1 0 0 1 .6 4.8v2.8a16.9 16.9 0 0 1-.6 4.8 6 6 0 0 1-2 3 8.3 8.3 0 0 1-3.7 1.6 31.6 31.6 0 0 1-5.9.4zm23.5-12.3q0-2.6-1.1-3.5t-4-.9h-18.4v11h18.5a6.5 6.5 0 0 0 3.9-.9q1.1-.9 1.1-3.4z"/><path id="path4823" class="cls-2" d="M296.2 63.6a18.4 18.4 0 0 1-4.6-.5 7.4 7.4 0 0 1-2.9-1.3 4.6 4.6 0 0 1-1.5-2.1 8.7 8.7 0 0 1-.4-2.8v-2.4a9.3 9.3 0 0 1 .4-2.9 4.7 4.7 0 0 1 1.4-2.1 6.5 6.5 0 0 1 2.7-1.3 17.7 17.7 0 0 1 4.4-.4h18.9v-.6q0-2.5-1-3.3a5.3 5.3 0 0 0-3.4-.8h-7V38h7a20.7 20.7 0 0 1 5.2.6 8.9 8.9 0 0 1 3.5 1.7 6.7 6.7 0 0 1 2 2.9 12.5 12.5 0 0 1 .6 4.2v6.2a17.2 17.2 0 0 1-.5 4.7 6.3 6.3 0 0 1-1.9 3.1 7.9 7.9 0 0 1-3.6 1.7 26.8 26.8 0 0 1-5.6.5zM314.7 52h-18.2a3.1 3.1 0 0 0-1.9.5 2.5 2.5 0 0 0-.7 2.1v1.7a2.1 2.1 0 0 0 .8 1.9 3.7 3.7 0 0 0 2.1.5H310l2.1-.2a3.2 3.2 0 0 0 1.5-.7 3 3 0 0 0 .9-1.4 7.8 7.8 0 0 0 .3-2.3z"/><path id="path4825" class="cls-2" d="M339.1 63.6a25.4 25.4 0 0 1-6-.6 8.6 8.6 0 0 1-3.8-1.9 7.1 7.1 0 0 1-2-3.4 19.4 19.4 0 0 1-.6-5.2v-3.4a19.7 19.7 0 0 1 .6-5.2 7.1 7.1 0 0 1 2-3.4 8.6 8.6 0 0 1 3.8-1.9 25.4 25.4 0 0 1 6-.6h14.2v-8.2h7v22.8a19.4 19.4 0 0 1-.6 5.2 7.1 7.1 0 0 1-2 3.4A8.5 8.5 0 0 1 354 63a25.1 25.1 0 0 1-5.9.6zm-5.3-11.2a11.7 11.7 0 0 0 .3 2.7 2.9 2.9 0 0 0 1 1.6 4 4 0 0 0 1.9.8l3.1.2h7.8l2.7-.2a3.3 3.3 0 0 0 1.7-.8 3.2 3.2 0 0 0 .9-1.6 11.9 11.9 0 0 0 .2-2.6v-8.4H340l-3 .2a4 4 0 0 0-1.9.8 2.9 2.9 0 0 0-1 1.6 11.7 11.7 0 0 0-.3 2.7z"/><path id="path4827" class="cls-1" d="M39.2 0L9.9 5.4A6.3 6.3 0 1 0 3.2 16v42.2c0 4 1.8 8.3 5.3 12.7A65.5 65.5 0 0 0 21.6 83a128.2 128.2 0 0 0 17.6 10.5A128.1 128.1 0 0 0 56.7 83a65.4 65.4 0 0 0 13.1-12.1c3.5-4.5 5.3-8.8 5.3-12.7V16A6.3 6.3 0 0 0 72 4.2a6.2 6.2 0 0 0-3.6 1.2zm-.1 6.2l26.7 4.9a5.9 5.9 0 0 0 .2 1.1L50.3 22.5a15.3 15.3 0 0 0-22.6.1l-15.5-10a6.3 6.3 0 0 0 .3-1.4zm28.8 9a6.5 6.5 0 0 0 1.8 1.1v41a10.4 10.4 0 0 1-.1 1.7 21.5 21.5 0 0 1-4.1 7.8 56.1 56.1 0 0 1-11.3 10.4 110.6 110.6 0 0 1-15 9 110.8 110.8 0 0 1-15-9A55.8 55.8 0 0 1 13 66.8a19.8 19.8 0 0 1-4.4-9.3V16.4a6.3 6.3 0 0 0 1.7-1l19.8 12.7a10.1 10.1 0 0 1 9-5.4 10 10 0 0 1 9 5.4z"/><g id="g4829"><path id="path4831" class="cls-2" d="M23 54.8a4.6 4.6 0 1 0 0 9.3 4.6 4.6 0 0 0 0-9.3z"/><path id="path4833" class="cls-2" d="M24.3 28.5a14.9 14.9 0 0 0 4.2 15.4l-7.3 14.8a2.8 2.8 0 0 0 2.3 3.8h11v-5.2h-6.8l6.4-12.8a2.7 2.7 0 0 0-.8-3.3 9.7 9.7 0 0 1-4.3-9.6z"/></g><g id="g4835"><path id="path4837" class="cls-2" d="M55.3 54.8a4.6 4.6 0 1 0 0 9.3 4.6 4.6 0 0 0 0-9.3z"/><path id="path4839" class="cls-2" d="M53.8 28.6l-4.7 3.1a10.2 10.2 0 0 1 .1 1.2 10 10 0 0 1-4.3 8.3 2.7 2.7 0 0 0-.8 3.3l6.3 12.8h-6.8v5.2h11a2.8 2.8 0 0 0 2.3-3.8l-7.3-14.8a15.2 15.2 0 0 0 4.8-11 15.3 15.3 0 0 0-.6-4.3z"/></g><path id="path4841" class="cls-1" d="M43.2 33.3a4.2 4.2 0 1 1-4.2-4.2 4.2 4.2 0 0 1 4.2 4.2z"/><path id="path4843" class="cls-1" d="M45.3 88.4a6.3 6.3 0 1 1-6.3-6.3 6.3 6.3 0 0 1 6.3 6.3z"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 4.7 KiB

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 393.94 476.2"><defs><style>.cls-1{opacity:0.04;}.cls-2{fill:#999;}.cls-3{fill:#4591c4;}</style></defs><title>CryptPadlogo</title><g id="Layer_2" data-name="Layer 2"><g id="svg2"><g id="g4764" class="cls-1"><path class="cls-2" d="M139.36,288.16l32.07-64.43a13.59,13.59,0,0,0-4.23-16.62,48.65,48.65,0,0,1-21.28-48.25l-23.47-15.31a75.74,75.74,0,0,0-3.36,21.87c0,22,9.52,41.45,24.35,55.54l-27.24,54.8c-.11,0-.2,0-.31,0a23.27,23.27,0,1,0,0,46.53,23,23,0,0,0,17.31-7.86h40.27V288.16Z"/><path class="cls-2" d="M278.28,275.73c-.57,0-1.11.13-1.68.17l-27.33-55.09c14.75-14.07,24.2-33.47,24.2-55.39a77.13,77.13,0,0,0-3.06-21.72l-23.62,15.45a51.69,51.69,0,0,1,.44,6.27A50.21,50.21,0,0,1,225.65,207a13.58,13.58,0,0,0-4.22,16.62l31.77,64.58h-34V314.4h41.63a23.23,23.23,0,1,0,17.41-38.67Z"/><polygon class="cls-2" points="270.41 143.7 270.41 143.7 270.41 143.7 270.41 143.7"/><circle class="cls-3" cx="196.06" cy="167.4" r="21.21"/><path class="cls-3" d="M362.25,21.36a31.14,31.14,0,0,0-18.1,5.8L197,0,50,27.16a31.62,31.62,0,1,0-33.68,53.4v212c0,19.95,8.93,41.51,26.62,64.08,15.66,20,37.82,40.46,65.89,60.83a603,603,0,0,0,57,36.21,31.54,31.54,0,0,0,60,1.25,606,606,0,0,0,59.26-37.46c28.09-20.37,50.23-40.86,65.9-60.83,17.7-22.6,26.61-44.13,26.61-64.08V80.38a31.46,31.46,0,0,0-15.39-59ZM62.82,55.94,196.61,31.32l134.33,24.8a29.58,29.58,0,0,0,.9,5.61L253.09,113a76.78,76.78,0,0,0-113.69.36L61.55,62.82A31.8,31.8,0,0,0,62.82,55.94ZM350.49,288.21a51.41,51.41,0,0,1-.73,8.51c-3.16,12.6-10.11,25.8-20.82,39.47-13.41,17.09-32.43,34.52-56.48,51.95a532.75,532.75,0,0,1-54.19,34.09,31.5,31.5,0,0,0-43.73-.62,534,534,0,0,1-53.06-33.47c-24-17.43-42.9-34.86-56.31-51.95-12.77-16.31-20.25-31.94-22.26-46.71,0-.41,0-.86,0-1.27V82.37a31.51,31.51,0,0,0,8.69-5.25l88.88,57.75.37-.36,10.5,6.88a51,51,0,0,1,45.07-27,50.37,50.37,0,0,1,45.08,27.15l22.09-14.3L341.44,76.4A32.55,32.55,0,0,0,350.49,82Z"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

@ -105,7 +105,7 @@ define([
var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ?
'/imprint.html' : AppConfig.imprint);
Pages.versionString = "v4.8.0";
Pages.versionString = "v4.9.0";
// used for the about menu

@ -123,7 +123,7 @@ module.exports.create = function (config) {
maxWorkers: config.maxWorkers,
disableIntegratedTasks: config.disableIntegratedTasks || false,
disableIntegratedEviction: typeof(config.disableIntegratedEviction) === 'undefined'? true: config.disableIntegratedEviction, // XXX false,
disableIntegratedEviction: typeof(config.disableIntegratedEviction) === 'undefined'? true: config.disableIntegratedEviction, // XXX 4.10.0 false,
lastEviction: +new Date(),
evictionReport: {},
commandTimers: {},

2
package-lock.json generated

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

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

@ -833,7 +833,8 @@ define([
var premium = t.some(function (msg) {
var _ed = Util.find(msg, ['content', 'msg', 'content', 'sender', 'edPublic']);
if (ed !== _ed) { return; }
return Util.find(msg, ['content', 'msg', 'content', 'sender', 'plan']);
return Util.find(msg, ['content', 'msg', 'content', 'sender', 'plan']) ||
Util.find(msg, ['content', 'msg', 'content', 'sender', 'quota', 'plan']);
});
var lastMsg = t[t.length - 1];
var lastMsgEd = Util.find(lastMsg, ['content', 'msg', 'content', 'sender', 'edPublic']);
@ -1471,11 +1472,8 @@ define([
var end = h('input');
var $start = $(start);
var $end = $(end);
var is24h = false;
var is24h = UIElements.is24h();
var dateFormat = "Y-m-d H:i";
try {
is24h = !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/);
} catch (e) {}
if (!is24h) { dateFormat = "Y-m-d h:i K"; }
var endPickr = Flatpickr(end, {

@ -14,11 +14,15 @@ html, body {
.report {
font-size: 30px;
max-width: 50%;
max-width: 26em;
margin: auto;
padding-top: 15px;
}
.summary, .failure, .error, .success {
margin-bottom: 1em;
}
.pending {
border: 1px solid @cryptpad_text_col;
.fa {
@ -42,10 +46,18 @@ html, body {
padding: 15px;
}
table {
td {
padding: 5px;
border: 1px solid @cryptpad_text_col;
.table-container {
overflow-x: auto;
width: 100%;
table {
td {
padding: 5px;
border: 1px solid @cryptpad_text_col;
font-size: 60%;
}
td:nth-child(2) {
word-break: break-word;
}
}
}
@ -72,7 +84,12 @@ html, body {
color: @cryptpad_color_link;
}
}
.cp-app-checkup-version {
.cp-notice-browser, .cp-notice-details, .cp-notice-other {
font-size: 70%;
}
.cp-app-checkup-version, .cp-app-checkup-browser {
text-decoration: underline;
}

@ -0,0 +1,36 @@
define([
], function () {
var Tools = {};
Tools.supportsSharedArrayBuffers = function () {
try {
return Object.prototype.toString.call(new window.WebAssembly.Memory({
shared: true,
initial: 0,
maximum: 0,
}).buffer) === '[object SharedArrayBuffer]';
} catch (err) {
console.error(err);
}
return false;
};
Tools.isSafari = function () {
return navigator.vendor.match(/apple/i);
};
Tools.isChrome = function () {
return navigator.vendor.match(/google/i);
};
Tools.guessBrowser = function () {
if (Tools.isChrome()) { return 'chrome/blink'; }
if (Tools.isSafari()) { return 'safari/webkit'; }
if (navigator.userAgent.match(/firefox\//i)) { return 'firefox/gecko'; }
if (navigator.userAgent.match(/edge\//i)) { return 'edge/edgehtml'; }
if (navigator.userAgent.match(/trident\//i)) { return 'ie/trident'; }
return navigator.userAgent + "\n" + navigator.vendor;
};
return Tools;
});

@ -13,13 +13,14 @@ define([
'/common/pinpad.js',
'/common/outer/network-config.js',
'/customize/pages.js',
'/checkup/checkup-tools.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/checkup/app-checkup.less',
], function ($, ApiConfig, Assertions, h, Messages, DomReady,
nThen, SFCommonO, Login, Hash, Util, Pinpad,
NetConfig, Pages) {
NetConfig, Pages, Tools) {
var Assert = Assertions();
var trimSlashes = function (s) {
if (typeof(s) !== 'string') { return s; }
@ -255,6 +256,11 @@ define([
]));
}));
// time out after 30 seconds
setTimeout(function () {
cb('TIMEOUT');
}, 30000);
var bytes = new Uint8Array(Login.requiredBytes);
var opt = Login.allocateBytes(bytes);
@ -698,6 +704,69 @@ define([
});
});
var safariGripe = function () {
return h('p.cp-notice-other', 'This is expected because Safari and platforms that use its engine lack commonly supported functionality.');
};
var browserIssue = function () {
return h('p.cp-notice-other', 'This test checks for the presence of features in your browser and is not necessarily caused by server misconfiguration.');
};
assert(function (cb, msg) {
cb = Util.once(cb);
setWarningClass(msg);
var notice = h('span', [
h('p', 'It appears that some features required for Office file format conversion are not present.'),
Tools.isSafari()? safariGripe(): undefined,
browserIssue(),
]);
msg.appendChild(notice);
var expected = [
'Atomics',
'SharedArrayBuffer',
'WebAssembly',
['WebAssembly', 'Memory'],
['WebAssembly', 'instantiate'],
['WebAssembly', 'instantiateStreaming'],
['Buffer', 'from'],
'SharedWorker',
'worker',
'crossOriginIsolated',
];
var responses = {};
nThen(function (w) {
deferredPostMessage({
command: 'CHECK_JS_APIS',
content: {
globals: expected,
},
}, w(function (response) {
Util.extend(responses, response);
}));
deferredPostMessage({
command: 'FANCY_API_CHECKS',
content: {
},
}, w(function (response) {
Util.extend(responses, response);
}));
}).nThen(function () {
if (!responses.Atomics || !responses.WebAssembly) {
return void cb(responses);
}
if (responses.SharedArrayBuffer || responses.SharedArrayBufferFallback) {
return cb(true);
}
return void cb(response);
});
});
var isHTTPS = function (host) {
return /^https:\/\//.test(host);
};
@ -705,17 +774,19 @@ define([
var isOnion = function (host) {
return /\.onion$/.test(host);
};
var isLocalhost = function (host) {
return /^http:\/\/localhost/.test(host);
};
assert(function (cb, msg) {
// provide an exception for development instances
if (/http:\/\/localhost/.test(trimmedUnsafe)) { return void cb(true); }
if (isLocalhost(trimmedUnsafe) && isLocalhost(window.location.href)) { return void cb(true); }
// if both the main and sandbox domains are onion addresses
// then the HTTPS requirement is unnecessary
if (isOnion(trimmedUnsafe) && isOnion(trimmedSafe)) { return void cb(true); }
// otherwise expect that both inner and outer domains use HTTPS
setWarningClass(msg);
msg.appendChild(h('span', [
"Both ",
code('httpUnsafeOrigin'),
@ -727,8 +798,6 @@ define([
'. ',
RESTART_WARNING(),
]));
console.error("HTTPS?", trimmedUnsafe, trimmedSafe);
cb(isHTTPS(trimmedUnsafe) && isHTTPS(trimmedSafe));
});
@ -826,17 +895,19 @@ define([
var failureReport = function (obj) {
var printableValue = obj.output;
try {
printableValue = JSON.stringify(obj.output);
printableValue = JSON.stringify(obj.output, null, ' ');
} catch (err) {
console.error(err);
}
return h('div.error', [
h('h5', obj.message),
h('table', [
row(["Failed test number", obj.test + 1]),
row(["Returned value", code(printableValue)]),
]),
h('div.table-container',
h('table', [
row(["Failed test number", obj.test + 1]),
row(["Returned value", h('pre', code(printableValue))]),
])
),
]);
};
@ -844,7 +915,7 @@ define([
var $progress = $('#cp-progress');
var versionStatement = function () {
return h('p', [
return h('p.cp--notice-version', [
"This instance is running ",
h('span.cp-app-checkup-version',[
"CryptPad",
@ -855,6 +926,16 @@ define([
]);
};
var browserStatement = function () {
var name = Tools.guessBrowser();
if (!name) { return; }
return h('p.cp-notice-browser', [
"You appear to be using a ",
h('span.cp-app-checkup-browser', name),
' browser to view this page.',
]);
};
Assert.run(function (state) {
var errors = state.errors;
var failed = errors.length;
@ -865,10 +946,11 @@ define([
var failedDetails = "Details found below";
var successDetails = "This checkup only tests the most common configuration issues. You may still experience errors or incorrect behaviour.";
var details = h('p', failed? failedDetails: successDetails);
var details = h('p.cp-notice-details', failed? failedDetails: successDetails);
var summary = h('div.summary.' + statusClass, [
versionStatement(),
browserStatement(),
h('p', Messages._getKey('assert_numberOfTestsPassed', [
state.passed,
state.total

@ -1,10 +1,12 @@
define([
'jquery',
'/common/common-util.js',
'/checkup/checkup-tools.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/checkup/app-checkup.less',
], function ($) {
], function ($, Util, Tools) {
var postMessage = function (content) {
window.parent.postMessage(JSON.stringify(content), '*');
};
@ -26,6 +28,26 @@ define([
});
};
COMMANDS.CHECK_JS_APIS = function (content, cb) {
var globalAPIs = content['globals'] || [];
var response = {};
globalAPIs.forEach(function (key) {
if (Array.isArray(key)) {
response[key.join('.')] = Boolean(Util.find(window, key));
return;
}
response[key] = Boolean(window[key]);
});
cb(response);
};
COMMANDS.FANCY_API_CHECKS = function (content, cb) {
cb({
SharedArrayBufferFallback: Tools.supportsSharedArrayBuffers(),
});
};
window.addEventListener("message", function (event) {
var txid, command;
if (event && event.data) {

@ -368,7 +368,7 @@ define([
var mkFilePicker = function (framework, editor, evModeChange) {
evModeChange.reg(function (mode) {
if (MEDIA_TAG_MODES.indexOf(mode) !== -1) {
// Embedding is endabled
// Embedding is enabled
framework.setMediaTagEmbedder(function (mt) {
editor.focus();
editor.replaceSelection($(mt)[0].outerHTML);

@ -119,6 +119,7 @@ define(function() {
file: 'cptools-file',
fileupload: 'cptools-file-upload',
folderupload: 'cptools-folder-upload',
link: 'fa-link',
pad: 'cptools-richtext',
code: 'cptools-code',
slide: 'cptools-slide',

@ -1050,6 +1050,7 @@ define([
var font = icon.indexOf('cptools') === 0 ? 'cptools' : 'fa';
if (type === 'fileupload') { type = 'file'; }
if (type === 'folderupload') { type = 'file'; }
if (type === 'link') { type = 'drive'; }
var appClass = ' cp-icon cp-icon-color-'+type;
$icon = $('<span>', {'class': font + ' ' + icon + appClass});
}
@ -1061,6 +1062,7 @@ define([
if (!data) { return $icon; }
var href = data.href || data.roHref;
var type = data.type;
if (data.static) { type = 'link'; }
if (!href && !type) { return $icon; }
if (!type) { type = Hash.parsePadUrl(href).type; }

@ -1032,10 +1032,19 @@ define([
icon: 'fa-picture-o',
action: function () {
var _cfg = {
types: ['file'],
types: ['file', 'link'],
where: ['root']
};
common.openFilePicker(_cfg, function (data) {
// Embed links
if (data.static) {
var a = h('a', {
href: data.href
}, data.name);
cfg.embed(a, data);
return;
}
// Embed files
if (data.type !== 'file') {
console.log("Unexpected data type picked " + data.type);
return;
@ -3021,6 +3030,75 @@ define([
UI.proposal(content, todo);
};
UIElements.displayOpenLinkModal = function (common, data, dismiss) {
var name = Util.fixHTML(data.title);
var url = data.href;
var user = data.name;
//Messages.link_open = "Open URL";
// openLinkInNewTab ("Open Link in New Tab")
// fc_open ("Open")
// share_linkOpen ("Preview")
// resources_openInNewTab ("Open it in a new tab")
Messages.link_open = Messages.fc_open; // XXX 4.10.0
//Messages.link_store = "Store link in drive";
// toolbar_storeInDrive ? ("Store in CryptDrive")
// autostore_store ? ("Store")
Messages.link_store = Messages.toolbar_storeInDrive; // XXX 4.10.0
var content = h('div', [
UI.setHTML(h('p'), Messages._getKey('notification_openLink', [name, user])),
h('pre', url),
UIElements.getVerifiedFriend(common, data.curve, user)
]);
var clicked = false;
var modal;
var buttons = [{
name: Messages.friendRequest_later,
onClick: function () {
if (clicked) { return true; }
clicked = true;
Feedback.send('LINK_RECEIVED_LATER');
},
keys: [27]
}, {
className: 'primary',
name: Messages.link_open,
onClick: function () {
if (clicked) { return true; }
clicked = true;
common.openUnsafeURL(url);
Feedback.send("LINK_RECEIVED_OPEN");
},
keys: [13]
}, {
className: 'primary',
name: Messages.link_store,
onClick: function () {
if (clicked) { return; }
clicked = true;
common.getSframeChannel().query("Q_DRIVE_USEROBJECT", {
cmd: "addLink",
data: {
name: name,
href: url,
path: ['root']
}
}, function () {
modal.closeModal();
dismiss();
Feedback.send("LINK_RECEIVED_STORE");
});
return true;
},
keys: [[13, 'ctrl']]
}];
var _modal = UI.dialog.customModal(content, {buttons: buttons});
modal = UI.openCustomModal(_modal);
return modal;
};
UIElements.displayAddOwnerModal = function (common, data) {
var priv = common.getMetadataMgr().getPrivateData();
var sframeChan = common.getSframeChannel();
@ -3618,6 +3696,13 @@ define([
return (pos.bottom < size) && (pos.y > 0);
};
UIElements.is24h = function () {
try {
return !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/);
} catch (e) {}
return false;
};
UIElements.openSnapshotsModal = function (common, load, make, remove) {
var modal;
var readOnly = common.getMetadataMgr().getPrivateData().readOnly;

@ -161,6 +161,10 @@
};
};
Util.inc = function (map, key, val) {
map[key] = (map[key] || 0) + (typeof(val) === 'number'? val: 1);
};
Util.find = function (map, path) {
var l = path.length;
for (var i = 0; i < l; i++) {

@ -443,6 +443,11 @@ define([
'data-icon': AppConfig.applicationsIcon.poll,
'data-type': 'poll'
}, Messages.button_newpoll)),
h('li', h('a.cp-app-drive-context-newdoc.dropdown-item.cp-app-drive-context-editable', {
'tabindex': '-1',
'data-icon': AppConfig.applicationsIcon.link,
'data-type': 'link'
}, Messages.fm_link_new)),
]),
]),
$separator.clone()[0],
@ -1106,11 +1111,24 @@ define([
common.getMediaTagPreview(mts, idx);
};
var refresh = APP.refresh = function () {
APP.displayDirectory(currentPath);
};
// `app`: true (force open wiht the app), false (force open in preview),
// falsy (open in preview if default is not using the app)
var defaultInApp = ['application/pdf'];
var openFile = function (el, isRo, app) {
var data = manager.getFileData(el);
if (data.static) {
if (data.href) {
common.openUnsafeURL(data.href);
manager.updateStaticAccess(el, refresh);
}
return;
}
if (!data || (!data.href && !data.roHref)) {
return void logError("Missing data for the file", el, data);
}
@ -1147,10 +1165,6 @@ define([
common.openURL(Hash.getNewPadURL(href, obj));
};
var refresh = APP.refresh = function () {
APP.displayDirectory(currentPath);
};
var pickFolderColor = function ($element, currentColor, cb) {
var colors = ["", "#f23c38", "#ff0073", "#da0eba", "#9d00ac", "#6c19b3", "#4a42b1", "#3d8af0", "#30a0f1", "#1fb9d1", "#009686", "#45b354", "#84c750", "#c6e144", "#faf147", "#fbc423", "#fc9819", "#fd5227", "#775549", "#9c9c9c", "#607a89"];
@ -1263,6 +1277,9 @@ define([
if ($element.is('.cp-border-color-sheet')) {
hide.push('download');
}
if ($element.is('.cp-app-drive-static')) {
hide.push('access', 'hashtag', 'properties', 'download');
}
if ($element.is('.cp-app-drive-element-file')) {
// No folder in files
hide.push('color');
@ -1899,6 +1916,31 @@ define([
// In list mode, display metadata from the filesData object
var addStaticData = function (element, $element, data) {
$element.addClass('cp-border-color-drive');
var name = data.name;
var $name = $('<span>', {'class': 'cp-app-drive-element-name'}).text(name);
$element.append($name);
if (getViewMode() === 'grid') {
//console.error(name, Util.fixHTML(name));
// this is only safe because our build of tippy sets titles as
// 'textContent' instead of innerHTML, otherwise
// we would need to use Util.fixHTML
$element.attr('title', name);
}
var type = Messages.fm_link_type;
var $type = $('<span>', {
'class': 'cp-app-drive-element-type cp-app-drive-element-list'
}).text(type);
var $adate = $('<span>', {
'class': 'cp-app-drive-element-atime cp-app-drive-element-list'
}).text(getDate(data.atime));
var $cdate = $('<span>', {
'class': 'cp-app-drive-element-ctime cp-app-drive-element-list'
}).text(getDate(data.ctime));
$element.append($type).append($adate).append($cdate);
};
var _addOwnership = function ($span, $state, data) {
if (data && Array.isArray(data.owners) && data.owners.indexOf(edPublic) !== -1) {
var $owned = $ownedIcon.clone().appendTo($state);
@ -1914,6 +1956,9 @@ define([
if (!manager.isFile(element)) { return; }
var data = manager.getFileData(element);
if (data.static) {
return addStaticData(element, $element, data);
}
if (!Object.keys(data).length) {
return true;
@ -2124,7 +2169,9 @@ define([
$icon = manager.isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone();
$icon.css("color", getFolderColor(path.concat(elPath)));
}
var classes = restrictedClass + roClass + liClass;
var staticClass = manager.isStaticFile(element) ? '.cp-app-drive-static' : '';
var classes = restrictedClass + roClass + liClass + staticClass;
var $element = $(h('li.cp-app-drive-element.cp-app-drive-element-row' + classes, {
draggable: true
}));
@ -2459,8 +2506,7 @@ define([
setViewMode(viewMode || 'grid');
showMode(viewMode);
$button.click(function (e) {
console.error(e);
$button.click(function () {
var viewMode = getViewMode();
var newViewMode = getOppositeViewMode(viewMode);
setViewMode(newViewMode);
@ -2686,6 +2732,74 @@ define([
});
$input.click();
};
var showLinkModal = function () {
var name, url;
var warning = h('div.alert.alert-warning', [
h('i.fa.fa-exclamation-triangle'),
h('span', Messages.fm_link_warning)
]);
var content = h('p', [
h('label', {for: 'cp-app-drive-link-name'}, Messages.fm_link_name),
name = h('input#cp-app-drive-link-name', { autocomplete: 'off', placeholder: Messages.fm_link_name_placeholder }),
h('label', {for: 'cp-app-drive-link-url'}, Messages.fm_link_url),
url = h('input#cp-app-drive-link-url', { type: 'url', autocomplete: 'off', placeholder: Messages.form_input_ph_url }),
warning,
]);
var protocolPattern = /https*:\/\//;
var fragmentPattern = /#.*$/;
var setNamePlaceholder = function (val) {
var temp = val.replace(protocolPattern, '').replace(fragmentPattern, '').trim().slice(0, 48);
if (!protocolPattern.test(val) || !temp) {
temp = Messages.fm_link_name_placeholder;
}
name.setAttribute('placeholder', temp);
};
var $warning = $(warning).hide();
var $url = $(url).on('change keypress keyup keydown', function () {
var v = $url.val().trim();
if (v.length > 200) {
$warning.show();
return;
}
setNamePlaceholder(v);
$warning.hide();
});
var buttons = [{
className: 'cancel',
name: Messages.cancelButton,
onClick: function () {},
keys: [27]
}];
buttons.push({
className: 'primary',
// We may want to use a new key here
iconClass: '.fa.fa-plus',
name: Messages.tag_add,
onClick: function () {
var $name = $(name);
var n = $name.val().trim() || $name.attr('placeholder');
var u = $url.val().trim();
if (!n || !u) { return true; }
if (!Util.isValidURL(u)) {
// XXX 4.10.0 add style for invalid input? input:invalid
UI.warn(Messages.fm_link_invalid);
return true;
}
manager.addLink(currentPath, {
name: n,
url: u
}, refresh);
Feedback.send("LINK_CREATED");
},
keys: [13]
});
var m = UI.dialog.customModal(content, {
buttons: buttons
});
UI.openCustomModal(m);
};
var addNewPadHandlers = function ($block, isInRoot) {
// Handlers
if (isInRoot) {
@ -2714,6 +2828,7 @@ define([
}
$block.find('a.cp-app-drive-new-fileupload, li.cp-app-drive-new-fileupload').click(showUploadFilesModal);
$block.find('a.cp-app-drive-new-folderupload, li.cp-app-drive-new-folderupload').click(showUploadFolderModal);
$block.find('a.cp-app-drive-new-link, li.cp-app-drive-new-link').click(showLinkModal);
}
$block.find('a.cp-app-drive-new-doc, li.cp-app-drive-new-doc')
.click(function () {
@ -2757,6 +2872,12 @@ define([
});
}
options.push({tag: 'hr'});
options.push({
tag: 'a',
attributes: {'class': 'cp-app-drive-new-link'},
content: $('<div>').append(getIcon('link')).html() + Messages.fm_link_new
});
options.push({tag: 'hr'});
}
getNewPadTypes().forEach(function (type) {
var attributes = {
@ -3073,6 +3194,13 @@ define([
$elementFolderUpload.append($('<span>', {'class': 'cp-app-drive-new-name'})
.text(Messages.uploadFolderButton));
}
// Link
var $elementLink = $('<li>', {
'class': 'cp-app-drive-new-link cp-app-drive-element-row ' +
'cp-app-drive-element-grid'
}).prepend(getIcon('link')).appendTo($container);
$elementLink.append($('<span>', {'class': 'cp-app-drive-new-name'})
.text(Messages.fm_link_type));
}
// Pads
getNewPadTypes().forEach(function (type) {
@ -3479,6 +3607,7 @@ define([
var path = paths[0];
if (manager.isPathIn(path, [TRASH])) { return; }
if (!file.channel) { file.channel = id; }
if (channels.indexOf(file.channel) !== -1) { return; }
channels.push(file.channel);
@ -4482,8 +4611,9 @@ define([
password: data.password
},
isTemplate: paths[0].path[0] === 'template',
title: data.title,
title: data.title || data.name,
sharedFolder: sf,
static: data.static ? data.href : undefined,
common: common
};
if (padType === 'file') {
@ -4501,6 +4631,20 @@ define([
data = manager.getSharedFolderData(el);
}
if (!data) { return; }
if (data.static) {
sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "addLink",
teamId: -1,
data: {
name: data.name,
href: data.href,
path: ['root']
}
}, function () {
UI.log(Messages.saved);
});
return;
}
sframeChan.query('Q_STORE_IN_TEAM', {
href: data.href || data.rohref,
password: data.password,
@ -4539,6 +4683,9 @@ define([
}
else if ($this.hasClass("cp-app-drive-context-newdoc")) {
var ntype = $this.data('type') || 'pad';
if (ntype === 'link') {
return void showLinkModal();
}
var path2 = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
openIn(ntype, path2, APP.team);
}

@ -90,7 +90,11 @@ define([
setTimeout(w);
});
if (res && /^http/.test(res)) {
href = Hash.getRelativeHref(res);
var _href = Hash.getRelativeHref(res);
if (_href) { href = _href; }
else {
href = res;
}
setTimeout(w);
return;
}
@ -109,6 +113,7 @@ define([
if (mailbox.notifications && mailbox.curvePublic) {
common.mailbox.sendTo("SHARE_PAD", {
href: href,
isStatic: Boolean(config.static),
password: config.password,
isTemplate: config.isTemplate,
name: myName,
@ -119,6 +124,9 @@ define([
channel: mailbox.notifications,
curvePublic: mailbox.curvePublic
});
if (config.static) {
Feedback.send("LINK_SHARED_WITH_CONTACT");
}
return;
}
}
@ -137,6 +145,21 @@ define([
});
return;
}
if (config.static) {
common.getSframeChannel().query("Q_DRIVE_USEROBJECT", {
cmd: "addLink",
teamId: team.id,
data: {
name: title,
href: href,
path: ['root']
}
}, function () {
UI.log(Messages.saved);
});
Feedback.send("LINK_ADDED_TO_DRIVE");
return;
}
sframeChan.query('Q_STORE_IN_TEAM', {
href: href,
password: config.password,
@ -346,6 +369,9 @@ define([
] : [
UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }),
];
if (opts.static) { linkContent = []; }
linkContent.push(h('div.cp-spacer'));
linkContent.push(UI.dialog.selectableArea('', { id: 'cp-share-link-preview', tabindex: 1, rows:3}));
@ -361,7 +387,7 @@ define([
// warning about sharing links
// when sharing a version hash, there is a similar warning and we want
// to avoid alert fatigue
if (!opts.versionHash) {
if (!opts.versionHash && !opts.static) {
var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable',
@ -405,6 +431,10 @@ define([
var v = opts.getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
});
if (opts.static) {
common.openUnsafeURL(v);
return true;
}
window.open(v);
return true;
},
@ -562,6 +592,7 @@ define([
});
};
opts.getLinkValue = function (initValue, cb) {
if (opts.static) { return opts.static; }
var val = initValue || {};
var edit = val.edit !== undefined ? val.edit : Util.isChecked($rights.find('#cp-share-editable-true'));
var embed = val.embed;
@ -686,7 +717,7 @@ define([
opts.access = true; // Allow the use of the modal even if the pad is not stored
var hashes = opts.hashes;
if (!hashes || (!hashes.editHash && !hashes.viewHash)) { return; }
if (!hashes || (!hashes.editHash && !hashes.viewHash && !opts.static)) { return; }
var teams = getEditableTeams(common, opts);
opts.teams = teams;
@ -705,19 +736,23 @@ define([
var $rights = opts.$rights = getRightsHeader(common, opts);
var resetTab = function () {
if (opts.static) { return; }
$rights.show();
$rights.find('label.cp-radio').show();
};
var onShowEmbed = function () {
if (opts.static) { return; }
$rights.find('#cp-share-bar').closest('label').hide();
$rights.find('input[type="radio"]:enabled').first().prop('checked', 'checked');
$rights.find('input[type="radio"]').trigger('change');
};
var onShowContacts = function () {
if (opts.static) { return; }
if (!hasFriends || priv.offline) {
$rights.hide();
}
};
if (opts.static) { $rights.hide(); }
var contactsActive = hasFriends && !priv.offline;
var tabs = [{
@ -732,13 +767,16 @@ define([
title: Messages.share_linkCategory,
icon: "fa fa-link",
active: !contactsActive,
}, {
getTab: getEmbedTab,
title: Messages.share_embedCategory,
icon: "fa fa-code",
onShow: onShowEmbed,
onHide: resetTab
}];
if (!opts.static) {
tabs.push({
getTab: getEmbedTab,
title: Messages.share_embedCategory,
icon: "fa fa-code",
onShow: onShowEmbed,
onHide: resetTab
});
}
Modal.getModal(common, opts, tabs, function (err, modal) {
// Hide the burn-after-reading option by default
var $modal = $(modal);

@ -92,6 +92,10 @@ define([
(type === 'file' ? 'notification_fileShared' : // Msg.notification_fileSharedTeam
'notification_padShared'); // Msg.notification_padSharedTeam
if (msg.content.isStatic) {
key = 'notification_linkShared'; // Msg.notification_linkShared;
}
var teamNotification = /^team-/.test(data.type) && Number(data.type.slice(5));
var teamName = '';
if (teamNotification) {
@ -109,6 +113,15 @@ define([
return Messages._getKey(key, [name, title, teamName]);
};
content.handler = function() {
if (msg.content.isStatic) {
UIElements.displayOpenLinkModal(common, {
curve: msg.author,
href: msg.content.href,
name: name,
title: title
}, defaultDismiss(common, data));
return;
}
var obj = {
p: msg.content.isTemplate ? ['template'] : undefined,
t: teamNotification || undefined,

@ -72,8 +72,31 @@ define([
return JSONSortify(obj);
};
/* Chrome 92 dropped support for SharedArrayBuffer in cross-origin contexts
where window.crossOriginIsolated is false.
Their blog (https://blog.chromium.org/2021/02/restriction-on-sharedarraybuffers.html)
isn't clear about why they're doing this, but since it's related to site-isolation
it seems they're trying to do vague security things.
In any case, there seems to be a workaround where you can still create them
by using `new WebAssembly.Memory({shared: true, ...})` instead of `new SharedArrayBuffer`.
This seems unreliable, but it's better than not being able to export, since
we actively rely on postMessage between iframes and therefore can't afford
to opt for full isolation.
*/
var supportsSharedArrayBuffers = function () {
try {
return Object.prototype.toString.call(new window.WebAssembly.Memory({shared: true, initial: 0, maximum: 0}).buffer) === '[object SharedArrayBuffer]';
} catch (err) {
console.error(err);
}
return false;
};
var supportsXLSX = function () {
return !(typeof(Atomics) === "undefined" || typeof (SharedArrayBuffer) === "undefined" || typeof(WebAssembly) === 'undefined');
return !(typeof(Atomics) === "undefined" || !supportsSharedArrayBuffers() /* || typeof (SharedArrayBuffer) === "undefined" */ || typeof(WebAssembly) === 'undefined');
};
@ -1915,7 +1938,9 @@ define([
var exportXLSXFile = function() {
var text = getContent();
var suggestion = Title.suggestTitle(Title.defaultTitle);
var ext = ['.xlsx', '.ods', '.bin', '.csv', '.pdf'];
var ext = ['.xlsx', '.ods', '.bin',
//'.csv', // XXX
'.pdf'];
var type = common.getMetadataMgr().getPrivateData().ooType;
var warning = '';
if (type==="presentation") {

@ -1,3 +1,8 @@
function SUPPORTS_SHARED_MEMORY() {
return typeof(SharedArrayBuffer) !== 'undefined';
}
// Support for growable heap + pthreads, where the buffer may change, so JS views
// must be updated.
function GROWABLE_HEAP_STORE_I8(ptr, value) {
@ -1030,7 +1035,7 @@ if (ENVIRONMENT_IS_PTHREAD) {
"maximum": 1073741824 / WASM_PAGE_SIZE,
"shared": true
});
if (!(wasmMemory.buffer instanceof SharedArrayBuffer)) {
if (Object.prototype.toString.call(wasmMemory.buffer) !== '[object SharedArrayBuffer]') {
err("requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag");
if (ENVIRONMENT_HAS_NODE) {
console.log("(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and also use a recent version)");
@ -2161,7 +2166,7 @@ var PThread = {
}),
receiveObjectTransfer: (function(data) {}),
allocateUnusedWorkers: (function(numWorkers, onFinishedLoading) {
if (typeof SharedArrayBuffer === "undefined") return;
if (!SUPPORTS_SHARED_MEMORY()) return;
var workers = [];
var numWorkersToCreate = numWorkers;
if (PThread.preallocatedWorkers.length > 0) {
@ -2276,7 +2281,7 @@ var PThread = {
}
}),
createNewWorkers: (function(numWorkers) {
if (typeof SharedArrayBuffer === "undefined") return [];
if (!SUPPORTS_SHARED_MEMORY()) return [];
var pthreadMainJs = "x2t.worker.js";
pthreadMainJs = locateFile(pthreadMainJs);
var newWorkers = [];
@ -5683,7 +5688,7 @@ function _emscripten_get_sbrk_ptr() {
}
Module["_emscripten_get_sbrk_ptr"] = _emscripten_get_sbrk_ptr;
function _emscripten_has_threading_support() {
return typeof SharedArrayBuffer !== "undefined";
return SUPPORTS_SHARED_MEMORY();
}
Module["_emscripten_has_threading_support"] = _emscripten_has_threading_support;
function _emscripten_is_main_browser_thread() {
@ -6761,7 +6766,7 @@ function _pthread_self() {
}
Module["_pthread_self"] = _pthread_self;
function _pthread_create(pthread_ptr, attr, start_routine, arg) {
if (typeof SharedArrayBuffer === "undefined") {
if (!SUPPORTS_SHARED_MEMORY()) {
err("Current environment does not support SharedArrayBuffer, pthreads are not available!");
return 6;
}

@ -1306,9 +1306,14 @@ define([
getAllStores().forEach(function (s) {
s.manager.getSecureFilesList(where).forEach(function (obj) {
var data = obj.data;
if (channels.indexOf(data.channel) !== -1) { return; }
if (channels.indexOf(data.channel || data.id) !== -1) { return; }
var id = obj.id;
if (data.channel) { channels.push(data.channel); }
if (data.channel) { channels.push(data.channel || data.id); }
// Only include static links if "link" is requested
if (data.static) {
if (types.indexOf('link') !== -1) { list[id] = data; }
return;
}
var parsed = Hash.parsePadUrl(data.href || data.roHref);
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) &&
!isFiltered(parsed.type, data)) {
@ -2053,8 +2058,17 @@ define([
} catch (e) {
console.error(e);
}
// Tell all the owners that the pad was deleted from the server
var curvePublic = store.proxy.curvePublic;
var curvePublic;
try {
// users in noDrive mode don't have a proxy and
// unregistered users don't have a curvePublic
curvePublic = store.proxy.curvePublic;
} catch (err) {
console.error(err);
return;
}
m.forEach(function (obj) {
var mb = JSON.parse(obj);
if (mb.curvePublic === curvePublic) { return; }

@ -97,7 +97,7 @@ define([
var checkCheckpoints = function (array) {
if (!Array.isArray(array)) { return; }
// Keep the last 100 messages
if (array.length > 100) { // XXX
if (array.length > 100) { // XXX 4.10.0
array.splice(0, array.length - 100);
}
// Remove every message before the first checkpoint

@ -238,8 +238,9 @@ define([
// content.name, content.title, content.href, content.password
if (isMuted(ctx, data)) { return void cb(true); }
var channel = Hash.hrefToHexChannelId(content.href, content.password);
// if the shared content is a 'link' then we can't use the channel to deduplicate notifications
// use href instead.
var channel = content.isStatic ? content.href : Hash.hrefToHexChannelId(content.href, content.password);
var parsed = Hash.parsePadUrl(content.href);
var mode = parsed.hashData && parsed.hashData.mode || 'n/a';

@ -195,7 +195,7 @@ define([
Pinpad.create(ctx.store.network, data, function (e, call) {
if (e) { return void cb(e); }
team.rpc = call;
team.onRpcReadyEvt.fire();
if (team && team.onRpcReadyEvt) { team.onRpcReadyEvt.fire(); }
cb();
}, Cache);
});

@ -20,6 +20,7 @@ define([
var ROOT = exp.ROOT;
var FILES_DATA = exp.FILES_DATA;
var STATIC_DATA = exp.STATIC_DATA;
var OLD_FILES_DATA = exp.OLD_FILES_DATA;
var UNSORTED = exp.UNSORTED;
var TRASH = exp.TRASH;
@ -78,6 +79,14 @@ define([
files[FILES_DATA][id] = data;
cb(null, id);
};
exp.pushLink = function (_data, cb) {
if (typeof cb !== "function") { cb = function () {}; }
if (readOnly) { return void cb('EFORBIDDEN'); }
var id = Util.createRandomInteger();
var data = clone(_data);
files[STATIC_DATA][id] = data;
cb(null, id);
};
exp.pushSharedFolder = function (_data, cb) {
if (typeof cb !== "function") { cb = function () {}; }
@ -136,7 +145,7 @@ define([
var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
var toClean = [];
exp.getFiles([FILES_DATA, SHARED_FOLDERS]).forEach(function (id) {
exp.getFiles([FILES_DATA, SHARED_FOLDERS, STATIC_DATA]).forEach(function (id) {
if (filesList.indexOf(id) === -1) {
var fd = exp.isSharedFolder(id) ? files[SHARED_FOLDERS][id] : exp.getFileData(id);
var channelId = fd.channel;
@ -146,6 +155,8 @@ define([
if (exp.isSharedFolder(id)) {
delete files[SHARED_FOLDERS][id];
if (config.removeProxy) { config.removeProxy(id); }
} else if (files[STATIC_DATA][id]) {
delete files[STATIC_DATA][id];
} else {
spliceFileData(id);
}
@ -242,6 +253,12 @@ define([
id = Number(id);
// Find and maybe update existing pads with the same channel id
var d = data[id];
// If we were given a static link, copy to STATIC_DATA
if (d.static) {
delete d.static;
files[STATIC_DATA][id] = d;
return;
}
// If we were given an edit link, encrypt its value if needed
if (d.href) { d.href = exp.cryptor.encrypt(d.href); }
var found = false;
@ -398,7 +415,7 @@ define([
if (!loggedIn && !config.testMode) { return; }
id = Number(id);
var data = files[FILES_DATA][id] || files[SHARED_FOLDERS][id];
var data = files[FILES_DATA][id] || files[STATIC_DATA][id] || files[SHARED_FOLDERS][id];
if (!data || typeof(data) !== "object") { return; }
var newPath = path, parentEl;
if (path && !Array.isArray(path)) {
@ -599,13 +616,18 @@ define([
var element = elem || files[ROOT];
if (!element) { return console.error("Invalid element in root"); }
var nbMetadataFolders = 0;
// caching this variables saves a lot of hashmap lookups in this loop
var static_data = files[STATIC_DATA];
var files_data = files[FILES_DATA];
var element_el;
for (var el in element) {
if (element[el] === null) {
element_el = element[el];
if (element_el === null) {
console.error('element[%s] is null', el);
delete element[el];
continue;
}
if (exp.isFolderData(element[el])) {
if (exp.isFolderData(element_el)) {
if (nbMetadataFolders !== 0) {
debug("Multiple metadata files in folder");
delete element[el];
@ -613,30 +635,30 @@ define([
nbMetadataFolders++;
continue;
}
if (!exp.isFile(element[el], true) && !exp.isFolder(element[el])) {
debug("An element in ROOT was not a folder nor a file. ", element[el]);
if (!exp.isFile(element_el, true) && !exp.isFolder(element_el)) {
debug("An element in ROOT was not a folder nor a file. ", element_el);
delete element[el];
continue;
}
if (exp.isFolder(element[el])) {
fixRoot(element[el]);
if (exp.isFolder(element_el)) {
fixRoot(element_el);
continue;
}
if (typeof element[el] === "string") {
if (typeof element_el === "string") {
// We have an old file (href) which is not in filesData: add it
var id = Util.createRandomInteger();
var key = Hash.createChannelId();
files[FILES_DATA][id] = {
href: exp.cryptor.encrypt(element[el]),
files_data[id] = {
href: exp.cryptor.encrypt(element_el),
filename: el
};
element[key] = id;
delete element[el];
}
if (typeof element[el] === "number") {
var data = files[FILES_DATA][element[el]];
if (typeof element_el === "number") {
var data = files_data[element_el] || static_data[element_el];
if (!data) {
debug("An element in ROOT doesn't have associated data", element[el], el);
debug("An element in ROOT doesn't have associated data", element_el, el);
delete element[el];
}
}
@ -845,6 +867,26 @@ define([
toClean.forEach(function (id) {
spliceFileData(id);
});
// make sure that links are displayed at least once in your drive if you are going to keep them
var sd = files[STATIC_DATA];
var toCleanSD = [];
for (var id2 in sd) {
id2 = Number(id2);
var el2 = sd[id2];
if (!el2 || typeof(el2) !== "object" || !el2.href) {
toCleanSD.push(id2);
continue;
}
if ((loggedIn || config.testMode) && rootFiles.indexOf(id2) === -1) {
toCleanSD.push(id2);
continue;
}
}
var spliceSD = function (id) {
if (readOnly) { return; }
delete files[STATIC_DATA][id];
};
toCleanSD.forEach(spliceSD);
};
var fixSharedFolders = function () {
if (sharedFolder) { return; }
@ -892,6 +934,12 @@ define([
}
}
};
var fixStaticData = function () {
if (!Util.isObject(files[STATIC_DATA])) {
debug("STATIC_DATA was not an object");
files[STATIC_DATA] = {};
}
};
var fixDrive = function () {
@ -900,6 +948,7 @@ define([
});
};
fixStaticData();
fixRoot();
fixTrashRoot();
fixTemplate();

@ -22,11 +22,11 @@ define([
// a cached version
if (Env.folders[id].offline && !lm.cache) {
Env.folders[id].offline = false;
if (Env.folders[id].userObject.fixFiles) { Env.folders[id].userObject.fixFiles(); }
Env.Store.refreshDriveUI();
}
return;
}
if (Env.folders[id]) { console.warn(Env.folders[id]); }
var cfg = getConfig(Env);
cfg.sharedFolder = true;
cfg.id = id;
@ -584,6 +584,24 @@ define([
});
});
};
// Add a link
var _addLink = function (Env, data, cb) {
data = data || {};
var resolved = _resolvePath(Env, data.path);
if (!resolved || !resolved.userObject) { return void cb({error: 'E_NOTFOUND'}); }
var uo = resolved.userObject;
var now = +new Date();
uo.pushLink({
name: data.name,
href: data.href,
atime: now,
ctime: now
}, function (e, id) {
if (e) { return void cb({error: e}); }
uo.add(id, resolved.path);
Env.onSync(cb);
});
};
var _restoreSharedFolder = function (Env, _data, cb) {
var fId = _data.id;
@ -1019,35 +1037,40 @@ define([
});
};
var _updateStaticAccess = function (Env, id, cb) {
var uo = _getUserObjectFromId(Env, id);
var sd = uo.getFileData(id, true);
sd.atime = +new Date();
Env.onSync(cb);
};
var COMMANDS = {
move: _move,
restore: _restore,
addFolder: _addFolder,
addSharedFolder: _addSharedFolder,
addLink: _addLink,
restoreSharedFolder: _restoreSharedFolder,
convertFolderToSharedFolder: _convertFolderToSharedFolder,
delete: _delete,
deleteOwned: _deleteOwned,
emptyTrash: _emptyTrash,
rename: _rename,
setFolderData: _setFolderData,
updateStaticAccess: _updateStaticAccess,
};
var onCommand = function (Env, cmdData, cb) {
var cmd = cmdData.cmd;
var data = cmdData.data || {};
switch (cmd) {
case 'move':
_move(Env, data, cb); break;
case 'restore':
_restore(Env, data, cb); break;
case 'addFolder':
_addFolder(Env, data, cb); break;
case 'addSharedFolder':
_addSharedFolder(Env, data, cb); break;
case 'restoreSharedFolder':
_restoreSharedFolder(Env, data, cb); break;
case 'convertFolderToSharedFolder':
_convertFolderToSharedFolder(Env, data, cb); break;
case 'delete':
_delete(Env, data, cb); break;
case 'deleteOwned':
_deleteOwned(Env, data, cb); break;
case 'emptyTrash':
_emptyTrash(Env, data, cb); break;
case 'rename':
_rename(Env, data, cb); break;
case 'setFolderData':
_setFolderData(Env, data, cb); break;
default:
cb();
var method = COMMANDS[cmd];
if (typeof(method) === 'function') {
return void method(Env, data, cb);
}
// if the command was not handled then call back
cb();
};
// Set the value everywhere the given pad is stored (main and shared folders)
@ -1129,8 +1152,8 @@ define([
data: uo.getFileData(id)
};
}).filter(function (d) {
if (channels.indexOf(d.data.channel) === -1) {
channels.push(d.data.channel);
if (channels.indexOf(d.data.channel || d.id) === -1) {
channels.push(d.data.channel || d.id);
return true;
}
});
@ -1233,7 +1256,10 @@ define([
Array.prototype.push.apply(result, sfChannels);
}
return result;
return result.filter(function (channel) {
if (typeof(channel) !== 'string') { return; }
return [32, 48].indexOf(channel.length) !== -1;
});
};
var addPad = function (Env, path, pad, cb) {
@ -1383,6 +1409,16 @@ define([
}
}, cb);
};
var addLinkInner = function (Env, path, data, cb) {
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "addLink",
data: {
path: path,
name: data.name,
href: data.url
}
}, cb);
};
var restoreSharedFolderInner = function (Env, fId, password, cb) {
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "restoreSharedFolder",
@ -1433,6 +1469,14 @@ define([
}, cb);
};
var updateStaticAccessInner = function (Env, id, cb) {
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "updateStaticAccess",
data: id
}, cb);
};
/* Tools */
var findChannels = _findChannels;
@ -1450,6 +1494,11 @@ define([
return String(uo.getTitle(id, type));
};
var isStaticFile = function (Env, id) {
var uo = _getUserObjectFromId(Env, id);
return uo.isStaticFile(id);
};
var isReadOnlyFile = function (Env, id) {
var uo = _getUserObjectFromId(Env, id);
return uo.isReadOnlyFile(id);
@ -1491,7 +1540,7 @@ define([
var files = [];
var userObjects = _getUserObjects(Env);
userObjects.forEach(function (uo) {
var data = uo.getFiles([UserObject.FILES_DATA]).map(function (id) {
var data = uo.getFiles([UserObject.FILES_DATA, UserObject.STATIC_DATA]).map(function (id) {
return [Number(id), uo.getFileData(id)];
});
Array.prototype.push.apply(files, data);
@ -1608,17 +1657,20 @@ define([
emptyTrash: callWithEnv(emptyTrashInner),
addFolder: callWithEnv(addFolderInner),
addSharedFolder: callWithEnv(addSharedFolderInner),
addLink: callWithEnv(addLinkInner),
restoreSharedFolder: callWithEnv(restoreSharedFolderInner),
convertFolderToSharedFolder: callWithEnv(convertFolderToSharedFolderInner),
delete: callWithEnv(deleteInner),
deleteOwned: callWithEnv(deleteOwnedInner),
restore: callWithEnv(restoreInner),
setFolderData: callWithEnv(setFolderDataInner),
updateStaticAccess: callWithEnv(updateStaticAccessInner),
// Tools
getFileData: callWithEnv(getFileData),
find: callWithEnv(find),
getTitle: callWithEnv(getTitle),
isReadOnlyFile: callWithEnv(isReadOnlyFile),
isStaticFile: callWithEnv(isStaticFile),
getFiles: callWithEnv(getFiles),
search: callWithEnv(search),
getRecentPads: callWithEnv(getRecentPads),

@ -733,11 +733,20 @@ define([
if (!common.isLoggedIn()) { return; }
$embedButton = common.createButton('mediatag', true).click(function () {
var cfg = {
types: ['file'],
types: ['file', 'link'],
where: ['root']
};
if ($embedButton.data('filter')) { cfg.filter = $embedButton.data('filter'); }
common.openFilePicker(cfg, function (data) {
// Embed links
if (data.static) {
var a = h('a', {
href: data.href
}, data.name);
mediaTagEmbedder($(a), data);
return;
}
// Embed files
if (data.type !== 'file') {
console.log("Unexpected data type picked " + data.type);
return;

@ -1925,7 +1925,6 @@ define([
var cryptputCfg = $.extend(true, {}, rtConfig, {password: password});
if (data.templateContent) {
Cryptget.put(currentPad.hash, JSON.stringify(data.templateContent), function () {
console.error(arguments);
startRealtime();
cb();
}, cryptputCfg);
@ -2004,6 +2003,8 @@ define([
sframeChan.on('EV_BURN_AFTER_READING', function () {
startRealtime();
// feedback fails for users in noDrive mode
Utils.Feedback.send("BURN_AFTER_READING", Boolean(cfg.noDrive));
});
sframeChan.ready();

@ -23,6 +23,12 @@ define([
var $title;
exp.setToolbar = function (toolbar) {
$title = toolbar && (toolbar.title || toolbar.pageTitle);
if ($title && exp.defaultTitle) {
var md = metadataMgr.getMetadata();
$title.find('input').prop('placeholder', md.defaultTitle);
$title.find('span.cp-toolbar-title-value').text(md.title || md.defaultTitle);
$title.find('input').val(md.title || md.defaultTitle);
}
};
exp.getTitle = function () { return exp.title; };

@ -921,7 +921,7 @@ define([
});
ctx.sframeChan.on('EV_WORKER_TIMEOUT', function () {
UI.errorLoadingScreen(Messages.timeoutError, false, function () {
UI.errorLoadingScreen(Messages.timeoutError, false, function () { // XXX mobile users can't necessarily hit 'ESC' as this message suggests. provice a click option
funcs.gotoURL('');
});
});

@ -878,10 +878,6 @@ MessengerUI, Messages, Pages) {
'class': "cp-toolbar-link-logo"
}).append(UIElements.getSvgLogo());
/*.append($('<img>', {
//src: '/customize/images/logo_white.png?' + ApiConfig.requireConf.urlArgs
src: '/customize/favicon/main-favicon.png?' + ApiConfig.requireConf.urlArgs
}));*/
var onClick = function (e) {
e.preventDefault();
if (e.ctrlKey) {

@ -1332,7 +1332,7 @@
"form_cantFindAnswers": "Deine vorigen Antworten für dieses Formular konnten nicht geladen werden.",
"form_updateWarning": "Trotzdem aktualisieren",
"form_submitWarning": "Trotzdem absenden",
"form_sent": "Gesendet",
"form_sent": "Deine Antwort wurde gesendet",
"form_update": "Aktualisieren",
"form_submit": "Absenden",
"form_type_checkbox": "Mehrfachauswahl",
@ -1368,5 +1368,16 @@
"admin_purpose_public": "Zur Bereitstellung eines kostenlosen Dienstes für die Allgemeinheit",
"resources_learnWhy": "Mehr über die Gründe erfahren",
"team_leaveOwner": "Bitte entferne dich von der Rolle des Eigentümers, bevor du das Teams verlässt. Beachte, dass Teams mindestens einen Eigentümer haben müssen. Bitte füge daher zunächst einen weiteren Eigentümer hinzu, sofern du derzeit der alleinige Eigentümer bist.",
"form_exportCSV": "Als CSV exportieren"
"form_exportCSV": "Als CSV exportieren",
"form_answerAs": "Antworten als",
"notification_openLink": "Du hast einen Link <b>{0}</b> von {1} erhalten:",
"fm_link_warning": "Warnung: URL ist länger als 200 Zeichen",
"notification_linkShared": "{0} hat einen Link mit dir geteilt: <b>{1}</b>",
"fm_link_name": "Bezeichnung des Links",
"fm_link_invalid": "Ungültige URL",
"form_anonName": "Dein Name",
"fm_link_name_placeholder": "Mein Link",
"fm_link_url": "URL",
"fm_link_type": "Link",
"fm_link_new": "Neuer Link"
}

@ -1315,7 +1315,7 @@
"form_updateWarning": "Mettre à jour avec erreurs",
"form_submitWarning": "Envoyer avec erreurs",
"form_delete": "Supprimer",
"form_sent": "Envoyé",
"form_sent": "Votre réponse a été envoyée",
"form_reset": "Effacer",
"form_update": "Mettre à jour",
"form_submit": "Envoyer",
@ -1368,5 +1368,16 @@
"admin_purpose_business": "Usage en entreprise",
"admin_instancePurposeHint": "À quel usage cette instance est-elle destinée ? Votre réponse sera utilisée pour définir la planification de nouvelles fonctionnalités (si votre télémétrie est activée).",
"team_leaveOwner": "Veuillez vous rétrograder de votre rôle de propriétaire avant de quitter l'équipe. Notez que les équipes doivent avoir au moins un propriétaire, veuillez en ajouter un autre avant de poursuivre si vous êtes actuellement le seul propriétaire.",
"form_exportCSV": "Exporter en CSV"
"form_exportCSV": "Exporter en CSV",
"fm_link_invalid": "URL invalide",
"fm_link_warning": "Attention : l'URL dépasse 200 caractères",
"form_answerAs": "Répondre en tant que",
"form_anonName": "Votre nom",
"notification_linkShared": "{0} a partagé un lien avec vous : <b>{1}</b>",
"fm_link_name_placeholder": "Nouveau lien",
"fm_link_url": "URL",
"fm_link_name": "Titre du lien",
"fm_link_type": "Lien",
"fm_link_new": "Nouveau Lien",
"notification_openLink": "Vous avez reçu un lien <b>{0}</b> de {1} :"
}

@ -1,7 +1,7 @@
{
"common_connectionLost": "<b>サーバーとの接続が切断しました</b><br>再接続するまで閲覧モードになります。",
"button_newsheet": "新規スプレッドシート",
"button_newkanban": "新規カンバン",
"button_newkanban": "新規カンバン",
"button_newwhiteboard": "新規ホワイトボード",
"button_newslide": "新規プレゼンテーション",
"button_newpoll": "新規投票・アンケート",
@ -111,7 +111,7 @@
"settings_backupCategory": "バックアップ",
"settings_backup": "バックアップ",
"settings_title": "設定",
"settings_cat_subscription": "サブスクリプション",
"settings_cat_subscription": "定額利用",
"settings_cat_drive": "CryptDrive",
"settings_cat_account": "アカウント",
"settings_save": "保存",
@ -125,7 +125,7 @@
"login_invalUser": "ユーザー名を入力してください",
"register_importRecent": "匿名セッションのドキュメントをインポート",
"importButton": "インポート",
"main_catch_phrase": "コラボレーションスイート<br>暗号化されかつオープンソース",
"main_catch_phrase": "コラボレーションスイート<br>端末間暗号化とオープンソース",
"tos_3rdparties": "私たちは、法令に基づく場合を除き、個人情報を第三者に提供しません。",
"tos_logs": "あなたのブラウザからサーバーに送信されたメタデータは、サービスを維持するために記録される場合があります。",
"tos_availability": "私たちはこのサービスがあなたの役に立つことを願っていますが、可用性や性能は保証できません。定期的にデータをエクスポートしてください。",
@ -150,7 +150,7 @@
"logoutButton": "ログアウト",
"login_login": "ログイン",
"autostore_hide": "保存しない",
"autostore_store": "保存する",
"autostore_store": "保存",
"autostore_notstored": "この{0}はあなたのCryptDriveに保存されていません。今すぐ保存しますか",
"user_displayName": "表示名",
"exportButton": "エクスポート",
@ -166,13 +166,13 @@
"profile_upload": " 新しいアバターをアップロード",
"teams_table_generic_edit": "編集: フォルダとパッドの作成、変更、削除が可能。",
"teams_table_generic_view": "表示: フォルダとパッドへのアクセス(閲覧のみ)。",
"teams_table_generic_own": "チームの管理: チーム名とチームのアバターの変更、オーナーの追加または削除、チームのサブスクリプションの変更、チームの削除が可能。",
"teams_table_generic_own": "チームの管理: チーム名とチームのアバターの変更、オーナーの追加または削除、チームの定額利用に関する変更、チームの削除が可能。",
"teams_table_owners": "チームの管理",
"teams_table_generic_admin": "メンバーの管理: メンバーの招待および取り消し、メンバーに管理者までの権限の付与が可能。",
"teams_table_admins": "メンバーの管理",
"teams_table_generic": "権限一覧",
"teams_table": "権限",
"contacts_fetchHistory": "古い履歴を取得する",
"contacts_fetchHistory": "古い履歴を取得",
"contacts_warning": "ここに入力した全てのメッセージは永続的であり、このパッドの現在および将来の全てのユーザーが確認できます。機密情報の入力には注意してください!",
"contacts_typeHere": "ここにメッセージを入力...",
"team_cat_drive": "ドライブ",
@ -228,14 +228,14 @@
"notifications_cat_friends": "連絡先リクエスト",
"notifications_dismiss": "確認済みにする",
"settings_autostoreMaybe": "手動 (確認しない)",
"settings_autostoreNo": "手動 (常に確認する)",
"settings_autostoreNo": "手動(常に確認)",
"settings_autostoreHint": "<b>自動</b> アクセスした全てのパッドをCryptDriveに保存します。<br><b>手動(常に確認)</b> 保存していないパッドにアクセスした際、CryptDriveに保存するかどうかを確認します。<br><b>手動(確認しない)</b> アクセス先のパッドはCryptDriveに自動で保存されません。保存オプションは表示されません。",
"settings_userFeedback": "ユーザーフィードバックを有効にする",
"settings_userFeedbackHint2": "あなたのパッドのコンテンツがサーバーと共有されることはありません。",
"settings_userFeedbackHint1": "CryptPadは、ユーザーエクスペリエンスの向上のため、いくつかの非常に基本的なフィードバックを、サーバーに提供します。 ",
"settings_userFeedbackTitle": "フィードバック",
"settings_autostoreYes": "自動",
"settings_importConfirm": "このブラウザで最近使用したパッドを、あなたのユーザーアカウントのCryptDriveにインポートしますか",
"settings_importConfirm": "このブラウザで最近用したパッドを、あなたのユーザーアカウントのCryptDriveにインポートしますか",
"settings_importDone": "インポートが完了しました",
"settings_import": "インポート",
"settings_importTitle": "このブラウザでの最近のパッドをあなたのCryptDriveにインポートします",
@ -246,7 +246,7 @@
"fc_delete": "ごみ箱へ移動",
"fc_remove": "削除",
"fc_restore": "復元",
"fc_delete_owned": "完全削除",
"fc_delete_owned": "破棄",
"creation_create": "作成",
"creation_password": "パスワード\n",
"creation_expireMonths": "か月",
@ -266,7 +266,7 @@
"features_f_cryptdrive1": "CryptDriveの全機能",
"features_f_anon_note": "追加機能あり",
"features_f_anon": "匿名ユーザーの全機能",
"features_f_storage0_note": "ドキュメントは{0}日以上使用されないと削除されます",
"features_f_storage0_note": "ドキュメントは{0}日以上用されないと削除されます",
"features_f_storage0": "一時的な保存",
"features_f_cryptdrive0_note": "アクセスしたパッドをブラウザに保存して、後で開くことができます",
"features_f_cryptdrive0": "CryptDriveへの限定的なアクセス",
@ -322,7 +322,7 @@
"properties_passwordSuccessFile": "パスワードは正常に変更されました。",
"drive_sfPasswordError": "誤ったパスワードです",
"team_title": "チーム: {0}",
"password_error": "ドキュメントが存在しません!<br>このエラーは、誤ったパスワードが入力された場合、またはドキュメントがサーバーから完全削除された場合に発生します。",
"password_error": "ドキュメントが存在しません!<br>このエラーは、誤ったパスワードが入力された場合、またはドキュメントがサーバーから破棄された場合に発生します。",
"password_error_seed": "パッドが存在しません!<br>このエラーは「パスワードが追加・変更された」場合、または「パッドがサーバーから削除された」場合に発生します。",
"password_submit": "送信",
"password_placeholder": "パスワードを入力...",
@ -332,7 +332,7 @@
"properties_addPassword": "パスワードを設定",
"history_close": "閉じる",
"history_restore": "復元",
"fm_emptyTrashOwned": "ごみ箱に、あなたが所有しているドキュメントが入っています。あなたのドライブからのみ<b>削除</b>するか、全てのユーザーから<b>完全削除</b>するかを選択できます。",
"fm_emptyTrashOwned": "ごみ箱に、あなたの所有するドキュメントが入っています。あなたのドライブからのみ<b>削除</b>するか、全てのユーザーから<b>破棄</b>するかを選択できます。",
"access_destroyPad": "このドキュメントまたはフォルダを完全に削除する",
"accessButton": "アクセス",
"access_allow": "リスト",
@ -417,7 +417,7 @@
"fm_noname": "無題のドキュメント",
"fm_openParent": "フォルダに表示",
"fm_newButtonTitle": "新しいドキュメントやフォルダを作成したり、ファイルを現在のフォルダにインポートしたりできます。",
"contacts_info4": "チャットの参加者のどちらも履歴を削除できます",
"contacts_info4": "チャットの参加者は誰でも履歴を消去できます",
"contacts_info2": "連絡先のアイコンをクリックしてチャットを開始",
"contacts_confirmRemove": "<em>{0}</em>をあなたの連絡先から削除してよろしいですか?",
"profile_error": "プロフィールの作成時にエラーが発生しました: {0}",
@ -475,7 +475,7 @@
"poll_create_option": "新しいオプションを追加",
"poll_create_user": "新しいユーザーを追加",
"pad_mediatagImport": "あなたのCryptDriveに保存",
"admin_listMyInstanceLabel": "このインスタンスをリストに表示する",
"admin_listMyInstanceLabel": "このインスタンスをリストに表示",
"admin_checkupTitle": "インスタンスの設定を検証",
"cba_disable": "消去して無効にする",
"upload_pending": "保留中",
@ -560,7 +560,7 @@
"form_form": "フォーム",
"form_editor": "エディタ",
"form_delete": "削除",
"form_sent": "送信しました",
"form_sent": "回答を送信しました",
"form_reset": "リセット",
"form_update": "更新",
"form_submit": "送信",
@ -595,7 +595,7 @@
"toolbar_file": "ファイル",
"drive_treeButton": "ファイル",
"toolbar_insert": "挿入",
"fm_sort": "並び替え",
"fm_sort": "並び替え",
"comments_comment": "コメント",
"comments_resolve": "解決",
"comments_reply": "返信",
@ -765,25 +765,25 @@
"errorState": "重大なエラー: {0}",
"realtime_unrecoverableError": "回復不能なエラーが発生しました。OKをクリックして再読み込みを行ってください。",
"disabledApp": "このアプリケーションは無効になっています。詳細については、このCryptPadの管理者にお問い合わせください。",
"deletedFromServer": "パッドは完全削除されました",
"deletedFromServer": "ドキュメントが破棄されました",
"newVersionError": "新しいバージョンのCryptPadがあります。<br><a href='#'>リロードする</a>と新しいバージョンを読み込みます。<em>Esc</em>キーを押すと<b>オフラインモード</b>でコンテンツにアクセスします。",
"errorRedirectToHome": "<em>Esc</em>キーを押すとCryptDriveにリダイレクトします。",
"errorCopy": " <em>Esc</em>キーを押すと、閲覧モードで引き続きコンテンツにアクセスできます。",
"invalidHashError": "要求したドキュメントのURLが無効です。",
"chainpadError": "コンテンツを更新する際に重大なエラーが発生しました。コンテンツが失われないよう、閲覧モードで表示されています。<br>このパッドを表示し続けるには<em>Esc</em>キーを押し、再度編集を試みるにはリロードをしてください。",
"inactiveError": "このパッドは使用されていなかったため削除されました。Escキーを押して新しいパッドを作成します。",
"deletedError": "このドキュメントは削除されたため、使用できなくなりました。",
"expiredError": "このパッドは使用期限が過ぎてしまったため、使用できなくなりました。",
"inactiveError": "このパッドは利用されていなかったため削除されました。Escキーを押すと新しいパッドを作成します。",
"deletedError": "このドキュメントは削除されたため、用できなくなりました。",
"expiredError": "このパッドは利用期限を過ぎてしまったため、利用できなくなりました。",
"anonymousStoreDisabled": "このCryptPadのインスタンスの管理者は、匿名ユーザーによる保存を無効に設定しています。CryptDriveを使用するにはログインする必要があります。",
"padNotPinnedVariable": "このパッドは{4}日使用しないと期限切れになります。{0}ログイン{1}または{2}登録{3}し保存してください。",
"padNotPinned": "このパッドは3ヶ月間使用しないと有効期限が切れます。{0}ログイン{1}するか{2}登録{3}して保存してください。",
"padNotPinnedVariable": "このパッドは{4}日間利用しないと有効期限が切れます。{0}ログイン{1}するか{2}登録{3}して保存してください。",
"padNotPinned": "このパッドは3か月間利用しないと有効期限が切れます。{0}ログイン{1}するか{2}登録{3}して保存してください。",
"onLogout": "ログアウトしました。{0}ここをクリック{1}するか<br><em>Escape</em>キーを押すと、閲覧モードでパッドにアクセスできます。",
"typeError": "このパッドは選択したアプリケーションと互換性がありません",
"form_type_page": "ページ分割",
"form_description_default": "ここにテキストを入力",
"team_pcsSelectHelp": "所有するパッドをチームのドライブに作成すると、そのパッドのオーナー権はチームに与えられます。",
"sharedFolders_create_owned": "所有するフォルダ",
"creation_owned1": "<b>所有している</b>項目は、オーナーの望むときにいつでも完全削除できます。完全削除すると、他のユーザーのCryptDriveからも削除されます。",
"creation_owned1": "<b>所有している</b>項目は、オーナーの望むときにいつでも破棄できます。破棄すると、他のユーザーのCryptDriveからも削除されます。",
"creation_owned": "パッドを所有",
"uploadFolder_modal_owner": "所有するファイル",
"upload_modal_owner": "所有するファイル",
@ -824,7 +824,7 @@
"pad_goToAnchor": "アンカーに移動",
"oo_cantMigrate": "この表はアップロードの最大のサイズを超えているため、移行することができません。",
"footer_roadmap": "ロードマップ",
"settings_deleteSubscription": "サブスクリプションを管理",
"settings_deleteSubscription": "定額利用を管理",
"broadcast_translations": "翻訳",
"admin_broadcastCancel": "メッセージを削除",
"admin_broadcastButton": "送信",
@ -1252,7 +1252,7 @@
"owner_removeMeConfirm": "オーナー権を放棄しようとしています。これは取り消せません。よろしいですか?",
"requestEdit_confirm": "{1}がパッド「<b>{0}</b>」の編集権を要求しました。編集権を与えますか?",
"admin_supportInitHelp": "サーバーはサポートメールボックスを使用するように設定されていません。サポートメールボックスを有効にし、ユーザーからメッセージを受け取るためには、サーバーの管理者に連絡し、「./scripts/generate-admin-keys.js」のスクリプトを実行してもらい、生成された公開鍵を「config.js」に保存して、秘密鍵をあなたに送信してもらうよう依頼する必要があります。",
"feedback_privacy": "私たちはプライバシー配慮すると同時に、CryptPadを使いやすくしたいと望んでいます。このファイルは、実行されたアクションを特定するパラメーターと共に要求され、ユーザーにとって重要なUI機能を特定するために使用されます。",
"feedback_privacy": "私たちはプライバシー配慮すると同時に、CryptPadを使いやすくしたいと望んでいます。このファイルは、実行されたアクションを特定するパラメーターと共に要求され、ユーザーにとって重要なUI機能を特定するために使用されます。",
"register_warning_note": "暗号化を行うCryptPadの性質上、サービス管理者は、ユーザー名とパスワードを忘れた場合にデータを回復することができません。ユーザー名とパスワードを安全な場所に保管してください。",
"history_restoreDriveTitle": "選択したバージョンのCryptDriveを復元",
"errorPopupBlocked": "新しいタブを開く許可が必要です。お使いのブラウザのアドレスバーから、ポップアップウィンドウを許可してください。これらのウィンドウが広告の表示に使用されることはありません。",
@ -1308,8 +1308,8 @@
"share_linkPasswordAlert": "この項目はパスワードで保護されています。リンクを受け取った相手はパスワードを入力する必要があります。",
"register_notes_title": "重要な注意事項",
"teams_table_specificHint": "以前のバージョンの共有フォルダでは、閲覧者は既存のパッドを変更することができます。 これらのフォルダで作成またはコピーしたパッドには、標準の権限が与えられます。",
"broadcast_maintenance": "<b>{0}</b>から<b>{1}</b>の間でメンテナンスを予定しています。その間CryptPadは使用できません。",
"admin_archiveHint": "ドキュメントを完全削除することなく、利用できないよう設定できます。「アーカイブ」フォルダに移動し、数日後に削除します(期間については設定ファイルより設定できます)。",
"broadcast_maintenance": "<b>{0}</b>から<b>{1}</b>の間でメンテナンスを予定しています。その間CryptPadは用できません。",
"admin_archiveHint": "ドキュメントを破棄することなく、利用できないよう設定できます。「アーカイブ」フォルダに移動し、数日後に削除します(期間については設定ファイルより設定できます)。",
"admin_unarchiveHint": "アーカイブされたドキュメントを復元できます",
"admin_registrationTitle": "登録を締め切る",
"admin_defaultlimitHint": "ユーザー定義のルールが適用されていない際の最大のストレージ容量(ユーザーとチームについて)を設定できます",
@ -1368,5 +1368,16 @@
"reminder_time": "<b>{0}</b>が今日の{1}にあります",
"form_answerWarning": "本人であることが確認されていません",
"team_leaveOwner": "チームから退出する前に、オーナーの役割から降格してください。チームには最低1人以上のオーナーが必要です。あなたが唯一のオーナーの場合は、別のオーナーを追加してください。",
"form_exportCSV": "CSVにエクスポート"
"form_exportCSV": "CSVにエクスポート",
"fm_link_new": "新しいリンク",
"notification_openLink": "{1}からリンク「 <b>{0}</b>」を受け取りました:",
"fm_link_type": "リンク",
"fm_link_url": "URL",
"fm_link_name": "リンク名",
"form_anonName": "あなたの名前",
"notification_linkShared": "{0}があなたとリンクを共有しました: <b>{1}</b>",
"fm_link_name_placeholder": "あなたのリンク",
"fm_link_warning": "注意URLが200字を超えています",
"fm_link_invalid": "URLが無効です",
"form_answerAs": "名前を記入してください"
}

@ -1230,7 +1230,7 @@
"genericCopySuccess": "Copied to clipboard",
"mediatag_defaultImageName": "image",
"register_registrationIsClosed": "Registration is closed.",
"oo_conversionSupport": "Your browser cannot handle conversion to and from Microsoft Office formats. We suggest using a recent version of Firefox or Chrome.",
"oo_conversionSupport": "Your browser cannot handle conversion to and from office formats. We suggest using a recent version of Firefox or Chrome.",
"oo_importBin": "Click OK to import CryptPad's internal .bin format.",
"admin_emailTitle": "Admin contact email",
"admin_emailHint": "Set the contact email for your instance here",
@ -1278,7 +1278,7 @@
"form_submit": "Submit",
"form_update": "Update",
"form_reset": "Reset",
"form_sent": "Sent",
"form_sent": "Your response was submitted",
"form_delete": "Delete",
"form_submitWarning": "Submit anyway",
"form_updateWarning": "Update anyway",
@ -1368,5 +1368,16 @@
"admin_purpose_business": "For a business or commercial organization",
"admin_instancePurposeHint": "Why do you run this instance? Your answer will be used to inform the development roadmap if your telemetry is enabled.",
"team_leaveOwner": "Please demote yourself from the owner role before leaving the team. Note that teams must have at least one owner, please add one before proceeding if you are currently the only owner.",
"form_exportCSV": "Export to CSV"
"form_exportCSV": "Export to CSV",
"notification_openLink": "You've received a link <b>{0}</b> from {1}:",
"fm_link_new": "New Link",
"fm_link_type": "Link",
"fm_link_name": "Link name",
"fm_link_url": "URL",
"fm_link_name_placeholder": "My link",
"notification_linkShared": "{0} has shared a link with you: <b>{1}</b>",
"form_anonName": "Your name",
"form_answerAs": "Answer as",
"fm_link_warning": "Warning: the URL exceeds 200 characters",
"fm_link_invalid": "Invalid URL"
}

@ -182,5 +182,27 @@
"tags_add": "Atnaujinkite pažymėtų padų žymas",
"tags_title": "Žymos (tik jums)",
"filePicker_filter": "Filtruokite failus pagal pavadinimą",
"filePicker_description": "Pasirinkite failą iš „CryptDrive“, kad jį įdėtumėte arba įkeltumėte naują"
"filePicker_description": "Pasirinkite failą iš „CryptDrive“, kad jį įdėtumėte arba įkeltumėte naują",
"oo_cantUpload": "Įkelti negalima, kol dalyvauja kiti vartotojai.",
"oo_reconnect": "Serverio ryšys atkurtas. Spustelėkite Gerai, jei norite iš naujo įkelti ir tęsti leidimą.",
"poll_comment_disabled": "Paskelbkite šią apklausą naudodami mygtuką ✓, kad įgalintumėte komentarus.",
"poll_comment_placeholder": "Jūsų komentaras",
"poll_comment_remove": "Ištrinti šį komentarą",
"poll_comment_submit": "Siųsti",
"poll_comment_add": "Pridėti komentarą",
"poll_comment_list": "Komentarai",
"poll_total": "Bendras",
"poll_bookmarked_col": "Tai yra jūsų pažymėtas stulpelis. Jums visada bus atrakinta ir rodoma pradžioje.",
"poll_bookmark_col": "Pažymėkite šį stulpelį taip, kad jis visada būtų atrakintas ir rodomas pradžioje",
"poll_unlocked": "Atrakinta",
"poll_locked": "Užrakinta",
"poll_edit": "Redaguoti",
"poll_remove": "Pašalinti",
"poll_descriptionHint": "Apibūdinkite apklausą ir, kai baigsite, naudokite mygtuką ✓ (paskelbti).\nAprašymas gali būti parašytas naudojant žymėjimo sintaksę ir galite įdėti laikmenos elementus iš savo „CryptDrive“.\nVisi, turintys nuorodą, gali pakeisti aprašą, tačiau tai nerekomenduojama.",
"poll_removeUser": "Ar tikrai norite pašalinti šį vartotoją?",
"poll_removeOption": "Ar tikrai norite pašalinti šią parinktį?",
"poll_userPlaceholder": "Vardas",
"poll_optionPlaceholder": "Pasirinkimai",
"poll_commit": "Pateikti",
"poll_create_option": "Pridėti naują parinktį"
}

@ -13,7 +13,8 @@
"todo": "待办事项",
"teams": "团队",
"sheet": "工作表",
"contacts": "联系我们"
"contacts": "联系我们",
"form": "从"
},
"button_newpad": "富文件檔案",
"button_newcode": "新代碼檔案",

@ -17,6 +17,7 @@ define([
var SHARED_FOLDERS_TEMP = module.SHARED_FOLDERS_TEMP = "sharedFoldersTemp"; // Maybe deleted or new password
var FILES_DATA = module.FILES_DATA = Constants.storageKey;
var OLD_FILES_DATA = module.OLD_FILES_DATA = Constants.oldStorageKey;
var STATIC_DATA = module.STATIC_DATA = 'static';
// Create untitled documents when no name is given
var getLocaleDate = function () {
@ -138,6 +139,7 @@ define([
var NEW_FILE_NAME = Messages.fm_newFile || 'New file';
exp.ROOT = ROOT;
exp.STATIC_DATA = STATIC_DATA;
exp.UNSORTED = UNSORTED;
exp.TRASH = TRASH;
exp.TEMPLATE = TEMPLATE;
@ -236,6 +238,10 @@ define([
return Boolean(data.roHref && !data.href);
};
exp.isStaticFile = function (element) {
return Boolean(files[STATIC_DATA] && files[STATIC_DATA][element]);
};
var isFolder = exp.isFolder = function (element) {
if (isFolderData(element)) { return false; }
return typeof(element) === "object" || isSharedFolder(element);
@ -310,7 +316,24 @@ define([
// Get data from AllFiles (Cryptpad_RECENTPADS)
var getFileData = exp.getFileData = function (file, editable) {
if (!file) { return; }
var data = files[FILES_DATA][file] || {};
var link;
try {
link = (files[STATIC_DATA] || {})[file];
} catch (err) {
console.error(err);
}
if (link) {
var _link = editable ? link : Util.clone(link);
if (!editable) { _link.static = true; }
return _link;
}
var data;
try {
data = files[FILES_DATA][file] || {};
} catch (err) {
console.error(err);
data = {};
}
if (!editable) {
data = JSON.parse(JSON.stringify(data));
if (data.href && data.href.indexOf('#') === -1) {
@ -344,7 +367,13 @@ define([
return '??';
}
var data = getFileData(file);
if (!file || !data || !(data.href || data.roHref)) {
if (!data) {
error("unable to retrieve data about the requested file: ", file, data);
return;
}
// handle links
if (data.static) { return data.name; }
if (!file || !(data.href || data.roHref)) {
error("getTitle called with a non-existing file id: ", file, data);
return;
}
@ -475,6 +504,11 @@ define([
});
return ret;
};
_getFiles[STATIC_DATA] = function () {
var ret = [];
if (!files[STATIC_DATA]) { return ret; }
return Object.keys(files[STATIC_DATA]).map(Number).filter(Boolean);
};
_getFiles[FILES_DATA] = function () {
var ret = [];
if (!files[FILES_DATA]) { return ret; }
@ -854,6 +888,7 @@ define([
// RENAME
exp.rename = function (path, newName, cb) {
cb = cb || function () {};
if (sframeChan) {
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "rename",
@ -891,9 +926,15 @@ define([
if (isSharedFolder(element)) {
data = files[SHARED_FOLDERS][element];
} else {
data = files[FILES_DATA][element];
data = files[FILES_DATA][element] || files[STATIC_DATA][element];
}
if (!data) { return; }
if (files[STATIC_DATA][element]) {
if (!newName || !newName.trim()) { return void cb(); }
data.name = newName;
cb();
return;
}
if (!newName || newName.trim() === "") {
delete data.filename;
if (typeof cb === "function") { cb(); }

@ -192,8 +192,8 @@ define([
},
};
Messages.convertPage = "Convert"; // XXX
Messages.convert_hint = "Pick the file you want to convert. The list of output format will be visible afterward."; // XXX
Messages.convertPage = "Convert"; // XXX 4.10.0
Messages.convert_hint = "Pick the file you want to convert. The list of output format will be visible afterward."; // XXX 4.10.0
var createToolbar = function () {
var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle', 'notifications'];

@ -247,6 +247,15 @@
.cp-form-anon-answer {
text-align: center;
margin: 20px 0 30px 0;
.cp-form-anon-answer-input {
margin-top: 20px;
display: flex;
white-space: nowrap;
align-items: center;
input {
margin-left: 10px;
}
}
}
}
@ -539,6 +548,7 @@
.cp-form-individual {
background: @cp_form-bg1;
padding: 10px;
margin-bottom: 20px;
& > *:not(:last-child) {
margin-right: 10px;
}
@ -551,6 +561,9 @@
margin-right: 5px;
}
}
a, a:visited {
color: @cryptpad_color_link;
}
}
}
}
@ -669,6 +682,16 @@
.avatar_main(30px);
margin-right: 10px;
}
&.cp-clickable {
cursor: pointer;
&:hover {
color: @cryptpad_color_link;
&::after {
font-family: FontAwesome;
content: "\00a0\f06e";
}
}
}
}
.cp-poll-time-day {
flex-basis: 100px;

@ -68,13 +68,9 @@ define([
var APP = window.APP = {
};
var is24h = false;
var is24h = UIElements.is24h();
var dateFormat = "Y-m-d H:i";
var timeFormat = "H:i";
try {
is24h = !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/);
} catch (e) {}
is24h = false;
if (!is24h) {
dateFormat = "Y-m-d h:i K";
timeFormat = "h:i K";
@ -298,6 +294,7 @@ define([
del
]);
$(del).click(function () {
var $block = $(el).closest('.cp-form-edit-block');
$(el).remove();
// We've just deleted an item/option so we should be under the MAX limit and
// we can show the "add" button again
@ -306,6 +303,13 @@ define([
$add.show();
if (v.type === "time") { $(addMultiple).show(); }
}
// decrement the max choices input when there are fewer options than the current maximum
if (maxInput) {
var inputs = $block.find('input').length;
var $maxInput = $(maxInput);
var currentMax = Number($maxInput.val());
$maxInput.val(Math.min(inputs, currentMax));
}
});
return el;
};
@ -588,7 +592,9 @@ define([
return weekDays.map(function (day) { return day.replace(/^./, function (str) { return str.toUpperCase(); }); });
};
var makePollTable = function (answers, opts) {
// "resultsPageObj" is an object with "content" and "answers"
// only available when viewing the Responses page
var makePollTable = function (answers, opts, resultsPageObj) {
// Sort date values
if (opts.type !== "text") {
opts.values.sort(function (a, b) {
@ -607,7 +613,7 @@ define([
_date = new Date(data);
data = Flatpickr.formatDate(_date, timeFormat);
}
var day = allDays[_date.getDay()];
var day = _date && allDays[_date.getDay()];
return h('div.cp-poll-cell.cp-form-poll-option', {
title: Util.fixHTML(data)
}, [
@ -667,13 +673,19 @@ define([
}, v);
return cell;
});
els.unshift(h('div.cp-poll-cell.cp-poll-answer-name', {
var nameCell;
els.unshift(nameCell = h('div.cp-poll-cell.cp-poll-answer-name', {
title: Util.fixHTML(name)
}, [
avatar,
h('span', name)
]));
bodyEls.push(h('div', els));
if (resultsPageObj && (APP.isEditor || APP.isAuditor)) {
$(nameCell).addClass('cp-clickable').click(function () {
APP.renderResults(resultsPageObj.content, resultsPageObj.answers, answerObj.curve);
});
}
});
}
var body = h('div.cp-form-poll-body', bodyEls);
@ -815,6 +827,7 @@ define([
if (filterCurve && user === filterCurve) { return; }
try {
return {
curve: user,
user: answers[user].msg._userdata,
results: answers[user].msg[uid]
};
@ -977,7 +990,7 @@ define([
printResults: function (answers, uid) {
var results = [];
var empty = 0;
Object.keys(answers).forEach(function (author) {
Object.keys(answers).forEach(function (author) { // TODO deduplicate these?
var obj = answers[author];
var answer = obj.msg[uid];
if (!answer || !answer.trim()) { return empty++; }
@ -1043,7 +1056,7 @@ define([
printResults: function (answers, uid) {
var results = [];
var empty = 0;
Object.keys(answers).forEach(function (author) {
Object.keys(answers).forEach(function (author) { // TODO deduplicate these
var obj = answers[author];
var answer = obj.msg[uid];
if (!answer || !answer.trim()) { return empty++; }
@ -1117,8 +1130,7 @@ define([
var obj = answers[author];
var answer = obj.msg[uid];
if (!answer || !answer.trim()) { return empty++; }
count[answer] = count[answer] || 0;
count[answer]++;
Util.inc(count, answer);
});
Object.keys(count).forEach(function (value) {
results.push(h('div.cp-form-results-type-radio-data', [
@ -1220,8 +1232,7 @@ define([
var c = count[q_uid] = count[q_uid] || {};
var res = answer[q_uid];
if (!res || !res.trim()) { return; }
c[res] = c[res] || 0;
c[res]++;
Util.inc(c, res);
});
});
Object.keys(count).forEach(function (q_uid) {
@ -1331,8 +1342,7 @@ define([
var answer = obj.msg[uid];
if (!Array.isArray(answer) || !answer.length) { return empty++; }
answer.forEach(function (val) {
count[val] = count[val] || 0;
count[val]++;
Util.inc(count, val);
});
});
Object.keys(count).forEach(function (value) {
@ -1447,8 +1457,7 @@ define([
var res = answer[q_uid];
if (!Array.isArray(res) || !res.length) { return; }
res.forEach(function (v) {
c[v] = c[v] || 0;
c[v]++;
Util.inc(c, v);
});
});
});
@ -1586,7 +1595,7 @@ define([
if (!Array.isArray(answer) || !answer.length) { return empty++; }
answer.forEach(function (el, i) {
var score = l - i;
count[el] = (count[el] || 0) + score;
Util.inc(count, el, score);
});
});
var sorted = Object.keys(count).sort(function (a, b) {
@ -1615,7 +1624,7 @@ define([
if (!opts) { opts = TYPES.poll.defaultOpts; }
if (!Array.isArray(opts.values)) { return; }
var lines = makePollTable(answers, opts);
var lines = makePollTable(answers, opts, false);
// Add form
var addLine = opts.values.map(function (data) {
@ -1699,31 +1708,53 @@ define([
};
},
printResults: function (answers, uid, form) {
printResults: function (answers, uid, form, content) {
var opts = form[uid].opts || TYPES.poll.defaultOpts;
var _answers = getBlockAnswers(answers, uid);
var lines = makePollTable(_answers, opts);
// If content is defined, we'll be able to click on a row to display
// all the answers of this user
var lines = makePollTable(_answers, opts, content && {
content: content,
answers: answers
});
var total = makePollTotal(_answers, opts);
if (total) { lines.push(h('div', total)); }
return h('div.cp-form-type-poll', lines);
},
exportCSV: function (answer) {
if (answer === false) { return; }
if (!answer || !answer.values) { return ['']; }
exportCSV: function (answer, form) {
var opts = form.opts || TYPES.poll.defaultOpts;
var q = form.q || Messages.form_default;
if (answer === false) {
var cols = opts.values.map(function (key) {
return q + ' | ' + key;
});
cols.unshift(q);
return cols;
}
if (!answer || !answer.values) {
var empty = opts.values.map(function () { return ''; });
empty.unshift('');
return empty;
}
var str = '';
Object.keys(answer.values).sort().forEach(function (k, i) {
if (i !== 0) { str += ';'; }
str += k.replace(';', '').replace(':', '') + ':' + answer.values[k];
});
return [str];
var res = opts.values.map(function (key) {
return answer.values[key] || '';
});
res.unshift(str);
return res;
},
icon: h('i.cptools.cptools-form-poll')
},
};
var renderResults = function (content, answers) {
var renderResults = APP.renderResults = function (content, answers, showUser) {
var $container = $('div.cp-form-creator-results').empty();
if (!Object.keys(answers || {}).length) {
@ -1739,7 +1770,7 @@ define([
var controls = h('div.cp-form-creator-results-controls');
var $controls = $(controls).appendTo($container);
var exportButton = h('button.btn.btn-secondary', Messages.exportButton); // XXX form_exportCSV;
var exportButton = h('button.btn.btn-secondary', Messages.form_exportCSV);
var exportCSV = h('div.cp-form-creator-results-export', exportButton);
$(exportCSV).appendTo($container);
var results = h('div.cp-form-creator-results-content');
@ -1767,7 +1798,9 @@ define([
var type = block.type;
var model = TYPES[type];
if (!model || !model.printResults) { return; }
var print = model.printResults(answers, uid, form);
// Only use content if we're not viewing individual answers
var print = model.printResults(answers, uid, form, !header && content);
var q = h('div.cp-form-block-question', block.q || Messages.form_default);
@ -1861,10 +1894,19 @@ define([
e.preventDefault();
APP.common.openURL(Hash.hashToHref(ud.profile, 'profile'));
});
if (showUser === curve) {
setTimeout(function () {
showUser = undefined;
$(viewButton).click();
});
}
return div;
});
$results.append(els);
});
if (showUser) {
$s.click();
}
};
var addResultsButton = function (framework, content) {
@ -1910,16 +1952,32 @@ define([
var makeFormControls = function (framework, content, update, evOnChange) {
var loggedIn = framework._.sfCommon.isLoggedIn();
var metadataMgr = framework._.cpNfInner.metadataMgr;
var user = metadataMgr.getUserData();
if (!loggedIn && !content.answers.anonymous) { return; }
var cbox;
var anonName, $anonName;
cbox = UI.createCheckbox('cp-form-anonymous',
Messages.form_anonymousBox, true, { mark: { tabindex:1 } });
var $anonBox = $(cbox).find('input');
if (loggedIn) {
if (!content.answers.anonymous || APP.cantAnon) {
$(cbox).hide().find('input').attr('disabled', 'disabled').prop('checked', false);
}
} else {
anonName = h('div.cp-form-anon-answer-input', [
Messages.form_answerAs,
h('input', {
value: user.name || '',
placeholder: Messages.form_anonName
})
]);
$anonName = $(anonName).hide();
$anonBox.on('change', function () {
if (Util.isChecked($anonBox)) { $anonName.hide(); }
else { $anonName.show(); }
});
}
var send = h('button.cp-open.btn.btn-primary', update ? Messages.form_update : Messages.form_submit);
@ -1937,14 +1995,16 @@ define([
if (!results) { return; }
var user = metadataMgr.getUserData();
if (!Util.isChecked($(cbox).find('input'))) {
if (!Util.isChecked($anonBox)) {
results._userdata = loggedIn ? {
avatar: user.avatar,
name: user.name,
notifications: user.notifications,
curvePublic: user.curvePublic,
profile: user.profile
} : { name: user.name };
} : {
name: $anonName ? $anonName.find('input').val() : user.name
};
}
var sframeChan = framework._.sfCommon.getSframeChannel();
@ -2024,7 +2084,10 @@ define([
return h('div.cp-form-send-container', [
invalid,
cbox ? h('div.cp-form-anon-answer', cbox) : undefined,
cbox ? h('div.cp-form-anon-answer', [
cbox,
anonName
]) : undefined,
reset, send
]);
};
@ -2501,10 +2564,11 @@ define([
var responseMsg = h('div.cp-form-response-msg-container');
var $responseMsg = $(responseMsg);
var refreshResponse = function () {
if (true) { return; } // XXX 4.10.0
$responseMsg.empty();
Messages.form_updateMsg = "Update response message"; // XXX
Messages.form_addMsg = "Add response message";
Messages.form_responseMsg = "Add a message that will be displayed in the response page.";
Messages.form_updateMsg = "Update response message"; // XXX 4.10.0
Messages.form_addMsg = "Add response message"; // XXX 4.10.0
Messages.form_responseMsg = "Add a message that will be displayed in the response page."; // XXX 4.10.0
var text = content.answers.msg ? Messages.form_updateMsg : Messages.form_addMsg;
var btn = h('button.btn.btn-secondary', text);
$(btn).click(function () {
@ -2533,7 +2597,7 @@ define([
name: Messages.settings_save,
onClick: function () {
var v = editor.getValue();
content.answers.msg = v.trim(0, 2000); // XXX max length?
content.answers.msg = v.trim(0, 2000); // XXX 4.10.0 max length?
framework.localChange();
framework._.cpNfInner.chainpad.onSettle(function () {
UI.log(Messages.saved);
@ -2559,9 +2623,9 @@ define([
}
UI.openCustomModal(APP.responseModal);
});
$responseMsg.append(btn);
// $responseMsg.append(btn); // XXX 4.10.0
};
refreshResponse();
//refreshResponse();
// Allow anonymous answers
var privacyContainer = h('div.cp-form-privacy-container');
@ -2658,7 +2722,7 @@ define([
evOnChange.reg(refreshPublic);
evOnChange.reg(refreshPrivacy);
evOnChange.reg(refreshEndDate);
evOnChange.reg(refreshResponse);
//evOnChange.reg(refreshResponse);
return [
endDateContainer,
@ -2716,6 +2780,11 @@ define([
var endDateEl = h('div.alert.alert-warning.cp-burn-after-reading');
var endDate;
var endDateTo;
// numbers greater than this overflow the maximum delay for a setTimeout
// which results in it being executed immediately (oops)
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#maximum_delay_value
var MAX_TIMEOUT_DELAY = 2147483647;
var refreshEndDateBanner = function (force) {
if (APP.isEditor) { return; }
var _endDate = content.answers.endDate;
@ -2736,10 +2805,21 @@ define([
APP.isClosed = endDate && endDate < (+new Date());
clearTimeout(endDateTo);
if (!APP.isClosed && endDate) {
setTimeout(function () {
// calculate how many ms in the future the poll will be closed
var diff = (endDate - +new Date() + 100);
// if that value would overflow, then check again in a day
// (if the tab is still open)
if (diff > MAX_TIMEOUT_DELAY) {
endDateTo = setTimeout(function () {
refreshEndDateBanner(true);
}, 1000 * 3600 * 24);
return;
}
endDateTo = setTimeout(function () {
refreshEndDateBanner(true);
$('.cp-form-send-container').find('.cp-open').remove();
},(endDate - +new Date() + 100));
$('.cp-form-send-container').find('.cp-open').hide();
}, diff);
}
};

@ -176,7 +176,7 @@ define([
validateKey: keys.secondaryValidateKey,
owners: [myKeys.edPublic],
crypto: crypto,
//Cache: Utils.Cache // XXX
//Cache: Utils.Cache // XXX 4.10.0
};
var results = {};
config.onError = function (info) {

@ -545,7 +545,7 @@
#kanban-addboard {
order: 2;
width: 300px;
width: 50px;
margin: 10px 5px;
border: 1px solid @cp_kanban-fg;
color: @cp_kanban-fg;

@ -1006,6 +1006,14 @@ define([
]);
$container.before(container);
var common = framework._.sfCommon;
var $button = common.createButton('toggle', true, {
element: $(container),
}, function () {
});
framework._.toolbar.$bottomL.append($button);
onRedraw.reg(function () {
// Redraw if new tags have been added to items
var old = Sortify(existing);

@ -127,6 +127,7 @@ define([
sframeChan.event("EV_SECURE_ACTION", {
type: parsed.type,
password: data.password,
static: data.static,
href: data.url,
name: data.name
});
@ -214,20 +215,21 @@ define([
$container.html('');
Object.keys(list).forEach(function (id) {
var data = list[id];
var name = data.filename || data.title || '?';
var name = data.filename || data.title || data.name || '?';
if (filter && name.toLowerCase().indexOf(filter.toLowerCase()) === -1) {
return;
}
var $span = $('<span>', {
'class': 'cp-filepicker-content-element',
'title': name,
'title': Util.fixHTML(name),
}).appendTo($container);
$span.append(UI.getFileIcon(data));
$('<span>', {'class': 'cp-filepicker-content-element-name'}).text(name)
.appendTo($span);
if (data.static) { $span.attr('title', Util.fixHTML(data.href)); }
$span.click(function () {
if (typeof onFilePicked === "function") {
onFilePicked({url: data.href, name: name, password: data.password});
onFilePicked({url: data.href, name: name, static: data.static, password: data.password});
}
});

@ -345,7 +345,7 @@ define([
var senderKey = content.sender && content.sender.edPublic;
var fromMe = senderKey === privateData.edPublic;
var fromAdmin = ctx.adminKeys.indexOf(senderKey) !== -1;
var fromPremium = Boolean(content.sender.plan);
var fromPremium = Boolean(content.sender.plan || Util.find(content, ['sender', 'quota', 'plan']));
var userData = h('div.cp-support-showdata', [
Messages.support_showData,

@ -24,7 +24,10 @@ define([
sframeChan.on('Q_DRIVE_USEROBJECT', function (data, cb) {
if (!teamId) { return void cb({error: 'EINVAL'}); }
data.teamId = teamId;
// a teamId of -1 bypasses guards against modifying your drive
// from the team app
if (data.teamId !== -1) { data.teamId = teamId; }
else { delete data.teamId; }
Cryptpad.userObjectCommand(data, cb);
});
sframeChan.on('Q_DRIVE_GETOBJECT', function (data, cb) {

Loading…
Cancel
Save