Merge branch 'staging' into hide-kanban-controls

pull/1/head
ansuz 4 years ago
commit 0497d663b6

File diff suppressed because it is too large Load Diff

@ -122,46 +122,6 @@ module.exports = {
], ],
*/ */
/* CryptPad's administration panel includes a "support" tab
* wherein administrators with a secret key can view messages
* sent from users via the encrypted forms on the /support/ page
*
* To enable this functionality:
* run `node ./scripts/generate-admin-keys.js`
* save the public key in your config in the value below
* add the private key via the admin panel
* and back it up in a secure manner
*
*/
// supportMailboxPublicKey: "",
/* We're very proud that CryptPad is available to the public as free software!
* We do, however, still need to pay our bills as we develop the platform.
*
* By default CryptPad will prompt users to consider donating to
* our OpenCollective campaign. We publish the state of our finances periodically
* so you can decide for yourself whether our expenses are reasonable.
*
* You can disable any solicitations for donations by setting 'removeDonateButton' to true,
* but we'd appreciate it if you didn't!
*/
//removeDonateButton: false,
/* CryptPad will display a point of contact for your instance on its contact page
* (/contact.html) if you provide it below.
*/
adminEmail: 'i.did.not.read.my.config@cryptpad.fr',
/*
* By default, CryptPad contacts one of our servers once a day.
* This check-in will also send some very basic information about your instance including its
* version and the adminEmail so we can reach you if we are aware of a serious problem.
* We will never sell it or send you marketing mail.
*
* If you want to block this check-in and remain set 'blockDailyCheck' to true.
*/
//blockDailyCheck: false,
/* ===================== /* =====================
* STORAGE * STORAGE
* ===================== */ * ===================== */
@ -316,4 +276,13 @@ module.exports = {
* (false by default) * (false by default)
*/ */
verbose: false, verbose: false,
/* Surplus information:
*
* 'installMethod' is included in server telemetry to voluntarily
* indicate how many instances are using unofficial installation methods
* such as Docker.
*
*/
installMethod: 'unspecified',
}; };

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

@ -23,9 +23,19 @@
<glyph unicode="&#xe90d;" glyph-name="code" horiz-adv-x="878" d="M827.746 734.667l-173.333 173.333c-20.556 20.556-61.667 37.778-91.111 37.778h-497.778c-29.444 0-53.333-23.889-53.333-53.333v-888.889c0-29.444 23.889-53.333 53.333-53.333h746.667c29.444 0 53.333 23.889 53.333 53.333v640c0 29.444-17.222 70.556-37.778 91.111zM581.079 870.222c9.444-3.333 18.889-8.333 22.778-12.222l173.889-173.889c3.889-3.889 8.889-13.333 12.222-22.778h-208.889zM794.413 21.333h-711.111v853.333h426.667v-231.111c0-29.444 23.889-53.333 53.333-53.333h231.111zM278.857 519.111l-125.556-167.222c-4.444-6.111-4.444-15 0-21.111l125.556-167.222c6.111-7.778 17.222-9.444 25-3.333l28.333 21.111c7.778 6.111 9.444 17.222 3.333 25l-101.111 135 101.111 135c6.111 7.778 4.444 18.889-3.333 25l-28.333 21.111c-7.777 6.111-18.889 4.444-25-3.333zM724.413 351.889l-125.556 167.222c-6.111 7.778-17.222 9.444-25 3.333l-28.333-21.111c-7.777-6.111-9.444-17.222-3.333-25l101.111-135-101.111-135c-6.111-7.778-4.444-18.889 3.333-25l28.333-21.111c7.778-6.111 18.889-4.444 25 3.333l125.556 167.222c4.444 6.111 4.444 15 0 21.111zM379.968 95.778l35-5.556c9.444-1.667 18.889 4.444 20.556 14.444l76.667 461.667c1.667 9.444-4.444 18.889-14.444 20.556l-35 5.556c-9.444 1.667-18.889-4.444-20.556-14.444l-76.667-461.667c-1.667-9.444 4.444-18.889 14.444-20.556z" /> <glyph unicode="&#xe90d;" glyph-name="code" horiz-adv-x="878" d="M827.746 734.667l-173.333 173.333c-20.556 20.556-61.667 37.778-91.111 37.778h-497.778c-29.444 0-53.333-23.889-53.333-53.333v-888.889c0-29.444 23.889-53.333 53.333-53.333h746.667c29.444 0 53.333 23.889 53.333 53.333v640c0 29.444-17.222 70.556-37.778 91.111zM581.079 870.222c9.444-3.333 18.889-8.333 22.778-12.222l173.889-173.889c3.889-3.889 8.889-13.333 12.222-22.778h-208.889zM794.413 21.333h-711.111v853.333h426.667v-231.111c0-29.444 23.889-53.333 53.333-53.333h231.111zM278.857 519.111l-125.556-167.222c-4.444-6.111-4.444-15 0-21.111l125.556-167.222c6.111-7.778 17.222-9.444 25-3.333l28.333 21.111c7.778 6.111 9.444 17.222 3.333 25l-101.111 135 101.111 135c6.111 7.778 4.444 18.889-3.333 25l-28.333 21.111c-7.777 6.111-18.889 4.444-25-3.333zM724.413 351.889l-125.556 167.222c-6.111 7.778-17.222 9.444-25 3.333l-28.333-21.111c-7.777-6.111-9.444-17.222-3.333-25l101.111-135-101.111-135c-6.111-7.778-4.444-18.889 3.333-25l28.333-21.111c7.778-6.111 18.889-4.444 25 3.333l125.556 167.222c4.444 6.111 4.444 15 0 21.111zM379.968 95.778l35-5.556c9.444-1.667 18.889 4.444 20.556 14.444l76.667 461.667c1.667 9.444-4.444 18.889-14.444 20.556l-35 5.556c-9.444 1.667-18.889-4.444-20.556-14.444l-76.667-461.667c-1.667-9.444 4.444-18.889 14.444-20.556z" />
<glyph unicode="&#xe90e;" glyph-name="richtext" horiz-adv-x="878" d="M827.746 734.667l-173.333 173.333c-20.556 20.556-61.667 37.778-91.111 37.778h-497.778c-29.444 0-53.333-23.889-53.333-53.333v-888.889c0-29.444 23.889-53.333 53.333-53.333h746.667c29.444 0 53.333 23.889 53.333 53.333v640c0 29.444-17.222 70.556-37.778 91.111zM581.079 870.222c9.444-3.333 18.889-8.333 22.778-12.222l173.889-173.889c3.889-3.889 8.889-13.333 12.222-22.778h-208.889zM794.413 21.333h-711.111v853.333h426.667v-231.111c0-29.444 23.889-53.333 53.333-53.333h231.111zM225.524 501.333v-35.556c0-10 7.778-17.778 17.778-17.778h391.111c10 0 17.778 7.778 17.778 17.778v35.556c0 10-7.778 17.778-17.778 17.778h-391.111c-10 0-17.778-7.778-17.778-17.778zM634.413 376.889h-391.111c-10 0-17.778-7.778-17.778-17.778v-35.556c0-10 7.778-17.778 17.778-17.778h391.111c10 0 17.778 7.778 17.778 17.778v35.556c0 10-7.778 17.778-17.778 17.778zM634.413 234.667h-391.111c-10 0-17.778-7.778-17.778-17.778v-35.556c0-10 7.778-17.778 17.778-17.778h391.111c10 0 17.778 7.778 17.778 17.778v35.556c0 10-7.778 17.778-17.778 17.778z" /> <glyph unicode="&#xe90e;" glyph-name="richtext" horiz-adv-x="878" d="M827.746 734.667l-173.333 173.333c-20.556 20.556-61.667 37.778-91.111 37.778h-497.778c-29.444 0-53.333-23.889-53.333-53.333v-888.889c0-29.444 23.889-53.333 53.333-53.333h746.667c29.444 0 53.333 23.889 53.333 53.333v640c0 29.444-17.222 70.556-37.778 91.111zM581.079 870.222c9.444-3.333 18.889-8.333 22.778-12.222l173.889-173.889c3.889-3.889 8.889-13.333 12.222-22.778h-208.889zM794.413 21.333h-711.111v853.333h426.667v-231.111c0-29.444 23.889-53.333 53.333-53.333h231.111zM225.524 501.333v-35.556c0-10 7.778-17.778 17.778-17.778h391.111c10 0 17.778 7.778 17.778 17.778v35.556c0 10-7.778 17.778-17.778 17.778h-391.111c-10 0-17.778-7.778-17.778-17.778zM634.413 376.889h-391.111c-10 0-17.778-7.778-17.778-17.778v-35.556c0-10 7.778-17.778 17.778-17.778h391.111c10 0 17.778 7.778 17.778 17.778v35.556c0 10-7.778 17.778-17.778 17.778zM634.413 234.667h-391.111c-10 0-17.778-7.778-17.778-17.778v-35.556c0-10 7.778-17.778 17.778-17.778h391.111c10 0 17.778 7.778 17.778 17.778v35.556c0 10-7.778 17.778-17.778 17.778z" />
<glyph unicode="&#xe90f;" glyph-name="file" horiz-adv-x="878" d="M827.745 734.666l-173.333 173.333c-20.556 20.556-61.667 37.778-91.111 37.778h-497.777c-29.444 0-53.333-23.889-53.333-53.333v-888.887c0-29.444 23.889-53.333 53.333-53.333h746.665c29.444 0 53.333 23.889 53.333 53.333v639.999c0 29.444-17.222 70.555-37.778 91.111zM581.079 870.221c9.444-3.333 18.889-8.333 22.778-12.222l173.889-173.889c3.889-3.889 8.889-13.333 12.222-22.778h-208.889zM794.412 21.334h-711.11v853.332h426.666v-231.111c0-29.444 23.889-53.333 53.333-53.333h231.111z" /> <glyph unicode="&#xe90f;" glyph-name="file" horiz-adv-x="878" d="M827.745 734.666l-173.333 173.333c-20.556 20.556-61.667 37.778-91.111 37.778h-497.777c-29.444 0-53.333-23.889-53.333-53.333v-888.887c0-29.444 23.889-53.333 53.333-53.333h746.665c29.444 0 53.333 23.889 53.333 53.333v639.999c0 29.444-17.222 70.555-37.778 91.111zM581.079 870.221c9.444-3.333 18.889-8.333 22.778-12.222l173.889-173.889c3.889-3.889 8.889-13.333 12.222-22.778h-208.889zM794.412 21.334h-711.11v853.332h426.666v-231.111c0-29.444 23.889-53.333 53.333-53.333h231.111z" />
<glyph unicode="&#xe910;" glyph-name="form-poll" horiz-adv-x="1094" d="M667.572 771.752c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525zM964.853 659.687c0 3.87-1.548 7.739-4.335 10.525l-45.506 45.507 45.507 45.507c2.787 2.786 4.335 6.656 4.335 10.525s-1.548 7.739-4.335 10.525l-21.051 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-45.507-45.507-45.507 45.507c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l45.507-45.507-45.507-45.507c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l21.051-21.051c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l45.507 45.507 45.507-45.507c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l21.051 21.051c2.787 2.786 4.335 6.656 4.335 10.525zM300.535 659.687c0 3.87-1.548 7.739-4.335 10.525l-45.506 45.507 45.507 45.507c2.787 2.786 4.335 6.656 4.335 10.525s-1.548 7.739-4.335 10.525l-21.051 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-45.507-45.507-45.507 45.507c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l45.507-45.507-45.507-45.507c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l21.051-21.051c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l45.507 45.507 45.507-45.507c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l21.051 21.051c2.787 2.786 4.335 6.656 4.335 10.525zM330.094 505.894c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525zM665.995 505.894c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525zM665.995 237.227c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525zM999.093 237.227c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525zM832.205 352.915h-31.571q-16.264 26.256-24.024 49.855-7.76 23.705-7.76 46.985t7.76 47.091q7.866 23.918 24.024 49.961h31.571q-13.606-25.193-20.41-49.323-6.803-24.024-6.803-47.516t6.697-47.623q6.803-24.13 20.516-49.43zM913.617 352.915q13.607 25.3 20.41 49.43t6.804 47.623-6.804 47.516q-6.803 24.13-20.41 49.323h31.571q16.158-26.044 23.918-49.961 7.866-23.811 7.866-47.091t-7.76-46.985q-7.76-23.599-24.024-49.855zM169.429 84.249h-31.571q-16.264 26.256-24.024 49.855-7.76 23.705-7.76 46.985t7.76 47.091q7.866 23.918 24.024 49.961h31.571q-13.606-25.193-20.41-49.323-6.803-24.024-6.803-47.516t6.697-47.623q6.803-24.13 20.516-49.43zM250.84 84.249q13.607 25.3 20.41 49.43t6.804 47.623-6.804 47.516q-6.803 24.13-20.41 49.323h31.571q16.158-26.044 23.918-49.961 7.866-23.811 7.866-47.091t-7.76-46.985q-7.76-23.599-24.024-49.855z" />
<glyph unicode="&#xe911;" glyph-name="palette" d="M408.6 950c-198.8-38.8-359-198.6-398.2-396.8-74-374 263.4-652.8 517.6-613.4 82.4 12.8 122.8 109.2 85 183.4-46.2 90.8 19.8 196.8 121.8 196.8h159.4c71.6 0 129.6 59.2 129.8 130.6-1 315.2-287.8 563.2-615.4 499.4zM192 320c-35.4 0-64 28.6-64 64s28.6 64 64 64 64-28.6 64-64-28.6-64-64-64zM256 576c-35.4 0-64 28.6-64 64s28.6 64 64 64 64-28.6 64-64-28.6-64-64-64zM512 704c-35.4 0-64 28.6-64 64s28.6 64 64 64 64-28.6 64-64-28.6-64-64-64zM768 576c-35.4 0-64 28.6-64 64s28.6 64 64 64 64-28.6 64-64-28.6-64-64-64z" /> <glyph unicode="&#xe911;" glyph-name="palette" d="M408.6 950c-198.8-38.8-359-198.6-398.2-396.8-74-374 263.4-652.8 517.6-613.4 82.4 12.8 122.8 109.2 85 183.4-46.2 90.8 19.8 196.8 121.8 196.8h159.4c71.6 0 129.6 59.2 129.8 130.6-1 315.2-287.8 563.2-615.4 499.4zM192 320c-35.4 0-64 28.6-64 64s28.6 64 64 64 64-28.6 64-64-28.6-64-64-64zM256 576c-35.4 0-64 28.6-64 64s28.6 64 64 64 64-28.6 64-64-28.6-64-64-64zM512 704c-35.4 0-64 28.6-64 64s28.6 64 64 64 64-28.6 64-64-28.6-64-64-64zM768 576c-35.4 0-64 28.6-64 64s28.6 64 64 64 64-28.6 64-64-28.6-64-64-64z" />
<glyph unicode="&#xe912;" glyph-name="folder-upload" d="M829.44 727.251h-296.84l-47.104 62.886c-25.923 34.066-66.406 55.898-111.999 56.139h-178.938c-77.457-0.137-140.211-62.891-140.348-140.335v-515.868c0.137-77.457 62.891-140.211 140.335-140.348h634.893c77.457 0.137 140.211 62.891 140.348 140.335v396.482c0 0.036 0 0.078 0 0.121 0 77.561-62.807 140.452-140.335 140.589h-0.013zM911.119 190.072c-0.068-45.083-36.597-81.611-81.673-81.679h-634.887c-45.083 0.068-81.611 36.597-81.679 81.673v515.862c0.068 45.083 36.597 81.611 81.673 81.679h178.906c26.48-0.030 50.004-12.656 64.908-32.207l0.146-0.199 47.104-62.765 17.709-24.094h326.114c45.125-0.069 81.679-36.665 81.679-81.799 0 0 0 0 0 0v0zM562.838 166.883h-102.039v203.957h-72.523l123.723 214.076 123.723-214.076h-72.885v-203.957z" /> <glyph unicode="&#xe912;" glyph-name="folder-upload" d="M829.44 727.251h-296.84l-47.104 62.886c-25.923 34.066-66.406 55.898-111.999 56.139h-178.938c-77.457-0.137-140.211-62.891-140.348-140.335v-515.868c0.137-77.457 62.891-140.211 140.335-140.348h634.893c77.457 0.137 140.211 62.891 140.348 140.335v396.482c0 0.036 0 0.078 0 0.121 0 77.561-62.807 140.452-140.335 140.589h-0.013zM911.119 190.072c-0.068-45.083-36.597-81.611-81.673-81.679h-634.887c-45.083 0.068-81.611 36.597-81.679 81.673v515.862c0.068 45.083 36.597 81.611 81.673 81.679h178.906c26.48-0.030 50.004-12.656 64.908-32.207l0.146-0.199 47.104-62.765 17.709-24.094h326.114c45.125-0.069 81.679-36.665 81.679-81.799 0 0 0 0 0 0v0zM562.838 166.883h-102.039v203.957h-72.523l123.723 214.076 123.723-214.076h-72.885v-203.957z" />
<glyph unicode="&#xe913;" glyph-name="add-bottom" d="M108.793 271.501c-15.312 0-27.996 12.684-27.996 27.996v55.992c0 15.312 12.684 28.006 27.996 28.006h225.414v-111.993zM108.793 495.478c-15.312 0-27.996 12.694-27.996 28.006v55.992c0 15.312 12.684 27.996 27.996 27.996h403.491v-111.993zM108.793 719.465c-15.312 0-27.996 12.694-27.996 28.006v55.992c0 15.312 12.684 27.996 27.996 27.996h615.968c15.312 0 27.996-12.684 27.996-27.996v-55.992c0-15.312-12.684-28.006-27.996-28.006zM943.202 287.839c0-19.465-15.792-35.257-35.258-35.257h-152.782v-152.782c0-19.465-15.792-35.257-35.258-35.257h-70.515c-19.465 0-35.257 15.792-35.257 35.257v152.782h-152.782c-19.465 0-35.257 15.792-35.257 35.257v70.515c0 19.465 15.792 35.258 35.257 35.258h152.782v152.782c0 19.465 15.792 35.258 35.257 35.258h70.515c19.465 0 35.258-15.792 35.258-35.258v-152.782h152.782c19.465 0 35.258-15.792 35.258-35.258z" /> <glyph unicode="&#xe913;" glyph-name="add-bottom" d="M108.793 271.501c-15.312 0-27.996 12.684-27.996 27.996v55.992c0 15.312 12.684 28.006 27.996 28.006h225.414v-111.993zM108.793 495.478c-15.312 0-27.996 12.694-27.996 28.006v55.992c0 15.312 12.684 27.996 27.996 27.996h403.491v-111.993zM108.793 719.465c-15.312 0-27.996 12.694-27.996 28.006v55.992c0 15.312 12.684 27.996 27.996 27.996h615.968c15.312 0 27.996-12.684 27.996-27.996v-55.992c0-15.312-12.684-28.006-27.996-28.006zM943.202 287.839c0-19.465-15.792-35.257-35.258-35.257h-152.782v-152.782c0-19.465-15.792-35.257-35.258-35.257h-70.515c-19.465 0-35.257 15.792-35.257 35.257v152.782h-152.782c-19.465 0-35.257 15.792-35.257 35.257v70.515c0 19.465 15.792 35.258 35.257 35.258h152.782v152.782c0 19.465 15.792 35.258 35.257 35.258h70.515c19.465 0 35.258-15.792 35.258-35.258v-152.782h152.782c19.465 0 35.258-15.792 35.258-35.258z" />
<glyph unicode="&#xe914;" glyph-name="add-top" d="M108.793 624.499c-15.312 0-27.996-12.684-27.996-27.996v-55.992c0-15.312 12.684-28.006 27.996-28.006h225.414v111.993zM108.793 400.522c-15.312 0-27.996-12.694-27.996-28.006v-55.992c0-15.312 12.684-27.996 27.996-27.996h403.491v111.993zM108.793 176.535c-15.312 0-27.996-12.694-27.996-28.006v-55.992c0-15.312 12.684-27.996 27.996-27.996h615.968c15.312 0 27.996 12.684 27.996 27.996v55.992c0 15.312-12.684 28.006-27.996 28.006zM943.202 608.161c0 19.465-15.792 35.257-35.258 35.257h-152.782v152.782c0 19.465-15.792 35.257-35.258 35.257h-70.515c-19.465 0-35.257-15.792-35.257-35.257v-152.782h-152.782c-19.465 0-35.257-15.792-35.257-35.257v-70.515c0-19.465 15.792-35.258 35.257-35.258h152.782v-152.782c0-19.465 15.792-35.258 35.257-35.258h70.515c19.465 0 35.258 15.792 35.258 35.258v152.782h152.782c19.465 0 35.258 15.792 35.258 35.258z" /> <glyph unicode="&#xe914;" glyph-name="add-top" d="M108.793 624.499c-15.312 0-27.996-12.684-27.996-27.996v-55.992c0-15.312 12.684-28.006 27.996-28.006h225.414v111.993zM108.793 400.522c-15.312 0-27.996-12.694-27.996-28.006v-55.992c0-15.312 12.684-27.996 27.996-27.996h403.491v111.993zM108.793 176.535c-15.312 0-27.996-12.694-27.996-28.006v-55.992c0-15.312 12.684-27.996 27.996-27.996h615.968c15.312 0 27.996 12.684 27.996 27.996v55.992c0 15.312-12.684 28.006-27.996 28.006zM943.202 608.161c0 19.465-15.792 35.257-35.258 35.257h-152.782v152.782c0 19.465-15.792 35.257-35.258 35.257h-70.515c-19.465 0-35.257-15.792-35.257-35.257v-152.782h-152.782c-19.465 0-35.257-15.792-35.257-35.257v-70.515c0-19.465 15.792-35.258 35.257-35.258h152.782v-152.782c0-19.465 15.792-35.258 35.257-35.258h70.515c19.465 0 35.258 15.792 35.258 35.258v152.782h152.782c19.465 0 35.258 15.792 35.258 35.258z" />
<glyph unicode="&#xe915;" glyph-name="destroy" d="M193.049 938.213c-25.264 0-45.745-20.492-45.745-45.757v-443.97h-67.453c-26.649-0.003-48.252-21.606-48.255-48.255 0.023-26.635 21.62-48.216 48.255-48.219h157.56l-80.102-86.481 86.481-94.352-86.396-94.23 109.353-119.164 39.535 49.965-63.439 69.247 86.347 94.181-86.262 94.085 80.344 86.748h171.688l-80.102-86.481 86.481-94.352-86.347-94.23 109.305-119.164 39.535 49.965-63.439 69.247 86.347 94.181-86.262 94.085 80.344 86.748h171.688l-80.102-86.481 86.529-94.352-86.396-94.23 109.341-119.164 39.499 49.965-63.451 69.199 86.396 94.23-86.299 94.133 80.344 86.699h105.763c26.64-0.004 48.244 21.579 48.267 48.219-0.003 26.654-21.613 48.259-48.267 48.255h-64.663v230.418c0 25.264-14.779 60.536-32.417 78.174l-148.718 148.719c-17.637 17.637-52.909 32.417-78.173 32.417zM208.317 877.2h366.078v-198.296c0-25.264 20.505-45.769 45.769-45.769h198.296v-184.65h-610.143zM635.409 873.392c8.104-2.86 16.213-7.154 19.549-10.49l149.204-149.204c3.337-3.336 7.63-11.446 10.49-19.549h-179.243z" /> <glyph unicode="&#xe915;" glyph-name="destroy" horiz-adv-x="1094" d="M191.671 946.511c-28.024 0-50.742-22.731-50.742-50.756v-399.384h-74.822c-29.561-0.003-53.524-23.966-53.527-53.527 0.025-29.545 23.982-53.484 53.527-53.487h174.773l-88.853-95.929 95.929-104.66-95.835-104.524 121.3-132.182 43.855 55.423-70.369 76.813 95.781 104.471-95.687 104.364 89.122 96.225h190.445l-88.853-95.929 95.929-104.66-95.781-104.524 121.246-132.182 43.855 55.423-70.369 76.813 95.781 104.471-95.687 104.364 89.122 96.225h190.445l-88.853-95.929 95.983-104.66-95.835-104.524 121.286-132.182 43.815 55.423-70.383 76.759 95.835 104.524-95.727 104.417 89.122 96.171h117.318c29.55-0.004 53.515 23.936 53.54 53.487-0.003 29.566-23.974 53.531-53.54 53.527h-71.728v162.501c0 28.024-16.394 67.15-35.958 86.714l-164.966 164.966c-19.564 19.564-58.69 35.958-86.714 35.958zM208.607 878.832h406.073v-219.96c0-28.024 22.745-50.769 50.769-50.769h219.96v-111.732h-676.802zM682.359 874.608c8.989-3.172 17.984-7.936 21.685-11.636l165.505-165.505c3.702-3.7 8.463-12.696 11.636-21.685h-198.826z" />
<glyph unicode="&#xe916;" glyph-name="form-list-check" horiz-adv-x="1094" d="M1012.362 763.843c0 8.831-7.792 16.623-16.623 16.623h-631.687c-8.831 0-16.623-7.792-16.623-16.623v-99.74c0-8.831 7.792-16.623 16.623-16.623h631.687c8.831 0 16.623 7.792 16.623 16.623zM1012.362 497.87c0 8.831-7.792 16.623-16.623 16.623h-631.687c-8.831 0-16.623-7.792-16.623-16.623v-99.74c0-8.831 7.792-16.623 16.623-16.623h631.687c8.831 0 16.623 7.792 16.623 16.623zM1012.362 231.897c0 8.831-7.792 16.623-16.623 16.623h-631.687c-8.831 0-16.623-7.792-16.623-16.623v-99.74c0-8.831 7.792-16.623 16.623-16.623h631.687c8.831 0 16.623 7.792 16.623 16.623zM279.136 239.834c0 22.077-17.911 39.988-39.988 39.988h-115.521c-22.077 0-39.988-17.911-39.988-39.988v-115.521c0-22.077 17.911-39.988 39.988-39.988h115.521c22.077 0 39.988 17.911 39.988 39.988zM307.396 779.338c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525zM307.396 513.228c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525z" />
<glyph unicode="&#xe917;" glyph-name="form-grid-check" horiz-adv-x="1094" d="M645.063 506.257c0 22.077-17.911 39.988-39.988 39.988h-115.521c-22.077 0-39.988-17.911-39.988-39.988v-115.521c0-22.077 17.911-39.988 39.988-39.988h115.521c22.077 0 39.988 17.911 39.988 39.988zM1011.782 771.036c0 22.077-17.911 39.988-39.988 39.988h-115.521c-22.077 0-39.988-17.911-39.988-39.988v-115.521c0-22.077 17.911-39.988 39.988-39.988h115.521c22.077 0 39.988 17.911 39.988 39.988zM279.090 506.257c0 22.077-17.911 39.988-39.988 39.988h-115.521c-22.077 0-39.988-17.911-39.988-39.988v-115.521c0-22.077 17.911-39.988 39.988-39.988h115.521c22.077 0 39.988 17.911 39.988 39.988zM279.136 239.834c0 22.077-17.911 39.988-39.988 39.988h-115.521c-22.077 0-39.988-17.911-39.988-39.988v-115.521c0-22.077 17.911-39.988 39.988-39.988h115.521c22.077 0 39.988 17.911 39.988 39.988zM1011.753 239.834c0 22.077-17.911 39.988-39.988 39.988h-115.521c-22.077 0-39.988-17.911-39.988-39.988v-115.521c0-22.077 17.911-39.988 39.988-39.988h115.521c22.077 0 39.988 17.911 39.988 39.988zM1047.594 513.673c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525zM676.789 238.106c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525zM676.789 771.743c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525zM318.428 771.743c0 3.87-1.548 7.739-4.335 10.525l-21.050 21.051c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-101.54-101.694-45.507 45.662c-2.787 2.786-6.655 4.334-10.525 4.334s-7.739-1.548-10.525-4.334l-21.051-21.051c-2.787-2.786-4.335-6.656-4.335-10.525s1.548-7.739 4.335-10.525l77.083-77.083c2.787-2.786 6.655-4.334 10.525-4.334s7.739 1.548 10.525 4.334l133.116 133.116c2.787 2.786 4.335 6.656 4.335 10.525z" />
<glyph unicode="&#xe918;" glyph-name="form-grid-radio" horiz-adv-x="1094" d="M181.227 823.091c-60.1 0-109.091-48.991-109.091-109.091s48.991-109.091 109.091-109.091c60.1 0 108.955 48.991 108.955 109.091s-48.855 109.091-108.955 109.091zM545.091 823.091c-60.1 0-109.091-48.991-109.091-109.091s48.991-109.091 109.091-109.091c60.1 0 109.091 48.991 109.091 109.091s-48.991 109.091-109.091 109.091zM908.955 823.091c-60.1 0-109.091-48.991-109.091-109.091s48.991-109.091 109.091-109.091c60.1 0 109.091 48.991 109.091 109.091s-48.991 109.091-109.091 109.091zM181.227 747.318c19.623 0 33.364-13.695 33.364-33.318 0-19.583-13.943-33.5-33.364-33.5-19.381 0-33.5 14.119-33.5 33.5 0 19.421 13.917 33.318 33.5 33.318zM181.227 556.955c-60.1 0-109.091-48.809-109.091-108.909s48.991-109.091 109.091-109.091c60.1 0 108.955 48.991 108.955 109.091s-48.855 108.909-108.955 108.909zM545.091 556.955c-60.1 0-109.091-48.809-109.091-108.909s48.991-109.091 109.091-109.091c60.1 0 109.091 48.991 109.091 109.091s-48.991 108.909-109.091 108.909zM908.955 556.955c-60.1 0-109.091-48.809-109.091-108.909s48.991-109.091 109.091-109.091c60.1 0 109.091 48.991 109.091 109.091s-48.991 108.909-109.091 108.909zM909.045 481.364c19.623 0 33.318-13.74 33.318-33.364 0-19.583-13.897-33.5-33.318-33.5-19.381 0-33.5 14.119-33.5 33.5 0 19.421 13.917 33.364 33.5 33.364zM181.227 291.045c-60.1 0-109.091-48.855-109.091-108.955s48.991-109.091 109.091-109.091c60.1 0 108.955 48.991 108.955 109.091s-48.855 108.955-108.955 108.955zM545.091 291.045c-60.1 0-109.091-48.855-109.091-108.955s48.991-109.091 109.091-109.091c60.1 0 109.091 48.991 109.091 109.091s-48.991 108.955-109.091 108.955zM908.955 291.045c-60.1 0-109.091-48.855-109.091-108.955s48.991-109.091 109.091-109.091c60.1 0 109.091 48.991 109.091 109.091s-48.991 108.955-109.091 108.955zM545.318 214c19.623 0 33.318-13.695 33.318-33.318 0-19.583-13.897-33.5-33.318-33.5-19.381 0-33.5 14.119-33.5 33.5 0 19.421 13.917 33.318 33.5 33.318z" />
<glyph unicode="&#xe919;" glyph-name="form-list-radio" horiz-adv-x="1094" d="M1012.362 763.843c0 8.831-7.792 16.623-16.623 16.623h-631.687c-8.831 0-16.623-7.792-16.623-16.623v-99.74c0-8.831 7.792-16.623 16.623-16.623h631.687c8.831 0 16.623 7.792 16.623 16.623zM1012.362 497.87c0 8.831-7.792 16.623-16.623 16.623h-631.687c-8.831 0-16.623-7.792-16.623-16.623v-99.74c0-8.831 7.792-16.623 16.623-16.623h631.687c8.831 0 16.623 7.792 16.623 16.623zM181.227 823.091c-60.1 0-109.091-48.991-109.091-109.091s48.991-109.091 109.091-109.091c60.1 0 108.955 48.991 108.955 109.091s-48.855 109.091-108.955 109.091zM181.227 747.318c19.623 0 33.364-13.695 33.364-33.318 0-19.583-13.943-33.5-33.364-33.5-19.381 0-33.5 14.119-33.5 33.5 0 19.421 13.917 33.318 33.5 33.318zM181.227 556.955c-60.1 0-109.091-48.809-109.091-108.909s48.991-109.091 109.091-109.091c60.1 0 108.955 48.991 108.955 109.091s-48.855 108.909-108.955 108.909zM181.227 291.045c-60.1 0-109.091-48.855-109.091-108.955s48.991-109.091 109.091-109.091c60.1 0 108.955 48.991 108.955 109.091s-48.855 108.955-108.955 108.955zM1012.362 231.897c0 8.831-7.792 16.623-16.623 16.623h-631.687c-8.831 0-16.623-7.792-16.623-16.623v-99.74c0-8.831 7.792-16.623 16.623-16.623h631.687c8.831 0 16.623 7.792 16.623 16.623z" />
<glyph unicode="&#xe91a;" glyph-name="form-page-break" horiz-adv-x="1094" d="M190.545 923.136c-28.106 0-50.909-22.803-50.909-50.909v-410.318h67.864v393.364h407.273v-220.591c0-28.106 22.803-50.909 50.909-50.909h220.636v-121.864h67.864v172.773c0 28.106-16.424 67.333-36.045 86.955l-165.455 165.455c-19.621 19.621-58.894 36.045-87 36.045zM682.682 851.046c9.015-3.182 18.015-7.97 21.727-11.682l166-166c3.712-3.712 8.455-12.712 11.636-21.727h-199.364zM139.636 236.273v-212.5c0-28.106 22.803-50.909 50.909-50.909h712.727c28.106 0 50.909 22.803 50.909 50.909v212.5h-67.864v-195.545h-678.818v195.545zM66.394 387.23v-65.283h195.505v65.283zM327.009 387.23v-65.283h195.505v65.283zM587.797 387.23v-65.283h195.505v65.283zM848.412 387.23v-65.283h179.012v65.283z" />
<glyph unicode="&#xe91b;" glyph-name="form-paragraph" horiz-adv-x="1094" d="M1012.362 182.027c0 18.182-15.065 33.247-33.247 33.247h-864.413c-18.182 0-33.247-15.065-33.247-33.247v-66.493c0-18.182 15.065-33.247 33.247-33.247h864.413c18.182 0 33.247 15.065 33.247 33.247zM812.882 381.507c0 18.182-15.065 33.247-33.247 33.247h-664.933c-18.182 0-33.247-15.065-33.247-33.247v-66.493c0-18.182 15.065-33.247 33.247-33.247h664.933c18.182 0 33.247 15.065 33.247 33.247zM945.869 580.987c0 18.182-15.065 33.247-33.247 33.247h-797.92c-18.182 0-33.247-15.065-33.247-33.247v-66.493c0-18.182 15.065-33.247 33.247-33.247h797.92c18.182 0 33.247 15.065 33.247 33.247zM746.389 780.467c0 18.182-15.065 33.247-33.247 33.247h-598.44c-18.182 0-33.247-15.065-33.247-33.247v-66.493c0-18.182 15.065-33.247 33.247-33.247h598.44c18.182 0 33.247 15.065 33.247 33.247z" />
<glyph unicode="&#xe91c;" glyph-name="form-text" horiz-adv-x="1094" d="M1012.362 481.247c0 18.182-15.065 33.247-33.247 33.247h-864.413c-18.182 0-33.247-15.065-33.247-33.247v-66.493c0-18.182 15.065-33.247 33.247-33.247h864.413c18.182 0 33.247 15.065 33.247 33.247z" />
<glyph unicode="&#xe91d;" glyph-name="form-list-ordered" horiz-adv-x="1094" d="M279.377 71.897c0 35.844-22.857 62.857-57.662 71.169l49.351 59.74v45.714h-172.987v-78.961h55.065v27.532c16.623 0 33.766 1.039 50.389 1.039v-0.52c-22.338-20.259-39.48-46.753-58.182-70.649l13.506-29.091c21.299 1.558 54.545 0 54.545-29.091 0-20.779-19.221-29.61-37.402-29.61-19.74 0-41.039 10.39-55.065 23.376l-29.61-45.714c23.377-23.377 56.623-34.286 89.35-34.286 54.026 0 98.701 32.208 98.701 89.35zM280.416 397.611h-54.545v-31.169h-65.974c1.558 40.519 114.285 57.662 114.285 134.545 0 51.428-41.558 80-89.87 80-39.48 0-74.805-20.26-91.948-56.104l44.156-30.649c8.831 14.545 23.896 30.13 42.078 30.13 17.143 0 28.052-9.351 28.052-27.013 0-43.636-117.402-58.182-117.402-154.285 0-9.351 1.558-18.701 3.117-28.052h188.051zM1012.362 231.897c0 9.351-7.792 16.623-16.623 16.623h-631.687c-9.351 0-16.623-7.273-16.623-16.623v-99.74c0-8.831 7.273-16.623 16.623-16.623h631.687c8.831 0 16.623 7.792 16.623 16.623zM280.936 698.908h-56.104v209.87h-55.065l-70.649-65.974 36.883-39.48c9.87 8.831 20.26 16.623 25.974 28.052h1.039v-6.234c0-42.078-0.52-84.156-0.52-126.233h-55.584v-51.428h174.025zM1012.362 497.87c0 9.351-7.792 16.623-16.623 16.623h-631.687c-9.351 0-16.623-7.273-16.623-16.623v-99.74c0-8.831 7.273-16.623 16.623-16.623h631.687c8.831 0 16.623 7.792 16.623 16.623zM1012.362 763.843c0 8.831-7.792 16.623-16.623 16.623h-631.687c-9.351 0-16.623-7.792-16.623-16.623v-99.74c0-8.831 7.273-16.623 16.623-16.623h631.687c8.831 0 16.623 7.792 16.623 16.623z" />
<glyph unicode="&#xe91e;" glyph-name="form-poll-maybe" horiz-adv-x="1094" d="M856.492 569.333c0 9.987-3.996 19.973-11.187 27.163l-54.325 54.327c-7.191 7.191-17.175 11.185-27.163 11.185s-19.972-3.995-27.163-11.185l-262.048-262.447-117.442 117.841c-7.191 7.19-17.175 11.185-27.163 11.185s-19.972-3.995-27.163-11.185l-54.327-54.327c-7.191-7.191-11.187-17.177-11.187-27.163s3.995-19.973 11.187-27.163l198.932-198.932c7.191-7.191 17.175-11.185 27.163-11.185s19.972 3.995 27.163 11.185l343.538 343.537c7.192 7.191 11.187 17.177 11.187 27.163zM65.903 444.364q0 120.454 35 225.454 35.454 105 101.818 184.090h73.636q-65.454-87.727-98.636-192.727-32.727-105-32.727-215.908 0-109.091 33.636-213.181t96.818-189.999h-72.727q-66.818 77.273-101.818 180.454t-35 221.818zM1027.916 444.364q0-119.545-35.454-222.727-35-103.182-101.363-179.545h-72.727q63.182 85.454 96.818 189.545 33.636 104.545 33.636 213.636 0 110.909-33.182 215.908-32.727 105-98.182 192.727h73.636q66.818-79.545 101.818-184.999 35-105 35-224.545z" />
</font></defs></svg> </font></defs></svg>

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 39 KiB

@ -1,9 +1,10 @@
@font-face { @font-face {
font-family: 'cptools'; font-family: 'cptools';
src: src: url('fonts/cptools.eot?chd5a1');
url('fonts/cptools.ttf?n9y2kz') format('truetype'), src: url('fonts/cptools.eot?chd5a1#iefix') format('embedded-opentype'),
url('fonts/cptools.woff?n9y2kz') format('woff'), url('fonts/cptools.ttf?chd5a1') format('truetype'),
url('fonts/cptools.svg?n9y2kz#cptools') format('svg'); url('fonts/cptools.woff?chd5a1') format('woff'),
url('fonts/cptools.svg?chd5a1#cptools') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: block; font-display: block;
@ -25,11 +26,38 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.cptools-sheet:before { .cptools-form-poll:before {
content: "\e908"; content: "\e910";
} }
.cptools-slide:before { .cptools-form-poll-maybe:before {
content: "\e907"; content: "\e91e";
}
.cptools-form-list-check:before {
content: "\e916";
}
.cptools-form-grid-check:before {
content: "\e917";
}
.cptools-form-grid-radio:before {
content: "\e918";
}
.cptools-form-list-radio:before {
content: "\e919";
}
.cptools-form-page-break:before {
content: "\e91a";
}
.cptools-form-paragraph:before {
content: "\e91b";
}
.cptools-form-text:before {
content: "\e91c";
}
.cptools-form-list-ordered:before {
content: "\e91d";
}
.cptools-folder-no-color:before {
content: "\e900";
} }
.cptools-whiteboard:before { .cptools-whiteboard:before {
content: "\e901"; content: "\e901";
@ -37,6 +65,9 @@
.cptools-new-template:before { .cptools-new-template:before {
content: "\e902"; content: "\e902";
} }
.cptools-shared-folder:before {
content: "\e903";
}
.cptools-file-upload:before { .cptools-file-upload:before {
content: "\e904"; content: "\e904";
} }
@ -46,9 +77,24 @@
.cptools-poll:before { .cptools-poll:before {
content: "\e906"; content: "\e906";
} }
.cptools-slide:before {
content: "\e907";
}
.cptools-sheet:before {
content: "\e908";
}
.cptools-folder-open:before {
content: "\e909";
}
.cptools-kanban:before { .cptools-kanban:before {
content: "\e90a"; content: "\e90a";
} }
.cptools-folder:before {
content: "\e90b";
}
.cptools-shared-folder-open:before {
content: "\e90c";
}
.cptools-code:before { .cptools-code:before {
content: "\e90d"; content: "\e90d";
} }
@ -58,8 +104,11 @@
.cptools-file:before { .cptools-file:before {
content: "\e90f"; content: "\e90f";
} }
.cptools-destroy:before { .cptools-palette:before {
content: "\e915"; content: "\e911";
}
.cptools-folder-upload:before {
content: "\e912";
} }
.cptools-add-bottom:before { .cptools-add-bottom:before {
content: "\e913"; content: "\e913";
@ -67,24 +116,6 @@
.cptools-add-top:before { .cptools-add-top:before {
content: "\e914"; content: "\e914";
} }
.cptools-folder-upload:before { .cptools-destroy:before {
content: "\e912"; content: "\e915";
}
.cptools-folder-no-color:before {
content: "\e900";
}
.cptools-shared-folder:before {
content: "\e903";
}
.cptools-folder-open:before {
content: "\e909";
}
.cptools-folder:before {
content: "\e90b";
}
.cptools-shared-folder-open:before {
content: "\e90c";
}
.cptools-palette:before {
content: "\e911";
} }

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

@ -153,7 +153,7 @@ define([
register: isRegister, register: isRegister,
}; };
var RT, blockKeys, blockHash, Pinpad, rpc, userHash; var RT, blockKeys, blockHash, blockUrl, Pinpad, rpc, userHash;
nThen(function (waitFor) { nThen(function (waitFor) {
// derive a predefined number of bytes from the user's inputs, // derive a predefined number of bytes from the user's inputs,
@ -171,7 +171,7 @@ define([
// the rest of their data // the rest of their data
// determine where a block for your set of keys would be stored // determine where a block for your set of keys would be stored
var blockUrl = Block.getBlockUrl(res.opt.blockKeys); blockUrl = Block.getBlockUrl(res.opt.blockKeys);
// Check whether there is a block at that location // Check whether there is a block at that location
Util.fetch(blockUrl, waitFor(function (err, block) { Util.fetch(blockUrl, waitFor(function (err, block) {
@ -412,12 +412,21 @@ define([
toPublish.edPublic = RT.proxy.edPublic; toPublish.edPublic = RT.proxy.edPublic;
var blockRequest = Block.serialize(JSON.stringify(toPublish), res.opt.blockKeys); var blockRequest = Block.serialize(JSON.stringify(toPublish), res.opt.blockKeys);
rpc.writeLoginBlock(blockRequest, waitFor(function (e) { rpc.writeLoginBlock(blockRequest, waitFor(function (e) {
if (e) { if (e) {
console.error(e); console.error(e);
waitFor.abort();
return void cb(e); return void cb(e);
} }
}));
}).nThen(function (waitFor) {
// confirm that the block was actually written before considering registration successful
Util.fetch(blockUrl, waitFor(function (err /*, block */) {
if (err) {
console.error(err);
waitFor.abort();
return void cb(err);
}
console.log("blockInfo available at:", blockHash); console.log("blockInfo available at:", blockHash);
LocalStore.setBlockHash(blockHash); LocalStore.setBlockHash(blockHash);

@ -12,7 +12,7 @@ define([
// Make sure we don't display non-translated content (empty button) // Make sure we don't display non-translated content (empty button)
$main.find('#data').removeClass('hidden'); $main.find('#data').removeClass('hidden');
if (LocalStore.isLoggedIn()) { if (LocalStore.isLoggedIn() && LocalStore.getDriveRedirectPreference()) {
if (window.location.pathname === '/') { if (window.location.pathname === '/') {
window.location = '/drive/'; window.location = '/drive/';
return; return;

@ -9,6 +9,7 @@ var map = {
'fr': 'Français', 'fr': 'Français',
//'hi': 'हिन्दी', //'hi': 'हिन्दी',
'it': 'Italiano', 'it': 'Italiano',
'ja': '日本語',
'nb': 'Norwegian Bokmål', 'nb': 'Norwegian Bokmål',
//'pl': 'Polski', //'pl': 'Polski',
'pt-br': 'Português do Brasil', 'pt-br': 'Português do Brasil',

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

@ -43,6 +43,17 @@ define([
return Pages.externalLink(el, Pages.localizeDocsLink(href)); return Pages.externalLink(el, Pages.localizeDocsLink(href));
}; };
var accounts = Pages.accounts = {
donateURL: AppConfig.donateURL || "https://opencollective.com/cryptpad/",
upgradeURL: AppConfig.upgradeURL
};
Pages.areSubscriptionsAllowed = function () {
try {
return ApiConfig.allowSubscriptions && accounts.upgradeURL && !ApiConfig.restrictRegistration;
} catch (err) { return void console.error(err); }
};
var languageSelector = function () { var languageSelector = function () {
var options = []; var options = [];
var languages = Msg._languages; var languages = Msg._languages;
@ -94,7 +105,7 @@ define([
var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ? var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ?
'/imprint.html' : AppConfig.imprint); '/imprint.html' : AppConfig.imprint);
Pages.versionString = "v4.5.0"; Pages.versionString = "v4.9.0";
// used for the about menu // used for the about menu
@ -133,7 +144,7 @@ define([
footerCol('footer_product', [ footerCol('footer_product', [
footLink('/what-is-cryptpad.html', 'topbar_whatIsCryptpad'), footLink('/what-is-cryptpad.html', 'topbar_whatIsCryptpad'),
Pages.docsLink, Pages.docsLink,
footLink('/features.html', 'pricing'), footLink('/features.html', Pages.areSubscriptionsAllowed()? 'pricing': 'features'), // Messages.pricing, Messages.features
Pages.githubLink, Pages.githubLink,
footLink('https://opencollective.com/cryptpad/contribute/', 'footer_donate'), footLink('https://opencollective.com/cryptpad/contribute/', 'footer_donate'),
]), ]),
@ -204,7 +215,7 @@ define([
h('div.collapse.navbar-collapse.justify-content-end#menuCollapse', [ h('div.collapse.navbar-collapse.justify-content-end#menuCollapse', [
h('a.nav-item.nav-link', { href: '/what-is-cryptpad.html'}, Msg.about), h('a.nav-item.nav-link', { href: '/what-is-cryptpad.html'}, Msg.about),
h('a.nav-item.nav-link', { href: 'https://docs.cryptpad.fr'}, Msg.docs_link), h('a.nav-item.nav-link', { href: 'https://docs.cryptpad.fr'}, Msg.docs_link),
h('a.nav-item.nav-link', { href: '/features.html'}, Msg.pricing), h('a.nav-item.nav-link', { href: '/features.html'}, Pages.areSubscriptionsAllowed()? Msg.pricing: Msg.features),
].concat(rightLinks)) ].concat(rightLinks))
); );
}; };

@ -8,10 +8,7 @@ define([
'/api/config', '/api/config',
'/common/common-ui-elements.js', '/common/common-ui-elements.js',
], function ($, h, Msg, AppConfig, LocalStore, Pages, Config, UIElements) { ], function ($, h, Msg, AppConfig, LocalStore, Pages, Config, UIElements) {
var accounts = { var accounts = Pages.accounts;
donateURL: AppConfig.donateURL || "https://opencollective.com/cryptpad/",
upgradeURL: AppConfig.upgradeURL
};
return function () { return function () {
Msg.features_f_apps_note = AppConfig.availablePadTypes.map(function (app) { Msg.features_f_apps_note = AppConfig.availablePadTypes.map(function (app) {
@ -145,10 +142,11 @@ define([
]), ]),
]), ]),
]); ]);
var availableFeatures = var availableFeatures = [
(Config.allowSubscriptions && accounts.upgradeURL && !Config.restrictRegistration) ? anonymousFeatures,
[anonymousFeatures, registeredFeatures, premiumFeatures] : registeredFeatures,
[anonymousFeatures, registeredFeatures]; Pages.areSubscriptionsAllowed() ? premiumFeatures: undefined,
];
return h('div#cp-main', [ return h('div#cp-main', [
Pages.infopageTopbar(), Pages.infopageTopbar(),

@ -31,7 +31,7 @@ define([
[ 'code', Msg.type.code], [ 'code', Msg.type.code],
[ 'slide', Msg.type.slide], [ 'slide', Msg.type.slide],
[ 'sheet', Msg.type.sheet], [ 'sheet', Msg.type.sheet],
[ 'poll', Msg.type.poll], [ 'form', Msg.type.form],
[ 'kanban', Msg.type.kanban], [ 'kanban', Msg.type.kanban],
[ 'whiteboard', Msg.type.whiteboard], [ 'whiteboard', Msg.type.whiteboard],
[ 'drive', Msg.type.drive] [ 'drive', Msg.type.drive]

@ -10,6 +10,7 @@
code: #EAA000; code: #EAA000;
slide: #e57614; slide: #e57614;
poll: #2c9e98; poll: #2c9e98;
form: #2c9e98;
whiteboard: #a72ba7; whiteboard: #a72ba7;
kanban: #8C4; kanban: #8C4;
sheet: #40865c; sheet: #40865c;
@ -52,7 +53,7 @@
@cryptpad_color_light_blue: #00b7d8; @cryptpad_color_light_blue: #00b7d8;
@cryptpad_color_red: #ff1100; @cryptpad_color_red: #ff1100;
@cryptpad_color_red_fade: fade(@cryptpad_color_red, 50%); @cryptpad_color_red_fade: fade(@cryptpad_color_red, 50%);
@cryptpad_color_red_fader: fade(@cryptpad_color_red, 25%); @cryptpad_color_red_fader: fade(@cryptpad_color_red, 15%);
@cryptpad_color_warn_red: @cryptpad_color_red_fade; @cryptpad_color_warn_red: @cryptpad_color_red_fade;
@cryptpad_color_dark_red: #9e0000; @cryptpad_color_dark_red: #9e0000;
@cryptpad_color_light_red: #FFD4D4; @cryptpad_color_light_red: #FFD4D4;
@ -63,7 +64,8 @@
@cryptpad_color_light_green: #c5ffa8; @cryptpad_color_light_green: #c5ffa8;
@cryptpad_color_light_green_fade: fade(@cryptpad_color_light_green, 20%); @cryptpad_color_light_green_fade: fade(@cryptpad_color_light_green, 20%);
@cryptpad_color_light_yellow: #FFE69C; @cryptpad_color_light_yellow: #FFE69C;
@cryptpad_color_yellow_fade: fade(#FFE69C, 15%); @cryptpad_color_yellow_fade: fade(@cryptpad_color_light_yellow, 50%);
@cryptpad_color_yellow_fader: fade(#FFE69C, 15%); // not in light theme
@cryptpad_color_lighter_blue: #d2e1f2; @cryptpad_color_lighter_blue: #d2e1f2;
@cryptpad_color_link:@cryptpad_color_brand_300; @cryptpad_color_link:@cryptpad_color_brand_300;
@ -114,7 +116,7 @@
@cp_forms-disabled: @cryptpad_color_grey_500; @cp_forms-disabled: @cryptpad_color_grey_500;
// Bootstrap alerts // Bootstrap alerts
@cp_alerts-warning-bg: @cryptpad_color_yellow_fade; @cp_alerts-warning-bg: @cryptpad_color_yellow_fader;
@cp_alerts-warning-fg: @cryptpad_color_light_yellow; @cp_alerts-warning-fg: @cryptpad_color_light_yellow;
@cp_alerts-warning-text: @cryptpad_color_light_yellow; @cp_alerts-warning-text: @cryptpad_color_light_yellow;
@cp_alerts-danger-bg: @cryptpad_color_red_fader; @cp_alerts-danger-bg: @cryptpad_color_red_fader;
@ -426,3 +428,13 @@
@cp_calendar-now: @cryptpad_color_brand_300; @cp_calendar-now: @cryptpad_color_brand_300;
@cp_calendar-now-fg: @cryptpad_color_grey_800; @cp_calendar-now-fg: @cryptpad_color_grey_800;
// Forms
@cp_form-bg1: @cryptpad_color_grey_800;
@cp_form-bg2: @cryptpad_color_grey_900;
@cp_form-border: @cryptpad_color_grey_800;
@cp_form-poll-color: @cryptpad_color_grey_800;
@cp_form-poll-no: fade(@cryptpad_color_red, 25%);
@cp_form-poll-yes: fade(@cryptpad_color_green, 25%);
@cp_form-poll-maybe: @cryptpad_color_grey_700;
@cp_form-poll-yes-color: @cryptpad_color_green;
@cp_form-invalid: @cryptpad_color_light_red;

@ -10,6 +10,7 @@
code: #EAA000; code: #EAA000;
slide: #e57614; slide: #e57614;
poll: #2c9e98; poll: #2c9e98;
form: #2c9e98;
whiteboard: #a72ba7; whiteboard: #a72ba7;
kanban: #8C4; kanban: #8C4;
sheet: #40865c; sheet: #40865c;
@ -52,7 +53,7 @@
@cryptpad_color_light_blue: #00b7d8; @cryptpad_color_light_blue: #00b7d8;
@cryptpad_color_red: #ff1100; @cryptpad_color_red: #ff1100;
@cryptpad_color_red_fade: fade(@cryptpad_color_red, 50%); @cryptpad_color_red_fade: fade(@cryptpad_color_red, 50%);
@cryptpad_color_red_fader: fade(@cryptpad_color_red, 25%); @cryptpad_color_red_fader: fade(@cryptpad_color_red, 15%);
@cryptpad_color_warn_red: @cryptpad_color_red_fade; @cryptpad_color_warn_red: @cryptpad_color_red_fade;
@cryptpad_color_dark_red: #9e0000; @cryptpad_color_dark_red: #9e0000;
@cryptpad_color_light_red: #FFD4D4; @cryptpad_color_light_red: #FFD4D4;
@ -425,3 +426,14 @@
@cp_calendar-border: @cryptpad_color_grey_300; @cp_calendar-border: @cryptpad_color_grey_300;
@cp_calendar-now: @cryptpad_color_brand; @cp_calendar-now: @cryptpad_color_brand;
@cp_calendar-now-fg: @cryptpad_color_grey_200; @cp_calendar-now-fg: @cryptpad_color_grey_200;
// Forms
@cp_form-bg1: @cryptpad_color_grey_200;
@cp_form-bg2: @cryptpad_color_grey_100;
@cp_form-border: @cryptpad_color_grey_200;
@cp_form-poll-color: @cryptpad_color_grey_800;
@cp_form-poll-no: fade(@cryptpad_color_light_red, 75%);
@cp_form-poll-yes: fade(@cryptpad_color_light_green, 75%);
@cp_form-poll-maybe: @cryptpad_color_grey_300;
@cp_form-poll-yes-color: @cryptpad_color_green;
@cp_form-invalid: @cryptpad_color_red;

@ -71,6 +71,12 @@
div.cp-button-confirm { div.cp-button-confirm {
display: inline-block; display: inline-block;
&.new {
vertical-align: top;
button {
height: 35px;
}
}
button { button {
margin: 0 !important; margin: 0 !important;
} }
@ -85,7 +91,7 @@
} }
} }
} }
button.cp-button-confirm-placeholder { button.cp-button-confirm-placeholder:not(.new) {
margin-bottom: 3px !important; margin-bottom: 3px !important;
} }

@ -154,6 +154,38 @@
color: @cp_markdown-block-fg; color: @cp_markdown-block-fg;
text-align: left; text-align: left;
} }
div.cp-inline-img-warning {
display: inline-block;
padding: 10px;
color: @cryptpad_text_col;
background-color: @cryptpad_color_red_fader;
border: 1px solid @cryptpad_color_red;
.cp-inline-img {
display: flex;
margin-bottom: 10px;
}
.cp-alt-txt {
margin-left: 10px;
font-family: monospace;
font-size: 0.8em;
color: fade(@cryptpad_text_col, 90%);
}
a {
color: @cryptpad_text_col;
font-size: 0.8em;
&.cp-remote-img::before { // .fa.fa-question-circle
font-family: FontAwesome;
//content: "\f08e\00a0";
content: "\f08e\00a0\00a0";
}
&.cp-learn-more::before { // .fa.fa-external-link
font-family: FontAwesome;
content: "\f059\00a0";
//content: "\f059\00a0\00a0";
}
}
}
} }
.markdown_cryptpad() { .markdown_cryptpad() {
@ -194,7 +226,7 @@
pre > code { pre > code {
display: block; display: block;
position: relative; position: relative;
width: 90%; width: 100%;
margin: auto; margin: auto;
padding: 5px; padding: 5px;
overflow-x: auto; overflow-x: auto;

@ -221,6 +221,9 @@
button { button {
line-height: 1.5; line-height: 1.5;
} }
img {
align-self: center;
}
& > iframe { & > iframe {
width: 100%; width: 100%;
height: 100%; height: 100%;

@ -12,6 +12,18 @@ html, body {
background-color: @cp_static-bg !important; background-color: @cp_static-bg !important;
color: @cryptpad_text_col; color: @cryptpad_text_col;
font-family: "IBM Plex Mono"; font-family: "IBM Plex Mono";
#cp-report, #cp-report-ui {
max-width: 900px;
margin: auto;
padding: 15px;
}
#cp-report {
border: 1px solid @cryptpad_text_col;
}
#cp-report-ui {
text-align: right;
}
} }

@ -64,7 +64,7 @@ server {
add_header Permissions-Policy interest-cohort=(); add_header Permissions-Policy interest-cohort=();
set $coop ''; set $coop '';
if ($uri ~ ^\/(sheet|presentation|doc)\/.*$) { set $coop 'same-origin'; } if ($uri ~ ^\/(sheet|presentation|doc|convert)\/.*$) { set $coop 'same-origin'; }
# Enable SharedArrayBuffer in Firefox (for .xlsx export) # Enable SharedArrayBuffer in Firefox (for .xlsx export)
add_header Cross-Origin-Resource-Policy cross-origin; add_header Cross-Origin-Resource-Policy cross-origin;
@ -167,6 +167,13 @@ server {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# These settings prevent both NGINX and the API server
# from setting the same headers and creating duplicates
proxy_hide_header Cross-Origin-Resource-Policy;
add_header Cross-Origin-Resource-Policy cross-origin;
proxy_hide_header Cross-Origin-Embedder-Policy;
add_header Cross-Origin-Embedder-Policy require-corp;
} }
# encrypted blobs are immutable and are thus cached for a year # encrypted blobs are immutable and are thus cached for a year
@ -207,7 +214,7 @@ server {
# The nodejs server has some built-in forwarding rules to prevent # The nodejs server has some built-in forwarding rules to prevent
# URLs like /pad from resulting in a 404. This simply adds a trailing slash # URLs like /pad from resulting in a 404. This simply adds a trailing slash
# to a variety of applications. # to a variety of applications.
location ~ ^/(register|login|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban|sheet|support|admin|notifications|teams|calendar|presentation|doc)$ { location ~ ^/(register|login|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban|sheet|support|admin|notifications|teams|calendar|presentation|doc|form|report|convert)$ {
rewrite ^(.*)$ $1/ redirect; rewrite ^(.*)$ $1/ redirect;
} }

@ -10,6 +10,7 @@ module.exports.create = function (Env) {
nThen(function (w) { nThen(function (w) {
Decrees.load(Env, w(function (err) { Decrees.load(Env, w(function (err) {
Env.flushCache();
if (err) { if (err) {
log.error('DECREES_LOADING', { log.error('DECREES_LOADING', {
error: err.code || err, error: err.code || err,

@ -317,6 +317,16 @@ var instanceStatus = function (Env, Server, cb) {
maxUploadSize: Env.maxUploadSize, maxUploadSize: Env.maxUploadSize,
premiumUploadSize: Env.premiumUploadSize, premiumUploadSize: Env.premiumUploadSize,
consentToContact: Env.consentToContact,
listMyInstance: Env.listMyInstance,
provideAggregateStatistics: Env.provideAggregateStatistics,
removeDonateButton: Env.removeDonateButton,
blockDailyCheck: Env.blockDailyCheck,
updateAvailable: Env.updateAvailable,
instancePurpose: Env.instancePurpose,
}); });
}; };

@ -1,14 +1,10 @@
/*jshint esversion: 6 */ /*jshint esversion: 6 */
/* globals Buffer*/ /* globals Buffer*/
var Block = module.exports; const Block = module.exports;
const Fs = require("fs");
const Fse = require("fs-extra");
const Path = require("path");
const Nacl = require("tweetnacl/nacl-fast"); const Nacl = require("tweetnacl/nacl-fast");
const nThen = require("nthen"); const nThen = require("nthen");
const Util = require("../common-util"); const Util = require("../common-util");
const BlockStore = require("../storage/block");
/* /*
We assume that the server is secured against MitM attacks We assume that the server is secured against MitM attacks
@ -31,7 +27,9 @@ const Util = require("../common-util");
author of the block, since we assume that the block will have been author of the block, since we assume that the block will have been
encrypted with xsalsa20-poly1305 which is authenticated. encrypted with xsalsa20-poly1305 which is authenticated.
*/ */
var validateLoginBlock = function (Env, publicKey, signature, block, cb) { // FIXME BLOCKS Block.validateLoginBlock = function (Env, publicKey, signature, block, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
// convert the public key to a Uint8Array and validate it // convert the public key to a Uint8Array and validate it
if (typeof(publicKey) !== 'string') { return void cb('E_INVALID_KEY'); } if (typeof(publicKey) !== 'string') { return void cb('E_INVALID_KEY'); }
@ -69,21 +67,7 @@ var validateLoginBlock = function (Env, publicKey, signature, block, cb) { // FI
// call back with (err) if unsuccessful // call back with (err) if unsuccessful
if (!verified) { return void cb("E_COULD_NOT_VERIFY"); } if (!verified) { return void cb("E_COULD_NOT_VERIFY"); }
return void cb(null, u8_block); return void cb(null, block);
};
var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS
// prepare publicKey to be used as a file name
var safeKey = Util.escapeKeyCharacters(publicKey);
// validate safeKey
if (typeof(safeKey) !== 'string') {
return;
}
// derive the full path
// /home/cryptpad/cryptpad/block/fg/fg32kefksjdgjkewrjksdfksjdfsdfskdjfsfd
return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey);
}; };
Block.validateAncestorProof = function (Env, proof, _cb) { Block.validateAncestorProof = function (Env, proof, _cb) {
@ -103,31 +87,26 @@ Block.validateAncestorProof = function (Env, proof, _cb) {
return void cb('E_INVALID_ANCESTOR_PROOF'); return void cb('E_INVALID_ANCESTOR_PROOF');
} }
// else fall through to next step // else fall through to next step
}).nThen(function (w) {
var path = createLoginBlockPath(Env, pub);
Fs.access(path, Fs.constants.F_OK, w(function (err) {
if (!err) { return; }
w.abort(); // else
return void cb("E_MISSING_ANCESTOR");
}));
}).nThen(function () { }).nThen(function () {
cb(void 0, pub); BlockStore.check(Env, pub, function (err) {
if (err) { return void cb('E_MISSING_ANCESTOR'); }
cb(void 0, pub);
});
}); });
} catch (err) { } catch (err) {
return void cb(err); return void cb(err);
} }
}; };
Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS Block.writeLoginBlock = function (Env, safeKey, msg, _cb) {
var cb = Util.once(Util.mkAsync(_cb)); var cb = Util.once(Util.mkAsync(_cb));
//console.log(msg);
var publicKey = msg[0]; var publicKey = msg[0];
var signature = msg[1]; var signature = msg[1];
var block = msg[2]; var block = msg[2];
var registrationProof = msg[3]; var registrationProof = msg[3];
var previousKey; var previousKey;
var validatedBlock, parsed, path; var validatedBlock, path;
nThen(function (w) { nThen(function (w) {
if (Util.escapeKeyCharacters(publicKey) !== safeKey) { if (Util.escapeKeyCharacters(publicKey) !== safeKey) {
w.abort(); w.abort();
@ -156,44 +135,26 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS
previousKey = provenKey; previousKey = provenKey;
})); }));
}).nThen(function (w) { }).nThen(function (w) {
validateLoginBlock(Env, publicKey, signature, block, w(function (e, _validatedBlock) { Env.validateLoginBlock(publicKey, signature, block, w(function (e, _validatedBlock) {
if (e) { if (e) {
w.abort(); w.abort();
return void cb(e); return void cb(e);
} }
if (!(_validatedBlock instanceof Uint8Array)) { if (typeof(_validatedBlock) !== 'string') {
w.abort(); w.abort();
return void cb('E_INVALID_BLOCK'); return void cb('E_INVALID_BLOCK_RETURNED');
} }
validatedBlock = _validatedBlock; validatedBlock = _validatedBlock;
// derive the filepath
path = createLoginBlockPath(Env, publicKey);
// make sure the path is valid
if (typeof(path) !== 'string') {
return void cb('E_INVALID_BLOCK_PATH');
}
parsed = Path.parse(path);
if (!parsed || typeof(parsed.dir) !== 'string') {
w.abort();
return void cb("E_INVALID_BLOCK_PATH_2");
}
}));
}).nThen(function (w) {
// make sure the path to the file exists
Fse.mkdirp(parsed.dir, w(function (e) {
if (e) {
w.abort();
cb(e);
}
})); }));
}).nThen(function () { }).nThen(function () {
// actually write the block var buffer;
Fs.writeFile(path, Buffer.from(validatedBlock), { encoding: "binary", }, function (err) { try {
if (err) { return void cb(err); } buffer = Buffer.from(Nacl.util.decodeBase64(validatedBlock));
} catch (err) {
return void cb('E_BLOCK_DESERIALIZATION');
}
BlockStore.write(Env, publicKey, buffer, function (err) {
Env.Log.info('BLOCK_WRITE_BY_OWNER', { Env.Log.info('BLOCK_WRITE_BY_OWNER', {
safeKey: safeKey, safeKey: safeKey,
blockId: publicKey, blockId: publicKey,
@ -201,11 +162,13 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS
previousKey: previousKey, previousKey: previousKey,
path: path, path: path,
}); });
cb(); cb(err);
}); });
}); });
}; };
const DELETE_BLOCK = Nacl.util.encodeBase64(Nacl.util.decodeUTF8('DELETE_BLOCK'));
/* /*
When users write a block, they upload the block, and provide When users write a block, they upload the block, and provide
a signature proving that they deserve to be able to write to a signature proving that they deserve to be able to write to
@ -216,10 +179,11 @@ Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS
information, we can just sign some constant and use that as proof. information, we can just sign some constant and use that as proof.
*/ */
Block.removeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS Block.removeLoginBlock = function (Env, safeKey, msg, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var publicKey = msg[0]; var publicKey = msg[0];
var signature = msg[1]; var signature = msg[1];
var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant
nThen(function (w) { nThen(function (w) {
if (Util.escapeKeyCharacters(publicKey) !== safeKey) { if (Util.escapeKeyCharacters(publicKey) !== safeKey) {
@ -227,26 +191,14 @@ Block.removeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS
return void cb("INCORRECT_KEY"); return void cb("INCORRECT_KEY");
} }
}).nThen(function () { }).nThen(function () {
validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) { Env.validateLoginBlock(publicKey, signature, DELETE_BLOCK, function (e) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
// derive the filepath BlockStore.archive(Env, publicKey, function (err) {
var path = createLoginBlockPath(Env, publicKey); Env.Log.info('ARCHIVAL_BLOCK_BY_OWNER_RPC', {
// make sure the path is valid
if (typeof(path) !== 'string') {
return void cb('E_INVALID_BLOCK_PATH');
}
// FIXME COLDSTORAGE
Fs.unlink(path, function (err) {
Env.Log.info('DELETION_BLOCK_BY_OWNER_RPC', {
publicKey: publicKey, publicKey: publicKey,
path: path,
status: err? String(err): 'SUCCESS', status: err? String(err): 'SUCCESS',
}); });
cb(err);
if (err) { return void cb(err); }
cb();
}); });
}); });
}); });

@ -8,7 +8,7 @@ const nThen = require("nthen");
//const escapeKeyCharacters = Util.escapeKeyCharacters; //const escapeKeyCharacters = Util.escapeKeyCharacters;
const unescapeKeyCharacters = Util.unescapeKeyCharacters; const unescapeKeyCharacters = Util.unescapeKeyCharacters;
var sumChannelSizes = function (sizes) { var sumChannelSizes = function (sizes) { // FIXME this synchronous code could be done by a worker
return Object.keys(sizes).map(function (id) { return sizes[id]; }) return Object.keys(sizes).map(function (id) { return sizes[id]; })
.filter(function (x) { .filter(function (x) {
// only allow positive numbers // only allow positive numbers
@ -115,8 +115,8 @@ Pinning.getTotalSize = function (Env, safeKey, cb) {
*/ */
Pinning.removePins = function (Env, safeKey, cb) { Pinning.removePins = function (Env, safeKey, cb) {
// FIXME respect the queue // FIXME respect the queue
Env.pinStore.removeChannel(safeKey, function (err) { Env.pinStore.archiveChannel(safeKey, function (err) {
Env.Log.info('DELETION_PIN_BY_OWNER_RPC', { Env.Log.info('ARCHIVAL_PIN_BY_OWNER_RPC', {
safeKey: safeKey, safeKey: safeKey,
status: err? String(err): 'SUCCESS', status: err? String(err): 'SUCCESS',
}); });
@ -145,7 +145,7 @@ var getFreeSpace = Pinning.getFreeSpace = function (Env, safeKey, cb) {
}); });
}; };
var getHash = Pinning.getHash = function (Env, safeKey, cb) { Pinning.getHash = function (Env, safeKey, cb) {
getChannelList(Env, safeKey, function (channels) { getChannelList(Env, safeKey, function (channels) {
Env.hashChannelList(channels, cb); Env.hashChannelList(channels, cb);
}); });
@ -166,12 +166,12 @@ Pinning.pinChannel = function (Env, safeKey, channels, cb) {
}); });
if (toStore.length === 0) { if (toStore.length === 0) {
return void getHash(Env, safeKey, cb); return void cb();
} }
getMultipleFileSize(Env, toStore, function (e, sizes) { getMultipleFileSize(Env, toStore, function (e, sizes) {
if (typeof(sizes) === 'undefined') { return void cb(e); } if (typeof(sizes) === 'undefined') { return void cb(e); }
var pinSize = sumChannelSizes(sizes); var pinSize = sumChannelSizes(sizes); // FIXME don't do this in the main thread...
getFreeSpace(Env, safeKey, function (e, free) { getFreeSpace(Env, safeKey, function (e, free) {
if (typeof(free) === 'undefined') { if (typeof(free) === 'undefined') {
@ -186,7 +186,7 @@ Pinning.pinChannel = function (Env, safeKey, channels, cb) {
toStore.forEach(function (channel) { toStore.forEach(function (channel) {
session.channels[channel] = true; session.channels[channel] = true;
}); });
getHash(Env, safeKey, cb); cb();
}); });
}); });
}); });
@ -208,7 +208,7 @@ Pinning.unpinChannel = function (Env, safeKey, channels, cb) {
}); });
if (toStore.length === 0) { if (toStore.length === 0) {
return void getHash(Env, safeKey, cb); return void cb();
} }
Env.pinStore.message(safeKey, JSON.stringify(['UNPIN', toStore, +new Date()]), Env.pinStore.message(safeKey, JSON.stringify(['UNPIN', toStore, +new Date()]),
@ -217,20 +217,19 @@ Pinning.unpinChannel = function (Env, safeKey, channels, cb) {
toStore.forEach(function (channel) { toStore.forEach(function (channel) {
delete session.channels[channel]; delete session.channels[channel];
}); });
getHash(Env, safeKey, cb); cb();
}); });
}); });
}; };
Pinning.resetUserPins = function (Env, safeKey, channelList, cb) { Pinning.resetUserPins = function (Env, safeKey, channelList, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (!Array.isArray(channelList)) { return void cb('INVALID_PIN_LIST'); } if (!Array.isArray(channelList)) { return void cb('INVALID_PIN_LIST'); }
var session = Core.getSession(Env.Sessions, safeKey); var session = Core.getSession(Env.Sessions, safeKey);
if (!channelList.length) { if (!channelList.length) {
return void getHash(Env, safeKey, function (e, hash) { return void cb();
if (e) { return cb(e); }
cb(void 0, hash);
});
} }
var pins = {}; var pins = {};
@ -270,9 +269,7 @@ Pinning.resetUserPins = function (Env, safeKey, channelList, cb) {
// update in-memory cache IFF the reset was allowed. // update in-memory cache IFF the reset was allowed.
session.channels = pins; session.channels = pins;
getHash(Env, safeKey, function (e, hash) { cb();
cb(e, hash);
});
}); });
}); });
}); });

@ -4,9 +4,9 @@ const Quota = module.exports;
//const Util = require("../common-util"); //const Util = require("../common-util");
const Keys = require("../keys"); const Keys = require("../keys");
const Package = require('../../package.json');
const Https = require("https"); const Https = require("https");
const Util = require("../common-util"); const Util = require("../common-util");
const Stats = require("../stats");
var validLimitFields = ['limit', 'plan', 'note', 'users', 'origin']; var validLimitFields = ['limit', 'plan', 'note', 'users', 'origin'];
@ -51,24 +51,66 @@ Quota.applyCustomLimits = function (Env) {
// console.log(Env.limits); // console.log(Env.limits);
}; };
var isRemoteVersionNewer = function (local, remote) {
try {
local = local.split('.').map(Number);
remote = remote.split('.').map(Number);
for (var i = 0; i < 3; i++) {
if (remote[i] < local[i]) { return false; }
if (remote[i] > local[i]) { return true; }
}
} catch (err) {
// if anything goes wrong just fall through and return false
// false negatives are better than false positives
}
return false;
};
/* /*
Env = { var Assert = require("assert");
myDomain, [
mySubdomain, // remote versions
adminEmail, ['4.5.0', '4.5.0', false], // equal semver should not prompt
Package.version, ['4.5.0', '4.5.1', true], // patch versions should prompt
['4.5.0', '4.6.0', true], // minor versions should prompt
['4.5.0', '5.0.0', true], // major versions should prompt
// local
['5.3.1', '4.9.0', false], // newer major should not prompt
['4.7.0', '4.6.0', false], // newer minor should not prompt
['4.7.0', '4.6.1', false], // newer patch should not prompt if other values are greater
].forEach(function (x) {
var result = isRemoteVersionNewer(x[0], x[1]);
Assert.equal(result, x[2]);
});
*/
// check if the remote endpoint reported an available server version
// which is newer than your current version (Env.version)
// if so, set Env.updateAvailable to the URL of its release notes
var checkUpdateAvailability = function (Env, json) {
if (!(json && typeof(json.updateAvailable) === 'string' && typeof(json.version) === 'string')) { return; }
// expects {updateAvailable: 'https://github.com/xwiki-labs/cryptpad/releases/4.7.0', version: '4.7.0'}
// the version string is provided explicitly even though it could be parsed from GitHub's URL
// this will allow old instances to understand responses of arbitrary URLs
// as long as we keep using semver for 'version'
if (!isRemoteVersionNewer(Env.version, json.version)) {
Env.updateAvailable = undefined;
return;
}
Env.updateAvailable = json.updateAvailable;
Env.Log.info('AN_UPDATE_IS_AVAILABLE', {
version: json.version,
updateAvailable: json.updateAvaiable,
});
}; };
*/
var queryAccountServer = function (Env, cb) { var queryAccountServer = function (Env, cb) {
var done = Util.once(Util.mkAsync(cb)); var done = Util.once(Util.mkAsync(cb));
var body = JSON.stringify({ var rawBody = Stats.instanceData(Env);
domain: Env.myDomain, Env.Log.info("SERVER_TELEMETRY", rawBody);
subdomain: Env.mySubdomain || null, var body = JSON.stringify(rawBody);
adminEmail: Env.adminEmail,
version: Package.version
});
var options = { var options = {
host: 'accounts.cryptpad.fr', host: 'accounts.cryptpad.fr',
path: '/api/getauthorized', path: '/api/getauthorized',
@ -92,6 +134,7 @@ var queryAccountServer = function (Env, cb) {
response.on('end', function () { response.on('end', function () {
try { try {
var json = JSON.parse(str); var json = JSON.parse(str);
checkUpdateAvailability(Env, json);
// don't overwrite the limits with junk data // don't overwrite the limits with junk data
if (json && json.message === 'EINVAL') { return void cb(); } if (json && json.message === 'EINVAL') { return void cb(); }
done(void 0, json); done(void 0, json);

@ -34,6 +34,13 @@ SET_MAINTENANCE
SET_ADMIN_EMAIL SET_ADMIN_EMAIL
SET_SUPPORT_MAILBOX SET_SUPPORT_MAILBOX
// COMMUNITY PARTICIPATION AND GOVERNANCE
CONSENT_TO_CONTACT
LIST_MY_INSTANCE
PROVIDE_AGGREGATE_STATISTICS
REMOVE_DONATE_BUTTON
BLOCK_DAILY_CHECK
NOT IMPLEMENTED: NOT IMPLEMENTED:
// RESTRICTED REGISTRATION // RESTRICTED REGISTRATION
@ -89,6 +96,21 @@ commands.DISABLE_INTEGRATED_EVICTION = makeBooleanSetter('disableIntegratedEvict
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['DISABLE_INTEGRATED_TASKS', [true]]], console.log) // CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['DISABLE_INTEGRATED_TASKS', [true]]], console.log)
commands.DISABLE_INTEGRATED_TASKS = makeBooleanSetter('disableIntegratedTasks'); commands.DISABLE_INTEGRATED_TASKS = makeBooleanSetter('disableIntegratedTasks');
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['CONSENT_TO_CONTACT', [true]]], console.log)
commands.CONSENT_TO_CONTACT = makeBooleanSetter('consentToContact');
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['LIST_MY_INSTANCE', [true]]], console.log)
commands.LIST_MY_INSTANCE = makeBooleanSetter('listMyInstance');
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['PROVIDE_AGGREGATE_STATISTICS', [true]]], console.log)
commands.PROVIDE_AGGREGATE_STATISTICS = makeBooleanSetter('provideAggregateStatistics');
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['REMOVE_DONATE_BUTTON', [true]]], console.log)
commands.REMOVE_DONATE_BUTTON = makeBooleanSetter('removeDonateButton');
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['BLOCK_DAILY_CHECK', [true]]], console.log)
commands.BLOCK_DAILY_CHECK = makeBooleanSetter('blockDailyCheck');
/* /*
var isNonNegativeNumber = function (n) { var isNonNegativeNumber = function (n) {
return !(typeof(n) !== 'number' || isNaN(n) || n < 0); return !(typeof(n) !== 'number' || isNaN(n) || n < 0);
@ -151,6 +173,9 @@ commands.SET_SUPPORT_MAILBOX = makeGenericSetter('supportMailbox', function (arg
return args_isString(args) && Core.isValidPublicKey(args[0]); return args_isString(args) && Core.isValidPublicKey(args[0]);
}); });
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_INSTANCE_PURPOSE', ["development"]]], console.log)
commands.SET_INSTANCE_PURPOSE = makeGenericSetter('instancePurpose', args_isString);
// Maintenance: Empty string or an object with a start and end time // Maintenance: Empty string or an object with a start and end time
var isNumber = function (value) { var isNumber = function (value) {
return typeof(value) === "number" && !isNaN(value); return typeof(value) === "number" && !isNaN(value);

@ -10,10 +10,22 @@ const Core = require("./commands/core");
const Quota = require("./commands/quota"); const Quota = require("./commands/quota");
const Util = require("./common-util"); const Util = require("./common-util");
const Package = require("../package.json");
module.exports.create = function (config) { var canonicalizeOrigin = function (s) {
if (typeof(s) === 'undefined') { return; }
return (s || '').trim().replace(/\/+$/, '');
};
module.exports.create = function (config) {
const Env = { const Env = {
version: Package.version,
installMethod: config.installMethod || undefined,
httpUnsafeOrigin: canonicalizeOrigin(config.httpUnsafeOrigin),
httpSafeOrigin: canonicalizeOrigin(config.httpSafeOrigin),
removeDonateButton: config.removeDonateButton,
OFFLINE_MODE: false, OFFLINE_MODE: false,
FRESH_KEY: '', FRESH_KEY: '',
FRESH_MODE: true, FRESH_MODE: true,
@ -97,6 +109,11 @@ module.exports.create = function (config) {
allowSubscriptions: config.allowSubscriptions === true, allowSubscriptions: config.allowSubscriptions === true,
blockDailyCheck: config.blockDailyCheck === true, blockDailyCheck: config.blockDailyCheck === true,
consentToContact: false,
listMyInstance: false,
provideAggregateStatistics: false,
updateAvailable: undefined,
myDomain: config.myDomain, myDomain: config.myDomain,
mySubdomain: config.mySubdomain, // only exists for the accounts integration mySubdomain: config.mySubdomain, // only exists for the accounts integration
customLimits: {}, customLimits: {},
@ -106,7 +123,7 @@ module.exports.create = function (config) {
maxWorkers: config.maxWorkers, maxWorkers: config.maxWorkers,
disableIntegratedTasks: config.disableIntegratedTasks || false, disableIntegratedTasks: config.disableIntegratedTasks || false,
disableIntegratedEviction: config.disableIntegratedEviction || false, disableIntegratedEviction: typeof(config.disableIntegratedEviction) === 'undefined'? true: config.disableIntegratedEviction, // XXX 4.10.0 false,
lastEviction: +new Date(), lastEviction: +new Date(),
evictionReport: {}, evictionReport: {},
commandTimers: {}, commandTimers: {},

@ -123,6 +123,8 @@ module.exports.create = function (Env, cb) {
Store.create({ Store.create({
filePath: pinPath, filePath: pinPath,
archivePath: Env.paths.archive, archivePath: Env.paths.archive,
// indicate that archives should be put in a 'pins' archvie folder
volumeId: 'pins',
}, w(function (err, s) { }, w(function (err, s) {
if (err) { throw err; } if (err) { throw err; }
Env.pinStore = s; Env.pinStore = s;

@ -701,6 +701,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
} }
if (msgCount === 0 && !metadata_cache[channelName] && Server.channelContainsUser(channelName, userId)) { if (msgCount === 0 && !metadata_cache[channelName] && Server.channelContainsUser(channelName, userId)) {
// TODO this might be a good place to reject channel creation by anonymous users
handleFirstMessage(Env, channelName, metadata); handleFirstMessage(Env, channelName, metadata);
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(metadata)]); Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(metadata)]);
} }
@ -962,7 +963,11 @@ HK.onChannelMessage = function (Env, Server, channel, msgStruct, cb) {
// validation can fail in multiple ways // validation can fail in multiple ways
if (err === 'FAILED') { if (err === 'FAILED') {
// we log this case, but not others for some reason // we log this case, but not others for some reason
Log.info("HK_SIGNED_MESSAGE_REJECTED", 'Channel '+channel.id); Log.info("HK_SIGNED_MESSAGE_REJECTED", {
channel: channel.id,
validateKey: metadata.validayKey,
message: signedMsg,
});
} }
// always abort if there was an error... // always abort if there was an error...
cb('FAILED_VALIDATION'); cb('FAILED_VALIDATION');

@ -0,0 +1,68 @@
/*jshint esversion: 6 */
const Stats = module.exports;
Stats.instanceData = function (Env) {
var data = {
version: Env.version,
installMethod: Env.installMethod,
domain: Env.myDomain,
subdomain: Env.mySubdomain,
httpUnsafeOrigin: Env.httpUnsafeOrigin,
httpSafeOrigin: Env.httpSafeOrigin,
adminEmail: Env.consentToContact? Env.adminEmail: undefined,
consentToContact: Boolean(Env.consentToContact),
instancePurpose: Env.instancePurpose === 'noanswer'? undefined: Env.instancePurpose,
};
/* We reserve the right to choose not to include instances
in our public directory at our discretion.
The following details will be included in your telemetry
as factors that may contribute to that decision.
These values are publicly available via /api/config
posting them to our server just makes it easier for us.
*/
if (Env.listMyInstance) {
// clearly indicate that you want to be listed
data.listMyInstance = Env.listMyInstance;
// you should have enabled your admin panel
data.adminKeys = Env.admins.length > 0;
// we expect that you enable your support mailbox
data.supportMailbox = Boolean(Env.supportMailbox);
// do you allow registration?
data.restrictRegistration = Boolean(Env.restrictRegistration);
// have you removed the donate button?
data.removeDonateButton = Boolean(Env.removeDonateButton);
// after how long do you consider a document to be inactive?
data.inactiveTime = Env.inactiveTime;
// how much storage do you offer to registered users?
data.defaultStorageLimit = Env.defaultStorageLimit;
// what size file upload do you permit
data.maxUploadSize = Env.maxUploadSize;
// how long do you retain inactive accounts?
data.accountRetentionTime = Env.accountRetentionTime;
// how long do you retain archived data?
//data.archiveRetentionTime = Env.archiveRetentionTime,
}
// we won't consider instances for public listings
// unless they opt to provide more info about themselves
if (!Env.provideAggregateStatistics) { return data; }
return data;
};

@ -0,0 +1,88 @@
/*jshint esversion: 6 */
const Block = module.exports;
const Util = require("../common-util");
const Path = require("path");
const Fs = require("fs");
const Fse = require("fs-extra");
const nThen = require("nthen");
Block.mkPath = function (Env, publicKey) {
// prepare publicKey to be used as a file name
var safeKey = Util.escapeKeyCharacters(publicKey);
// validate safeKey
if (typeof(safeKey) !== 'string') { return; }
// derive the full path
// /home/cryptpad/cryptpad/block/fg/fg32kefksjdgjkewrjksdfksjdfsdfskdjfsfd
return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey);
};
Block.mkArchivePath = function (Env, publicKey) {
// prepare publicKey to be used as a file name
var safeKey = Util.escapeKeyCharacters(publicKey);
// validate safeKey
if (typeof(safeKey) !== 'string') {
return;
}
// derive the full path
// /home/cryptpad/cryptpad/block/fg/fg32kefksjdgjkewrjksdfksjdfsdfskdjfsfd
return Path.join(Env.paths.archive, 'block', safeKey.slice(0, 2), safeKey);
};
Block.archive = function (Env, publicKey, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
// derive the filepath
var currentPath = Block.mkPath(Env, publicKey);
// make sure the path is valid
if (typeof(currentPath) !== 'string') {
return void cb('E_INVALID_BLOCK_PATH');
}
var archivePath = Block.mkArchivePath(Env, publicKey);
// make sure the path is valid
if (typeof(archivePath) !== 'string') {
return void cb('E_INVALID_BLOCK_ARCHIVAL_PATH');
}
Fse.move(currentPath, archivePath, {
overwrite: true,
}, cb);
};
Block.check = function (Env, publicKey, _cb) { // 'check' because 'exists' implies boolean
var cb = Util.once(Util.mkAsync(_cb));
var path = Block.mkPath(Env, publicKey);
Fs.access(path, Fs.constants.F_OK, cb);
};
Block.write = function (Env, publicKey, buffer, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var path = Block.mkPath(Env, publicKey);
if (typeof(path) !== 'string') { return void cb('INVALID_PATH'); }
var parsed = Path.parse(path);
nThen(function (w) {
Fse.mkdirp(parsed.dir, w(function (err) {
if (!err) { return; }
w.abort();
cb(err);
}));
}).nThen(function (w) {
Block.archive(Env, publicKey, w(function (/* err */) {
/*
we proceed even if there are errors.
it might be ENOENT (there is no file to archive)
or EACCES (bad filesystem permissions for the existing archived block?)
or lots of other things, none of which justify preventing the write
*/
}));
}).nThen(function () {
Fs.writeFile(path, buffer, { encoding: 'binary' }, cb);
});
};

@ -51,7 +51,7 @@ var mkPath = function (env, channelId) {
}; };
var mkArchivePath = function (env, channelId) { var mkArchivePath = function (env, channelId) {
return Path.join(env.archiveRoot, 'datastore', channelId.slice(0, 2), channelId) + '.ndjson'; return Path.join(env.archiveRoot, env.volumeId, channelId.slice(0, 2), channelId) + '.ndjson';
}; };
var mkMetadataPath = function (env, channelId) { var mkMetadataPath = function (env, channelId) {
@ -59,7 +59,7 @@ var mkMetadataPath = function (env, channelId) {
}; };
var mkArchiveMetadataPath = function (env, channelId) { var mkArchiveMetadataPath = function (env, channelId) {
return Path.join(env.archiveRoot, 'datastore', channelId.slice(0, 2), channelId) + '.metadata.ndjson'; return Path.join(env.archiveRoot, env.volumeId, channelId.slice(0, 2), channelId) + '.metadata.ndjson';
}; };
var mkTempPath = function (env, channelId) { var mkTempPath = function (env, channelId) {
@ -1044,6 +1044,9 @@ module.exports.create = function (conf, _cb) {
var env = { var env = {
root: conf.filePath || './datastore', root: conf.filePath || './datastore',
archiveRoot: conf.archivePath || './data/archive', archiveRoot: conf.archivePath || './data/archive',
// supply a volumeId if you want a store to archive channels to and from
// to its own subpath within the archive directory
volumeId: conf.volumeId || 'datastore',
channels: { }, channels: { },
batchGetChannel: BatchRead('store_batch_channel'), batchGetChannel: BatchRead('store_batch_channel'),
}; };
@ -1076,7 +1079,7 @@ module.exports.create = function (conf, _cb) {
} }
})); }));
// make sure the cold storage directory exists // make sure the cold storage directory exists
Fse.mkdirp(env.archiveRoot, PERMISSIVE, w(function (err) { Fse.mkdirp(Path.join(env.archiveRoot, env.volumeId), PERMISSIVE, w(function (err) {
if (err && err.code !== 'EEXIST') { if (err && err.code !== 'EEXIST') {
w.abort(); w.abort();
return void cb(err); return void cb(err);

@ -66,6 +66,9 @@ const init = function (config, _cb) {
Store.create({ Store.create({
filePath: config.pinPath, filePath: config.pinPath,
archivePath: config.archivePath, archivePath: config.archivePath,
// important to initialize the pinstore with its own volume id
// otherwise archived pin logs will get mixed in with channels
volumeId: 'pins',
}, w(function (err, _pinStore) { }, w(function (err, _pinStore) {
if (err) { if (err) {
w.abort(); w.abort();
@ -694,6 +697,10 @@ COMMANDS.VALIDATE_ANCESTOR_PROOF = function (data, cb) {
Block.validateAncestorProof(Env, data && data.proof, cb); Block.validateAncestorProof(Env, data && data.proof, cb);
}; };
COMMANDS.VALIDATE_LOGIN_BLOCK = function (data, cb) {
Block.validateLoginBlock(Env, data.publicKey, data.signature, data.block, cb);
};
process.on('message', function (data) { process.on('message', function (data) {
if (!data || !data.txid || !data.pid) { if (!data || !data.txid || !data.pid) {
return void process.send({ return void process.send({

@ -451,6 +451,15 @@ Workers.initialize = function (Env, config, _cb) {
}, cb); }, cb);
}; };
Env.validateLoginBlock = function (publicKey, signature, block, cb) {
sendCommand({
command: 'VALIDATE_LOGIN_BLOCK',
publicKey: publicKey,
signature: signature,
block: block,
}, cb);
};
cb(void 0); cb(void 0);
}); });
}; };

203
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "cryptpad", "name": "cryptpad",
"version": "4.5.0", "version": "4.9.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -36,15 +36,15 @@
} }
}, },
"@types/minimatch": { "@types/minimatch": {
"version": "3.0.3", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==",
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "14.14.16", "version": "15.12.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.16.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz",
"integrity": "sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==", "integrity": "sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==",
"dev": true "dev": true
}, },
"accepts": { "accepts": {
@ -57,9 +57,9 @@
} }
}, },
"ajv": { "ajv": {
"version": "6.12.0", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -187,16 +187,16 @@
"optional": true "optional": true
}, },
"aws4": { "aws4": {
"version": "1.9.1", "version": "1.11.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true "dev": true
}, },
"base": { "base": {
@ -389,9 +389,9 @@
"optional": true "optional": true
}, },
"chainpad-crypto": { "chainpad-crypto": {
"version": "0.2.5", "version": "0.2.6",
"resolved": "https://registry.npmjs.org/chainpad-crypto/-/chainpad-crypto-0.2.5.tgz", "resolved": "https://registry.npmjs.org/chainpad-crypto/-/chainpad-crypto-0.2.6.tgz",
"integrity": "sha512-K9vRsAspuX+uU1goXPz0CawpLIaOHq+1JP3WfDLqaz67LbCX/MLIUt9aMcSeIJcwZ9uMpqnbMGRktyVPoz6MCA==", "integrity": "sha512-yk7bBj22rs9PaX6LiqQxiw+Vj/afBStsNP1xP/B4Uh9a8iyJ7JrSZJ2Hj3d6fyqxJlBrMAX82Aj5PCZb5dzzHw==",
"requires": { "requires": {
"tweetnacl": "git+https://github.com/dchest/tweetnacl-js.git#v0.12.2" "tweetnacl": "git+https://github.com/dchest/tweetnacl-js.git#v0.12.2"
}, },
@ -688,15 +688,15 @@
}, },
"dependencies": { "dependencies": {
"domelementtype": { "domelementtype": {
"version": "2.0.1", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"dev": true "dev": true
}, },
"entities": { "entities": {
"version": "2.0.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true "dev": true
} }
} }
@ -754,9 +754,9 @@
"dev": true "dev": true
}, },
"errno": { "errno": {
"version": "0.1.7", "version": "0.1.8",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -973,9 +973,9 @@
"optional": true "optional": true
}, },
"fast-deep-equal": { "fast-deep-equal": {
"version": "3.1.1", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true, "dev": true,
"optional": true "optional": true
}, },
@ -1069,9 +1069,9 @@
} }
}, },
"forwarded": { "forwarded": {
"version": "0.1.2", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
}, },
"fragment-cache": { "fragment-cache": {
"version": "0.2.1", "version": "0.2.1",
@ -1134,9 +1134,9 @@
} }
}, },
"glob": { "glob": {
"version": "7.1.6", "version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
@ -1191,9 +1191,9 @@
} }
}, },
"graceful-fs": { "graceful-fs": {
"version": "4.2.3", "version": "4.2.6",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
}, },
"har-schema": { "har-schema": {
"version": "2.0.0", "version": "2.0.0",
@ -1203,13 +1203,13 @@
"optional": true "optional": true
}, },
"har-validator": { "har-validator": {
"version": "5.1.3", "version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"ajv": "^6.5.5", "ajv": "^6.12.3",
"har-schema": "^2.0.0" "har-schema": "^2.0.0"
} }
}, },
@ -1346,9 +1346,9 @@
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
}, },
"ipaddr.js": { "ipaddr.js": {
"version": "1.9.0", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
}, },
"is-accessor-descriptor": { "is-accessor-descriptor": {
"version": "0.1.6", "version": "0.1.6",
@ -1527,16 +1527,16 @@
"optional": true "optional": true
}, },
"jshint": { "jshint": {
"version": "2.11.0", "version": "2.13.0",
"resolved": "https://registry.npmjs.org/jshint/-/jshint-2.11.0.tgz", "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.0.tgz",
"integrity": "sha512-ooaD/hrBPhu35xXW4gn+o3SOuzht73gdBuffgJzrZBJZPGgGiiTvJEgTyxFvBO2nz0+X1G6etF8SzUODTlLY6Q==", "integrity": "sha512-Nd+md9wIeyfDK+RGrbOBzwLONSTdihGMtyGYU/t7zYcN2EgUa4iuY3VK2oxtPYrW5ycTj18iC+UbhNTxe4C66g==",
"dev": true, "dev": true,
"requires": { "requires": {
"cli": "~1.0.0", "cli": "~1.0.0",
"console-browserify": "1.1.x", "console-browserify": "1.1.x",
"exit": "0.1.x", "exit": "0.1.x",
"htmlparser2": "3.8.x", "htmlparser2": "3.8.x",
"lodash": "~4.17.11", "lodash": "~4.17.21",
"minimatch": "~3.0.2", "minimatch": "~3.0.2",
"shelljs": "0.3.x", "shelljs": "0.3.x",
"strip-json-comments": "1.0.x" "strip-json-comments": "1.0.x"
@ -1591,9 +1591,9 @@
} }
}, },
"jszip": { "jszip": {
"version": "3.2.2", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.2.tgz", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.6.0.tgz",
"integrity": "sha512-NmKajvAFQpbg3taXQXr/ccS2wcucR1AZ+NtyWp2Nq7HHVsXhcJFR8p0Baf32C2yVvBylFWVeKf+WI2AnvlPhpA==", "integrity": "sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"lie": "~3.3.0", "lie": "~3.3.0",
@ -1654,15 +1654,6 @@
"promise": "^7.1.1", "promise": "^7.1.1",
"request": "^2.83.0", "request": "^2.83.0",
"source-map": "~0.6.0" "source-map": "~0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"optional": true
}
} }
}, },
"lesshint": { "lesshint": {
@ -1791,16 +1782,16 @@
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
}, },
"mime-db": { "mime-db": {
"version": "1.43.0", "version": "1.48.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ=="
}, },
"mime-types": { "mime-types": {
"version": "2.1.26", "version": "2.1.31",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==",
"requires": { "requires": {
"mime-db": "1.43.0" "mime-db": "1.48.0"
} }
}, },
"minimatch": { "minimatch": {
@ -1841,9 +1832,9 @@
} }
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.3", "version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -1880,9 +1871,9 @@
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
}, },
"netflux-websocket": { "netflux-websocket": {
"version": "0.1.20", "version": "0.1.21",
"resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-0.1.20.tgz", "resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-0.1.21.tgz",
"integrity": "sha512-svFkw4ol4gmkcXKnx5kF/8tR9mmtTCDzUlLy4mSlcNl/4iWlbDmgwp/+aJ3nqtv8fg12m+DAFGX2+fbC0//dcg==" "integrity": "sha512-Zjl5lefg8urC0a0T7YCPGiUgRsISZBsTZl1STylmQz8Bq4ohcZ8cP3r6VoCpeVcvJ1Y/e3ZCXPxndWlNP9Jfug=="
}, },
"nthen": { "nthen": {
"version": "0.1.8", "version": "0.1.8",
@ -2049,22 +2040,14 @@
"dev": true "dev": true
}, },
"postcss": { "postcss": {
"version": "7.0.35", "version": "7.0.36",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^2.4.2", "chalk": "^2.4.2",
"source-map": "^0.6.1", "source-map": "^0.6.1",
"supports-color": "^6.1.0" "supports-color": "^6.1.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
} }
}, },
"postcss-less": { "postcss-less": {
@ -2115,12 +2098,12 @@
} }
}, },
"proxy-addr": { "proxy-addr": {
"version": "2.0.5", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"requires": { "requires": {
"forwarded": "~0.1.2", "forwarded": "0.2.0",
"ipaddr.js": "1.9.0" "ipaddr.js": "1.9.1"
} }
}, },
"prr": { "prr": {
@ -2131,9 +2114,9 @@
"optional": true "optional": true
}, },
"psl": { "psl": {
"version": "1.7.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
"integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
"dev": true, "dev": true,
"optional": true "optional": true
}, },
@ -2193,9 +2176,9 @@
} }
}, },
"repeat-element": { "repeat-element": {
"version": "1.1.3", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
"integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
"dev": true "dev": true
}, },
"repeat-string": { "repeat-string": {
@ -2412,6 +2395,12 @@
"requires": { "requires": {
"is-extendable": "^0.1.0" "is-extendable": "^0.1.0"
} }
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
} }
} }
}, },
@ -2495,9 +2484,9 @@
} }
}, },
"source-map": { "source-map": {
"version": "0.5.7", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
}, },
"source-map-resolve": { "source-map-resolve": {
@ -2514,9 +2503,9 @@
} }
}, },
"source-map-url": { "source-map-url": {
"version": "0.4.0", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
"dev": true "dev": true
}, },
"split-string": { "split-string": {
@ -2793,9 +2782,9 @@
} }
}, },
"uri-js": { "uri-js": {
"version": "4.2.2", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {

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

@ -1,20 +1,18 @@
[![An XWiki Labs Project](https://raw.githubusercontent.com/xwiki-labs/xwiki-labs-logo/master/projects/xwikilabs/xlabs-project.png "XWiki labs")](https://labs.xwiki.com/xwiki/bin/view/Main/WebHome) # CryptPad
CryptPad is a collaboration suite that is end-to-end-encrypted and open-source. It is built to enable collaboration, synchronizing changes to documents in real time. Because all data is encrypted, the service and its administrators have no way of seeing the content being edited and stored.
![CryptPad screenshot](screenshot.png "Private real-time collaboration on a Rich Text document.") ![CryptPad screenshot](screenshot.png "Private real-time collaboration on a Rich Text document.")
CryptPad is the **Zero Knowledge** realtime collaborative editor. # Installation
Encryption carried out in your web browser protects the data from the server, the cloud ## For development
and the NSA. It relies on the [ChainPad] realtime engine.
<!--If you'd like to know more, please read [the Whitepaper]().--> Our [developer guide](https://docs.cryptpad.fr/en/dev_guide/setup.html) provides instructions for setting up a local instance without HTTPS or our more advanced security features.
# Installation ## For production
Installing CryptPad is pretty straightforward. You can read all about it in the Configuring CryptPad for production requires a little more work, but the process is described in our [admin installation guide](https://docs.cryptpad.fr/en/admin_guide/installation.html). From there you can find more information about customization and maintenance.
[installation guide](https://github.com/xwiki-labs/cryptpad/wiki/Installation-guide).
It also contains information on keeping your instance of CryptPad up to date.
## Current version ## Current version
@ -24,33 +22,40 @@ The most recent version and all past release notes can be found [here](https://g
See [Cryptpad-Docker](https://github.com/xwiki-labs/cryptpad-docker) repository for details on how to get up-and-running with Cryptpad in Docker. This repository is maintained by the community and not officially supported. See [Cryptpad-Docker](https://github.com/xwiki-labs/cryptpad-docker) repository for details on how to get up-and-running with Cryptpad in Docker. This repository is maintained by the community and not officially supported.
# Security # Security
CryptPad is *private*, not *anonymous*. Privacy protects your data, anonymity protects you. CryptPad offers a variety of collaborative tools that encrypt your data in your browser
As such, it is possible for a collaborator on the pad to include some silly/ugly/nasty things before it is sent to the server and your collaborators. In the event that the server is
in a CryptPad such as an image which reveals your IP address when your browser automatically compromized the database holds encrypted data that is not of much value to attackers.
loads it or a script which plays Rick Astleys's greatest hits. It is possible for anyone
who does not have the key to be able to change anything in the pad or add anything, even the The code which performs the encryption is still loaded from the host server like any
server, however the clients will notice this because the content hashes in CryptPad will fail to other web page, so you still need to trust the administrator to keep their server secure
validate. and to send you the right code. An expert can download code from the server and check
that it isn't doing anything malicious like leaking your encryption keys, which is why
The server does have a certain power, it can send you evil javascript which does the wrong this is considered an [active attack].
thing (leaks the key or the data back to the server or to someone else). This is however an
[active attack] which makes it detectable. The NSA really hates doing these because they might The platform is designed to minimize what data is exposed to its operators. User registration
get caught and laughed at and humiliated in front of the whole world (again). If you're making and account access is based on a cryptographic key that is derived from your username
the NSA mad enough for them to use an active attack against you, Great Success Highfive, now take and password so the server never needs to see either and you don't need to worry about
the battery out of your computer before it spawns Agent Smith. whether they are being stored securely. It is impossible to verify whether a server's
operators are logging your IP or other activity, so if you consider this information
Still there are other low-lives in the world so using CryptPad over HTTPS is probably a good idea. sensitive it is safest to assume it is being recorded and access your preferred instance
via [Tor browser].
A correctly configured instance has safeguards to prevent collaborators from doing some
nasty things like injecting scripts into collaborative documents or uploads. The project
is actively maintained and bugs that our safeguards don't catch tend to get fixed quickly.
For this reason it is best to only use instances that are running the most recent version,
which is currently on a three-week release cycle. It is difficult for a non-expert to
determine whether an instance is otherwise configured correctly, so we are actively
working on allowing administrators to opt in to a public directory of servers that
meet our strict criteria for safety.
# Translations # Translations
We'd like to make it easy for more people to use encryption in their routine activities. CryptPad can be translated with nothing more than a web browser via our
As such, we've tried to make language-specific parts of CryptPad translatable. If you're [Weblate instance](https://weblate.cryptpad.fr/projects/cryptpad/app/).
able to translate CryptPad's interface, and would like to help, please contact us! More information about this can be found in [our translation guide](/customize.dist/translations/README.md).
You can also see [our translation guide](/customize.dist/translations/README.md).
# Contacting Us # Contacting Us
@ -61,13 +66,13 @@ via our [GitHub issue tracker](https://github.com/xwiki-labs/cryptpad/issues/),
# Team # Team
CryptPad is actively developed by a team at [XWiki SAS](https://www.xwiki.com), a company that has been building Open-Source software since 2004 with contributors from around the world. Between 2015 and 2019 it was funded by a research grant from the French state through [BPI France](https://www.bpifrance.fr/). It is currently financed by [NLnet PET](https://nlnet.nl/PET/), subscribers of CryptPad.fr and donations to our [Open-Collective campaign](https://opencollective.com/cryptpad). CryptPad is actively developed by a team at [XWiki SAS](https://www.xwiki.com), a company that has been building Open-Source software since 2004 with contributors from around the world. Between 2015 and 2019 it was funded by a research grant from the French state through [BPI France](https://www.bpifrance.fr/). In the years since we have been funded by [NLnet PET](https://nlnet.nl/PET/), [NGI TRUST](https://www.ngi.eu/ngi-projects/ngi-trust/), [NGI DAPSI](https://dapsi.ngi.eu/), subscribers of CryptPad.fr, and donations to our [Open-Collective campaign](https://opencollective.com/cryptpad).
# Contributing # Contributing
We love Open Source and we love contribution. Learn more about [contributing](https://docs.cryptpad.fr/en/how_to_contribute.html). We love Open Source and we love contribution. Learn more about [contributing](https://docs.cryptpad.fr/en/how_to_contribute.html).
If you have any questions or comments, or if you're interested in contributing to Cryptpad, come say hi on IRC, `#cryptpad` on Freenode. If you have any questions or comments, or if you're interested in contributing to Cryptpad, come say hi in our [Matrix channel](https://app.element.io/#/room/#cryptpad:matrix.xwiki.com).
# License # License
@ -78,5 +83,6 @@ published by the Free Software Foundation, either version 3 of the License, or (
any later version. If you wish to use this technology in a proprietary product, please contact any later version. If you wish to use this technology in a proprietary product, please contact
sales@xwiki.com. sales@xwiki.com.
[ChainPad]: https://github.com/xwiki-contrib/chainpad [Tor browser]: https://www.torproject.org/download/
[active attack]: https://en.wikipedia.org/wiki/Attack_(computing)#Types_of_attack [active attack]: https://en.wikipedia.org/wiki/Attack_(computing)#Types_of_attack

@ -56,6 +56,8 @@ var prepareEnv = function (Env, cb) {
Store.create({ Store.create({
filePath: config.pinPath, filePath: config.pinPath,
// archive pin logs to their own subpath
volumeId: 'pins',
}, w(function (err, _) { }, w(function (err, _) {
if (err) { if (err) {
w.abort(); w.abort();

@ -56,6 +56,8 @@ var prepareEnv = function (Env, cb) {
Store.create({ Store.create({
filePath: config.pinPath, filePath: config.pinPath,
// archive pin logs to their own subpath
volumeId: 'pins',
}, w(function (err, _) { }, w(function (err, _) {
if (err) { if (err) {
w.abort(); w.abort();

@ -9,6 +9,7 @@ var simpleTags = [
// FIXME // FIXME
"<a href='#'>", "<a href='#'>",
'<a href="#docs">',
'<h3>', '<h3>',
'</h3>', '</h3>',
@ -70,7 +71,7 @@ processLang(EN, 'en', true);
[ [
'ar', 'ar',
'bn_BD', //'bn_BD',
'ca', 'ca',
'de', 'de',
'es', 'es',
@ -86,11 +87,15 @@ processLang(EN, 'en', true);
'ro', 'ro',
'ru', 'ru',
'sv', 'sv',
'te', //'te',
'tr', 'tr',
'zh', 'zh',
].forEach(function (lang) { ].forEach(function (lang) {
var map = require("../www/common/translations/messages." + lang + ".json"); try {
if (!Object.keys(map).length) { return; } var map = require("../www/common/translations/messages." + lang + ".json");
processLang(map, lang); if (!Object.keys(map).length) { return; }
processLang(map, lang);
} catch (err) {
console.error(err);
}
}); });

@ -0,0 +1,8 @@
// TODO unify the following scripts
// unused-translations.js
// find-html-translations
// more linting
// Search for 'Cryptpad' string (should be 'CryptPad')
// Search English for -ise\s

@ -122,14 +122,11 @@ var createUser = function (config, cb) {
}); });
})); }));
}).nThen(function (w) { }).nThen(function (w) {
user.rpc.reset([], w(function (err, hash) { user.rpc.reset([], w(function (err) {
if (err) { if (err) {
w.abort(); w.abort();
user.shutdown(); user.shutdown();
return console.log("RESET_ERR"); return console.log("TEST_RESET_ERR");
}
if (!hash || hash !== EMPTY_ARRAY_HASH) {
throw new Error("EXPECTED EMPTY ARRAY HASH");
} }
})); }));
}).nThen(function (w) { }).nThen(function (w) {
@ -214,17 +211,17 @@ var createUser = function (config, cb) {
// TODO check your quota usage // TODO check your quota usage
}).nThen(function (w) { }).nThen(function (w) {
user.rpc.unpin([user.mailboxChannel], w(function (err, hash) { user.rpc.unpin([user.mailboxChannel], w(function (err) {
if (err) { if (err) {
w.abort(); w.abort();
return void cb(err); return void cb(err);
} }
}));
}).nThen(function (w) {
user.rpc.getServerHash(w(function (err, hash) {
console.log(hash);
if (hash[0] !== EMPTY_ARRAY_HASH) { user.latestPinHash = hash;
//console.log('UNPIN_RESPONSE', hash);
throw new Error("UNPIN_DIDNT_WORK");
}
user.latestPinHash = hash[0];
})); }));
}).nThen(function (w) { }).nThen(function (w) {
// clean up the pin list to avoid lots of accounts on the server // clean up the pin list to avoid lots of accounts on the server
@ -304,7 +301,8 @@ nThen(function (w) {
}, w(function (err, roster) { }, w(function (err, roster) {
if (err) { if (err) {
w.abort(); w.abort();
return void console.trace(err); console.error(err);
return void console.error("ROSTER_ERROR");
} }
oscar.roster = roster; oscar.roster = roster;
oscar.destroy.reg(function () { oscar.destroy.reg(function () {

@ -4,7 +4,6 @@
var Express = require('express'); var Express = require('express');
var Http = require('http'); var Http = require('http');
var Fs = require('fs'); var Fs = require('fs');
var Package = require('./package.json');
var Path = require("path"); var Path = require("path");
var nThen = require("nthen"); var nThen = require("nthen");
var Util = require("./lib/common-util"); var Util = require("./lib/common-util");
@ -16,21 +15,20 @@ var Env = require("./lib/env").create(config);
var app = Express(); var app = Express();
var canonicalizeOrigin = function (s) { var fancyURL = function (domain, path) {
return (s || '').trim().replace(/\/+$/, ''); try {
if (domain && path) { return new URL(path, domain).href; }
return new URL(domain);
} catch (err) {}
return false;
}; };
(function () { (function () {
// you absolutely must provide an 'httpUnsafeOrigin' // you absolutely must provide an 'httpUnsafeOrigin' (a truthy string)
if (typeof(config.httpUnsafeOrigin) !== 'string') { if (!Env.httpUnsafeOrigin || typeof(Env.httpUnsafeOrigin) !== 'string') {
throw new Error("No 'httpUnsafeOrigin' provided"); throw new Error("No 'httpUnsafeOrigin' provided");
} }
config.httpUnsafeOrigin = canonicalizeOrigin(config.httpUnsafeOrigin);
if (typeof(config.httpSafeOrigin) === 'string') {
config.httpSafeOrigin = canonicalizeOrigin(config.httpSafeOrigin);
}
// fall back to listening on a local address // fall back to listening on a local address
// if httpAddress is not a string // if httpAddress is not a string
if (typeof(config.httpAddress) !== 'string') { if (typeof(config.httpAddress) !== 'string') {
@ -42,26 +40,11 @@ var canonicalizeOrigin = function (s) {
config.httpPort = 3000; config.httpPort = 3000;
} }
if (typeof(config.httpSafeOrigin) !== 'string') { if (typeof(Env.httpSafeOrigin) !== 'string') {
Env.NO_SANDBOX = true; Env.NO_SANDBOX = true;
if (typeof(config.httpSafePort) !== 'number') { if (typeof(config.httpSafePort) !== 'number') {
config.httpSafePort = config.httpPort + 1; config.httpSafePort = config.httpPort + 1;
} }
if (Env.DEV_MODE) { return; }
console.log(`
m m mm mmmmm mm m mmmmm mm m mmm m
# # # ## # "# #"m # # #"m # m" " #
" #"# # # # #mmmm" # #m # # # #m # # mm #
## ##" #mm# # "m # # # # # # # # #
# # # # # " # ## mm#mm # ## "mmm" #
`);
console.log("\nNo 'httpSafeOrigin' provided.");
console.log("Your configuration probably isn't taking advantage of all of CryptPad's security features!");
console.log("This is acceptable for development, otherwise your users may be at risk.\n");
console.log("Serving sandboxed content via port %s.\nThis is probably not what you want for a production instance!\n", config.httpSafePort);
} }
}()); }());
@ -94,42 +77,35 @@ var setHeaders = (function () {
} }
} else { } else {
// use the default CSP headers constructed with your domain // use the default CSP headers constructed with your domain
headers['Content-Security-Policy'] = Default.contentSecurity(config.httpUnsafeOrigin); headers['Content-Security-Policy'] = Default.contentSecurity(Env.httpUnsafeOrigin);
} }
const padHeaders = Util.clone(headers); const padHeaders = Util.clone(headers);
if (typeof(config.padContentSecurity) === 'string') { if (typeof(config.padContentSecurity) === 'string') {
padHeaders['Content-Security-Policy'] = config.padContentSecurity; padHeaders['Content-Security-Policy'] = config.padContentSecurity;
} else { } else {
padHeaders['Content-Security-Policy'] = Default.padContentSecurity(config.httpUnsafeOrigin); padHeaders['Content-Security-Policy'] = Default.padContentSecurity(Env.httpUnsafeOrigin);
} }
if (Object.keys(headers).length) { if (Object.keys(headers).length) {
return function (req, res) { return function (req, res) {
// apply a bunch of cross-origin headers for XLSX export in FF and printing elsewhere // apply a bunch of cross-origin headers for XLSX export in FF and printing elsewhere
applyHeaderMap(res, { applyHeaderMap(res, {
"Cross-Origin-Opener-Policy": /^\/sheet\//.test(req.url)? 'same-origin': '', "Cross-Origin-Opener-Policy": /^\/(sheet|presentation|doc|convert)\//.test(req.url)? 'same-origin': '',
"Cross-Origin-Embedder-Policy": 'require-corp',
}); });
if (Env.NO_SANDBOX) { // handles correct configuration for local development if (Env.NO_SANDBOX) { // handles correct configuration for local development
// https://stackoverflow.com/questions/11531121/add-duplicate-http-response-headers-in-nodejs // https://stackoverflow.com/questions/11531121/add-duplicate-http-response-headers-in-nodejs
applyHeaderMap(res, { applyHeaderMap(res, {
"Cross-Origin-Resource-Policy": 'cross-origin', "Cross-Origin-Resource-Policy": 'cross-origin',
"Cross-Origin-Embedder-Policy": 'require-corp',
}); });
} }
// Don't set CSP headers on /api/config because they aren't necessary and they cause problems // Don't set CSP headers on /api/ endpoints
// because they aren't necessary and they cause problems
// when duplicated by NGINX in production environments // when duplicated by NGINX in production environments
if (/^\/api\/(broadcast|config)/.test(req.url)) { if (/^\/api\/(broadcast|config)/.test(req.url)) { return; }
/*
if (Env.NO_SANDBOX) {
applyHeaderMap(res, {
"Cross-Origin-Resource-Policy": 'cross-origin',
});
}
*/
return;
}
applyHeaderMap(res, { applyHeaderMap(res, {
"Cross-Origin-Resource-Policy": 'cross-origin', "Cross-Origin-Resource-Policy": 'cross-origin',
}); });
@ -165,7 +141,7 @@ app.head(/^\/common\/feedback\.html/, function (req, res, next) {
app.use('/blob', function (req, res, next) { app.use('/blob', function (req, res, next) {
if (req.method === 'HEAD') { if (req.method === 'HEAD') {
Express.static(Path.join(__dirname, (config.blobPath || './blob')), { Express.static(Path.join(__dirname, Env.paths.blob), {
setHeaders: function (res, path, stat) { setHeaders: function (res, path, stat) {
res.set('Access-Control-Allow-Origin', '*'); res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Headers', 'Content-Length'); res.set('Access-Control-Allow-Headers', 'Content-Length');
@ -205,13 +181,13 @@ var mainPagePattern = new RegExp('^\/(' + mainPages.join('|') + ').html$');
app.get(mainPagePattern, Express.static(__dirname + '/customize')); app.get(mainPagePattern, Express.static(__dirname + '/customize'));
app.get(mainPagePattern, Express.static(__dirname + '/customize.dist')); app.get(mainPagePattern, Express.static(__dirname + '/customize.dist'));
app.use("/blob", Express.static(Path.join(__dirname, (config.blobPath || './blob')), { app.use("/blob", Express.static(Path.join(__dirname, Env.paths.blob), {
maxAge: Env.DEV_MODE? "0d": "365d" maxAge: Env.DEV_MODE? "0d": "365d"
})); }));
app.use("/datastore", Express.static(Path.join(__dirname, (config.filePath || './datastore')), { app.use("/datastore", Express.static(Path.join(__dirname, Env.paths.data), {
maxAge: "0d" maxAge: "0d"
})); }));
app.use("/block", Express.static(Path.join(__dirname, (config.blockPath || '/block')), { app.use("/block", Express.static(Path.join(__dirname, Env.paths.block), {
maxAge: "0d", maxAge: "0d",
})); }));
@ -266,12 +242,12 @@ var serveConfig = makeRouteCache(function (host) {
'var obj = ' + JSON.stringify({ 'var obj = ' + JSON.stringify({
requireConf: { requireConf: {
waitSeconds: 600, waitSeconds: 600,
urlArgs: 'ver=' + Package.version + cacheString(), urlArgs: 'ver=' + Env.version + cacheString(),
}, },
removeDonateButton: (config.removeDonateButton === true), removeDonateButton: (Env.removeDonateButton === true),
allowSubscriptions: (config.allowSubscriptions === true), allowSubscriptions: (Env.allowSubscriptions === true),
websocketPath: config.externalWebsocketURL, websocketPath: config.externalWebsocketURL,
httpUnsafeOrigin: config.httpUnsafeOrigin, httpUnsafeOrigin: Env.httpUnsafeOrigin,
adminEmail: Env.adminEmail, adminEmail: Env.adminEmail,
adminKeys: Env.admins, adminKeys: Env.admins,
inactiveTime: Env.inactiveTime, inactiveTime: Env.inactiveTime,
@ -279,10 +255,10 @@ var serveConfig = makeRouteCache(function (host) {
defaultStorageLimit: Env.defaultStorageLimit, defaultStorageLimit: Env.defaultStorageLimit,
maxUploadSize: Env.maxUploadSize, maxUploadSize: Env.maxUploadSize,
premiumUploadSize: Env.premiumUploadSize, premiumUploadSize: Env.premiumUploadSize,
restrictRegistration: Env.restrictRegistration, // FIXME see the race condition in env.js restrictRegistration: Env.restrictRegistration,
}, null, '\t'), }, null, '\t'),
'obj.httpSafeOrigin = ' + (function () { 'obj.httpSafeOrigin = ' + (function () {
if (config.httpSafeOrigin) { return '"' + config.httpSafeOrigin + '"'; } if (Env.httpSafeOrigin) { return '"' + Env.httpSafeOrigin + '"'; }
if (config.httpSafePort) { if (config.httpSafePort) {
return "(function () { return window.location.origin.replace(/\:[0-9]+$/, ':" + return "(function () { return window.location.origin.replace(/\:[0-9]+$/, ':" +
config.httpSafePort + "'); }())"; config.httpSafePort + "'); }())";
@ -345,7 +321,19 @@ nThen(function (w) {
var port = config.httpPort; var port = config.httpPort;
var ps = port === 80? '': ':' + port; var ps = port === 80? '': ':' + port;
console.log('[%s] server available http://%s%s', new Date().toISOString(), hostName, ps); var roughAddress = 'http://' + hostName + ps;
var betterAddress = fancyURL(Env.httpUnsafeOrigin);
if (betterAddress) {
console.log('Serving content for %s via %s.\n', betterAddress, roughAddress);
} else {
console.log('Serving content via %s.\n', roughAddress);
}
if (!Env.admins.length) {
console.log("Your instance is not correctly configured for safe use in production.\nSee %s for more information.\n",
fancyURL(Env.httpUnsafeOrigin, '/checkup/') || 'https://your-domain.com/checkup/'
);
}
}); });
if (config.httpSafePort) { if (config.httpSafePort) {

@ -203,6 +203,17 @@
} }
} }
.cp-admin-radio-container {
display: flex;
align-items: left; //center;
flex-wrap: wrap;
flex-direction: column;
label {
margin-right: 40px;
margin-top: 5px;
}
}
.cp-admin-broadcast-form { .cp-admin-broadcast-form {
input.flatpickr-input { input.flatpickr-input {
width: 307.875px !important; // same width as flatpickr calendar width: 307.875px !important; // same width as flatpickr calendar

@ -16,6 +16,7 @@ define([
'/support/ui.js', '/support/ui.js',
'/lib/datepicker/flatpickr.js', '/lib/datepicker/flatpickr.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'css!/lib/datepicker/flatpickr.min.css', 'css!/lib/datepicker/flatpickr.min.css',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
@ -44,6 +45,7 @@ define([
'instanceStatus': {} 'instanceStatus': {}
}; };
var Nacl = window.nacl;
var common; var common;
var sFrameChan; var sFrameChan;
@ -54,6 +56,7 @@ define([
'cp-admin-archive', 'cp-admin-archive',
'cp-admin-unarchive', 'cp-admin-unarchive',
'cp-admin-registration', 'cp-admin-registration',
'cp-admin-email'
], ],
'quota': [ // Msg.admin_cat_quota 'quota': [ // Msg.admin_cat_quota
'cp-admin-defaultlimit', 'cp-admin-defaultlimit',
@ -71,7 +74,8 @@ define([
], ],
'support': [ // Msg.admin_cat_support 'support': [ // Msg.admin_cat_support
'cp-admin-support-list', 'cp-admin-support-list',
'cp-admin-support-init' 'cp-admin-support-init',
'cp-admin-support-priv',
], ],
'broadcast': [ // Msg.admin_cat_broadcast 'broadcast': [ // Msg.admin_cat_broadcast
'cp-admin-maintenance', 'cp-admin-maintenance',
@ -81,15 +85,28 @@ define([
'performance': [ // Msg.admin_cat_performance 'performance': [ // Msg.admin_cat_performance
'cp-admin-refresh-performance', 'cp-admin-refresh-performance',
'cp-admin-performance-profiling', 'cp-admin-performance-profiling',
] ],
'network': [ // Msg.admin_cat_network
'cp-admin-update-available',
'cp-admin-checkup',
'cp-admin-block-daily-check',
//'cp-admin-provide-aggregate-statistics',
'cp-admin-list-my-instance',
'cp-admin-consent-to-contact',
'cp-admin-remove-donate-button',
'cp-admin-instance-purpose',
],
}; };
var create = {}; var create = {};
var keyToCamlCase = function (key) {
return key.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
};
var makeBlock = function (key, addButton) { // Title, Hint, maybeButton var makeBlock = function (key, addButton) { // Title, Hint, maybeButton
// Convert to camlCase for translation keys // Convert to camlCase for translation keys
var safeKey = key.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); var safeKey = keyToCamlCase(key);
var $div = $('<div>', {'class': 'cp-admin-' + key + ' cp-sidebarlayout-element'}); var $div = $('<div>', {'class': 'cp-admin-' + key + ' cp-sidebarlayout-element'});
$('<label>').text(Messages['admin_'+safeKey+'Title'] || key).appendTo($div); $('<label>').text(Messages['admin_'+safeKey+'Title'] || key).appendTo($div);
$('<span>', {'class': 'cp-sidebarlayout-description'}) $('<span>', {'class': 'cp-sidebarlayout-description'})
@ -256,7 +273,7 @@ define([
var $div = makeBlock(key); // Msg.admin_registrationHint, .admin_registrationTitle, .admin_registrationButton var $div = makeBlock(key); // Msg.admin_registrationHint, .admin_registrationTitle, .admin_registrationButton
var state = APP.instanceStatus.restrictRegistration; var state = APP.instanceStatus.restrictRegistration;
var $cbox = $(UI.createCheckbox('cp-settings-userfeedback', var $cbox = $(UI.createCheckbox('cp-settings-' + key,
Messages.admin_registrationTitle, Messages.admin_registrationTitle,
state, { label: { class: 'noTitle' } })); state, { label: { class: 'noTitle' } }));
var spinner = UI.makeSpinner($cbox); var spinner = UI.makeSpinner($cbox);
@ -267,8 +284,11 @@ define([
sFrameChan.query('Q_ADMIN_RPC', { sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE', cmd: 'ADMIN_DECREE',
data: ['RESTRICT_REGISTRATION', [val]] data: ['RESTRICT_REGISTRATION', [val]]
}, function (e) { }, function (e, response) {
if (e) { UI.warn(Messages.error); console.error(e); } if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
}
APP.updateStatus(function () { APP.updateStatus(function () {
spinner.done(); spinner.done();
state = APP.instanceStatus.restrictRegistration; state = APP.instanceStatus.restrictRegistration;
@ -282,6 +302,94 @@ define([
return $div; return $div;
}; };
var makeAdminCheckbox = function (data) {
return function () {
var state = data.getState();
var key = data.key;
var $div = makeBlock(key);
var labelKey = 'admin_' + keyToCamlCase(key) + 'Label';
var titleKey = 'admin_' + keyToCamlCase(key) + 'Title';
var $cbox = $(UI.createCheckbox('cp-admin-' + key,
Messages[labelKey] || Messages[titleKey],
state, { label: { class: 'noTitle' } }));
var spinner = UI.makeSpinner($cbox);
var $checkbox = $cbox.find('input').on('change', function() {
spinner.spin();
var val = $checkbox.is(':checked') || false;
$checkbox.attr('disabled', 'disabled');
data.query(val, function (state) {
spinner.done();
$checkbox[0].checked = state;
$checkbox.removeAttr('disabled');
});
});
$cbox.appendTo($div);
return $div;
};
};
// Msg.admin_registrationHint, .admin_registrationTitle, .admin_registrationButton
create['registration'] = makeAdminCheckbox({
key: 'registration',
getState: function () {
return APP.instanceStatus.restrictRegistration;
},
query: function (val, setState) {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['RESTRICT_REGISTRATION', [val]]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
}
APP.updateStatus(function () {
setState(APP.instanceStatus.restrictRegistration);
});
});
},
});
create['email'] = function () {
var key = 'email';
var $div = makeBlock(key, true); // Msg.admin_emailHint, Msg.admin_emailTitle, Msg.admin_emailButton
var $button = $div.find('button');
var input = h('input', {
type: 'email',
value: ApiConfig.adminEmail || ''
});
var $input = $(input);
var innerDiv = h('div.cp-admin-setlimit-form', input);
var spinner = UI.makeSpinner($(innerDiv));
$button.click(function () {
if (!$input.val()) { return; }
spinner.spin();
$button.attr('disabled', 'disabled');
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['SET_ADMIN_EMAIL', [$input.val()]]
}, function (e, response) {
$button.removeAttr('disabled');
if (e || response.error) {
UI.warn(Messages.error);
$input.val('');
console.error(e, response);
spinner.hide();
return;
}
spinner.done();
UI.log(Messages.saved);
});
});
$button.before(innerDiv);
return $div;
};
var getPrettySize = UIElements.prettySize; var getPrettySize = UIElements.prettySize;
create['defaultlimit'] = function () { create['defaultlimit'] = function () {
@ -316,8 +424,11 @@ define([
sFrameChan.query('Q_ADMIN_RPC', { sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE', cmd: 'ADMIN_DECREE',
data: ['UPDATE_DEFAULT_STORAGE', data] data: ['UPDATE_DEFAULT_STORAGE', data]
}, function (e) { }, function (e, response) {
if (e) { UI.warn(Messages.error); return void console.error(e); } if (e || response.error) {
UI.warn(Messages.error);
return void console.error(e, response);
}
var limit = getPrettySize(l); var limit = getPrettySize(l);
$div.find('.cp-admin-defaultlimit-value').text(Messages._getKey('admin_limit', [limit])); $div.find('.cp-admin-defaultlimit-value').text(Messages._getKey('admin_limit', [limit]));
}); });
@ -448,8 +559,12 @@ define([
sFrameChan.query('Q_ADMIN_RPC', { sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE', cmd: 'ADMIN_DECREE',
data: ['RM_QUOTA', data] data: ['RM_QUOTA', data]
}, function (e) { }, function (e, response) {
if (e) { UI.warn(Messages.error); console.error(e); } if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
return;
}
APP.refreshLimits(); APP.refreshLimits();
$key.val(''); $key.val('');
}); });
@ -462,8 +577,12 @@ define([
sFrameChan.query('Q_ADMIN_RPC', { sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE', cmd: 'ADMIN_DECREE',
data: ['SET_QUOTA', data] data: ['SET_QUOTA', data]
}, function (e) { }, function (e, response) {
if (e) { UI.warn(Messages.error); console.error(e); } if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
return;
}
APP.refreshLimits(); APP.refreshLimits();
$key.val(''); $key.val('');
}); });
@ -637,8 +756,13 @@ define([
}; };
var supportKey = ApiConfig.supportMailbox; var supportKey = ApiConfig.supportMailbox;
var checkAdminKey = function (priv) {
if (!supportKey) { return; }
return Hash.checkBoxKeyPair(priv, supportKey);
};
create['support-list'] = function () { create['support-list'] = function () {
if (!supportKey || !APP.privateKey) { return; } if (!supportKey || !APP.privateKey || !checkAdminKey(APP.privateKey)) { return; }
var $container = makeBlock('support-list'); // Msg.admin_supportListHint, .admin_supportListTitle var $container = makeBlock('support-list'); // Msg.admin_supportListHint, .admin_supportListTitle
var $div = $(h('div.cp-support-container')).appendTo($container); var $div = $(h('div.cp-support-container')).appendTo($container);
@ -709,7 +833,8 @@ define([
var premium = t.some(function (msg) { var premium = t.some(function (msg) {
var _ed = Util.find(msg, ['content', 'msg', 'content', 'sender', 'edPublic']); var _ed = Util.find(msg, ['content', 'msg', 'content', 'sender', 'edPublic']);
if (ed !== _ed) { return; } 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 lastMsg = t[t.length - 1];
var lastMsgEd = Util.find(lastMsg, ['content', 'msg', 'content', 'sender', 'edPublic']); var lastMsgEd = Util.find(lastMsg, ['content', 'msg', 'content', 'sender', 'edPublic']);
@ -898,16 +1023,63 @@ define([
return $container; return $container;
}; };
create['support-priv'] = function () {
if (!supportKey || !APP.privateKey || !checkAdminKey(APP.privateKey)) { return; }
var checkAdminKey = function (priv) { var $div = makeBlock('support-priv', true); // Msg.admin_supportPrivHint, .admin_supportPrivTitle, .admin_supportPrivButton
if (!supportKey) { return; } var $button = $div.find('button').click(function () {
return Hash.checkBoxKeyPair(priv, supportKey); $button.remove();
var $selectable = $(UI.dialog.selectable(APP.privateKey)).css({ 'max-width': '28em' });
$div.append($selectable);
});
return $div;
}; };
create['support-init'] = function () { create['support-init'] = function () {
var $div = makeBlock('support-init'); // Msg.admin_supportInitHint, .admin_supportInitTitle var $div = makeBlock('support-init'); // Msg.admin_supportInitHint, .admin_supportInitTitle
if (!supportKey) { if (!supportKey) {
$div.append(h('p', Messages.admin_supportInitHelp)); (function () {
$div.append(h('p', Messages.admin_supportInitHelp));
var button = h('button.btn.btn-primary', Messages.admin_supportInitGenerate);
var $button = $(button).appendTo($div);
$div.append($button);
var spinner = UI.makeSpinner($div);
$button.click(function () {
spinner.spin();
$button.attr('disabled', 'disabled');
var keyPair = Nacl.box.keyPair();
var pub = Nacl.util.encodeBase64(keyPair.publicKey);
var priv = Nacl.util.encodeBase64(keyPair.secretKey);
// Store the private key first. It won't be used until the decree is accepted.
sFrameChan.query("Q_ADMIN_MAILBOX", priv, function (err, obj) {
if (err || (obj && obj.error)) {
console.error(err || obj.error);
UI.warn(Messages.error);
spinner.hide();
return;
}
// Then send the decree
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['SET_SUPPORT_MAILBOX', [pub]]
}, function (e, response) {
$button.removeAttr('disabled');
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
spinner.hide();
return;
}
spinner.done();
UI.log(Messages.saved);
supportKey = pub;
APP.privateKey = priv;
$('.cp-admin-support-init').hide();
APP.$rightside.append(create['support-list']());
APP.$rightside.append(create['support-priv']());
});
});
});
})();
return $div; return $div;
} }
if (!APP.privateKey || !checkAdminKey(APP.privateKey)) { if (!APP.privateKey || !checkAdminKey(APP.privateKey)) {
@ -937,6 +1109,7 @@ define([
APP.privateKey = key; APP.privateKey = key;
$('.cp-admin-support-init').hide(); $('.cp-admin-support-init').hide();
APP.$rightside.append(create['support-list']()); APP.$rightside.append(create['support-list']());
APP.$rightside.append(create['support-priv']());
}); });
}); });
return $div; return $div;
@ -1030,9 +1203,10 @@ define([
sFrameChan.query('Q_ADMIN_RPC', { sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE', cmd: 'ADMIN_DECREE',
data: ['SET_LAST_BROADCAST_HASH', [lastHash]] data: ['SET_LAST_BROADCAST_HASH', [lastHash]]
}, function (e) { }, function (e, response) {
if (e) { if (e || response.error) {
console.error(e); UI.warn(Messages.error);
console.error(e, response);
return; return;
} }
console.log('lastBroadcastHash updated'); console.log('lastBroadcastHash updated');
@ -1298,20 +1472,21 @@ define([
var end = h('input'); var end = h('input');
var $start = $(start); var $start = $(start);
var $end = $(end); var $end = $(end);
var is24h = false; var is24h = UIElements.is24h();
try { var dateFormat = "Y-m-d H:i";
is24h = !new Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }).format(0).match(/AM/); if (!is24h) { dateFormat = "Y-m-d h:i K"; }
} catch (e) {}
var endPickr = Flatpickr(end, { var endPickr = Flatpickr(end, {
enableTime: true, enableTime: true,
time_24hr: is24h, time_24hr: is24h,
dateFormat: dateFormat,
minDate: new Date() minDate: new Date()
}); });
Flatpickr(start, { Flatpickr(start, {
enableTime: true, enableTime: true,
time_24hr: is24h, time_24hr: is24h,
minDate: new Date(), minDate: new Date(),
dateFormat: dateFormat,
onChange: function () { onChange: function () {
endPickr.set('minDate', new Date($start.val())); endPickr.set('minDate', new Date($start.val()));
} }
@ -1336,9 +1511,10 @@ define([
sFrameChan.query('Q_ADMIN_RPC', { sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE', cmd: 'ADMIN_DECREE',
data: ['SET_MAINTENANCE', [data]] data: ['SET_MAINTENANCE', [data]]
}, function (e) { }, function (e, response) {
if (e) { if (e || response.error) {
UI.warn(Messages.error); console.error(e); UI.warn(Messages.error);
console.error(e, response);
$button.prop('disabled', ''); $button.prop('disabled', '');
return; return;
} }
@ -1419,7 +1595,7 @@ define([
var getData = function () { var getData = function () {
var url = $input.val(); var url = $input.val();
if (!Util.isValidURL(url)) { if (!Util.isValidURL(url)) {
console.error('Invalid URL'); console.error('Invalid URL', url);
return false; return false;
} }
return url; return url;
@ -1430,10 +1606,11 @@ define([
sFrameChan.query('Q_ADMIN_RPC', { sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE', cmd: 'ADMIN_DECREE',
data: ['SET_SURVEY_URL', [data]] data: ['SET_SURVEY_URL', [data]]
}, function (e) { }, function (e, response) {
if (e) { if (e || response.error) {
$button.prop('disabled', ''); $button.prop('disabled', '');
UI.warn(Messages.error); console.error(e); UI.warn(Messages.error);
console.error(e, response);
return; return;
} }
// Maintenance applied, send notification // Maintenance applied, send notification
@ -1529,11 +1706,12 @@ define([
sFrameChan.query('Q_ADMIN_RPC', { sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'GET_WORKER_PROFILES', cmd: 'GET_WORKER_PROFILES',
}, function (e, data) { }, function (e, data) {
if (e) { return void console.error(e); } if (e || data.error) {
UI.warn(Messages.error);
return void console.error(e, data);
}
//console.info(data); //console.info(data);
$div.find("table").remove(); $div.find("table").remove();
process(data); process(data);
$div.append(table); $div.append(table);
}); });
@ -1545,6 +1723,202 @@ define([
return $div; return $div;
}; };
create['update-available'] = function () { // Messages.admin_updateAvailableTitle.admin_updateAvailableHint.admin_updateAvailableLabel.admin_updateAvailableButton
if (!APP.instanceStatus.updateAvailable) { return; }
var $div = makeBlock('update-available', true);
var updateURL = 'https://github.com/xwiki-labs/cryptpad/releases/latest';
if (typeof(APP.instanceStatus.updateAvailable) === 'string') {
updateURL = APP.instanceStatus.updateAvailable;
}
$div.find('button').click(function () {
common.openURL(updateURL);
});
return $div;
};
create['checkup'] = function () {
var $div = makeBlock('checkup', true); // Messages.admin_checkupButton.admin_checkupHint.admin_checkupTitle
$div.find('button').click(function () {
common.openURL('/checkup/');
});
return $div;
};
create['consent-to-contact'] = makeAdminCheckbox({ // Messages.admin_consentToContactTitle.admin_consentToContactHint.admin_consentToContactLabel
key: 'consent-to-contact',
getState: function () {
return APP.instanceStatus.consentToContact;
},
query: function (val, setState) {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['CONSENT_TO_CONTACT', [val]]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
}
APP.updateStatus(function () {
setState(APP.instanceStatus.consentToContact);
});
});
},
});
create['list-my-instance'] = makeAdminCheckbox({ // Messages.admin_listMyInstanceTitle.admin_listMyInstanceHint.admin_listMyInstanceLabel
key: 'list-my-instance',
getState: function () {
return APP.instanceStatus.listMyInstance;
},
query: function (val, setState) {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['LIST_MY_INSTANCE', [val]]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
}
APP.updateStatus(function () {
setState(APP.instanceStatus.listMyInstance);
});
});
},
});
create['provide-aggregate-statistics'] = makeAdminCheckbox({ // Messages.admin_provideAggregateStatisticsTitle.admin_provideAggregateStatisticsHint.admin_provideAggregateStatisticsLabel
key: 'provide-aggregate-statistics',
getState: function () {
return APP.instanceStatus.provideAggregateStatistics;
},
query: function (val, setState) {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['PROVIDE_AGGREGATE_STATISTICS', [val]]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
}
APP.updateStatus(function () {
setState(APP.instanceStatus.provideAggregateStatistics);
});
});
},
});
create['remove-donate-button'] = makeAdminCheckbox({ // Messages.admin_removeDonateButtonTitle.admin_removeDonateButtonHint.admin_removeDonateButtonLabel
key: 'remove-donate-button',
getState: function () {
return APP.instanceStatus.removeDonateButton;
},
query: function (val, setState) {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['REMOVE_DONATE_BUTTON', [val]]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
}
APP.updateStatus(function () {
setState(APP.instanceStatus.removeDonateButton);
});
});
},
});
create['block-daily-check'] = makeAdminCheckbox({ // Messages.admin_blockDailyCheckTitle.admin_blockDailyCheckHint.admin_blockDailyCheckLabel
key: 'block-daily-check',
getState: function () {
return APP.instanceStatus.blockDailyCheck;
},
query: function (val, setState) {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['BLOCK_DAILY_CHECK', [val]]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
}
APP.updateStatus(function () {
setState(APP.instanceStatus.blockDailyCheck);
});
});
},
});
var sendDecree = function (data, cb) {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: data,
}, cb);
};
create['instance-purpose'] = function () {
var key = 'instance-purpose';
var $div = makeBlock(key); // Messages.admin_instancePurposeTitle.admin_instancePurposeHint
var values = [
'noanswer', // Messages.admin_purpose_noanswer
'experiment', // Messages.admin_purpose_experiment
'personal', // Messages.admin_purpose_personal
'education', // Messages.admin_purpose_education
'org', // Messages.admin_purpose_org
'business', // Messages.admin_purpose_business
'public', // Messages.admin_purpose_public
];
var defaultPurpose = 'noanswer';
var purpose = APP.instanceStatus.instancePurpose || defaultPurpose;
var opts = h('div.cp-admin-radio-container', [
values.map(function (key) {
var full_key = 'admin_purpose_' + key;
return UI.createRadio('cp-instance-purpose-radio', 'cp-instance-purpose-radio-'+key,
Messages[full_key] || Messages._getKey(full_key, [defaultPurpose]),
key === purpose, {
input: { value: key },
label: { class: 'noTitle' }
});
})
]);
var $opts = $(opts);
//var $br = $(h('br',));
//$div.append($br);
$div.append(opts);
var setPurpose = function (value, cb) {
sendDecree([
'SET_INSTANCE_PURPOSE',
[ value]
], cb);
};
$opts.on('change', function () {
var val = $opts.find('input:radio:checked').val();
console.log(val);
//spinner.spin();
setPurpose(val, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
//spinner.hide();
return;
}
//spinner.done();
UI.log(Messages.saved);
});
});
return $div;
};
var hideCategories = function () { var hideCategories = function () {
APP.$rightside.find('> div').hide(); APP.$rightside.find('> div').hide();
}; };
@ -1562,6 +1936,7 @@ define([
support: 'fa fa-life-ring', support: 'fa fa-life-ring',
broadcast: 'fa fa-bullhorn', broadcast: 'fa fa-bullhorn',
performance: 'fa fa-heartbeat', performance: 'fa fa-heartbeat',
network: 'fa fa-sitemap', // or fa-university ?
}; };
var createLeftside = function () { var createLeftside = function () {
@ -1573,6 +1948,7 @@ define([
if (active.indexOf('-') !== -1) { if (active.indexOf('-') !== -1) {
active = active.split('-')[0]; active = active.split('-')[0];
} }
if (!categories[active]) { active = 'general'; }
common.setHash(active); common.setHash(active);
Object.keys(categories).forEach(function (key) { Object.keys(categories).forEach(function (key) {
var $category = $('<div>', {'class': 'cp-sidebarlayout-category'}).appendTo($categories); var $category = $('<div>', {'class': 'cp-sidebarlayout-category'}).appendTo($categories);

@ -250,6 +250,12 @@ define([
secret.hashData.ownerKey === "uPmJDtDJ9okhdIyQ-8zphYlpaAonJDOC6MAcYY6iBwWBQr+XmrQ9uGY9WkApJTfEfAu5QcqaDCw1Ul+JXKcYkA" && secret.hashData.ownerKey === "uPmJDtDJ9okhdIyQ-8zphYlpaAonJDOC6MAcYY6iBwWBQr+XmrQ9uGY9WkApJTfEfAu5QcqaDCw1Ul+JXKcYkA" &&
!secret.hashData.present); !secret.hashData.present);
}, "test support for owner key in version 1 file hash failed to parse"); }, "test support for owner key in version 1 file hash failed to parse");
assert(function (cb) {
var parsed = Hash.parsePadUrl('/file/#/2/file/JQU88aX+ieXR58L5T787434a/');
var secret = Hash.getSecrets('file', parsed.hash);
return cb(secret.type === 'file' && secret.password === undefined &&
secret.channel === "2031a9b51247a07ad398227367c1b95efaad969b209a279c");
}, "test support for v2 file hash");
assert(function (cb) { assert(function (cb) {
var secret = Hash.parsePadUrl('/invite/#/2/invite/edit/oRE0oLCtEXusRDyin7GyLGcS/p/'); var secret = Hash.parsePadUrl('/invite/#/2/invite/edit/oRE0oLCtEXusRDyin7GyLGcS/p/');

@ -17,6 +17,7 @@ define([
'/customize/application_config.js', '/customize/application_config.js',
'/lib/calendar/tui-calendar.min.js', '/lib/calendar/tui-calendar.min.js',
'/calendar/export.js', '/calendar/export.js',
'/lib/datepicker/flatpickr.js',
'/common/inner/share.js', '/common/inner/share.js',
'/common/inner/access.js', '/common/inner/access.js',
@ -46,6 +47,7 @@ define([
AppConfig, AppConfig,
Calendar, Calendar,
Export, Export,
Flatpickr,
Share, Access, Properties Share, Access, Properties
) )
{ {
@ -119,7 +121,7 @@ define([
}; };
var getWeekDays = function (large) { var getWeekDays = function (large) {
var baseDate = new Date(Date.UTC(2017, 0, 1)); // just a Sunday var baseDate = new Date(2017, 0, 1); // just a Sunday
var weekDays = []; var weekDays = [];
for(var i = 0; i < 7; i++) { for(var i = 0; i < 7; i++) {
weekDays.push(baseDate.toLocaleDateString(undefined, { weekday: 'long' })); weekDays.push(baseDate.toLocaleDateString(undefined, { weekday: 'long' }));
@ -169,9 +171,9 @@ define([
var obj = data.content[uid]; var obj = data.content[uid];
obj.title = obj.title || ""; obj.title = obj.title || "";
obj.location = obj.location || ""; obj.location = obj.location || "";
if (obj.isAllDay && obj.startDay) { obj.start = +new Date(obj.startDay); } if (obj.isAllDay && obj.startDay) { obj.start = +Flatpickr.parseDate((obj.startDay)); }
if (obj.isAllDay && obj.endDay) { if (obj.isAllDay && obj.endDay) {
var endDate = new Date(obj.endDay); var endDate = Flatpickr.parseDate(obj.endDay);
endDate.setHours(23); endDate.setHours(23);
endDate.setMinutes(59); endDate.setMinutes(59);
endDate.setSeconds(59); endDate.setSeconds(59);

@ -14,13 +14,17 @@ html, body {
.report { .report {
font-size: 30px; font-size: 30px;
max-width: 50%; max-width: 26em;
margin: auto; margin: auto;
padding-top: 15px; padding-top: 15px;
} }
.summary, .failure, .error, .success {
margin-bottom: 1em;
}
.pending { .pending {
border: 1px solid white; border: 1px solid @cryptpad_text_col;
.fa { .fa {
margin-right: 20px; margin-right: 20px;
} }
@ -42,10 +46,18 @@ html, body {
padding: 15px; padding: 15px;
} }
table { .table-container {
td { overflow-x: auto;
padding: 5px; width: 100%;
border: 1px solid white; 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; 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; 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/pinpad.js',
'/common/outer/network-config.js', '/common/outer/network-config.js',
'/customize/pages.js', '/customize/pages.js',
'/checkup/checkup-tools.js',
'/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/tweetnacl/nacl-fast.min.js',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/checkup/app-checkup.less', 'less!/checkup/app-checkup.less',
], function ($, ApiConfig, Assertions, h, Messages, DomReady, ], function ($, ApiConfig, Assertions, h, Messages, DomReady,
nThen, SFCommonO, Login, Hash, Util, Pinpad, nThen, SFCommonO, Login, Hash, Util, Pinpad,
NetConfig, Pages) { NetConfig, Pages, Tools) {
var Assert = Assertions(); var Assert = Assertions();
var trimSlashes = function (s) { var trimSlashes = function (s) {
if (typeof(s) !== 'string') { return s; } if (typeof(s) !== 'string') { return s; }
@ -30,8 +31,12 @@ define([
Assert(f, msg || h('span.advisory-text.cp-danger')); Assert(f, msg || h('span.advisory-text.cp-danger'));
}; };
var code = function (content) {
return h('code', content);
};
var CONFIG_PATH = function () { var CONFIG_PATH = function () {
return h('code', 'cryptpad/config/config.js'); return code('cryptpad/config/config.js');
}; };
var API_CONFIG_LINK = function () { var API_CONFIG_LINK = function () {
return h('a', { return h('a', {
@ -50,15 +55,31 @@ define([
]); ]);
}; };
var link = function (href, text) {
return h('a', {
href: href,
rel: 'noopener noreferrer',
target: '_blank',
}, text);
};
var setWarningClass = function (msg) {
$(msg).removeClass('cp-danger').addClass('cp-warning');
};
var cacheBuster = function (url) {
return url + '?test=' + (+new Date());
};
var trimmedSafe = trimSlashes(ApiConfig.httpSafeOrigin); var trimmedSafe = trimSlashes(ApiConfig.httpSafeOrigin);
var trimmedUnsafe = trimSlashes(ApiConfig.httpUnsafeOrigin); var trimmedUnsafe = trimSlashes(ApiConfig.httpUnsafeOrigin);
assert(function (cb, msg) { assert(function (cb, msg) {
msg.appendChild(h('span', [ msg.appendChild(h('span', [
"CryptPad's sandbox requires that both ", "CryptPad's sandbox requires that both ",
h('code', 'httpUnsafeOrigin'), code('httpUnsafeOrigin'),
' and ', ' and ',
h('code', 'httpSafeOrigin'), code('httpSafeOrigin'),
" be configured in ", " be configured in ",
CONFIG_PATH(), CONFIG_PATH(),
'. ', '. ',
@ -71,9 +92,9 @@ define([
assert(function (cb, msg) { assert(function (cb, msg) {
msg.appendChild(h('span', [ msg.appendChild(h('span', [
h('code', 'httpUnsafeOrigin'), code('httpUnsafeOrigin'),
' and ', ' and ',
h('code', 'httpSafeOrigin'), code('httpSafeOrigin'),
' are equivalent. ', ' are equivalent. ',
"In order for CryptPad's security features to be as effective as intended they must be different. ", "In order for CryptPad's security features to be as effective as intended they must be different. ",
"See ", "See ",
@ -87,9 +108,9 @@ define([
assert(function (cb, msg) { assert(function (cb, msg) {
msg.appendChild(h('span', [ msg.appendChild(h('span', [
h('code', 'httpUnsafeOrigin'), code('httpUnsafeOrigin'),
' and ', ' and ',
h('code', 'httpSafeOrigin'), code('httpSafeOrigin'),
' must not contain trailing slashes. This can be configured in ', ' must not contain trailing slashes. This can be configured in ',
CONFIG_PATH(), CONFIG_PATH(),
'. ', '. ',
@ -101,10 +122,10 @@ define([
assert(function (cb, msg) { assert(function (cb, msg) {
msg.appendChild(h("span", [ msg.appendChild(h("span", [
"It appears that you are trying to load this page via an origin other than its main domain (", "It appears that you are trying to load this page via an origin other than its main domain (",
h('code', ApiConfig.httpUnsafeOrigin), code(ApiConfig.httpUnsafeOrigin),
"). See the ", "). See the ",
h('code', 'httpUnsafeOrigin'), code('httpUnsafeOrigin'),
" option in ", " option in ",
CONFIG_PATH(), CONFIG_PATH(),
" which is exposed via ", " which is exposed via ",
@ -117,7 +138,7 @@ define([
var checkAvailability = function (url, cb) { var checkAvailability = function (url, cb) {
$.ajax({ $.ajax({
url: url, url: cacheBuster(url),
data: {}, data: {},
complete: function (xhr) { complete: function (xhr) {
cb(xhr.status === 200); cb(xhr.status === 200);
@ -128,7 +149,7 @@ define([
assert(function (cb, msg) { assert(function (cb, msg) {
msg.appendChild(h('span', [ msg.appendChild(h('span', [
"The main domain (configured via ", "The main domain (configured via ",
h('code', 'httpUnsafeOrigin'), code('httpUnsafeOrigin'),
' as ', ' as ',
ApiConfig.httpUnsafeOrigin, ApiConfig.httpUnsafeOrigin,
' in ', ' in ',
@ -145,13 +166,13 @@ define([
assert(function (cb, msg) { assert(function (cb, msg) {
msg.appendChild(h('span', [ msg.appendChild(h('span', [
"Your browser was not able to load an iframe using the origin specified as ", "Your browser was not able to load an iframe using the origin specified as ",
h('code', "httpSafeOrigin"), code("httpSafeOrigin"),
" (", " (",
ApiConfig.httpSafeOrigin, ApiConfig.httpSafeOrigin,
") in ", ") in ",
CONFIG_PATH(), CONFIG_PATH(),
". This can be caused by an invalid ", ". This can be caused by an invalid ",
h('code', 'httpUnsafeDomain'), code('httpUnsafeDomain'),
', invalid CSP configuration in your reverse proxy, invalid SSL certificates, and many other factors. ', ', invalid CSP configuration in your reverse proxy, invalid SSL certificates, and many other factors. ',
'More information about your particular error may be found in your browser console. ', 'More information about your particular error may be found in your browser console. ',
RESTART_WARNING(), RESTART_WARNING(),
@ -169,10 +190,13 @@ define([
}).nThen(function () { }).nThen(function () {
// Iframe is loaded // Iframe is loaded
clearTimeout(to); clearTimeout(to);
console.log("removing sandbox iframe");
$('iframe#sbox-iframe').remove();
cb(true); cb(true);
}); });
}); });
var shared_websocket;
// Test Websocket // Test Websocket
var evWSError = Util.mkEvent(true); var evWSError = Util.mkEvent(true);
assert(function (_cb, msg) { assert(function (_cb, msg) {
@ -185,6 +209,7 @@ define([
})); }));
var ws = new WebSocket(NetConfig.getWebsocketURL()); var ws = new WebSocket(NetConfig.getWebsocketURL());
shared_websocket = ws;
var to = setTimeout(function () { var to = setTimeout(function () {
console.error('Websocket TIMEOUT'); console.error('Websocket TIMEOUT');
evWSError.fire(); evWSError.fire();
@ -203,6 +228,7 @@ define([
}); });
// Test login block // Test login block
var shared_realtime;
assert(function (_cb, msg) { assert(function (_cb, msg) {
var websocketErr = "No WebSocket available"; var websocketErr = "No WebSocket available";
var cb = Util.once(Util.both(_cb, function (status) { var cb = Util.once(Util.both(_cb, function (status) {
@ -221,7 +247,7 @@ define([
msg.appendChild(h('span', [ msg.appendChild(h('span', [
"Unable to create, retrieve, or remove encrypted credentials from the server. ", "Unable to create, retrieve, or remove encrypted credentials from the server. ",
"This is most commonly caused by a mismatch between the value of the ", "This is most commonly caused by a mismatch between the value of the ",
h('code', 'blockPath'), code('blockPath'),
' value configured in ', ' value configured in ',
CONFIG_PATH(), CONFIG_PATH(),
" and the corresponding settings in your reverse proxy's configuration file,", " and the corresponding settings in your reverse proxy's configuration file,",
@ -230,6 +256,11 @@ define([
])); ]));
})); }));
// time out after 30 seconds
setTimeout(function () {
cb('TIMEOUT');
}, 30000);
var bytes = new Uint8Array(Login.requiredBytes); var bytes = new Uint8Array(Login.requiredBytes);
var opt = Login.allocateBytes(bytes); var opt = Login.allocateBytes(bytes);
@ -237,7 +268,7 @@ define([
var blockUrl = Login.Block.getBlockUrl(opt.blockKeys); var blockUrl = Login.Block.getBlockUrl(opt.blockKeys);
var blockRequest = Login.Block.serialize("{}", opt.blockKeys); var blockRequest = Login.Block.serialize("{}", opt.blockKeys);
var removeRequest = Login.Block.remove(opt.blockKeys); var removeRequest = Login.Block.remove(opt.blockKeys);
console.log('Test block URL:', blockUrl); console.warn('Testing block URL (%s). One 404 is normal.', blockUrl);
var userHash = '/2/drive/edit/000000000000000000000000'; var userHash = '/2/drive/edit/000000000000000000000000';
var secret = Hash.getSecrets('drive', userHash); var secret = Hash.getSecrets('drive', userHash);
@ -264,7 +295,7 @@ define([
console.error("Can't create new channel. This may also be a websocket issue."); console.error("Can't create new channel. This may also be a websocket issue.");
return void cb(false); return void cb(false);
} }
RT = rt; shared_realtime = RT = rt;
var proxy = rt.proxy; var proxy = rt.proxy;
proxy.edPublic = opt.edPublic; proxy.edPublic = opt.edPublic;
proxy.edPrivate = opt.edPrivate; proxy.edPrivate = opt.edPrivate;
@ -330,14 +361,13 @@ define([
}).nThen(function () { }).nThen(function () {
cb(true); cb(true);
}); });
}); });
var sheetURL = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html'; var sheetURL = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html';
assert(function (cb, msg) { assert(function (cb, msg) {
msg.innerText = "Missing HTTP headers required for .xlsx export from sheets. "; msg.innerText = "Missing HTTP headers required for .xlsx export from sheets. ";
var url = sheetURL; var url = cacheBuster(sheetURL);
var expect = { var expect = {
'cross-origin-resource-policy': 'cross-origin', 'cross-origin-resource-policy': 'cross-origin',
'cross-origin-embedder-policy': 'require-corp', 'cross-origin-embedder-policy': 'require-corp',
@ -351,11 +381,11 @@ define([
if (response !== expect[k]) { if (response !== expect[k]) {
msg.appendChild(h('span', [ msg.appendChild(h('span', [
'A value of ', 'A value of ',
h('code', expect[k]), code(expect[k]),
' was expected for the ', ' was expected for the ',
h('code', k), code(k),
' HTTP header, but instead a value of "', ' HTTP header, but instead a value of "',
h('code', response), code(response),
'" was received.', '" was received.',
])); ]));
return true; // returning true indicates that a value is incorrect return true; // returning true indicates that a value is incorrect
@ -366,67 +396,55 @@ define([
}); });
assert(function (cb, msg) { assert(function (cb, msg) {
msg.innerText = "Missing HTTP header required to disable Google's Floc."; setWarningClass(msg);
$.ajax('/?'+ (+new Date()), {
complete: function (xhr) {
cb(xhr.getResponseHeader('permissions-policy') === 'interest-cohort=()');
},
});
});
assert(function (cb, msg) { var printMessage = function (value) {
msg = msg; msg.appendChild(h('span', [
return void cb(true); "This instance hasn't opted out of participation in Google's ",
/* code('FLoC'),
msg.appendChild(h('span', [ " targeted advertizing network. ",
"The spreadsheet editor's code was not served with the required Content-Security Policy headers. ",
"This is most often caused by incorrectly configured sandbox parameters (", "This can be done by setting a ",
h('code', 'httpUnsafeOrigin'), code('permissions-policy'),
' and ', " HTTP header with a value of ",
h('code', 'httpSafeOrigin'), code('"interest-cohort=()"'),
' in ', " in the configuration of its reverse proxy instead of the current value (",
CONFIG_PATH, code(value),
"), or settings in your reverse proxy's configuration which don't match your application server's config. ", "). See the provided NGINX configuration file for an example. ",
RESTART_WARNING(),
])); h('p', [
link("https://www.eff.org/deeplinks/2021/04/am-i-floced-launch", 'Learn more'),
]),
]));
};
$.ajax(sheetURL, { $.ajax('/?'+ (+new Date()), {
complete: function (xhr) { complete: function (xhr) {
var csp = xhr.getResponseHeader('Content-Security-Policy'); var header = xhr.getResponseHeader('permissions-policy');
if (!/unsafe\-eval/.test(csp)) { printMessage(JSON.stringify(header));
// OnlyOffice requires unsafe-eval cb(header === 'interest-cohort=()' || header);
console.error('CSP', csp);
return cb("expected 'unsafe-eval'");
}
if (!/unsafe\-inline/.test(csp)) {
// OnlyOffice also requires unsafe-inline
console.error('CSP', csp);
return cb("expected 'unsafe-inline'");
}
cb(true);
}, },
}); */ });
}); });
assert(function (cb, msg) { assert(function (cb, msg) {
msg.appendChild(h('span', [ msg.appendChild(h('span', [
h('code', '/api/broadcast'), code('/api/broadcast'),
" could not be loaded. This can be caused by an outdated application server or an incorrectly configured reverse proxy. ", " could not be loaded. This can be caused by an outdated application server or an incorrectly configured reverse proxy. ",
"Even if the most recent code has been downloaded it's possible the application server has not been restarted. ", "Even if the most recent code has been downloaded it's possible the application server has not been restarted. ",
"Your browser console may provide more details as to why this resource could not be loaded. ", "Your browser console may provide more details as to why this resource could not be loaded. ",
])); ]));
$.ajax('/api/broadcast', { $.ajax(cacheBuster('/api/broadcast'), {
dataType: 'text', dataType: 'text',
complete: function (xhr) { complete: function (xhr) {
console.log(xhr);
cb(xhr.status === 200); cb(xhr.status === 200);
}, },
}); });
}); });
var checkAPIHeaders = function (url, cb) { var checkAPIHeaders = function (url, msg, cb) {
$.ajax(url, { $.ajax(cacheBuster(url), {
dataType: 'text', dataType: 'text',
complete: function (xhr) { complete: function (xhr) {
var allHeaders = xhr.getAllResponseHeaders(); var allHeaders = xhr.getAllResponseHeaders();
@ -445,15 +463,31 @@ define([
var expect = { var expect = {
'cross-origin-resource-policy': 'cross-origin', 'cross-origin-resource-policy': 'cross-origin',
'cross-origin-embedder-policy': 'require-corp',
}; };
var incorrect = Object.keys(expect).some(function (k) { var incorrect = false;
Object.keys(expect).forEach(function (k) {
var response = xhr.getResponseHeader(k); var response = xhr.getResponseHeader(k);
if (response !== expect[k]) { var expected = expect[k];
return true; if (response !== expected) {
incorrect = true;
msg.appendChild(h('p', [
'The ',
code(k),
' header for ',
code(url),
" is '",
code(response),
"' instead of '",
code(expected),
"' as expected.",
]));
} }
}); });
if (duplicated || incorrect) { console.error(allHeaders); } if (duplicated || incorrect) { console.debug(allHeaders); }
cb(!duplicated && !incorrect); cb(!duplicated && !incorrect);
}, },
}); });
@ -464,19 +498,15 @@ define([
assert(function (cb, msg) { assert(function (cb, msg) {
var url = '/api/config'; var url = '/api/config';
msg.innerText = url + INCORRECT_HEADER_TEXT; msg.innerText = url + INCORRECT_HEADER_TEXT;
checkAPIHeaders(url, cb); checkAPIHeaders(url, msg, cb);
}); });
assert(function (cb, msg) { assert(function (cb, msg) {
var url = '/api/broadcast'; var url = '/api/broadcast';
msg.innerText = url + INCORRECT_HEADER_TEXT; msg.innerText = url + INCORRECT_HEADER_TEXT;
checkAPIHeaders(url, cb); checkAPIHeaders(url, msg, cb);
}); });
var setWarningClass = function (msg) {
$(msg).removeClass('cp-danger').addClass('cp-warning');
};
assert(function (cb, msg) { assert(function (cb, msg) {
var email = ApiConfig.adminEmail; var email = ApiConfig.adminEmail;
if (typeof(email) === 'string' && email && email !== 'i.did.not.read.my.config@cryptpad.fr') { if (typeof(email) === 'string' && email && email !== 'i.did.not.read.my.config@cryptpad.fr') {
@ -486,10 +516,11 @@ define([
setWarningClass(msg); setWarningClass(msg);
msg.appendChild(h('span', [ msg.appendChild(h('span', [
'This instance does not provide a valid ', 'This instance does not provide a valid ',
h('code', 'adminEmail'), code('adminEmail'),
' which can make it difficult to contact its adminstrator to report vulnerabilities or abusive content.', ' which can make it difficult to contact its adminstrator to report vulnerabilities or abusive content.',
' This can be configured in ', CONFIG_PATH(), '. ', " This can be configured on your instance's admin panel. Use the provided ",
RESTART_WARNING(), code("Flush cache'"),
" button for this change to take effect for all users.",
])); ]));
cb(email); cb(email);
}); });
@ -499,12 +530,9 @@ define([
setWarningClass(msg); setWarningClass(msg);
msg.appendChild(h('span', [ msg.appendChild(h('span', [
"This instance's encrypted support ticket functionality has not been enabled. This can make it difficult for its users to safely report issues that concern sensitive information. ", "This instance's encrypted support ticket functionality has not been enabled. This can make it difficult for its users to safely report issues that concern sensitive information. ",
"This can be configured via the ", "This can be configured via the admin panel's ",
h('code', 'supportMailbox'), code('Support'),
" attribute in ", " tab.",
CONFIG_PATH(),
". ",
RESTART_WARNING(),
])); ]));
cb(support && typeof(support) === 'string' && support.length === 44); cb(support && typeof(support) === 'string' && support.length === 44);
}); });
@ -517,7 +545,7 @@ define([
setWarningClass(msg); setWarningClass(msg);
msg.appendChild(h('span', [ msg.appendChild(h('span', [
"This instance has not been configured to support web administration. This can be enabled by adding a registered user's public signing key to the ", "This instance has not been configured to support web administration. This can be enabled by adding a registered user's public signing key to the ",
h('code', 'adminKeys'), code('adminKeys'),
' array in ', ' array in ',
CONFIG_PATH(), CONFIG_PATH(),
'. ', '. ',
@ -526,6 +554,331 @@ define([
cb(false); cb(false);
}); });
var response = Util.response(function (err) {
console.error('SANDBOX_ERROR', err);
});
var sandboxIframe = h('iframe', {
class: 'sandbox-test',
src: cacheBuster(trimmedSafe + '/checkup/sandbox/index.html'),
});
document.body.appendChild(sandboxIframe);
var sandboxIframeReady = Util.mkEvent(true);
setTimeout(function () {
sandboxIframeReady.fire("TIMEOUT");
}, 10 * 1000);
var postMessage = function (content, cb) {
try {
var txid = Util.uid();
content.txid = txid;
response.expect(txid, cb, 15000);
sandboxIframe.contentWindow.postMessage(JSON.stringify(content), '*');
} catch (err) {
console.error(err);
}
};
var deferredPostMessage = function (content, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
nThen(function (w) {
sandboxIframeReady.reg(w(function (err) {
if (!err) { return; }
w.abort();
cb(err);
}));
}).nThen(function () {
postMessage(content, cb);
});
};
window.addEventListener('message', function (event) {
try {
var msg = JSON.parse(event.data);
if (msg.command === 'READY') { return void sandboxIframeReady.fire(); }
if (msg.q === "READY") { return; } // ignore messages from the usual sandboxed iframe
var txid = msg.txid;
if (!txid) { return console.log("no handler for ", txid); }
response.handle(txid, msg.content);
} catch (err) {
console.error(event);
console.error(err);
}
});
var parseCSP = function (CSP) {
//console.error(CSP);
var CSP_headers = {};
CSP.split(";")
.forEach(function (rule) {
rule = (rule || "").trim();
if (!rule) { return; }
var parts = rule.split(/\s/);
var first = parts[0];
var rest = rule.slice(first.length + 1);
CSP_headers[first] = rest;
//console.error(rule.trim());
//console.info("[%s] '%s'", first, rest);
});
return CSP_headers;
};
var hasUnsafeEval = function (CSP_headers) {
return /unsafe\-eval/.test(CSP_headers['script-src']);
};
var hasUnsafeInline = function (CSP_headers) {
return /unsafe\-inline/.test(CSP_headers['script-src']);
};
var hasOnlyOfficeHeaders = function (CSP_headers) {
if (!hasUnsafeEval(CSP_headers)) {
console.error("NO_UNSAFE_EVAL");
console.log(CSP_headers);
return false;
}
if (!hasUnsafeInline(CSP_headers)) {
console.error("NO_UNSAFE_INLINE");
return void false;
}
return true;
};
var CSP_WARNING = function (url) {
return h('span', [
code(url),
' does not have the required ',
code("'content-security-policy'"),
' headers set. This is most often related to incorrectly configured sandbox domains or reverse proxies.',
]);
};
assert(function (_cb, msg) {
var url = '/sheet/inner.html';
var cb = Util.once(Util.mkAsync(_cb));
msg.appendChild(CSP_WARNING(url));
deferredPostMessage({
command: 'GET_HEADER',
content: {
url: url,
header: 'content-security-policy',
},
}, function (content) {
var CSP_headers = parseCSP(content);
cb(hasOnlyOfficeHeaders(CSP_headers));
});
});
assert(function (cb, msg) {
var url = '/common/onlyoffice/v4/web-apps/apps/spreadsheeteditor/main/index.html';
msg.appendChild(CSP_WARNING(url));
deferredPostMessage({
command: 'GET_HEADER',
content: {
url: url,
header: 'content-security-policy',
},
}, function (content) {
var CSP_headers = parseCSP(content);
cb(hasOnlyOfficeHeaders(CSP_headers));
});
});
assert(function (cb, msg) {
var url = '/sheet/inner.html';
msg.appendChild(h('span', [
code(url),
' does not have the required ',
code("'cross-origin-opener-policy'"),
' headers set.',
]));
deferredPostMessage({
command: 'GET_HEADER',
content: {
url: url,
header: 'cross-origin-opener-policy',
},
}, function (content) {
cb(content === 'same-origin');
});
});
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);
};
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 (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
msg.appendChild(h('span', [
"Both ",
code('httpUnsafeOrigin'),
' and ',
code('httpSafeOrigin'),
' should be accessed via HTTPS for production use. ',
"This can be configured via ",
CONFIG_PATH(),
'. ',
RESTART_WARNING(),
]));
cb(isHTTPS(trimmedUnsafe) && isHTTPS(trimmedSafe));
});
[
'sheet',
'presentation',
'doc',
'convert',
].forEach(function (url) {
assert(function (cb, msg) {
var header = 'cross-origin-opener-policy';
var expected = 'same-origin';
deferredPostMessage({
command: 'GET_HEADER',
content: {
url: '/' + url + '/',
header: header,
}
}, function (content) {
msg.appendChild(h('span', [
code(url),
' was served without the correct ',
code(header),
' HTTP header value (',
code(expected),
'). This will interfere with your ability to convert between office file formats.'
]));
cb(content === expected);
});
});
});
/*
assert(function (cb, msg) {
setWarningClass(msg);
$.ajax(cacheBuster('/'), {
dataType: 'text',
complete: function (xhr) {
var serverToken = xhr.getResponseHeader('server');
if (serverToken === null) { return void cb(true); }
var lowered = (serverToken || '').toLowerCase();
var family;
['Apache', 'Caddy', 'NGINX'].some(function (pattern) {
if (lowered.indexOf(pattern.toLowerCase()) !== -1) {
family = pattern;
return true;
}
});
var text = [
"This instance is set to respond with an HTTP ",
code("server"),
" header. This information can make it easier for attackers to find and exploit known vulnerabilities. ",
];
if (family === 'NGINX') { // FIXME incorrect instructions for HTTP2. needs a recompile?
msg.appendChild(h('span', text.concat([
"This can be addressed by setting ",
code("server_tokens off"),
" in your global NGINX config."
])));
return void cb(serverToken);
}
// handle other
msg.appendChild(h('span', text.concat([
"In this case, it appears that the host server is running ",
code(serverToken),
" instead of ",
code("NGINX"),
" as recommended. As such, you may not benefit from the latest security enhancements that are tested and maintained by the CryptPad development team.",
])));
cb(serverToken);
}
});
});
*/
if (false) { if (false) {
assert(function (cb, msg) { assert(function (cb, msg) {
msg.innerText = 'fake test to simulate failure'; msg.innerText = 'fake test to simulate failure';
@ -540,12 +893,21 @@ define([
}; };
var failureReport = function (obj) { var failureReport = function (obj) {
var printableValue = obj.output;
try {
printableValue = JSON.stringify(obj.output, null, ' ');
} catch (err) {
console.error(err);
}
return h('div.error', [ return h('div.error', [
h('h5', obj.message), h('h5', obj.message),
h('table', [ h('div.table-container',
row(["Failed test number", obj.test + 1]), h('table', [
row(["Returned value", obj.output]), row(["Failed test number", obj.test + 1]),
]), row(["Returned value", h('pre', code(printableValue))]),
])
),
]); ]);
}; };
@ -553,7 +915,7 @@ define([
var $progress = $('#cp-progress'); var $progress = $('#cp-progress');
var versionStatement = function () { var versionStatement = function () {
return h('p', [ return h('p.cp--notice-version', [
"This instance is running ", "This instance is running ",
h('span.cp-app-checkup-version',[ h('span.cp-app-checkup-version',[
"CryptPad", "CryptPad",
@ -564,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) { Assert.run(function (state) {
var errors = state.errors; var errors = state.errors;
var failed = errors.length; var failed = errors.length;
@ -574,10 +946,11 @@ define([
var failedDetails = "Details found below"; 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 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, [ var summary = h('div.summary.' + statusClass, [
versionStatement(), versionStatement(),
browserStatement(),
h('p', Messages._getKey('assert_numberOfTestsPassed', [ h('p', Messages._getKey('assert_numberOfTestsPassed', [
state.passed, state.passed,
state.total state.total
@ -592,6 +965,14 @@ define([
$progress.remove(); $progress.remove();
$('body').prepend(report); $('body').prepend(report);
try {
console.log('closing shared websocket');
shared_websocket.close();
} catch (err) { console.error(err); }
try {
console.log('closing shared realtime');
shared_realtime.network.disconnect();
} catch (err) { console.error(err); }
}, function (i, total) { }, function (i, total) {
console.log('test '+ i +' completed'); console.log('test '+ i +' completed');
completed++; completed++;

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
</head>
<body>
<div id="cp-progress"></div>
<iframe-placeholder>

@ -0,0 +1,78 @@
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 ($, Util, Tools) {
var postMessage = function (content) {
window.parent.postMessage(JSON.stringify(content), '*');
};
postMessage({ command: "READY", });
var getHeaders = function (url, cb) {
$.ajax(url + "?test=" + (+new Date()), {
dataType: 'text',
complete: function (xhr) {
var allHeaders = xhr.getAllResponseHeaders();
return void cb(void 0, allHeaders, xhr);
},
});
};
var COMMANDS = {};
COMMANDS.GET_HEADER = function (content, cb) {
var url = content.url;
getHeaders(url, function (err, headers, xhr) {
cb(xhr.getResponseHeader(content.header));
});
};
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) {
try {
//console.log(JSON.parse(event.data));
var msg = JSON.parse(event.data);
command = msg.command;
txid = msg.txid;
if (!txid) { return; }
COMMANDS[command](msg.content, function (response) {
// postMessage with same txid
postMessage({
txid: txid,
content: response,
});
});
} catch (err) {
postMessage({
txid: txid,
content: err,
});
console.error(err, command);
}
} else {
console.error(event);
}
});
});

@ -257,8 +257,8 @@ define([
'class': 'cp-splitter' 'class': 'cp-splitter'
}).appendTo($previewContainer); }).appendTo($previewContainer);
$preview.on('scroll', function() { $previewContainer.on('scroll', function() {
splitter.css('top', $preview.scrollTop() + 'px'); splitter.css('top', $previewContainer.scrollTop() + 'px');
}); });
var $target = $codeMirrorContainer; var $target = $codeMirrorContainer;
@ -368,7 +368,7 @@ define([
var mkFilePicker = function (framework, editor, evModeChange) { var mkFilePicker = function (framework, editor, evModeChange) {
evModeChange.reg(function (mode) { evModeChange.reg(function (mode) {
if (MEDIA_TAG_MODES.indexOf(mode) !== -1) { if (MEDIA_TAG_MODES.indexOf(mode) !== -1) {
// Embedding is endabled // Embedding is enabled
framework.setMediaTagEmbedder(function (mt) { framework.setMediaTagEmbedder(function (mt) {
editor.focus(); editor.focus();
editor.replaceSelection($(mt)[0].outerHTML); editor.replaceSelection($(mt)[0].outerHTML);
@ -507,7 +507,7 @@ define([
var fileHost = privateData.fileHost || privateData.origin; var fileHost = privateData.fileHost || privateData.origin;
var src = fileHost + Hash.getBlobPathFromHex(secret.channel); var src = fileHost + Hash.getBlobPathFromHex(secret.channel);
var key = Hash.encodeBase64(secret.keys.cryptKey); var key = Hash.encodeBase64(secret.keys.cryptKey);
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>'; var mt = UI.mediaTag(src, key).outerHTML;
editor.replaceSelection(mt); editor.replaceSelection(mt);
} }
}; };

@ -4,15 +4,15 @@
* file (make a copy from /customize.dist/application_config.js) * file (make a copy from /customize.dist/application_config.js)
*/ */
define(function() { define(function() {
var config = {}; var AppConfig = {};
/* Select the buttons displayed on the main page to create new collaborative sessions. /* Select the buttons displayed on the main page to create new collaborative sessions.
* Removing apps from the list will prevent users from accessing them. They will instead be * Removing apps from the list will prevent users from accessing them. They will instead be
* redirected to the drive. * redirected to the drive.
* You should never remove the drive from this list. * You should never remove the drive from this list.
*/ */
config.availablePadTypes = ['drive', 'teams', 'pad', 'sheet', 'code', 'slide', 'poll', 'kanban', 'whiteboard', AppConfig.availablePadTypes = ['drive', 'teams', 'pad', 'sheet', 'code', 'slide', 'poll', 'kanban', 'whiteboard',
/*'doc', 'presentation',*/ 'file', /*'todo',*/ 'contacts' /*, 'calendar' */]; /*'doc', 'presentation',*/ 'file', /*'todo',*/ 'contacts', 'form', 'convert'];
/* The registered only types are apps restricted to registered users. /* The registered only types are apps restricted to registered users.
* You should never remove apps from this list unless you know what you're doing. The apps * You should never remove apps from this list unless you know what you're doing. The apps
* listed here by default can't work without a user account. * listed here by default can't work without a user account.
@ -20,7 +20,13 @@ define(function() {
* users and these users will be redirected to the login page if they still try to access * users and these users will be redirected to the login page if they still try to access
* the app * the app
*/ */
config.registeredOnlyTypes = ['file', 'contacts', 'notifications', 'support']; AppConfig.registeredOnlyTypes = ['file', 'contacts', 'notifications', 'support'];
// to prevent apps that aren't officially supported from showing up
// in the document creation modal
AppConfig.hiddenTypes = ['drive', 'teams', 'contacts', 'todo', 'file', 'accounts', 'calendar', 'poll', 'convert',
//'doc', 'presentation'
];
/* CryptPad is available is multiple languages, but only English and French are maintained /* CryptPad is available is multiple languages, but only English and French are maintained
* by the developers. The other languages may be outdated, and any missing string for a langauge * by the developers. The other languages may be outdated, and any missing string for a langauge
@ -30,37 +36,37 @@ define(function() {
* can be found at the top of the file `/customize.dist/messages.js`. The list should only * can be found at the top of the file `/customize.dist/messages.js`. The list should only
* contain languages code ('en', 'fr', 'de', 'pt-br', etc.), not their full name. * contain languages code ('en', 'fr', 'de', 'pt-br', etc.), not their full name.
*/ */
//config.availableLanguages = ['en', 'fr', 'de']; //AppConfig.availableLanguages = ['en', 'fr', 'de'];
/* You can display a link to the imprint (legal notice) of your website in the static pages /* You can display a link to the imprint (legal notice) of your website in the static pages
* footer. To do so, you can either set the following value to `true` and create an imprint.html page * footer. To do so, you can either set the following value to `true` and create an imprint.html page
* in the `customize` directory. You can also set it to an absolute URL if your imprint page already exists. * in the `customize` directory. You can also set it to an absolute URL if your imprint page already exists.
*/ */
config.imprint = false; AppConfig.imprint = false;
// config.imprint = true; // AppConfig.imprint = true;
// config.imprint = 'https://xwiki.com/en/company/legal-notice'; // AppConfig.imprint = 'https://xwiki.com/en/company/legal-notice';
/* You can display a link to your own privacy policy in the static pages footer. /* You can display a link to your own privacy policy in the static pages footer.
* To do so, set the following value to the absolute URL of your privacy policy. * To do so, set the following value to the absolute URL of your privacy policy.
*/ */
// config.privacy = 'https://xwiki.com/en/company/PrivacyPolicy'; // AppConfig.privacy = 'https://xwiki.com/en/company/PrivacyPolicy';
/* We (the project's developers) include the ability to display a 'Roadmap' in static pages footer. /* We (the project's developers) include the ability to display a 'Roadmap' in static pages footer.
* This is disabled by default. * This is disabled by default.
* We use this to publish the project's development roadmap, but you can use it however you like. * We use this to publish the project's development roadmap, but you can use it however you like.
* To do so, set the following value to an absolute URL. * To do so, set the following value to an absolute URL.
*/ */
//config.roadmap = 'https://cryptpad.fr/kanban/#/2/kanban/view/PLM0C3tFWvYhd+EPzXrbT+NxB76Z5DtZhAA5W5hG9wo/'; //AppConfig.roadmap = 'https://cryptpad.fr/kanban/#/2/kanban/view/PLM0C3tFWvYhd+EPzXrbT+NxB76Z5DtZhAA5W5hG9wo/';
/* Cryptpad apps use a common API to display notifications to users /* Cryptpad apps use a common API to display notifications to users
* by default, notifications are hidden after 5 seconds * by default, notifications are hidden after 5 seconds
* You can change their duration here (measured in milliseconds) * You can change their duration here (measured in milliseconds)
*/ */
config.notificationTimeout = 5000; AppConfig.notificationTimeout = 5000;
config.disableUserlistNotifications = false; AppConfig.disableUserlistNotifications = false;
// Update the default colors available in the whiteboard application // Update the default colors available in the whiteboard application
config.whiteboardPalette = [ AppConfig.whiteboardPalette = [
'#000000', // black '#000000', // black
'#FFFFFF', // white '#FFFFFF', // white
'#848484', // grey '#848484', // grey
@ -82,14 +88,14 @@ define(function() {
// Background color in the apps with centered content: // Background color in the apps with centered content:
// - file app in view mode // - file app in view mode
// - rich text app when editor's width reduced in settings // - rich text app when editor's width reduced in settings
config.appBackgroundColor = '#666'; AppConfig.appBackgroundColor = '#666';
// Set enableTemplates to false to remove the button allowing users to save a pad as a template // Set enableTemplates to false to remove the button allowing users to save a pad as a template
// and remove the template category in CryptDrive // and remove the template category in CryptDrive
config.enableTemplates = true; AppConfig.enableTemplates = true;
// Set enableHistory to false to remove the "History" button in all the apps. // Set enableHistory to false to remove the "History" button in all the apps.
config.enableHistory = true; AppConfig.enableHistory = true;
/* user passwords are hashed with scrypt, and salted with their username. /* user passwords are hashed with scrypt, and salted with their username.
this value will be appended to the username, causing the resulting hash this value will be appended to the username, causing the resulting hash
@ -101,22 +107,24 @@ define(function() {
created. Changing it at a later time will break logins for all existing created. Changing it at a later time will break logins for all existing
users. users.
*/ */
config.loginSalt = ''; AppConfig.loginSalt = '';
config.minimumPasswordLength = 8; AppConfig.minimumPasswordLength = 8;
// Amount of time (ms) before aborting the session when the algorithm cannot synchronize the pad // Amount of time (ms) before aborting the session when the algorithm cannot synchronize the pad
config.badStateTimeout = 30000; AppConfig.badStateTimeout = 30000;
// Customize the icon used for each application. // Customize the icon used for each application.
// You can update the colors by making a copy of /customize.dist/src/less2/include/colortheme.less // You can update the colors by making a copy of /customize.dist/src/less2/include/colortheme.less
config.applicationsIcon = { AppConfig.applicationsIcon = {
file: 'cptools-file', file: 'cptools-file',
fileupload: 'cptools-file-upload', fileupload: 'cptools-file-upload',
folderupload: 'cptools-folder-upload', folderupload: 'cptools-folder-upload',
link: 'fa-link',
pad: 'cptools-richtext', pad: 'cptools-richtext',
code: 'cptools-code', code: 'cptools-code',
slide: 'cptools-slide', slide: 'cptools-slide',
poll: 'cptools-poll', poll: 'cptools-poll',
form: 'cptools-poll',
whiteboard: 'cptools-whiteboard', whiteboard: 'cptools-whiteboard',
todo: 'cptools-todo', todo: 'cptools-todo',
contacts: 'fa-address-book', contacts: 'fa-address-book',
@ -130,50 +138,49 @@ define(function() {
// Ability to create owned pads and expiring pads through a new pad creation screen. // Ability to create owned pads and expiring pads through a new pad creation screen.
// The new screen can be disabled by the users in their settings page // The new screen can be disabled by the users in their settings page
config.displayCreationScreen = true; AppConfig.displayCreationScreen = true;
// Prevent anonymous users from storing pads in their drive // Prevent anonymous users from storing pads in their drive
config.disableAnonymousStore = false; // NOTE: this is only enforced client-side as the server does not distinguish between users drives and pads
AppConfig.disableAnonymousStore = false;
// Prevent anonymous users from creating new pads (they can still access and edit existing ones)
// NOTE: this is only enforced client-side and will not prevent malicious clients from storing data
AppConfig.disableAnonymousPadCreation = false;
// Hide the usage bar in settings and drive // Hide the usage bar in settings and drive
//config.hideUsageBar = true; //AppConfig.hideUsageBar = true;
// Disable feedback for all the users and hide the settings part about feedback // Disable feedback for all the users and hide the settings part about feedback
//config.disableFeedback = true; //AppConfig.disableFeedback = true;
// Add new options in the share modal (extend an existing tab or add a new tab).
// More info about how to use it on the wiki:
// https://github.com/xwiki-labs/cryptpad/wiki/Application-config#configcustomizeshareoptions
//config.customizeShareOptions = function (hashes, tabs, config) {};
// Add code to be executed on every page before loading the user object. `isLoggedIn` (bool) is // Add code to be executed on every page before loading the user object. `isLoggedIn` (bool) is
// indicating if the user is registered or anonymous. Here you can change the way anonymous users // indicating if the user is registered or anonymous. Here you can change the way anonymous users
// work in CryptPad, use an external SSO or even force registration // work in CryptPad, use an external SSO or even force registration
// *NOTE*: You have to call the `callback` function to continue the loading process // *NOTE*: You have to call the `callback` function to continue the loading process
//config.beforeLogin = function(isLoggedIn, callback) {}; //AppConfig.beforeLogin = function(isLoggedIn, callback) {};
// Add code to be executed on every page after the user object is loaded (also work for // Add code to be executed on every page after the user object is loaded (also work for
// unregistered users). This allows you to interact with your users' drive // unregistered users). This allows you to interact with your users' drive
// *NOTE*: You have to call the `callback` function to continue the loading process // *NOTE*: You have to call the `callback` function to continue the loading process
//config.afterLogin = function(api, callback) {}; //AppConfig.afterLogin = function(api, callback) {};
// Disabling the profile app allows you to import the profile informations (display name, avatar) // Disabling the profile app allows you to import the profile informations (display name, avatar)
// from an external source and make sure the users can't change them from CryptPad. // from an external source and make sure the users can't change them from CryptPad.
// You can use config.afterLogin to import these values in the users' drive. // You can use AppConfig.afterLogin to import these values in the users' drive.
//config.disableProfile = true; //AppConfig.disableProfile = true;
// Disable the use of webworkers and sharedworkers in CryptPad. // Disable the use of webworkers and sharedworkers in CryptPad.
// Workers allow us to run the websockets connection and open the user drive in a separate thread. // Workers allow us to run the websockets connection and open the user drive in a separate thread.
// SharedWorkers allow us to load only one websocket and one user drive for all the browser tabs, // SharedWorkers allow us to load only one websocket and one user drive for all the browser tabs,
// making it much faster to open new tabs. // making it much faster to open new tabs.
config.disableWorkers = false; AppConfig.disableWorkers = false;
// Teams are always loaded during the initial loading screen (for the first tab only if // Teams are always loaded during the initial loading screen (for the first tab only if
// SharedWorkers are available). Allowing users to be members of multiple teams can // SharedWorkers are available). Allowing users to be members of multiple teams can
// make them have a very slow loading time. To avoid impacting the user experience // make them have a very slow loading time. To avoid impacting the user experience
// significantly, we're limiting the number of teams per user to 3 by default. // significantly, we're limiting the number of teams per user to 3 by default.
// You can change this value here. // You can change this value here.
//config.maxTeamsSlots = 5; //AppConfig.maxTeamsSlots = 5;
// Each team is considered as a registered user by the server. Users and teams are indistinguishable // Each team is considered as a registered user by the server. Users and teams are indistinguishable
// in the database so teams will offer the same storage limits as users by default. // in the database so teams will offer the same storage limits as users by default.
@ -181,7 +188,7 @@ define(function() {
// We're limiting the number of teams each user is able to own to 1 in order to make sure // We're limiting the number of teams each user is able to own to 1 in order to make sure
// users don't use "fake" teams (1 member) just to increase their storage limit. // users don't use "fake" teams (1 member) just to increase their storage limit.
// You can change the value here. // You can change the value here.
// config.maxOwnedTeams = 5; // AppConfig.maxOwnedTeams = 5;
// The userlist displayed in collaborative documents is stored alongside the document data. // The userlist displayed in collaborative documents is stored alongside the document data.
// Everytime someone with edit rights joins a document or modify their user data (display // Everytime someone with edit rights joins a document or modify their user data (display
@ -192,14 +199,14 @@ define(function() {
// position of other users' cursor. You can configure the number of user from which the session // position of other users' cursor. You can configure the number of user from which the session
// will enter into degraded mode. A big number may result in collaborative edition being broken, // will enter into degraded mode. A big number may result in collaborative edition being broken,
// but this number depends on the network and CPU performances of each user's device. // but this number depends on the network and CPU performances of each user's device.
config.degradedLimit = 8; AppConfig.degradedLimit = 8;
// In "legacy" mode, one-time users were always creating an "anonymous" drive when visiting CryptPad // In "legacy" mode, one-time users were always creating an "anonymous" drive when visiting CryptPad
// in which they could store their pads. The new "driveless" mode allow users to open an existing // in which they could store their pads. The new "driveless" mode allow users to open an existing
// pad without creating a drive in the background. The drive will only be created if they visit // pad without creating a drive in the background. The drive will only be created if they visit
// a different page (Drive, Settings, etc.) or try to create a new pad themselves. You can disable // a different page (Drive, Settings, etc.) or try to create a new pad themselves. You can disable
// the driveless mode by changing the following value to "false" // the driveless mode by changing the following value to "false"
config.allowDrivelessMode = true; AppConfig.allowDrivelessMode = true;
return config; return AppConfig;
}); });

@ -1,13 +1,15 @@
define(['jquery'], function ($) { define(['jquery'], function ($) {
var Clipboard = {}; var Clipboard = {};
// copy arbitrary text to the clipboard var copy = function (text, multiline) {
// return boolean indicating success
Clipboard.copy = function (text) {
var $ta = $('<input>', { var $ta = $('<input>', {
type: 'text', type: 'text',
}).val(text); }).val(text);
if (multiline) {
$ta = $('<textarea>').val(text);
}
$('body').append($ta); $('body').append($ta);
if (!($ta.length && $ta[0].select)) { if (!($ta.length && $ta[0].select)) {
@ -29,5 +31,15 @@ define(['jquery'], function ($) {
return success; return success;
}; };
// copy arbitrary text to the clipboard
// return boolean indicating success
Clipboard.copy = function (text) {
return copy(text);
};
Clipboard.copy.multiline = function (text) {
return copy(text, true);
};
return Clipboard; return Clipboard;
}); });

@ -10,6 +10,7 @@ define(['/customize/application_config.js'], function (AppConfig) {
oldStorageKey: 'CryptPad_RECENTPADS', oldStorageKey: 'CryptPad_RECENTPADS',
storageKey: 'filesData', storageKey: 'filesData',
tokenKey: 'loginToken', tokenKey: 'loginToken',
prefersDriveRedirectKey: 'prefersDriveRedirect',
displayPadCreationScreen: 'displayPadCreationScreen', displayPadCreationScreen: 'displayPadCreationScreen',
deprecatedKey: 'deprecated', deprecatedKey: 'deprecated',
MAX_TEAMS_SLOTS: AppConfig.maxTeamsSlots || 5, MAX_TEAMS_SLOTS: AppConfig.maxTeamsSlots || 5,

@ -34,6 +34,12 @@ var factory = function (Util, Crypto, Keys, Nacl) {
var keyPair = Nacl.sign.keyPair.fromSecretKey(privateKey); var keyPair = Nacl.sign.keyPair.fromSecretKey(privateKey);
return Nacl.util.encodeBase64(keyPair.publicKey); return Nacl.util.encodeBase64(keyPair.publicKey);
}; };
Hash.getCurvePublicFromPrivate = function (curvePrivateSafeStr) {
var curvePrivateStr = Crypto.b64AddSlashes(curvePrivateSafeStr);
var privateKey = Nacl.util.decodeBase64(curvePrivateStr);
var keyPair = Nacl.box.keyPair.fromSecretKey(privateKey);
return Nacl.util.encodeBase64(keyPair.publicKey);
};
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (secret) { var getEditHashFromKeys = Hash.getEditHashFromKeys = function (secret) {
var version = secret.version; var version = secret.version;
@ -209,6 +215,17 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
}); });
return k ? Crypto.b64AddSlashes(k) : ''; return k ? Crypto.b64AddSlashes(k) : '';
}; };
var getAuditorKey = function (hashArr) {
var k;
// Check if we have a ownerKey for this pad
hashArr.some(function (data) {
if (/^auditor=/.test(data)) {
k = data.slice(8);
return true;
}
});
return k ? Crypto.b64AddSlashes(k) : '';
};
var getOwnerKey = function (hashArr) { var getOwnerKey = function (hashArr) {
var k; var k;
// Check if we have a ownerKey for this pad // Check if we have a ownerKey for this pad
@ -231,6 +248,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
parsed.present = options.indexOf('present') !== -1; parsed.present = options.indexOf('present') !== -1;
parsed.embed = options.indexOf('embed') !== -1; parsed.embed = options.indexOf('embed') !== -1;
parsed.versionHash = getVersionHash(options); parsed.versionHash = getVersionHash(options);
parsed.auditorKey = getAuditorKey(options);
parsed.newPadOpts = getNewPadOpts(options); parsed.newPadOpts = getNewPadOpts(options);
parsed.loginOpts = getLoginOpts(options); parsed.loginOpts = getLoginOpts(options);
parsed.ownerKey = getOwnerKey(options); parsed.ownerKey = getOwnerKey(options);
@ -272,6 +290,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
present: parsed.present, present: parsed.present,
ownerKey: parsed.ownerKey, ownerKey: parsed.ownerKey,
versionHash: parsed.versionHash, versionHash: parsed.versionHash,
auditorKey: parsed.auditorKey,
newPadOpts: parsed.newPadOpts, newPadOpts: parsed.newPadOpts,
loginOpts: parsed.loginOpts, loginOpts: parsed.loginOpts,
password: parsed.password password: parsed.password
@ -298,6 +317,10 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
if (versionHash) { if (versionHash) {
hash += 'hash=' + Crypto.b64RemoveSlashes(versionHash) + '/'; hash += 'hash=' + Crypto.b64RemoveSlashes(versionHash) + '/';
} }
var auditorKey = typeof(opts.auditorKey) !== "undefined" ? opts.auditorKey : parsed.auditorKey;
if (auditorKey) {
hash += 'auditor=' + Crypto.b64RemoveSlashes(auditorKey) + '/';
}
if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; } if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; }
if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; } if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; }
return hash; return hash;
@ -621,6 +644,27 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
return hashes; return hashes;
}; };
Hash.getFormData = function (secret, hash, password) {
secret = secret || Hash.getSecrets('form', hash, password);
var keys = secret && secret.keys;
var secondary = keys && keys.secondaryKey;
if (!secondary) { return; }
var curvePair = Nacl.box.keyPair.fromSecretKey(Nacl.util.decodeUTF8(secondary).slice(0,32));
var ret = {};
ret.form_public = Nacl.util.encodeBase64(curvePair.publicKey);
var privateKey = ret.form_private = Nacl.util.encodeBase64(curvePair.secretKey);
var auditorHash = Hash.getViewHashFromKeys({
version: 1,
channel: secret.channel,
keys: { viewKeyStr: Nacl.util.encodeBase64(keys.cryptKey) }
});
var _parsed = Hash.parseTypeHash('pad', auditorHash);
ret.form_auditorHash = _parsed.getHash({auditorKey: privateKey});
return ret;
};
// STORAGE // STORAGE
Hash.hrefToHexChannelId = function (href, password) { Hash.hrefToHexChannelId = function (href, password) {
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);

@ -41,6 +41,15 @@ define([
return e; return e;
}; };
// FIXME almost everywhere this is used would also be
// a good candidate for sframe-common's getMediatagFromHref
UI.mediaTag = function (src, key) {
return h('media-tag', {
src: src,
'data-crypto-key': 'cryptpad:' + key,
});
};
var findCancelButton = UI.findCancelButton = function (root) { var findCancelButton = UI.findCancelButton = function (root) {
if (root) { if (root) {
return $(root).find('button.cancel').last(); return $(root).find('button.cancel').last();
@ -747,6 +756,7 @@ define([
cb = Util.once(cb); cb = Util.once(cb);
} }
var classes = 'btn ' + (config.classes || 'btn-primary'); var classes = 'btn ' + (config.classes || 'btn-primary');
var newCls = config.new ? '.new' : '';
var button = h('button', { var button = h('button', {
"class": classes, "class": classes,
@ -759,7 +769,7 @@ define([
}); });
var timer = h('div.cp-button-timer', div); var timer = h('div.cp-button-timer', div);
var content = h('div.cp-button-confirm', [ var content = h('div.cp-button-confirm'+newCls, [
button, button,
timer timer
]); ]);
@ -795,7 +805,8 @@ define([
to = setTimeout(todo, INTERVAL); to = setTimeout(todo, INTERVAL);
}; };
$(originalBtn).addClass('cp-button-confirm-placeholder').click(function (e) { var newCls2 = config.new ? 'new' : '';
$(originalBtn).addClass('cp-button-confirm-placeholder').addClass(newCls2).click(function (e) {
e.stopPropagation(); e.stopPropagation();
// If we have a validation function, continue only if it's true // If we have a validation function, continue only if it's true
if (config.validate && !config.validate()) { return; } if (config.validate && !config.validate()) { return; }
@ -1039,6 +1050,7 @@ define([
var font = icon.indexOf('cptools') === 0 ? 'cptools' : 'fa'; var font = icon.indexOf('cptools') === 0 ? 'cptools' : 'fa';
if (type === 'fileupload') { type = 'file'; } if (type === 'fileupload') { type = 'file'; }
if (type === 'folderupload') { type = 'file'; } if (type === 'folderupload') { type = 'file'; }
if (type === 'link') { type = 'drive'; }
var appClass = ' cp-icon cp-icon-color-'+type; var appClass = ' cp-icon cp-icon-color-'+type;
$icon = $('<span>', {'class': font + ' ' + icon + appClass}); $icon = $('<span>', {'class': font + ' ' + icon + appClass});
} }
@ -1050,6 +1062,7 @@ define([
if (!data) { return $icon; } if (!data) { return $icon; }
var href = data.href || data.roHref; var href = data.href || data.roHref;
var type = data.type; var type = data.type;
if (data.static) { type = 'link'; }
if (!href && !type) { return $icon; } if (!href && !type) { return $icon; }
if (!type) { type = Hash.parsePadUrl(href).type; } if (!type) { type = Hash.parsePadUrl(href).type; }
@ -1175,6 +1188,7 @@ define([
var label = h('span.cp-checkmark-label', labelTxt); var label = h('span.cp-checkmark-label', labelTxt);
$mark.keydown(function (e) { $mark.keydown(function (e) {
if ($input.is(':disabled')) { return; }
if (e.which === 32) { if (e.which === 32) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
@ -1220,20 +1234,22 @@ define([
$.extend(markOpts, opts.mark || {}); $.extend(markOpts, opts.mark || {});
var input = h('input', inputOpts); var input = h('input', inputOpts);
var $input = $(input);
var mark = h('span.cp-radio-mark', markOpts); var mark = h('span.cp-radio-mark', markOpts);
var label = h('span.cp-checkmark-label', labelTxt); var label = h('span.cp-checkmark-label', labelTxt);
$(mark).keydown(function (e) { $(mark).keydown(function (e) {
if ($input.is(':disabled')) { return; }
if (e.which === 32) { if (e.which === 32) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if ($(input).is(':checked')) { return; } if ($input.is(':checked')) { return; }
$(input).prop('checked', !$(input).is(':checked')); $input.prop('checked', !$input.is(':checked'));
$(input).change(); $input.change();
} }
}); });
$(input).change(function () { $(mark).focus(); }); $input.change(function () { $(mark).focus(); });
var radio = h('label', labelOpts, [ var radio = h('label', labelOpts, [
input, input,

@ -936,7 +936,8 @@ define([
return button; return button;
}; };
var createMdToolbar = function (common, editor) { var createMdToolbar = function (common, editor, cfg) {
cfg = cfg || {};
var $toolbar = $('<div>', { var $toolbar = $('<div>', {
'class': 'cp-markdown-toolbar' 'class': 'cp-markdown-toolbar'
}); });
@ -1025,9 +1026,50 @@ define([
icon: 'fa-newspaper-o' icon: 'fa-newspaper-o'
} }
}; };
if (typeof(cfg.embed) === "function") {
actions.embed = { // Messages.mdToolbar_embed
icon: 'fa-picture-o',
action: function () {
var _cfg = {
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;
}
if (data.type !== 'file') { console.log('unhandled embed type ' + data.type); return; }
common.setPadAttribute('atime', +new Date(), null, data.href);
var privateDat = common.getMetadataMgr().getPrivateData();
var origin = privateDat.fileHost || privateDat.origin;
var src = data.src = data.src.slice(0,1) === '/' ? origin + data.src : data.src;
cfg.embed(h('media-tag', {
src: src,
'data-crypto-key': 'cryptpad:' + data.key,
}), data);
});
}
};
}
var onClick = function () { var onClick = function () {
var type = $(this).attr('data-type'); var type = $(this).attr('data-type');
var texts = editor.getSelections(); var texts = editor.getSelections();
if (actions[type].action) {
return actions[type].action();
}
var newTexts = texts.map(function (str) { var newTexts = texts.map(function (str) {
str = str || Messages.mdToolbar_defaultText; str = str || Messages.mdToolbar_defaultText;
if (actions[type].apply) { if (actions[type].apply) {
@ -1054,7 +1096,7 @@ define([
}).appendTo($toolbar); }).appendTo($toolbar);
return $toolbar; return $toolbar;
}; };
UIElements.createMarkdownToolbar = function (common, editor) { UIElements.createMarkdownToolbar = function (common, editor, opts) {
var readOnly = common.getMetadataMgr().getPrivateData().readOnly; var readOnly = common.getMetadataMgr().getPrivateData().readOnly;
if (readOnly) { if (readOnly) {
return { return {
@ -1064,7 +1106,7 @@ define([
}; };
} }
var $toolbar = createMdToolbar(common, editor); var $toolbar = createMdToolbar(common, editor, opts);
var cfg = { var cfg = {
title: Messages.mdToolbar_button, title: Messages.mdToolbar_button,
element: $toolbar element: $toolbar
@ -1133,6 +1175,7 @@ define([
sheet: 'sheets', sheet: 'sheets',
poll: 'poll', poll: 'poll',
kanban: 'kanban', kanban: 'kanban',
form: 'form',
whiteboard: 'whiteboard', whiteboard: 'whiteboard',
}; };
@ -1472,11 +1515,13 @@ define([
if (config.isSelect) { if (config.isSelect) {
var pressed = ''; var pressed = '';
var to; var to;
$container.onChange = Util.mkEvent();
$container.on('click', 'a', function () { $container.on('click', 'a', function () {
value = $(this).data('value'); value = $(this).data('value');
var $val = $(this); var $val = $(this);
var textValue = $val.html() || value; var textValue = $val.html() || value;
$button.find('.cp-dropdown-button-title').html(textValue); $button.find('.cp-dropdown-button-title').html(textValue);
$container.onChange.fire(textValue, value);
}); });
$container.keydown(function (e) { $container.keydown(function (e) {
var $value = $innerblock.find('[data-value].cp-dropdown-element-active:visible'); var $value = $innerblock.find('[data-value].cp-dropdown-element-active:visible');
@ -1616,7 +1661,7 @@ define([
var $displayedName = $('<span>', {'class': displayNameCls}); var $displayedName = $('<span>', {'class': displayNameCls});
var priv = metadataMgr.getPrivateData(); var priv = metadataMgr.getPrivateData();
var accountName = priv.accountName; var accountName = Util.fixHTML(priv.accountName);
var origin = priv.origin; var origin = priv.origin;
var padType = metadataMgr.getMetadata().type; var padType = metadataMgr.getMetadata().type;
@ -1626,7 +1671,8 @@ define([
var $userAdminContent = $('<p>'); var $userAdminContent = $('<p>');
if (accountName) { if (accountName) {
var $userAccount = $('<span>').append(Messages.user_accountName + ': '); var $userAccount = $('<span>').append(Messages.user_accountName + ': ');
$userAdminContent.append($userAccount).append(Util.fixHTML(accountName));
$userAdminContent.append($userAccount).append(accountName);
$userAdminContent.append($('<br>')); $userAdminContent.append($('<br>'));
} }
if (config.displayName && !AppConfig.disableProfile) { if (config.displayName && !AppConfig.disableProfile) {
@ -1849,8 +1895,11 @@ define([
}, },
content: h('span', Messages.logoutEverywhere), content: h('span', Messages.logoutEverywhere),
action: function () { action: function () {
Common.getSframeChannel().query('Q_LOGOUT_EVERYWHERE', null, function () { UI.confirm(Messages.settings_logoutEverywhereConfirm, function (yes) {
Common.gotoURL(origin + '/'); if (!yes) { return; }
Common.getSframeChannel().query('Q_LOGOUT_EVERYWHERE', null, function () {
Common.gotoURL(origin + '/');
});
}); });
}, },
}); });
@ -2038,14 +2087,9 @@ define([
var $container = $('<div>'); var $container = $('<div>');
var i = 0; var i = 0;
var types = AppConfig.availablePadTypes.filter(function (p) { var types = AppConfig.availablePadTypes.filter(function (p) {
if (p === 'drive') { return; } if (AppConfig.hiddenTypes.indexOf(p) !== -1) { return; }
if (p === 'teams') { return; }
if (p === 'contacts') { return; }
if (p === 'todo') { return; }
if (p === 'file') { return; }
if (p === 'accounts') { return; }
if (p === 'calendar') { return; }
if (!common.isLoggedIn() && AppConfig.registeredOnlyTypes && if (!common.isLoggedIn() && AppConfig.registeredOnlyTypes &&
AppConfig.registeredOnlyTypes.indexOf(p) !== -1) { return; } AppConfig.registeredOnlyTypes.indexOf(p) !== -1) { return; }
return true; return true;
@ -2224,7 +2268,7 @@ define([
// Title // Title
//$creation.append(h('h2.cp-creation-title', Messages.newButtonTitle)); //$creation.append(h('h2.cp-creation-title', Messages.newButtonTitle));
var newPadH3Title = Messages['button_new' + type]; var newPadH3Title = Messages['button_new' + type]; // Messages.button_newform
var title = h('div.cp-creation-title', [ var title = h('div.cp-creation-title', [
UI.getFileIcon({type: type})[0], UI.getFileIcon({type: type})[0],
@ -2414,6 +2458,7 @@ define([
'title': name, 'title': name,
}).appendTo($container); }).appendTo($container);
$span.data('id', obj.id); $span.data('id', obj.id);
if (obj.content) { $span.data('content', obj.content); }
if (idx === selected) { $span.addClass('cp-creation-template-selected'); } if (idx === selected) { $span.addClass('cp-creation-template-selected'); }
if (!obj.thumbnail) { if (!obj.thumbnail) {
$span.append(obj.icon || h('span.cptools.cptools-template')); $span.append(obj.icon || h('span.cptools.cptools-template'));
@ -2569,6 +2614,7 @@ define([
var $template = $creation.find('.cp-creation-template-selected'); var $template = $creation.find('.cp-creation-template-selected');
var templateId = $template.data('id') || undefined; var templateId = $template.data('id') || undefined;
var templateContent = $template.data('content') || undefined;
// Team // Team
var team; var team;
if (teamValue) { if (teamValue) {
@ -2581,6 +2627,7 @@ define([
password: passwordVal, password: passwordVal,
expire: expireVal, expire: expireVal,
templateId: templateId, templateId: templateId,
templateContent: templateContent,
team: team team: team
}; };
}; };
@ -2626,6 +2673,17 @@ define([
$creation.focus(); $creation.focus();
}; };
UIElements.loginErrorScreenContent = function (common) {
var msg = Pages.setHTML(h('span'), Messages.restrictedLoginPrompt);
$(msg).find('a').attr({
href: '/login/',
}).click(function (ev) {
ev.preventDefault();
common.setLoginRedirect('login');
});
return msg;
};
var autoStoreModal = {}; var autoStoreModal = {};
UIElements.onServerError = function (common, err, toolbar, cb) { UIElements.onServerError = function (common, err, toolbar, cb) {
//if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; } //if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; }
@ -2673,6 +2731,10 @@ define([
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); } if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
} else if (err.type === 'ERESTRICTED') { } else if (err.type === 'ERESTRICTED') {
msg = Messages.restrictedError; msg = Messages.restrictedError;
if (!common.isLoggedIn()) {
msg = UIElements.loginErrorScreenContent(common);
}
if (toolbar && typeof toolbar.failed === "function") { toolbar.failed(true); } if (toolbar && typeof toolbar.failed === "function") { toolbar.failed(true); }
} else if (err.type === 'HASH_NOT_FOUND' && priv.isHistoryVersion) { } else if (err.type === 'HASH_NOT_FOUND' && priv.isHistoryVersion) {
msg = Messages.oo_deletedVersion; msg = Messages.oo_deletedVersion;
@ -2833,7 +2895,13 @@ define([
UIElements.displayStorePadPopup = function (common, data) { UIElements.displayStorePadPopup = function (common, data) {
if (storePopupState) { return; } if (storePopupState) { return; }
storePopupState = true; storePopupState = true;
if (data && data.stored) { return; } // We won't display the popup for dropped files // We won't display the popup for dropped files or already stored pads
if (data && data.stored) {
if (!data.inMyDrive) {
$('.cp-toolbar-storeindrive').show();
}
return;
}
var priv = common.getMetadataMgr().getPrivateData(); var priv = common.getMetadataMgr().getPrivateData();
// This pad will be deleted automatically, it shouldn't be stored // This pad will be deleted automatically, it shouldn't be stored
@ -2962,6 +3030,75 @@ define([
UI.proposal(content, todo); 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) { UIElements.displayAddOwnerModal = function (common, data) {
var priv = common.getMetadataMgr().getPrivateData(); var priv = common.getMetadataMgr().getPrivateData();
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
@ -3012,6 +3149,7 @@ define([
// ACCEPT // ACCEPT
sframeChan.query('Q_SET_PAD_METADATA', { sframeChan.query('Q_SET_PAD_METADATA', {
channel: msg.content.channel, channel: msg.content.channel,
channels: msg.content.channels,
command: 'ADD_OWNERS', command: 'ADD_OWNERS',
value: [priv.edPublic] value: [priv.edPublic]
}, function (err, res) { }, function (err, res) {
@ -3061,6 +3199,7 @@ define([
// Remove yourself from the pending owners // Remove yourself from the pending owners
sframeChan.query('Q_SET_PAD_METADATA', { sframeChan.query('Q_SET_PAD_METADATA', {
channel: msg.content.channel, channel: msg.content.channel,
channels: msg.content.channels,
command: 'RM_PENDING_OWNERS', command: 'RM_PENDING_OWNERS',
value: [priv.edPublic] value: [priv.edPublic]
}, function (err, res) { }, function (err, res) {
@ -3077,6 +3216,7 @@ define([
// Remove yourself from the pending owners // Remove yourself from the pending owners
sframeChan.query('Q_SET_PAD_METADATA', { sframeChan.query('Q_SET_PAD_METADATA', {
channel: msg.content.channel, channel: msg.content.channel,
channels: msg.content.channels,
command: 'RM_PENDING_OWNERS', command: 'RM_PENDING_OWNERS',
value: [priv.edPublic] value: [priv.edPublic]
}, function (err, res) { }, function (err, res) {
@ -3556,6 +3696,13 @@ define([
return (pos.bottom < size) && (pos.y > 0); 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) { UIElements.openSnapshotsModal = function (common, load, make, remove) {
var modal; var modal;
var readOnly = common.getMetadataMgr().getPrivateData().readOnly; var readOnly = common.getMetadataMgr().getPrivateData().readOnly;

@ -9,6 +9,15 @@
return Array.prototype.slice.call(A, start, end); return Array.prototype.slice.call(A, start, end);
}; };
Util.shuffleArray = function (a) {
for (var i = a.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
};
Util.bake = function (f, args) { Util.bake = function (f, args) {
if (typeof(args) === 'undefined') { args = []; } if (typeof(args) === 'undefined') { args = []; }
if (!Array.isArray(args)) { args = [args]; } if (!Array.isArray(args)) { args = [args]; }
@ -152,6 +161,10 @@
}; };
}; };
Util.inc = function (map, key, val) {
map[key] = (map[key] || 0) + (typeof(val) === 'number'? val: 1);
};
Util.find = function (map, path) { Util.find = function (map, path) {
var l = path.length; var l = path.length;
for (var i = 0; i < l; i++) { for (var i = 0; i < l; i++) {
@ -559,8 +572,8 @@
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
'((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
'(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string '(\\?[;&a-z\\d%_.~+=-]*)?'); // query string
'(\\#[-a-z\\d_]*)?$','i'); // fragment locator //'(\\#[-a-z\\d_]*)?$','i'); // fragment locator
return !!pattern.test(str); return !!pattern.test(str);
}; };

@ -76,7 +76,7 @@ define([
postMessage("GET", { postMessage("GET", {
key: ['edPrivate'], key: ['edPrivate'],
}, waitFor(function (obj) { }, waitFor(function (obj) {
if (obj.error) { return; } if (!obj || obj.error) { return; }
try { try {
keys.push({ keys.push({
edPrivate: obj, edPrivate: obj,
@ -84,14 +84,21 @@ define([
}); });
} catch (e) { console.error(e); } } catch (e) { console.error(e); }
})); }));
// Push teams keys // Push teams keys
postMessage("GET", { postMessage("GET", {
key: ['teams'], key: ['teams'],
}, waitFor(function (obj) { }, waitFor(function (obj) {
if (obj.error) { return; } if (!obj || obj.error) { return; }
Object.keys(obj || {}).forEach(function (id) { Object.keys(obj || {}).forEach(function (id) {
var t = obj[id]; var t = obj[id];
var _keys = t.keys.drive || {}; var _keys = {};
try {
_keys = t.keys.drive || {};
} catch (err) {
console.error(err);
}
_keys.id = id;
if (!_keys.edPrivate) { return; } if (!_keys.edPrivate) { return; }
keys.push(t.keys.drive); keys.push(t.keys.drive);
}); });
@ -101,6 +108,57 @@ define([
}); });
}; };
common.getFormKeys = function (cb) {
var curvePrivate;
var formSeed;
Nthen(function (waitFor) {
postMessage("GET", {
key: ['curvePrivate'],
}, waitFor(function (obj) {
if (!obj || obj.error) { return; }
curvePrivate = obj;
}));
postMessage("GET", {
key: ['form_seed'],
}, waitFor(function (obj) {
if (!obj || obj.error) { return; }
formSeed = obj;
}));
}).nThen(function () {
cb({
curvePrivate: curvePrivate,
curvePublic: curvePrivate && Hash.getCurvePublicFromPrivate(curvePrivate),
formSeed: formSeed
});
});
};
common.getFormAnswer = function (data, cb) {
postMessage("GET", {
key: ['forms', data.channel],
}, cb);
};
common.storeFormAnswer = function (data) {
postMessage("SET", {
key: ['forms', data.channel],
value: {
hash: data.hash,
curvePrivate: data.curvePrivate,
anonymous: data.anonymous
}
}, function (obj) {
if (obj && obj.error) {
if (obj.error === "ENODRIVE") {
var answered = JSON.parse(localStorage.CP_formAnswered || "[]");
if (answered.indexOf(data.channel) === -1) { answered.push(data.channel); }
localStorage.CP_formAnswered = JSON.stringify(answered);
return;
}
console.error(obj.error);
}
});
};
common.makeNetwork = function (cb) { common.makeNetwork = function (cb) {
require([ require([
'/bower_components/netflux-websocket/netflux-client.js', '/bower_components/netflux-websocket/netflux-client.js',
@ -712,6 +770,10 @@ define([
delete meta.chat2; delete meta.chat2;
delete meta.chat; delete meta.chat;
delete meta.cursor; delete meta.cursor;
if (meta.type === "form") {
delete parsed.answers;
}
} }
}; };
@ -1103,6 +1165,11 @@ define([
postMessage('BURN_PAD', data); postMessage('BURN_PAD', data);
}; };
common.setDriveRedirectPreference = function (data, cb) {
LocalStore.setDriveRedirectPreference(data && data.value);
cb();
};
common.changePadPassword = function (Crypt, Crypto, data, cb) { common.changePadPassword = function (Crypt, Crypto, data, cb) {
var href = data.href; var href = data.href;
var oldPassword = data.oldPassword; var oldPassword = data.oldPassword;
@ -1870,6 +1937,17 @@ define([
waitFor.abort(); waitFor.abort();
return void cb(obj); return void cb(obj);
} }
}));
}).nThen(function (waitFor) {
var blockUrl = Block.getBlockUrl(blockKeys);
Util.fetch(blockUrl, waitFor(function (err /* block */) {
if (err) {
console.error(err);
waitFor.abort();
return cb({
error: err,
});
}
console.log("new login block written"); console.log("new login block written");
var newBlockHash = Block.getBlockHash(blockKeys); var newBlockHash = Block.getBlockHash(blockKeys);
LocalStore.setBlockHash(newBlockHash); LocalStore.setBlockHash(newBlockHash);
@ -2506,6 +2584,11 @@ define([
} }
if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); } if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); }
var prefersDriveRedirect = data[Constants.prefersDriveRedirectKey];
if (typeof(prefersDriveRedirect) === 'boolean') {
LocalStore.setDriveRedirectPreference(prefersDriveRedirect);
}
initialized = true; initialized = true;
channelIsReady(); channelIsReady();
}); });

@ -8,11 +8,14 @@ define([
'/common/inner/common-mediatag.js', '/common/inner/common-mediatag.js',
'/common/media-tag.js', '/common/media-tag.js',
'/customize/messages.js', '/customize/messages.js',
'/common/less.min.js',
'/customize/pages.js',
'/common/highlight/highlight.pack.js', '/common/highlight/highlight.pack.js',
'/lib/diff-dom/diffDOM.js', '/lib/diff-dom/diffDOM.js',
'/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/tweetnacl/nacl-fast.min.js',
'css!/common/highlight/styles/'+ (window.CryptPad_theme === 'dark' ? 'dark.css' : 'github.css') 'css!/common/highlight/styles/'+ (window.CryptPad_theme === 'dark' ? 'dark.css' : 'github.css')
],function ($, ApiConfig, Marked, Hash, Util, h, MT, MediaTag, Messages) { ],function ($, ApiConfig, Marked, Hash, Util, h, MT, MediaTag, Messages, Less, Pages) {
var DiffMd = {}; var DiffMd = {};
var Highlight = window.hljs; var Highlight = window.hljs;
@ -172,12 +175,16 @@ define([
return h('div.cp-md-toc', content).outerHTML; return h('div.cp-md-toc', content).outerHTML;
}; };
DiffMd.render = function (md, sanitize, restrictedMd) { var noHeadingId = false;
DiffMd.render = function (md, sanitize, restrictedMd, noId) {
Marked.setOptions({ Marked.setOptions({
renderer: restrictedMd ? restrictedRenderer : renderer, renderer: restrictedMd ? restrictedRenderer : renderer,
}); });
noHeadingId = noId;
var r = Marked(md, { var r = Marked(md, {
sanitize: sanitize sanitize: sanitize,
headerIds: !noId,
gfm: true,
}); });
// Add Table of Content // Add Table of Content
@ -207,7 +214,11 @@ define([
}; };
restrictedRenderer.code = renderer.code; restrictedRenderer.code = renderer.code;
var _heading = renderer.heading;
renderer.heading = function (text, level) { renderer.heading = function (text, level) {
if (noHeadingId) {
return _heading.apply(this, arguments);
}
var i = 0; var i = 0;
var safeText = text.toLowerCase().replace(/[^\w]+/g, '-'); var safeText = text.toLowerCase().replace(/[^\w]+/g, '-');
var getId = function () { var getId = function () {
@ -266,9 +277,43 @@ define([
return '<li>' + text + '</li>\n'; return '<li>' + text + '</li>\n';
}; };
var qualifiedHref = function (href) {
if (typeof(window.URL) === 'undefined') { return href; }
try {
var url = new URL(href, ApiConfig.httpUnsafeOrigin);
return url.href;
} catch (err) {
console.error(err);
return href;
}
};
var isLocalURL = function (href) {
// treat all URLs as remote if you are using an ancient browser
if (typeof(window.URL) === 'undefined') { return false; }
try {
var url = new URL(href, ApiConfig.httpUnsafeOrigin);
// FIXME data URLs can be quite large, but that should be addressed
// in the source markdown's, not the renderer
if (url.protocol === 'data:') { return true; }
var localURL = new URL(ApiConfig.httpUnsafeOrigin);
return url.host === localURL.host;
} catch (err) {
return true;
}
};
renderer.image = function (href, title, text) { renderer.image = function (href, title, text) {
if (isLocalURL(href) && href.slice(0, 6) !== '/file/') {
return h('img', {
src: href,
title: title || '',
alt: text,
}).outerHTML;
}
if (href.slice(0,6) === '/file/') { if (href.slice(0,6) === '/file/') {
// DEPRECATED
// Mediatag using markdown syntax should not be used anymore so they don't support // Mediatag using markdown syntax should not be used anymore so they don't support
// password-protected files // password-protected files
console.log('DEPRECATED: mediatag using markdown syntax!'); console.log('DEPRECATED: mediatag using markdown syntax!');
@ -276,19 +321,38 @@ define([
var secret = Hash.getSecrets('file', parsed.hash); var secret = Hash.getSecrets('file', parsed.hash);
var src = (ApiConfig.fileHost || '') +Hash.getBlobPathFromHex(secret.channel); var src = (ApiConfig.fileHost || '') +Hash.getBlobPathFromHex(secret.channel);
var key = Hash.encodeBase64(secret.keys.cryptKey); var key = Hash.encodeBase64(secret.keys.cryptKey);
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>'; var mt = h('media-tag', {
if (mediaMap[src]) { src: src,
mt += mediaMap[src]; 'data-crypto-key': 'cryptpad:' + key,
} });
mt += '</media-tag>'; return mt.outerHTML;
return mt;
}
var out = '<img src="' + href + '" alt="' + text + '"';
if (title) {
out += ' title="' + title + '"';
} }
out += this.options.xhtml ? '/>' : '>';
return out; var warning = h('div.cp-inline-img-warning', [
h('div.cp-inline-img', [
h('img.cp-inline-img', {
src: '/images/broken.png',
//title: title || '', // FIXME sort out tippy issues (double-title)
}),
h('p.cp-alt-txt', text),
]),
h('span.cp-img-block-notice', {
}, Messages.resources_imageBlocked),
h('br'),
h('a.cp-remote-img', {
href: qualifiedHref(href),
}, [
Messages.resources_openInNewTab
]),
h('br'),
h('a.cp-learn-more', {
href: Pages.localizeDocsLink('https://docs.cryptpad.fr/en/user_guide/security.html#remote-content'),
}, [
Messages.resources_learnWhy
]),
]);
return warning.outerHTML;
}; };
restrictedRenderer.image = renderer.image; restrictedRenderer.image = renderer.image;
@ -473,6 +537,75 @@ define([
} }
}; };
var applyCSS = function (el, css) {
var style = h('style');
style.appendChild(document.createTextNode(css));
el.innerText = '';
el.appendChild(style);
};
// trim non-functional text from less input so that
// the compiler is only triggered when there has been a functional change
var canonicalizeLess = function (source) {
return (source || '')
// leading and trailing spaces are irrelevant
.trim()
// line comments are easy to disregard
.replace(/\/\/[^\n]*/g, '')
// lines with nothing but spaces and tabs can be ignored
.replace(/^[ \t]*$/g, '')
// consecutive newlines make no difference
.replace(/\n+/g, '');
};
var rendered_less = {};
var getRenderedLess = (function () {
var timeouts = {};
return function (src) {
if (!rendered_less[src]) { return; }
if (timeouts[src]) {
clearTimeout(timeouts[src]);
}
// avoid memory leaks by deleting cached content
// 15s after it was last accessed
timeouts[src] = setTimeout(function () {
delete rendered_less[src];
delete timeouts[src];
}, 15000);
return rendered_less[src];
};
}());
plugins.less = {
name: 'less',
attr: 'less-src',
render: function renderLess ($el, opt) {
var src = canonicalizeLess($el.text());
if (!src) { return; }
var el = $el[0];
var rendered = getRenderedLess(src);
if (rendered) { return void applyCSS(el, rendered); }
var scope = opt.scope.attr('id') || 'cp-app-code-preview-content';
var scoped_src = '#' + scope + ' { ' + src + '}';
//console.error("RENDERING LESS");
Less.render(scoped_src, {}, function (err, result) {
// the console is the only feedback for users to know that they did something wrong
// but less rendering isn't intended so much as a feature but a useful tool to avoid
// leaking styles from the preview into the rest of the DOM. This is an improvement.
if (err) {
// we assume the compiler is deterministic. Something that returns an error once
// will do it again, so avoid successive calls by caching a truthy
// but non-functional string to block them.
rendered_less[src] = ' ';
return void console.error(err);
}
var css = rendered_less[src] = result.css;
applyCSS(el, css);
});
},
};
var getAvailableCachedElement = function ($content, cache, src) { var getAvailableCachedElement = function ($content, cache, src) {
var cached = cache[src]; var cached = cache[src];
if (!Array.isArray(cached)) { return; } if (!Array.isArray(cached)) { return; }
@ -546,7 +679,8 @@ define([
// caching their source as you go // caching their source as you go
$(newDomFixed).find('pre[data-plugin]').each(function (index, el) { $(newDomFixed).find('pre[data-plugin]').each(function (index, el) {
if (el.childNodes.length === 1 && el.childNodes[0].nodeType === 3) { if (el.childNodes.length === 1 && el.childNodes[0].nodeType === 3) {
var plugin = plugins[el.getAttribute('data-plugin')]; var type = el.getAttribute('data-plugin');
var plugin = plugins[type];
if (!plugin) { return; } if (!plugin) { return; }
var src = canonicalizeMermaidSource(el.childNodes[0].wholeText); var src = canonicalizeMermaidSource(el.childNodes[0].wholeText);
el.setAttribute(plugin.attr, src); el.setAttribute(plugin.attr, src);
@ -559,7 +693,8 @@ define([
var scrollTop = $parent.scrollTop(); var scrollTop = $parent.scrollTop();
// iterate over rendered mermaid charts // iterate over rendered mermaid charts
$content.find('pre[data-plugin]:not([processed="true"])').each(function (index, el) { $content.find('pre[data-plugin]:not([processed="true"])').each(function (index, el) {
var plugin = plugins[el.getAttribute('data-plugin')]; var type = el.getAttribute('data-plugin');
var plugin = plugins[type];
if (!plugin) { return; } if (!plugin) { return; }
// retrieve the attached source code which it was drawn // retrieve the attached source code which it was drawn
@ -666,7 +801,11 @@ define([
if (typeof(patch) === 'string') { if (typeof(patch) === 'string') {
throw new Error(patch); throw new Error(patch);
} else { } else {
DD.apply($content[0], patch); try {
DD.apply($content[0], patch);
} catch (err) {
console.error(err);
}
var $mts = $content.find('media-tag'); var $mts = $content.find('media-tag');
$mts.each(function (i, el) { $mts.each(function (i, el) {
var $mt = $(el).contextmenu(function (e) { var $mt = $(el).contextmenu(function (e) {
@ -721,9 +860,35 @@ define([
if (target) { target.scrollIntoView(); } if (target) { target.scrollIntoView(); }
}); });
// replace remote images with links to those images
$content.find('div.cp-inline-img-warning').each(function (index, el) {
if (!el) { return; }
var link = el.querySelector('a.cp-remote-img');
if (!link) { return; }
link.onclick = function (ev) {
ev.preventDefault();
ev.stopPropagation();
common.openURL(link.href);
};
});
// transform style tags into pre tags with the same content
// to be handled by the less rendering plugin
$content.find('style').each(function (index, el) {
var parent = el.parentElement;
var pre = h('pre', {
'data-plugin': 'less',
'less-src': canonicalizeLess(el.innerText),
style: 'display: none',
}, el.innerText);
parent.replaceChild(pre, el);
});
// loop over plugin elements in the rendered content // loop over plugin elements in the rendered content
$content.find('pre[data-plugin]').each(function (index, el) { $content.find('pre[data-plugin]').each(function (index, el) {
var plugin = plugins[el.getAttribute('data-plugin')]; var type = el.getAttribute('data-plugin');
var plugin = plugins[type];
if (!plugin) { return; } if (!plugin) { return; }
var $el = $(el); var $el = $(el);
$el.off('contextmenu').on('contextmenu', function (e) { $el.off('contextmenu').on('contextmenu', function (e) {
@ -742,13 +907,17 @@ define([
// you can assume that the index of your rendered charts matches that // you can assume that the index of your rendered charts matches that
// of those in the markdown source. // of those in the markdown source.
var src = plugin.source[index]; var src = plugin.source[index];
el.setAttribute(plugin.attr, src); if (src) {
el.setAttribute(plugin.attr, src);
}
var cached = getAvailableCachedElement($content, plugin.cache, src); var cached = getAvailableCachedElement($content, plugin.cache, src);
// check if you had cached a pre-rendered instance of the supplied source // check if you had cached a pre-rendered instance of the supplied source
if (typeof(cached) !== 'object') { if (typeof(cached) !== 'object') {
try { try {
plugin.render($el); plugin.render($el, {
scope: $content,
});
} catch (e) { console.error(e); } } catch (e) { console.error(e); }
return; return;
} }

@ -351,7 +351,7 @@ define([
h('li', h('a.cp-app-drive-context-openro.dropdown-item', { h('li', h('a.cp-app-drive-context-openro.dropdown-item', {
'tabindex': '-1', 'tabindex': '-1',
'data-icon': faReadOnly, 'data-icon': faReadOnly,
}, Messages.fc_open_ro)), }, h('span.cp-text', Messages.fc_open_ro))),
h('li', h('a.cp-app-drive-context-openincode.dropdown-item', { h('li', h('a.cp-app-drive-context-openincode.dropdown-item', {
'tabindex': '-1', 'tabindex': '-1',
'data-icon': faOpenInCode, 'data-icon': faOpenInCode,
@ -443,6 +443,11 @@ define([
'data-icon': AppConfig.applicationsIcon.poll, 'data-icon': AppConfig.applicationsIcon.poll,
'data-type': 'poll' 'data-type': 'poll'
}, Messages.button_newpoll)), }, 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], $separator.clone()[0],
@ -1106,11 +1111,24 @@ define([
common.getMediaTagPreview(mts, idx); common.getMediaTagPreview(mts, idx);
}; };
var refresh = APP.refresh = function () {
APP.displayDirectory(currentPath);
};
// `app`: true (force open wiht the app), false (force open in preview), // `app`: true (force open wiht the app), false (force open in preview),
// falsy (open in preview if default is not using the app) // falsy (open in preview if default is not using the app)
var defaultInApp = ['application/pdf']; var defaultInApp = ['application/pdf'];
var openFile = function (el, isRo, app) { var openFile = function (el, isRo, app) {
var data = manager.getFileData(el); 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)) { if (!data || (!data.href && !data.roHref)) {
return void logError("Missing data for the file", el, data); return void logError("Missing data for the file", el, data);
} }
@ -1147,10 +1165,6 @@ define([
common.openURL(Hash.getNewPadURL(href, obj)); common.openURL(Hash.getNewPadURL(href, obj));
}; };
var refresh = APP.refresh = function () {
APP.displayDirectory(currentPath);
};
var pickFolderColor = function ($element, currentColor, cb) { 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"]; 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')) { if ($element.is('.cp-border-color-sheet')) {
hide.push('download'); hide.push('download');
} }
if ($element.is('.cp-app-drive-static')) {
hide.push('access', 'hashtag', 'properties', 'download');
}
if ($element.is('.cp-app-drive-element-file')) { if ($element.is('.cp-app-drive-element-file')) {
// No folder in files // No folder in files
hide.push('color'); hide.push('color');
@ -1646,6 +1663,19 @@ define([
} }
paths = getSelectedPaths($element); paths = getSelectedPaths($element);
$('.cp-app-drive-context-openro .cp-text').text(Messages.fc_open_ro);
if (paths.length === 1) {
var metadata = manager.getFileData(manager.find(paths[0].path));
if (metadata.roHref) {
var parsed = Hash.parsePadUrl(metadata.roHref);
// Forms: change "Open (read-only)" to "Open (as participant)"
if (parsed.type === "form") {
$('.cp-app-drive-context-openro .cp-text').text(Messages.fc_open_formro);
}
}
}
} }
$contextMenu.attr('data-menu-type', type); $contextMenu.attr('data-menu-type', type);
@ -1886,6 +1916,31 @@ define([
// In list mode, display metadata from the filesData object // 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) { var _addOwnership = function ($span, $state, data) {
if (data && Array.isArray(data.owners) && data.owners.indexOf(edPublic) !== -1) { if (data && Array.isArray(data.owners) && data.owners.indexOf(edPublic) !== -1) {
var $owned = $ownedIcon.clone().appendTo($state); var $owned = $ownedIcon.clone().appendTo($state);
@ -1901,6 +1956,9 @@ define([
if (!manager.isFile(element)) { return; } if (!manager.isFile(element)) { return; }
var data = manager.getFileData(element); var data = manager.getFileData(element);
if (data.static) {
return addStaticData(element, $element, data);
}
if (!Object.keys(data).length) { if (!Object.keys(data).length) {
return true; return true;
@ -2054,6 +2112,7 @@ define([
// can't share the read-only URL and we don't have access to the edit one. // can't share the read-only URL and we don't have access to the edit one.
// We should hide the share button. // We should hide the share button.
if (!data.href && !ro) { return; } if (!data.href && !ro) { return; }
$shareBlock.click(function () { $shareBlock.click(function () {
Share.getShareModal(common, { Share.getShareModal(common, {
teamId: APP.team, teamId: APP.team,
@ -2110,7 +2169,9 @@ define([
$icon = manager.isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone(); $icon = manager.isFolderEmpty(root[key]) ? $folderEmptyIcon.clone() : $folderIcon.clone();
$icon.css("color", getFolderColor(path.concat(elPath))); $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, { var $element = $(h('li.cp-app-drive-element.cp-app-drive-element-row' + classes, {
draggable: true draggable: true
})); }));
@ -2275,7 +2336,8 @@ define([
var createTitle = function ($container, path, noStyle) { var createTitle = function ($container, path, noStyle) {
if (!path || path.length === 0) { return; } if (!path || path.length === 0) { return; }
var isTrash = manager.isPathIn(path, [TRASH]); var isTrash = manager.isPathIn(path, [TRASH]);
if (APP.mobile() && !noStyle) { // noStyle means title in search result // we assume that users viewing shared folders may want to see the "bread-crumb"
if (!APP.newSharedFolder && APP.mobile() && !noStyle) { // noStyle means title in search result
return $container; return $container;
} }
var isVirtual = virtualCategories.indexOf(path[0]) !== -1; var isVirtual = virtualCategories.indexOf(path[0]) !== -1;
@ -2387,7 +2449,14 @@ define([
} }
if (!APP.loggedIn) { if (!APP.loggedIn) {
msg = APP.newSharedFolder ? Messages.fm_info_sharedFolder : Messages._getKey('fm_info_anonymous', [ApiConfig.inactiveTime || 90]); msg = APP.newSharedFolder ? Messages.fm_info_sharedFolder : Messages._getKey('fm_info_anonymous', [ApiConfig.inactiveTime || 90]);
return $(common.fixLinks($box.html(msg))); var docsLink = 'https://docs.cryptpad.fr/en/user_guide/user_account.html#account-types';
$box.html(msg).find('a[href="#docs"]').each(function () {
$(this).attr({
href: Pages.localizeDocsLink(docsLink),
target: '_blank',
});
});
return $(common.fixLinks($box));
} }
if (!msg || APP.store['hide-info-' + path[0]] === '1') { if (!msg || APP.store['hide-info-' + path[0]] === '1') {
$box.hide(); $box.hide();
@ -2437,8 +2506,7 @@ define([
setViewMode(viewMode || 'grid'); setViewMode(viewMode || 'grid');
showMode(viewMode); showMode(viewMode);
$button.click(function (e) { $button.click(function () {
console.error(e);
var viewMode = getViewMode(); var viewMode = getViewMode();
var newViewMode = getOppositeViewMode(viewMode); var newViewMode = getOppositeViewMode(viewMode);
setViewMode(newViewMode); setViewMode(newViewMode);
@ -2550,13 +2618,7 @@ define([
var getNewPadTypes = function () { var getNewPadTypes = function () {
var arr = []; var arr = [];
AppConfig.availablePadTypes.forEach(function (type) { AppConfig.availablePadTypes.forEach(function (type) {
if (type === 'drive') { return; } if (AppConfig.hiddenTypes.indexOf(type) !== -1) { return; }
if (type === 'teams') { return; }
if (type === 'contacts') { return; }
if (type === 'todo') { return; }
if (type === 'file') { return; }
if (type === 'accounts') { return; }
if (type === 'calendar') { return; }
if (!APP.loggedIn && AppConfig.registeredOnlyTypes && if (!APP.loggedIn && AppConfig.registeredOnlyTypes &&
AppConfig.registeredOnlyTypes.indexOf(type) !== -1) { AppConfig.registeredOnlyTypes.indexOf(type) !== -1) {
return; return;
@ -2670,6 +2732,74 @@ define([
}); });
$input.click(); $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) { var addNewPadHandlers = function ($block, isInRoot) {
// Handlers // Handlers
if (isInRoot) { if (isInRoot) {
@ -2698,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-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-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') $block.find('a.cp-app-drive-new-doc, li.cp-app-drive-new-doc')
.click(function () { .click(function () {
@ -2741,6 +2872,12 @@ define([
}); });
} }
options.push({tag: 'hr'}); 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) { getNewPadTypes().forEach(function (type) {
var attributes = { var attributes = {
@ -3057,6 +3194,13 @@ define([
$elementFolderUpload.append($('<span>', {'class': 'cp-app-drive-new-name'}) $elementFolderUpload.append($('<span>', {'class': 'cp-app-drive-new-name'})
.text(Messages.uploadFolderButton)); .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 // Pads
getNewPadTypes().forEach(function (type) { getNewPadTypes().forEach(function (type) {
@ -3132,7 +3276,8 @@ define([
if (APP.$burnThisDrive) { if (APP.$burnThisDrive) {
APP.toolbar.$bottomR.append(APP.$burnThisDrive); APP.toolbar.$bottomR.append(APP.$burnThisDrive);
} }
collapseTreeButton(); // this button is not useful for unregistered users who do not have a tree worth looking at
if (APP.loggedIn) { collapseTreeButton(); }
return $toolbar; return $toolbar;
}; };
@ -3462,6 +3607,7 @@ define([
var path = paths[0]; var path = paths[0];
if (manager.isPathIn(path, [TRASH])) { return; } if (manager.isPathIn(path, [TRASH])) { return; }
if (!file.channel) { file.channel = id; }
if (channels.indexOf(file.channel) !== -1) { return; } if (channels.indexOf(file.channel) !== -1) { return; }
channels.push(file.channel); channels.push(file.channel);
@ -4434,6 +4580,17 @@ define([
data = sf ? manager.getSharedFolderData(el) : manager.getFileData(el); data = sf ? manager.getSharedFolderData(el) : manager.getFileData(el);
} }
parsed = (data.href && data.href.indexOf('#') !== -1) ? Hash.parsePadUrl(data.href) : {}; parsed = (data.href && data.href.indexOf('#') !== -1) ? Hash.parsePadUrl(data.href) : {};
// Form: get auditor hash
var auditorHash;
if (parsed.hash && parsed.type === "form") {
var formData = Hash.getFormData(null, parsed.hash, data.password);
console.log(formData);
if (formData) {
auditorHash = formData.form_auditorHash;
}
}
var roParsed = Hash.parsePadUrl(data.roHref); var roParsed = Hash.parsePadUrl(data.roHref);
var padType = parsed.type || roParsed.type; var padType = parsed.type || roParsed.type;
var ro = !sf || (folders[el] && folders[el].version >= 2); var ro = !sf || (folders[el] && folders[el].version >= 2);
@ -4448,13 +4605,15 @@ define([
viewHash: ro && roParsed.hash, viewHash: ro && roParsed.hash,
fileHash: parsed.hash fileHash: parsed.hash
}, },
auditorHash: auditorHash,
fileData: { fileData: {
hash: parsed.hash, hash: parsed.hash,
password: data.password password: data.password
}, },
isTemplate: paths[0].path[0] === 'template', isTemplate: paths[0].path[0] === 'template',
title: data.title, title: data.title || data.name,
sharedFolder: sf, sharedFolder: sf,
static: data.static ? data.href : undefined,
common: common common: common
}; };
if (padType === 'file') { if (padType === 'file') {
@ -4472,6 +4631,20 @@ define([
data = manager.getSharedFolderData(el); data = manager.getSharedFolderData(el);
} }
if (!data) { return; } 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', { sframeChan.query('Q_STORE_IN_TEAM', {
href: data.href || data.rohref, href: data.href || data.rohref,
password: data.password, password: data.password,
@ -4510,6 +4683,9 @@ define([
} }
else if ($this.hasClass("cp-app-drive-context-newdoc")) { else if ($this.hasClass("cp-app-drive-context-newdoc")) {
var ntype = $this.data('type') || 'pad'; var ntype = $this.data('type') || 'pad';
if (ntype === 'link') {
return void showLinkModal();
}
var path2 = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath; var path2 = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
openIn(ntype, path2, APP.team); openIn(ntype, path2, APP.team);
} }

@ -32,6 +32,12 @@ define([
var teamOwner = data.teamId; var teamOwner = data.teamId;
var title = opts.title; var title = opts.title;
var p = priv.propChannels;
var otherChan;
if (p && p.answersChannel) {
otherChan = [p.answersChannel];
}
opts = opts || {}; opts = opts || {};
var redrawAll = function () {}; var redrawAll = function () {};
@ -255,6 +261,7 @@ define([
// Send the command // Send the command
sframeChan.query('Q_SET_PAD_METADATA', { sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel, channel: channel,
channels: otherChan,
command: 'ADD_OWNERS', command: 'ADD_OWNERS',
value: toAddTeams.map(function (obj) { return obj.edPublic; }), value: toAddTeams.map(function (obj) { return obj.edPublic; }),
teamId: teamOwner teamId: teamOwner
@ -290,6 +297,7 @@ define([
// Send the command // Send the command
sframeChan.query('Q_SET_PAD_METADATA', { sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel, channel: channel,
channels: otherChan,
command: 'ADD_PENDING_OWNERS', command: 'ADD_PENDING_OWNERS',
value: toAdd, value: toAdd,
teamId: teamOwner teamId: teamOwner
@ -310,6 +318,7 @@ define([
// Send the command // Send the command
sframeChan.query('Q_SET_PAD_METADATA', { sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel, channel: channel,
channels: otherChan,
command: 'ADD_OWNERS', command: 'ADD_OWNERS',
value: [priv.edPublic], value: [priv.edPublic],
teamId: teamOwner teamId: teamOwner
@ -338,6 +347,7 @@ define([
if (!friend) { return; } if (!friend) { return; }
common.mailbox.sendTo("ADD_OWNER", { common.mailbox.sendTo("ADD_OWNER", {
channel: channel, channel: channel,
channels: otherChan,
href: href, href: href,
calendar: opts.calendar, calendar: opts.calendar,
password: data.password || priv.password, password: data.password || priv.password,
@ -417,6 +427,12 @@ define([
var allowed = data.allowed || []; var allowed = data.allowed || [];
var teamOwner = data.teamId; var teamOwner = data.teamId;
var p = priv.propChannels;
var otherChan;
if (p && p.answersChannel) {
otherChan = [p.answersChannel];
}
var redrawAll = function () {}; var redrawAll = function () {};
var addBtn = h('button.btn.btn-primary.cp-access-add', [h('i.fa.fa-arrow-left'), h('i.fa.fa-arrow-up')]); var addBtn = h('button.btn.btn-primary.cp-access-add', [h('i.fa.fa-arrow-left'), h('i.fa.fa-arrow-up')]);
@ -495,6 +511,7 @@ define([
// Send the command // Send the command
sframeChan.query('Q_SET_PAD_METADATA', { sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel, channel: channel,
channels: otherChan,
command: 'RM_ALLOWED', command: 'RM_ALLOWED',
value: [ed], value: [ed],
teamId: teamOwner teamId: teamOwner
@ -524,6 +541,7 @@ define([
var val = $checkbox.is(':checked'); var val = $checkbox.is(':checked');
sframeChan.query('Q_SET_PAD_METADATA', { sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel, channel: channel,
channels: otherChan,
command: 'RESTRICT_ACCESS', command: 'RESTRICT_ACCESS',
value: [Boolean(val)], value: [Boolean(val)],
teamId: teamOwner teamId: teamOwner
@ -659,6 +677,7 @@ define([
// Send the command // Send the command
sframeChan.query('Q_SET_PAD_METADATA', { sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel, channel: channel,
channels: otherChan,
command: 'ADD_ALLOWED', command: 'ADD_ALLOWED',
value: toAdd, value: toAdd,
teamId: teamOwner teamId: teamOwner
@ -987,6 +1006,15 @@ define([
UI.findCancelButton().click(); UI.findCancelButton().click();
if (err || (obj && obj.error)) { UI.warn(Messages.error); } if (err || (obj && obj.error)) { UI.warn(Messages.error); }
}); });
// If this is a form wiht a answer channel, delete it too
var p = priv.propChannels;
if (p.answersChannel) {
sframeChan.query('Q_DELETE_OWNED', {
teamId: typeof(owned) !== "boolean" ? owned : undefined,
channel: p.answersChannel
}, function () {});
}
}); });
if (!opts.noEditPassword) { $d.append(h('br')); } if (!opts.noEditPassword) { $d.append(h('br')); }
$d.append(h('div', [ $d.append(h('div', [
@ -1020,7 +1048,7 @@ define([
var owned = Modal.isOwned(Env, data); var owned = Modal.isOwned(Env, data);
// Request edit access // Request edit access
if (common.isLoggedIn() && ((data.roHref && !data.href) || data.fakeHref) && !owned && !opts.calendar) { if (common.isLoggedIn() && ((data.roHref && !data.href) || data.fakeHref) && !owned && !opts.calendar && priv.app !== 'form') {
var requestButton = h('button.btn.btn-secondary.no-margin.cp-access-margin-right', var requestButton = h('button.btn.btn-secondary.no-margin.cp-access-margin-right',
Messages.requestEdit_button); Messages.requestEdit_button);
var requestBlock = h('p', requestButton); var requestBlock = h('p', requestButton);
@ -1058,7 +1086,7 @@ define([
var canMute = data.mailbox && owned === true && ( var canMute = data.mailbox && owned === true && (
(typeof (data.mailbox) === "string" && data.owners[0] === edPublic) || (typeof (data.mailbox) === "string" && data.owners[0] === edPublic) ||
data.mailbox[edPublic]); data.mailbox[edPublic]);
if (owned === true && !opts.calendar) { if (owned === true && !opts.calendar && priv.app !== 'form') {
var cbox = UI.createCheckbox('cp-access-mute', Messages.access_muteRequests, !canMute); var cbox = UI.createCheckbox('cp-access-mute', Messages.access_muteRequests, !canMute);
var $cbox = $(cbox); var $cbox = $(cbox);
var spinner = UI.makeSpinner($cbox); var spinner = UI.makeSpinner($cbox);

@ -127,9 +127,8 @@ define([
if (e || !data) { return void displayDefault(); } if (e || !data) { return void displayDefault(); }
if (typeof data !== "number") { return void displayDefault(); } if (typeof data !== "number") { return void displayDefault(); }
if (Util.bytesToMegabytes(data) > 0.5) { return void displayDefault(); } if (Util.bytesToMegabytes(data) > 0.5) { return void displayDefault(); }
var $img = $('<media-tag>').appendTo($container); var mt = UI.mediaTag(src, cryptKey);
$img.attr('src', src); var $img = $(mt).appendTo($container);
$img.attr('data-crypto-key', 'cryptpad:' + cryptKey);
MT.displayMediatagImage(common, $img, function (err, $image) { MT.displayMediatagImage(common, $img, function (err, $image) {
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
centerImage($img, $image); centerImage($img, $image);

@ -24,6 +24,7 @@ define([
if (privateData.propChannels) { if (privateData.propChannels) {
var p = privateData.propChannels; var p = privateData.propChannels;
data.channel = data.channel || p.channel; data.channel = data.channel || p.channel;
data.answersChannel = data.answersChannel || p.answersChannel;
data.rtChannel = data.rtChannel || p.rtChannel; data.rtChannel = data.rtChannel || p.rtChannel;
data.lastVersion = data.lastVersion || p.lastVersion; data.lastVersion = data.lastVersion || p.lastVersion;
data.lastCpHash = data.lastCpHash || p.lastCpHash; data.lastCpHash = data.lastCpHash || p.lastCpHash;
@ -75,6 +76,7 @@ define([
var bytes = 0; var bytes = 0;
var historyBytes; var historyBytes;
var chan = [data.channel]; var chan = [data.channel];
if (data.answersChannel) { chan.push(data.answersChannel); }
if (data.rtChannel) { chan.push(data.rtChannel); } if (data.rtChannel) { chan.push(data.rtChannel); }
if (data.lastVersion) { chan.push(Hash.hrefToHexChannelId(data.lastVersion)); } if (data.lastVersion) { chan.push(Hash.hrefToHexChannelId(data.lastVersion)); }

@ -90,7 +90,11 @@ define([
setTimeout(w); setTimeout(w);
}); });
if (res && /^http/.test(res)) { if (res && /^http/.test(res)) {
href = Hash.getRelativeHref(res); var _href = Hash.getRelativeHref(res);
if (_href) { href = _href; }
else {
href = res;
}
setTimeout(w); setTimeout(w);
return; return;
} }
@ -109,6 +113,7 @@ define([
if (mailbox.notifications && mailbox.curvePublic) { if (mailbox.notifications && mailbox.curvePublic) {
common.mailbox.sendTo("SHARE_PAD", { common.mailbox.sendTo("SHARE_PAD", {
href: href, href: href,
isStatic: Boolean(config.static),
password: config.password, password: config.password,
isTemplate: config.isTemplate, isTemplate: config.isTemplate,
name: myName, name: myName,
@ -119,6 +124,9 @@ define([
channel: mailbox.notifications, channel: mailbox.notifications,
curvePublic: mailbox.curvePublic curvePublic: mailbox.curvePublic
}); });
if (config.static) {
Feedback.send("LINK_SHARED_WITH_CONTACT");
}
return; return;
} }
} }
@ -137,6 +145,21 @@ define([
}); });
return; 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', { sframeChan.query('Q_STORE_IN_TEAM', {
href: href, href: href,
password: config.password, password: config.password,
@ -346,6 +369,9 @@ define([
] : [ ] : [
UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }), UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }),
]; ];
if (opts.static) { linkContent = []; }
linkContent.push(h('div.cp-spacer')); linkContent.push(h('div.cp-spacer'));
linkContent.push(UI.dialog.selectableArea('', { id: 'cp-share-link-preview', tabindex: 1, rows:3})); linkContent.push(UI.dialog.selectableArea('', { id: 'cp-share-link-preview', tabindex: 1, rows:3}));
@ -361,7 +387,7 @@ define([
// warning about sharing links // warning about sharing links
// when sharing a version hash, there is a similar warning and we want // when sharing a version hash, there is a similar warning and we want
// to avoid alert fatigue // to avoid alert fatigue
if (!opts.versionHash) { if (!opts.versionHash && !opts.static) {
var localStore = window.cryptpadStore; var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times'); var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable', var shareLinkWarning = h('div.alert.alert-warning.dismissable',
@ -405,6 +431,10 @@ define([
var v = opts.getLinkValue({ var v = opts.getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed')) embed: Util.isChecked($link.find('#cp-share-embed'))
}); });
if (opts.static) {
common.openUnsafeURL(v);
return true;
}
window.open(v); window.open(v);
return true; return true;
}, },
@ -494,7 +524,20 @@ define([
var parsed = Hash.parsePadUrl(pathname); var parsed = Hash.parsePadUrl(pathname);
var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1; var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1;
var versionHash = hashes.viewHash && opts.versionHash; var versionHash = hashes.viewHash && opts.versionHash;
var canBAR = parsed.type !== 'drive' && !versionHash; var isForm = parsed.type === "form"; // && opts.auditorHash;
var canBAR = parsed.type !== 'drive' && !versionHash && !isForm;
var labelEdit = Messages.share_linkEdit;
var labelView = Messages.share_linkView;
var auditor;
if (isForm) {
labelEdit = Messages.share_formEdit;
labelView = Messages.share_formView;
auditor = UI.createRadio('accessRights', 'cp-share-form', Messages.share_formAuditor, false, {
mark: {tabindex:1},
});
}
var burnAfterReading = (hashes.viewHash && canBAR) ? var burnAfterReading = (hashes.viewHash && canBAR) ?
UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, { UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, {
@ -505,12 +548,13 @@ define([
h('label', Messages.share_linkAccess), h('label', Messages.share_linkAccess),
h('div.radio-group',[ h('div.radio-group',[
UI.createRadio('accessRights', 'cp-share-editable-false', UI.createRadio('accessRights', 'cp-share-editable-false',
Messages.share_linkView, true, { mark: {tabindex:1} }), labelView, true, { mark: {tabindex:1} }),
canPresent ? UI.createRadio('accessRights', 'cp-share-present', canPresent ? UI.createRadio('accessRights', 'cp-share-present',
Messages.share_linkPresent, false, { mark: {tabindex:1} }) : undefined, Messages.share_linkPresent, false, { mark: {tabindex:1} }) : undefined,
UI.createRadio('accessRights', 'cp-share-editable-true', UI.createRadio('accessRights', 'cp-share-editable-true',
Messages.share_linkEdit, false, { mark: {tabindex:1} })]), labelEdit, false, { mark: {tabindex:1} }),
burnAfterReading auditor]),
burnAfterReading,
]); ]);
// Burn after reading // Burn after reading
@ -548,11 +592,13 @@ define([
}); });
}; };
opts.getLinkValue = function (initValue, cb) { opts.getLinkValue = function (initValue, cb) {
if (opts.static) { return opts.static; }
var val = initValue || {}; var val = initValue || {};
var edit = val.edit !== undefined ? val.edit : Util.isChecked($rights.find('#cp-share-editable-true')); var edit = val.edit !== undefined ? val.edit : Util.isChecked($rights.find('#cp-share-editable-true'));
var embed = val.embed; var embed = val.embed;
var present = val.present !== undefined ? val.present : Util.isChecked($rights.find('#cp-share-present')); var present = val.present !== undefined ? val.present : Util.isChecked($rights.find('#cp-share-present'));
var burnAfterReading = Util.isChecked($rights.find('#cp-share-bar')); var burnAfterReading = Util.isChecked($rights.find('#cp-share-bar'));
var formAuditor = Util.isChecked($rights.find('#cp-share-form'));
if (versionHash) { if (versionHash) {
edit = false; edit = false;
present = false; present = false;
@ -569,6 +615,9 @@ define([
} }
var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash
: hashes.viewHash; : hashes.viewHash;
if (formAuditor && opts.auditorHash) {
hash = opts.auditorHash;
}
var href = burnAfterReading ? opts.burnAfterReadingUrl var href = burnAfterReading ? opts.burnAfterReadingUrl
: (origin + pathname + '#' + hash); : (origin + pathname + '#' + hash);
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
@ -594,6 +643,9 @@ define([
$rights.find('#cp-share-present').removeAttr('checked').attr('disabled', true); $rights.find('#cp-share-present').removeAttr('checked').attr('disabled', true);
$rights.find('#cp-share-editable-true').attr('checked', true); $rights.find('#cp-share-editable-true').attr('checked', true);
} }
if (isForm && !opts.auditorHash) {
$rights.find('#cp-share-form').removeAttr('checked').attr('disabled', true);
}
var getLink = function () { var getLink = function () {
return $rights.parent().find('#cp-share-link-preview'); return $rights.parent().find('#cp-share-link-preview');
@ -665,7 +717,7 @@ define([
opts.access = true; // Allow the use of the modal even if the pad is not stored opts.access = true; // Allow the use of the modal even if the pad is not stored
var hashes = opts.hashes; 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); var teams = getEditableTeams(common, opts);
opts.teams = teams; opts.teams = teams;
@ -684,19 +736,23 @@ define([
var $rights = opts.$rights = getRightsHeader(common, opts); var $rights = opts.$rights = getRightsHeader(common, opts);
var resetTab = function () { var resetTab = function () {
if (opts.static) { return; }
$rights.show(); $rights.show();
$rights.find('label.cp-radio').show(); $rights.find('label.cp-radio').show();
}; };
var onShowEmbed = function () { var onShowEmbed = function () {
if (opts.static) { return; }
$rights.find('#cp-share-bar').closest('label').hide(); $rights.find('#cp-share-bar').closest('label').hide();
$rights.find('input[type="radio"]:enabled').first().prop('checked', 'checked'); $rights.find('input[type="radio"]:enabled').first().prop('checked', 'checked');
$rights.find('input[type="radio"]').trigger('change'); $rights.find('input[type="radio"]').trigger('change');
}; };
var onShowContacts = function () { var onShowContacts = function () {
if (opts.static) { return; }
if (!hasFriends || priv.offline) { if (!hasFriends || priv.offline) {
$rights.hide(); $rights.hide();
} }
}; };
if (opts.static) { $rights.hide(); }
var contactsActive = hasFriends && !priv.offline; var contactsActive = hasFriends && !priv.offline;
var tabs = [{ var tabs = [{
@ -711,13 +767,16 @@ define([
title: Messages.share_linkCategory, title: Messages.share_linkCategory,
icon: "fa fa-link", icon: "fa fa-link",
active: !contactsActive, 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) { Modal.getModal(common, opts, tabs, function (err, modal) {
// Hide the burn-after-reading option by default // Hide the burn-after-reading option by default
var $modal = $(modal); var $modal = $(modal);

@ -8,7 +8,8 @@ define([
'/common/common-constants.js', '/common/common-constants.js',
'/customize/messages.js', '/customize/messages.js',
'/customize/pages.js', '/customize/pages.js',
], function($, h, Hash, UI, UIElements, Util, Constants, Messages, Pages) { '/lib/datepicker/flatpickr.js',
], function($, h, Hash, UI, UIElements, Util, Constants, Messages, Pages, Flatpickr) {
var handlers = {}; var handlers = {};
@ -91,6 +92,10 @@ define([
(type === 'file' ? 'notification_fileShared' : // Msg.notification_fileSharedTeam (type === 'file' ? 'notification_fileShared' : // Msg.notification_fileSharedTeam
'notification_padShared'); // Msg.notification_padSharedTeam '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 teamNotification = /^team-/.test(data.type) && Number(data.type.slice(5));
var teamName = ''; var teamName = '';
if (teamNotification) { if (teamNotification) {
@ -108,6 +113,15 @@ define([
return Messages._getKey(key, [name, title, teamName]); return Messages._getKey(key, [name, title, teamName]);
}; };
content.handler = function() { 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 = { var obj = {
p: msg.content.isTemplate ? ['template'] : undefined, p: msg.content.isTemplate ? ['template'] : undefined,
t: teamNotification || undefined, t: teamNotification || undefined,
@ -477,7 +491,7 @@ define([
var nowDateStr = new Date().toLocaleDateString(); var nowDateStr = new Date().toLocaleDateString();
var startDate = new Date(start); var startDate = new Date(start);
if (msg.isAllDay && msg.startDay) { if (msg.isAllDay && msg.startDay) {
startDate = new Date(msg.startDay); startDate = Flatpickr.parseDate(msg.startDay);
} }
// Missed events // Missed events

@ -1,7 +1,7 @@
define(['/api/config'], function (ApiConfig) { define(['/api/config'], function (ApiConfig) {
var Module = {}; var Module = {};
var apps = ['code', 'slide', 'pad', 'kanban', 'whiteboard', 'sheet', 'poll', 'teams']; var apps = ['code', 'slide', 'pad', 'kanban', 'whiteboard', 'sheet', 'poll', 'teams', 'form'];
var app = window.location.pathname.slice(1, -1); // remove "/" at the beginnin and the end var app = window.location.pathname.slice(1, -1); // remove "/" at the beginnin and the end
var suffix = apps.indexOf(app) !== -1 ? '-'+app : ''; var suffix = apps.indexOf(app) !== -1 ? '-'+app : '';

@ -72,8 +72,31 @@ define([
return JSONSortify(obj); 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 () { 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 exportXLSXFile = function() {
var text = getContent(); var text = getContent();
var suggestion = Title.suggestTitle(Title.defaultTitle); 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 type = common.getMetadataMgr().getPrivateData().ooType;
var warning = ''; var warning = '';
if (type==="presentation") { if (type==="presentation") {

@ -12867,13 +12867,13 @@ deleted){self.dependencyFormulas.delColumnTable(tableName,deleted);var wsActive=
wsActive.getHidden())wsActive.setHidden(false);if(!bNoBuildDep)this.dependencyFormulas.initOpen();if(bSnapshot)this.snapshot=this._getSnapshot()};Workbook.prototype.initPostOpenZip=function(pivotCaches){var t=this;this.forEach(function(ws){ws.initPostOpenZip(pivotCaches,t.oNumFmtsOpen)})};Workbook.prototype.setCommonIndexObjectsFrom=function(wb){this.oStyleManager=wb.oStyleManager;this.sharedStrings=wb.sharedStrings;this.workbookFormulas=wb.workbookFormulas};Workbook.prototype.forEach=function(callback, wsActive.getHidden())wsActive.setHidden(false);if(!bNoBuildDep)this.dependencyFormulas.initOpen();if(bSnapshot)this.snapshot=this._getSnapshot()};Workbook.prototype.initPostOpenZip=function(pivotCaches){var t=this;this.forEach(function(ws){ws.initPostOpenZip(pivotCaches,t.oNumFmtsOpen)})};Workbook.prototype.setCommonIndexObjectsFrom=function(wb){this.oStyleManager=wb.oStyleManager;this.sharedStrings=wb.sharedStrings;this.workbookFormulas=wb.workbookFormulas};Workbook.prototype.forEach=function(callback,
isCopyPaste){if(isCopyPaste||isCopyPaste===false)callback(this.getActiveWs(),this.getActive());else for(var i=0,l=this.aWorksheets.length;i<l;++i)callback(this.aWorksheets[i],i)};Workbook.prototype.rebuildColors=function(){AscCommonExcel.g_oColorManager.rebuildColors();this.forEach(function(ws){ws.rebuildColors()})};Workbook.prototype.getDefaultFont=function(){return g_oDefaultFormat.Font.getName()};Workbook.prototype.getDefaultSize=function(){return g_oDefaultFormat.Font.getSize()};Workbook.prototype.getActive= isCopyPaste){if(isCopyPaste||isCopyPaste===false)callback(this.getActiveWs(),this.getActive());else for(var i=0,l=this.aWorksheets.length;i<l;++i)callback(this.aWorksheets[i],i)};Workbook.prototype.rebuildColors=function(){AscCommonExcel.g_oColorManager.rebuildColors();this.forEach(function(ws){ws.rebuildColors()})};Workbook.prototype.getDefaultFont=function(){return g_oDefaultFormat.Font.getName()};Workbook.prototype.getDefaultSize=function(){return g_oDefaultFormat.Font.getSize()};Workbook.prototype.getActive=
function(){return this.nActive};Workbook.prototype.getActiveWs=function(){return this.getWorksheet(this.nActive)};Workbook.prototype.setActive=function(index){if(index>=0&&index<this.aWorksheets.length){this.nActive=index;this.cleanFindResults();return true}return false};Workbook.prototype.setActiveById=function(sheetId){var ws=this.getWorksheetById(sheetId);if(!ws&&Array.isArray(this.aWorksheets))ws=this.aWorksheets[this.aWorksheets.length-1];return this.setActive(ws.getIndex())};Workbook.prototype.getSheetIdByIndex= function(){return this.nActive};Workbook.prototype.getActiveWs=function(){return this.getWorksheet(this.nActive)};Workbook.prototype.setActive=function(index){if(index>=0&&index<this.aWorksheets.length){this.nActive=index;this.cleanFindResults();return true}return false};Workbook.prototype.setActiveById=function(sheetId){var ws=this.getWorksheetById(sheetId);if(!ws&&Array.isArray(this.aWorksheets))ws=this.aWorksheets[this.aWorksheets.length-1];return this.setActive(ws.getIndex())};Workbook.prototype.getSheetIdByIndex=
function(index){var ws=this.getWorksheet(index);return ws?ws.getId():null};Workbook.prototype.getWorksheet=function(index){if(index>=0&&index<this.aWorksheets.length)return this.aWorksheets[index];return null};Workbook.prototype.getWorksheetById=function(id){return this.aWorksheetsById[id]};Workbook.prototype.getWorksheetByName=function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==name)return this.aWorksheets[i];return null};Workbook.prototype.getWorksheetIndexByName= function(index){var ws=this.getWorksheet(index);return ws?ws.getId():null};Workbook.prototype.getWorksheet=function(index){if(index>=0&&index<this.aWorksheets.length)return this.aWorksheets[index];return null};Workbook.prototype.getWorksheetById=function(id){if(this.aWorksheetsById[id])return this.aWorksheetsById[id];var s;this.aWorksheets.some(function(_s){if(_s.Id===id){s=_s;return true}});return s};Workbook.prototype.getWorksheetByName=function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==
function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==name)return i;return null};Workbook.prototype.getWorksheetCount=function(){return this.aWorksheets.length};Workbook.prototype.createWorksheet=function(indexBefore,sName,sId){this.dependencyFormulas.lockRecal();History.Create_NewPoint();History.TurnOff();var wsActive=this.getActiveWs();var oNewWorksheet=new Worksheet(this,this.aWorksheets.length,sId);if(this.checkValidSheetName(sName))oNewWorksheet.sName=sName; name)return this.aWorksheets[i];return null};Workbook.prototype.getWorksheetIndexByName=function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==name)return i;return null};Workbook.prototype.getWorksheetCount=function(){return this.aWorksheets.length};Workbook.prototype.createWorksheet=function(indexBefore,sName,sId){this.dependencyFormulas.lockRecal();History.Create_NewPoint();History.TurnOff();var wsActive=this.getActiveWs();var oNewWorksheet=new Worksheet(this,
oNewWorksheet.initPostOpen(this.wsHandlers,{});if(null!=indexBefore&&indexBefore>=0&&indexBefore<this.aWorksheets.length)this.aWorksheets.splice(indexBefore,0,oNewWorksheet);else{indexBefore=this.aWorksheets.length;this.aWorksheets.push(oNewWorksheet)}this.aWorksheetsById[oNewWorksheet.getId()]=oNewWorksheet;this._updateWorksheetIndexes(wsActive);History.TurnOn();this._insertWorksheetFormula(oNewWorksheet.index);History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null, this.aWorksheets.length,sId);if(this.checkValidSheetName(sName))oNewWorksheet.sName=sName;oNewWorksheet.initPostOpen(this.wsHandlers,{});if(null!=indexBefore&&indexBefore>=0&&indexBefore<this.aWorksheets.length)this.aWorksheets.splice(indexBefore,0,oNewWorksheet);else{indexBefore=this.aWorksheets.length;this.aWorksheets.push(oNewWorksheet)}this.aWorksheetsById[oNewWorksheet.getId()]=oNewWorksheet;this._updateWorksheetIndexes(wsActive);History.TurnOn();this._insertWorksheetFormula(oNewWorksheet.index);
null,new UndoRedoData_SheetAdd(indexBefore,oNewWorksheet.getName(),null,oNewWorksheet.getId()));History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(oNewWorksheet.getId());this.dependencyFormulas.unlockRecal();return oNewWorksheet};Workbook.prototype.copyWorksheet=function(index,insertBefore,sName,sId,bFromRedo,tableNames,opt_sheet,opt_base64){var renameParams;if(index>=0&&index<this.aWorksheets.length){this.dependencyFormulas.buildDependency();History.TurnOff();var wsActive=this.getActiveWs(); History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null,null,new UndoRedoData_SheetAdd(indexBefore,oNewWorksheet.getName(),null,oNewWorksheet.getId()));History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(oNewWorksheet.getId());this.dependencyFormulas.unlockRecal();return oNewWorksheet};Workbook.prototype.copyWorksheet=function(index,insertBefore,sName,sId,bFromRedo,tableNames,opt_sheet,opt_base64){var renameParams;if(index>=0&&index<this.aWorksheets.length){this.dependencyFormulas.buildDependency();
var wsFrom=opt_sheet?opt_sheet:this.aWorksheets[index];var newSheet=new Worksheet(this,-1,sId);if(null!=insertBefore&&insertBefore>=0&&insertBefore<this.aWorksheets.length)this.aWorksheets.splice(insertBefore,0,newSheet);else this.aWorksheets.push(newSheet);if(opt_sheet)this.addingWorksheet=newSheet;this.aWorksheetsById[newSheet.getId()]=newSheet;this._updateWorksheetIndexes(wsActive);renameParams=newSheet.copyFrom(wsFrom,sName,tableNames);newSheet.copyFromFormulas(renameParams);newSheet.initPostOpen(this.wsHandlers, History.TurnOff();var wsActive=this.getActiveWs();var wsFrom=opt_sheet?opt_sheet:this.aWorksheets[index];var newSheet=new Worksheet(this,-1,sId);if(null!=insertBefore&&insertBefore>=0&&insertBefore<this.aWorksheets.length)this.aWorksheets.splice(insertBefore,0,newSheet);else this.aWorksheets.push(newSheet);if(opt_sheet)this.addingWorksheet=newSheet;this.aWorksheetsById[newSheet.getId()]=newSheet;this._updateWorksheetIndexes(wsActive);renameParams=newSheet.copyFrom(wsFrom,sName,tableNames);newSheet.copyFromFormulas(renameParams);
{},{});History.TurnOn();this.dependencyFormulas.copyDefNameByWorksheet(wsFrom,newSheet,renameParams,opt_sheet);if(opt_sheet)this.dependencyFormulas.copyDefNameByWorkbook(wsFrom,newSheet,renameParams,opt_sheet);this._insertWorksheetFormula(insertBefore);if(!tableNames)tableNames=newSheet.getTableNames();History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null,null,new UndoRedoData_SheetAdd(insertBefore,newSheet.getName(),wsFrom.getId(),newSheet.getId(),tableNames,opt_base64)); newSheet.initPostOpen(this.wsHandlers,{},{});History.TurnOn();this.dependencyFormulas.copyDefNameByWorksheet(wsFrom,newSheet,renameParams,opt_sheet);if(opt_sheet)this.dependencyFormulas.copyDefNameByWorkbook(wsFrom,newSheet,renameParams,opt_sheet);this._insertWorksheetFormula(insertBefore);if(!tableNames)tableNames=newSheet.getTableNames();History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null,null,new UndoRedoData_SheetAdd(insertBefore,newSheet.getName(),wsFrom.getId(),
History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(newSheet.getId());newSheet.copyFromAfterInsert(wsFrom);if(!(bFromRedo===true)){wsFrom.copyObjects(newSheet,renameParams);var i;if(wsFrom.aNamedSheetViews)for(i=0;i<wsFrom.aNamedSheetViews.length;++i)newSheet.addNamedSheetView(wsFrom.aNamedSheetViews[i].clone(renameParams.tableNameMap));if(wsFrom.dataValidations&&wsFrom.dataValidations.elems)for(i=0;i<wsFrom.dataValidations.elems.length;++i)newSheet.addDataValidation(wsFrom.dataValidations.elems[i].clone(), newSheet.getId(),tableNames,opt_base64));History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(newSheet.getId());newSheet.copyFromAfterInsert(wsFrom);if(!(bFromRedo===true)){wsFrom.copyObjects(newSheet,renameParams);var i;if(wsFrom.aNamedSheetViews)for(i=0;i<wsFrom.aNamedSheetViews.length;++i)newSheet.addNamedSheetView(wsFrom.aNamedSheetViews[i].clone(renameParams.tableNameMap));if(wsFrom.dataValidations&&wsFrom.dataValidations.elems)for(i=0;i<wsFrom.dataValidations.elems.length;++i)newSheet.addDataValidation(wsFrom.dataValidations.elems[i].clone(),
true)}this.sortDependency();if(opt_sheet)this.addingWorksheet=null}return renameParams};Workbook.prototype.insertWorksheet=function(index,sheet){var wsActive=this.getActiveWs();if(null!=index&&index>=0&&index<this.aWorksheets.length)this.aWorksheets.splice(index,0,sheet);else this.aWorksheets.push(sheet);this.aWorksheetsById[sheet.getId()]=sheet;this._updateWorksheetIndexes(wsActive);this._insertWorksheetFormula(index);this._insertTablePartsName(sheet);sheet._BuildDependencies(sheet.getCwf());this.sortDependency()}; true)}this.sortDependency();if(opt_sheet)this.addingWorksheet=null}return renameParams};Workbook.prototype.insertWorksheet=function(index,sheet){var wsActive=this.getActiveWs();if(null!=index&&index>=0&&index<this.aWorksheets.length)this.aWorksheets.splice(index,0,sheet);else this.aWorksheets.push(sheet);this.aWorksheetsById[sheet.getId()]=sheet;this._updateWorksheetIndexes(wsActive);this._insertWorksheetFormula(index);this._insertTablePartsName(sheet);sheet._BuildDependencies(sheet.getCwf());this.sortDependency()};
Workbook.prototype._insertTablePartsName=function(sheet){if(sheet&&sheet.TableParts&&sheet.TableParts.length)for(var i=0;i<sheet.TableParts.length;i++){var tablePart=sheet.TableParts[i];this.dependencyFormulas.addTableName(sheet,tablePart);tablePart.buildDependencies()}};Workbook.prototype._insertWorksheetFormula=function(index){if(index>0&&index<this.aWorksheets.length){var oWsBefore=this.aWorksheets[index-1];this.dependencyFormulas.changeSheet(this.dependencyFormulas.prepareChangeSheet(oWsBefore.getId(), Workbook.prototype._insertTablePartsName=function(sheet){if(sheet&&sheet.TableParts&&sheet.TableParts.length)for(var i=0;i<sheet.TableParts.length;i++){var tablePart=sheet.TableParts[i];this.dependencyFormulas.addTableName(sheet,tablePart);tablePart.buildDependencies()}};Workbook.prototype._insertWorksheetFormula=function(index){if(index>0&&index<this.aWorksheets.length){var oWsBefore=this.aWorksheets[index-1];this.dependencyFormulas.changeSheet(this.dependencyFormulas.prepareChangeSheet(oWsBefore.getId(),
{insert:index}))}};Workbook.prototype.replaceWorksheet=function(indexFrom,indexTo){if(indexFrom>=0&&indexFrom<this.aWorksheets.length&&indexTo>=0&&indexTo<this.aWorksheets.length){var wsActive=this.getActiveWs();var oWsFrom=this.aWorksheets[indexFrom];var tempW={wF:oWsFrom,wFI:indexFrom,wTI:indexTo};if(tempW.wFI<tempW.wTI)tempW.wTI++;this.dependencyFormulas.lockRecal();var prepared=this.dependencyFormulas.prepareChangeSheet(oWsFrom.getId(),{replace:tempW},null);var movedSheet=this.aWorksheets.splice(indexFrom, {insert:index}))}};Workbook.prototype.replaceWorksheet=function(indexFrom,indexTo){if(indexFrom>=0&&indexFrom<this.aWorksheets.length&&indexTo>=0&&indexTo<this.aWorksheets.length){var wsActive=this.getActiveWs();var oWsFrom=this.aWorksheets[indexFrom];var tempW={wF:oWsFrom,wFI:indexFrom,wTI:indexTo};if(tempW.wFI<tempW.wTI)tempW.wTI++;this.dependencyFormulas.lockRecal();var prepared=this.dependencyFormulas.prepareChangeSheet(oWsFrom.getId(),{replace:tempW},null);var movedSheet=this.aWorksheets.splice(indexFrom,

@ -10071,13 +10071,13 @@ deleted){self.dependencyFormulas.delColumnTable(tableName,deleted);var wsActive=
wsActive.getHidden())wsActive.setHidden(false);if(!bNoBuildDep)this.dependencyFormulas.initOpen();if(bSnapshot)this.snapshot=this._getSnapshot()};Workbook.prototype.initPostOpenZip=function(pivotCaches){var t=this;this.forEach(function(ws){ws.initPostOpenZip(pivotCaches,t.oNumFmtsOpen)})};Workbook.prototype.setCommonIndexObjectsFrom=function(wb){this.oStyleManager=wb.oStyleManager;this.sharedStrings=wb.sharedStrings;this.workbookFormulas=wb.workbookFormulas};Workbook.prototype.forEach=function(callback, wsActive.getHidden())wsActive.setHidden(false);if(!bNoBuildDep)this.dependencyFormulas.initOpen();if(bSnapshot)this.snapshot=this._getSnapshot()};Workbook.prototype.initPostOpenZip=function(pivotCaches){var t=this;this.forEach(function(ws){ws.initPostOpenZip(pivotCaches,t.oNumFmtsOpen)})};Workbook.prototype.setCommonIndexObjectsFrom=function(wb){this.oStyleManager=wb.oStyleManager;this.sharedStrings=wb.sharedStrings;this.workbookFormulas=wb.workbookFormulas};Workbook.prototype.forEach=function(callback,
isCopyPaste){if(isCopyPaste||isCopyPaste===false)callback(this.getActiveWs(),this.getActive());else for(var i=0,l=this.aWorksheets.length;i<l;++i)callback(this.aWorksheets[i],i)};Workbook.prototype.rebuildColors=function(){AscCommonExcel.g_oColorManager.rebuildColors();this.forEach(function(ws){ws.rebuildColors()})};Workbook.prototype.getDefaultFont=function(){return g_oDefaultFormat.Font.getName()};Workbook.prototype.getDefaultSize=function(){return g_oDefaultFormat.Font.getSize()};Workbook.prototype.getActive= isCopyPaste){if(isCopyPaste||isCopyPaste===false)callback(this.getActiveWs(),this.getActive());else for(var i=0,l=this.aWorksheets.length;i<l;++i)callback(this.aWorksheets[i],i)};Workbook.prototype.rebuildColors=function(){AscCommonExcel.g_oColorManager.rebuildColors();this.forEach(function(ws){ws.rebuildColors()})};Workbook.prototype.getDefaultFont=function(){return g_oDefaultFormat.Font.getName()};Workbook.prototype.getDefaultSize=function(){return g_oDefaultFormat.Font.getSize()};Workbook.prototype.getActive=
function(){return this.nActive};Workbook.prototype.getActiveWs=function(){return this.getWorksheet(this.nActive)};Workbook.prototype.setActive=function(index){if(index>=0&&index<this.aWorksheets.length){this.nActive=index;this.cleanFindResults();return true}return false};Workbook.prototype.setActiveById=function(sheetId){var ws=this.getWorksheetById(sheetId);if(!ws&&Array.isArray(this.aWorksheets))ws=this.aWorksheets[this.aWorksheets.length-1];return this.setActive(ws.getIndex())};Workbook.prototype.getSheetIdByIndex= function(){return this.nActive};Workbook.prototype.getActiveWs=function(){return this.getWorksheet(this.nActive)};Workbook.prototype.setActive=function(index){if(index>=0&&index<this.aWorksheets.length){this.nActive=index;this.cleanFindResults();return true}return false};Workbook.prototype.setActiveById=function(sheetId){var ws=this.getWorksheetById(sheetId);if(!ws&&Array.isArray(this.aWorksheets))ws=this.aWorksheets[this.aWorksheets.length-1];return this.setActive(ws.getIndex())};Workbook.prototype.getSheetIdByIndex=
function(index){var ws=this.getWorksheet(index);return ws?ws.getId():null};Workbook.prototype.getWorksheet=function(index){if(index>=0&&index<this.aWorksheets.length)return this.aWorksheets[index];return null};Workbook.prototype.getWorksheetById=function(id){return this.aWorksheetsById[id]};Workbook.prototype.getWorksheetByName=function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==name)return this.aWorksheets[i];return null};Workbook.prototype.getWorksheetIndexByName= function(index){var ws=this.getWorksheet(index);return ws?ws.getId():null};Workbook.prototype.getWorksheet=function(index){if(index>=0&&index<this.aWorksheets.length)return this.aWorksheets[index];return null};Workbook.prototype.getWorksheetById=function(id){if(this.aWorksheetsById[id])return this.aWorksheetsById[id];var s;this.aWorksheets.some(function(_s){if(_s.Id===id){s=_s;return true}});return s};Workbook.prototype.getWorksheetByName=function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==
function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==name)return i;return null};Workbook.prototype.getWorksheetCount=function(){return this.aWorksheets.length};Workbook.prototype.createWorksheet=function(indexBefore,sName,sId){this.dependencyFormulas.lockRecal();History.Create_NewPoint();History.TurnOff();var wsActive=this.getActiveWs();var oNewWorksheet=new Worksheet(this,this.aWorksheets.length,sId);if(this.checkValidSheetName(sName))oNewWorksheet.sName=sName; name)return this.aWorksheets[i];return null};Workbook.prototype.getWorksheetIndexByName=function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==name)return i;return null};Workbook.prototype.getWorksheetCount=function(){return this.aWorksheets.length};Workbook.prototype.createWorksheet=function(indexBefore,sName,sId){this.dependencyFormulas.lockRecal();History.Create_NewPoint();History.TurnOff();var wsActive=this.getActiveWs();var oNewWorksheet=new Worksheet(this,
oNewWorksheet.initPostOpen(this.wsHandlers,{});if(null!=indexBefore&&indexBefore>=0&&indexBefore<this.aWorksheets.length)this.aWorksheets.splice(indexBefore,0,oNewWorksheet);else{indexBefore=this.aWorksheets.length;this.aWorksheets.push(oNewWorksheet)}this.aWorksheetsById[oNewWorksheet.getId()]=oNewWorksheet;this._updateWorksheetIndexes(wsActive);History.TurnOn();this._insertWorksheetFormula(oNewWorksheet.index);History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null, this.aWorksheets.length,sId);if(this.checkValidSheetName(sName))oNewWorksheet.sName=sName;oNewWorksheet.initPostOpen(this.wsHandlers,{});if(null!=indexBefore&&indexBefore>=0&&indexBefore<this.aWorksheets.length)this.aWorksheets.splice(indexBefore,0,oNewWorksheet);else{indexBefore=this.aWorksheets.length;this.aWorksheets.push(oNewWorksheet)}this.aWorksheetsById[oNewWorksheet.getId()]=oNewWorksheet;this._updateWorksheetIndexes(wsActive);History.TurnOn();this._insertWorksheetFormula(oNewWorksheet.index);
null,new UndoRedoData_SheetAdd(indexBefore,oNewWorksheet.getName(),null,oNewWorksheet.getId()));History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(oNewWorksheet.getId());this.dependencyFormulas.unlockRecal();return oNewWorksheet};Workbook.prototype.copyWorksheet=function(index,insertBefore,sName,sId,bFromRedo,tableNames,opt_sheet,opt_base64){var renameParams;if(index>=0&&index<this.aWorksheets.length){this.dependencyFormulas.buildDependency();History.TurnOff();var wsActive=this.getActiveWs(); History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null,null,new UndoRedoData_SheetAdd(indexBefore,oNewWorksheet.getName(),null,oNewWorksheet.getId()));History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(oNewWorksheet.getId());this.dependencyFormulas.unlockRecal();return oNewWorksheet};Workbook.prototype.copyWorksheet=function(index,insertBefore,sName,sId,bFromRedo,tableNames,opt_sheet,opt_base64){var renameParams;if(index>=0&&index<this.aWorksheets.length){this.dependencyFormulas.buildDependency();
var wsFrom=opt_sheet?opt_sheet:this.aWorksheets[index];var newSheet=new Worksheet(this,-1,sId);if(null!=insertBefore&&insertBefore>=0&&insertBefore<this.aWorksheets.length)this.aWorksheets.splice(insertBefore,0,newSheet);else this.aWorksheets.push(newSheet);if(opt_sheet)this.addingWorksheet=newSheet;this.aWorksheetsById[newSheet.getId()]=newSheet;this._updateWorksheetIndexes(wsActive);renameParams=newSheet.copyFrom(wsFrom,sName,tableNames);newSheet.copyFromFormulas(renameParams);newSheet.initPostOpen(this.wsHandlers, History.TurnOff();var wsActive=this.getActiveWs();var wsFrom=opt_sheet?opt_sheet:this.aWorksheets[index];var newSheet=new Worksheet(this,-1,sId);if(null!=insertBefore&&insertBefore>=0&&insertBefore<this.aWorksheets.length)this.aWorksheets.splice(insertBefore,0,newSheet);else this.aWorksheets.push(newSheet);if(opt_sheet)this.addingWorksheet=newSheet;this.aWorksheetsById[newSheet.getId()]=newSheet;this._updateWorksheetIndexes(wsActive);renameParams=newSheet.copyFrom(wsFrom,sName,tableNames);newSheet.copyFromFormulas(renameParams);
{},{});History.TurnOn();this.dependencyFormulas.copyDefNameByWorksheet(wsFrom,newSheet,renameParams,opt_sheet);if(opt_sheet)this.dependencyFormulas.copyDefNameByWorkbook(wsFrom,newSheet,renameParams,opt_sheet);this._insertWorksheetFormula(insertBefore);if(!tableNames)tableNames=newSheet.getTableNames();History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null,null,new UndoRedoData_SheetAdd(insertBefore,newSheet.getName(),wsFrom.getId(),newSheet.getId(),tableNames,opt_base64)); newSheet.initPostOpen(this.wsHandlers,{},{});History.TurnOn();this.dependencyFormulas.copyDefNameByWorksheet(wsFrom,newSheet,renameParams,opt_sheet);if(opt_sheet)this.dependencyFormulas.copyDefNameByWorkbook(wsFrom,newSheet,renameParams,opt_sheet);this._insertWorksheetFormula(insertBefore);if(!tableNames)tableNames=newSheet.getTableNames();History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null,null,new UndoRedoData_SheetAdd(insertBefore,newSheet.getName(),wsFrom.getId(),
History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(newSheet.getId());newSheet.copyFromAfterInsert(wsFrom);if(!(bFromRedo===true)){wsFrom.copyObjects(newSheet,renameParams);var i;if(wsFrom.aNamedSheetViews)for(i=0;i<wsFrom.aNamedSheetViews.length;++i)newSheet.addNamedSheetView(wsFrom.aNamedSheetViews[i].clone(renameParams.tableNameMap));if(wsFrom.dataValidations&&wsFrom.dataValidations.elems)for(i=0;i<wsFrom.dataValidations.elems.length;++i)newSheet.addDataValidation(wsFrom.dataValidations.elems[i].clone(), newSheet.getId(),tableNames,opt_base64));History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(newSheet.getId());newSheet.copyFromAfterInsert(wsFrom);if(!(bFromRedo===true)){wsFrom.copyObjects(newSheet,renameParams);var i;if(wsFrom.aNamedSheetViews)for(i=0;i<wsFrom.aNamedSheetViews.length;++i)newSheet.addNamedSheetView(wsFrom.aNamedSheetViews[i].clone(renameParams.tableNameMap));if(wsFrom.dataValidations&&wsFrom.dataValidations.elems)for(i=0;i<wsFrom.dataValidations.elems.length;++i)newSheet.addDataValidation(wsFrom.dataValidations.elems[i].clone(),
true)}this.sortDependency();if(opt_sheet)this.addingWorksheet=null}return renameParams};Workbook.prototype.insertWorksheet=function(index,sheet){var wsActive=this.getActiveWs();if(null!=index&&index>=0&&index<this.aWorksheets.length)this.aWorksheets.splice(index,0,sheet);else this.aWorksheets.push(sheet);this.aWorksheetsById[sheet.getId()]=sheet;this._updateWorksheetIndexes(wsActive);this._insertWorksheetFormula(index);this._insertTablePartsName(sheet);sheet._BuildDependencies(sheet.getCwf());this.sortDependency()}; true)}this.sortDependency();if(opt_sheet)this.addingWorksheet=null}return renameParams};Workbook.prototype.insertWorksheet=function(index,sheet){var wsActive=this.getActiveWs();if(null!=index&&index>=0&&index<this.aWorksheets.length)this.aWorksheets.splice(index,0,sheet);else this.aWorksheets.push(sheet);this.aWorksheetsById[sheet.getId()]=sheet;this._updateWorksheetIndexes(wsActive);this._insertWorksheetFormula(index);this._insertTablePartsName(sheet);sheet._BuildDependencies(sheet.getCwf());this.sortDependency()};
Workbook.prototype._insertTablePartsName=function(sheet){if(sheet&&sheet.TableParts&&sheet.TableParts.length)for(var i=0;i<sheet.TableParts.length;i++){var tablePart=sheet.TableParts[i];this.dependencyFormulas.addTableName(sheet,tablePart);tablePart.buildDependencies()}};Workbook.prototype._insertWorksheetFormula=function(index){if(index>0&&index<this.aWorksheets.length){var oWsBefore=this.aWorksheets[index-1];this.dependencyFormulas.changeSheet(this.dependencyFormulas.prepareChangeSheet(oWsBefore.getId(), Workbook.prototype._insertTablePartsName=function(sheet){if(sheet&&sheet.TableParts&&sheet.TableParts.length)for(var i=0;i<sheet.TableParts.length;i++){var tablePart=sheet.TableParts[i];this.dependencyFormulas.addTableName(sheet,tablePart);tablePart.buildDependencies()}};Workbook.prototype._insertWorksheetFormula=function(index){if(index>0&&index<this.aWorksheets.length){var oWsBefore=this.aWorksheets[index-1];this.dependencyFormulas.changeSheet(this.dependencyFormulas.prepareChangeSheet(oWsBefore.getId(),
{insert:index}))}};Workbook.prototype.replaceWorksheet=function(indexFrom,indexTo){if(indexFrom>=0&&indexFrom<this.aWorksheets.length&&indexTo>=0&&indexTo<this.aWorksheets.length){var wsActive=this.getActiveWs();var oWsFrom=this.aWorksheets[indexFrom];var tempW={wF:oWsFrom,wFI:indexFrom,wTI:indexTo};if(tempW.wFI<tempW.wTI)tempW.wTI++;this.dependencyFormulas.lockRecal();var prepared=this.dependencyFormulas.prepareChangeSheet(oWsFrom.getId(),{replace:tempW},null);var movedSheet=this.aWorksheets.splice(indexFrom, {insert:index}))}};Workbook.prototype.replaceWorksheet=function(indexFrom,indexTo){if(indexFrom>=0&&indexFrom<this.aWorksheets.length&&indexTo>=0&&indexTo<this.aWorksheets.length){var wsActive=this.getActiveWs();var oWsFrom=this.aWorksheets[indexFrom];var tempW={wF:oWsFrom,wFI:indexFrom,wTI:indexTo};if(tempW.wFI<tempW.wTI)tempW.wTI++;this.dependencyFormulas.lockRecal();var prepared=this.dependencyFormulas.prepareChangeSheet(oWsFrom.getId(),{replace:tempW},null);var movedSheet=this.aWorksheets.splice(indexFrom,

@ -10019,13 +10019,13 @@ deleted){self.dependencyFormulas.delColumnTable(tableName,deleted);var wsActive=
wsActive.getHidden())wsActive.setHidden(false);if(!bNoBuildDep)this.dependencyFormulas.initOpen();if(bSnapshot)this.snapshot=this._getSnapshot()};Workbook.prototype.initPostOpenZip=function(pivotCaches){var t=this;this.forEach(function(ws){ws.initPostOpenZip(pivotCaches,t.oNumFmtsOpen)})};Workbook.prototype.setCommonIndexObjectsFrom=function(wb){this.oStyleManager=wb.oStyleManager;this.sharedStrings=wb.sharedStrings;this.workbookFormulas=wb.workbookFormulas};Workbook.prototype.forEach=function(callback, wsActive.getHidden())wsActive.setHidden(false);if(!bNoBuildDep)this.dependencyFormulas.initOpen();if(bSnapshot)this.snapshot=this._getSnapshot()};Workbook.prototype.initPostOpenZip=function(pivotCaches){var t=this;this.forEach(function(ws){ws.initPostOpenZip(pivotCaches,t.oNumFmtsOpen)})};Workbook.prototype.setCommonIndexObjectsFrom=function(wb){this.oStyleManager=wb.oStyleManager;this.sharedStrings=wb.sharedStrings;this.workbookFormulas=wb.workbookFormulas};Workbook.prototype.forEach=function(callback,
isCopyPaste){if(isCopyPaste||isCopyPaste===false)callback(this.getActiveWs(),this.getActive());else for(var i=0,l=this.aWorksheets.length;i<l;++i)callback(this.aWorksheets[i],i)};Workbook.prototype.rebuildColors=function(){AscCommonExcel.g_oColorManager.rebuildColors();this.forEach(function(ws){ws.rebuildColors()})};Workbook.prototype.getDefaultFont=function(){return g_oDefaultFormat.Font.getName()};Workbook.prototype.getDefaultSize=function(){return g_oDefaultFormat.Font.getSize()};Workbook.prototype.getActive= isCopyPaste){if(isCopyPaste||isCopyPaste===false)callback(this.getActiveWs(),this.getActive());else for(var i=0,l=this.aWorksheets.length;i<l;++i)callback(this.aWorksheets[i],i)};Workbook.prototype.rebuildColors=function(){AscCommonExcel.g_oColorManager.rebuildColors();this.forEach(function(ws){ws.rebuildColors()})};Workbook.prototype.getDefaultFont=function(){return g_oDefaultFormat.Font.getName()};Workbook.prototype.getDefaultSize=function(){return g_oDefaultFormat.Font.getSize()};Workbook.prototype.getActive=
function(){return this.nActive};Workbook.prototype.getActiveWs=function(){return this.getWorksheet(this.nActive)};Workbook.prototype.setActive=function(index){if(index>=0&&index<this.aWorksheets.length){this.nActive=index;this.cleanFindResults();return true}return false};Workbook.prototype.setActiveById=function(sheetId){var ws=this.getWorksheetById(sheetId);if(!ws&&Array.isArray(this.aWorksheets))ws=this.aWorksheets[this.aWorksheets.length-1];return this.setActive(ws.getIndex())};Workbook.prototype.getSheetIdByIndex= function(){return this.nActive};Workbook.prototype.getActiveWs=function(){return this.getWorksheet(this.nActive)};Workbook.prototype.setActive=function(index){if(index>=0&&index<this.aWorksheets.length){this.nActive=index;this.cleanFindResults();return true}return false};Workbook.prototype.setActiveById=function(sheetId){var ws=this.getWorksheetById(sheetId);if(!ws&&Array.isArray(this.aWorksheets))ws=this.aWorksheets[this.aWorksheets.length-1];return this.setActive(ws.getIndex())};Workbook.prototype.getSheetIdByIndex=
function(index){var ws=this.getWorksheet(index);return ws?ws.getId():null};Workbook.prototype.getWorksheet=function(index){if(index>=0&&index<this.aWorksheets.length)return this.aWorksheets[index];return null};Workbook.prototype.getWorksheetById=function(id){return this.aWorksheetsById[id]};Workbook.prototype.getWorksheetByName=function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==name)return this.aWorksheets[i];return null};Workbook.prototype.getWorksheetIndexByName= function(index){var ws=this.getWorksheet(index);return ws?ws.getId():null};Workbook.prototype.getWorksheet=function(index){if(index>=0&&index<this.aWorksheets.length)return this.aWorksheets[index];return null};Workbook.prototype.getWorksheetById=function(id){if(this.aWorksheetsById[id])return this.aWorksheetsById[id];var s;this.aWorksheets.some(function(_s){if(_s.Id===id){s=_s;return true}});return s};Workbook.prototype.getWorksheetByName=function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==
function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==name)return i;return null};Workbook.prototype.getWorksheetCount=function(){return this.aWorksheets.length};Workbook.prototype.createWorksheet=function(indexBefore,sName,sId){this.dependencyFormulas.lockRecal();History.Create_NewPoint();History.TurnOff();var wsActive=this.getActiveWs();var oNewWorksheet=new Worksheet(this,this.aWorksheets.length,sId);if(this.checkValidSheetName(sName))oNewWorksheet.sName=sName; name)return this.aWorksheets[i];return null};Workbook.prototype.getWorksheetIndexByName=function(name){for(var i=0;i<this.aWorksheets.length;i++)if(this.aWorksheets[i].getName()==name)return i;return null};Workbook.prototype.getWorksheetCount=function(){return this.aWorksheets.length};Workbook.prototype.createWorksheet=function(indexBefore,sName,sId){this.dependencyFormulas.lockRecal();History.Create_NewPoint();History.TurnOff();var wsActive=this.getActiveWs();var oNewWorksheet=new Worksheet(this,
oNewWorksheet.initPostOpen(this.wsHandlers,{});if(null!=indexBefore&&indexBefore>=0&&indexBefore<this.aWorksheets.length)this.aWorksheets.splice(indexBefore,0,oNewWorksheet);else{indexBefore=this.aWorksheets.length;this.aWorksheets.push(oNewWorksheet)}this.aWorksheetsById[oNewWorksheet.getId()]=oNewWorksheet;this._updateWorksheetIndexes(wsActive);History.TurnOn();this._insertWorksheetFormula(oNewWorksheet.index);History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null, this.aWorksheets.length,sId);if(this.checkValidSheetName(sName))oNewWorksheet.sName=sName;oNewWorksheet.initPostOpen(this.wsHandlers,{});if(null!=indexBefore&&indexBefore>=0&&indexBefore<this.aWorksheets.length)this.aWorksheets.splice(indexBefore,0,oNewWorksheet);else{indexBefore=this.aWorksheets.length;this.aWorksheets.push(oNewWorksheet)}this.aWorksheetsById[oNewWorksheet.getId()]=oNewWorksheet;this._updateWorksheetIndexes(wsActive);History.TurnOn();this._insertWorksheetFormula(oNewWorksheet.index);
null,new UndoRedoData_SheetAdd(indexBefore,oNewWorksheet.getName(),null,oNewWorksheet.getId()));History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(oNewWorksheet.getId());this.dependencyFormulas.unlockRecal();return oNewWorksheet};Workbook.prototype.copyWorksheet=function(index,insertBefore,sName,sId,bFromRedo,tableNames,opt_sheet,opt_base64){var renameParams;if(index>=0&&index<this.aWorksheets.length){this.dependencyFormulas.buildDependency();History.TurnOff();var wsActive=this.getActiveWs(); History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null,null,new UndoRedoData_SheetAdd(indexBefore,oNewWorksheet.getName(),null,oNewWorksheet.getId()));History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(oNewWorksheet.getId());this.dependencyFormulas.unlockRecal();return oNewWorksheet};Workbook.prototype.copyWorksheet=function(index,insertBefore,sName,sId,bFromRedo,tableNames,opt_sheet,opt_base64){var renameParams;if(index>=0&&index<this.aWorksheets.length){this.dependencyFormulas.buildDependency();
var wsFrom=opt_sheet?opt_sheet:this.aWorksheets[index];var newSheet=new Worksheet(this,-1,sId);if(null!=insertBefore&&insertBefore>=0&&insertBefore<this.aWorksheets.length)this.aWorksheets.splice(insertBefore,0,newSheet);else this.aWorksheets.push(newSheet);if(opt_sheet)this.addingWorksheet=newSheet;this.aWorksheetsById[newSheet.getId()]=newSheet;this._updateWorksheetIndexes(wsActive);renameParams=newSheet.copyFrom(wsFrom,sName,tableNames);newSheet.copyFromFormulas(renameParams);newSheet.initPostOpen(this.wsHandlers, History.TurnOff();var wsActive=this.getActiveWs();var wsFrom=opt_sheet?opt_sheet:this.aWorksheets[index];var newSheet=new Worksheet(this,-1,sId);if(null!=insertBefore&&insertBefore>=0&&insertBefore<this.aWorksheets.length)this.aWorksheets.splice(insertBefore,0,newSheet);else this.aWorksheets.push(newSheet);if(opt_sheet)this.addingWorksheet=newSheet;this.aWorksheetsById[newSheet.getId()]=newSheet;this._updateWorksheetIndexes(wsActive);renameParams=newSheet.copyFrom(wsFrom,sName,tableNames);newSheet.copyFromFormulas(renameParams);
{},{});History.TurnOn();this.dependencyFormulas.copyDefNameByWorksheet(wsFrom,newSheet,renameParams,opt_sheet);if(opt_sheet)this.dependencyFormulas.copyDefNameByWorkbook(wsFrom,newSheet,renameParams,opt_sheet);this._insertWorksheetFormula(insertBefore);if(!tableNames)tableNames=newSheet.getTableNames();History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null,null,new UndoRedoData_SheetAdd(insertBefore,newSheet.getName(),wsFrom.getId(),newSheet.getId(),tableNames,opt_base64)); newSheet.initPostOpen(this.wsHandlers,{},{});History.TurnOn();this.dependencyFormulas.copyDefNameByWorksheet(wsFrom,newSheet,renameParams,opt_sheet);if(opt_sheet)this.dependencyFormulas.copyDefNameByWorkbook(wsFrom,newSheet,renameParams,opt_sheet);this._insertWorksheetFormula(insertBefore);if(!tableNames)tableNames=newSheet.getTableNames();History.Add(AscCommonExcel.g_oUndoRedoWorkbook,AscCH.historyitem_Workbook_SheetAdd,null,null,new UndoRedoData_SheetAdd(insertBefore,newSheet.getName(),wsFrom.getId(),
History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(newSheet.getId());newSheet.copyFromAfterInsert(wsFrom);if(!(bFromRedo===true)){wsFrom.copyObjects(newSheet,renameParams);var i;if(wsFrom.aNamedSheetViews)for(i=0;i<wsFrom.aNamedSheetViews.length;++i)newSheet.addNamedSheetView(wsFrom.aNamedSheetViews[i].clone(renameParams.tableNameMap));if(wsFrom.dataValidations&&wsFrom.dataValidations.elems)for(i=0;i<wsFrom.dataValidations.elems.length;++i)newSheet.addDataValidation(wsFrom.dataValidations.elems[i].clone(), newSheet.getId(),tableNames,opt_base64));History.SetSheetUndo(wsActive.getId());History.SetSheetRedo(newSheet.getId());newSheet.copyFromAfterInsert(wsFrom);if(!(bFromRedo===true)){wsFrom.copyObjects(newSheet,renameParams);var i;if(wsFrom.aNamedSheetViews)for(i=0;i<wsFrom.aNamedSheetViews.length;++i)newSheet.addNamedSheetView(wsFrom.aNamedSheetViews[i].clone(renameParams.tableNameMap));if(wsFrom.dataValidations&&wsFrom.dataValidations.elems)for(i=0;i<wsFrom.dataValidations.elems.length;++i)newSheet.addDataValidation(wsFrom.dataValidations.elems[i].clone(),
true)}this.sortDependency();if(opt_sheet)this.addingWorksheet=null}return renameParams};Workbook.prototype.insertWorksheet=function(index,sheet){var wsActive=this.getActiveWs();if(null!=index&&index>=0&&index<this.aWorksheets.length)this.aWorksheets.splice(index,0,sheet);else this.aWorksheets.push(sheet);this.aWorksheetsById[sheet.getId()]=sheet;this._updateWorksheetIndexes(wsActive);this._insertWorksheetFormula(index);this._insertTablePartsName(sheet);sheet._BuildDependencies(sheet.getCwf());this.sortDependency()}; true)}this.sortDependency();if(opt_sheet)this.addingWorksheet=null}return renameParams};Workbook.prototype.insertWorksheet=function(index,sheet){var wsActive=this.getActiveWs();if(null!=index&&index>=0&&index<this.aWorksheets.length)this.aWorksheets.splice(index,0,sheet);else this.aWorksheets.push(sheet);this.aWorksheetsById[sheet.getId()]=sheet;this._updateWorksheetIndexes(wsActive);this._insertWorksheetFormula(index);this._insertTablePartsName(sheet);sheet._BuildDependencies(sheet.getCwf());this.sortDependency()};
Workbook.prototype._insertTablePartsName=function(sheet){if(sheet&&sheet.TableParts&&sheet.TableParts.length)for(var i=0;i<sheet.TableParts.length;i++){var tablePart=sheet.TableParts[i];this.dependencyFormulas.addTableName(sheet,tablePart);tablePart.buildDependencies()}};Workbook.prototype._insertWorksheetFormula=function(index){if(index>0&&index<this.aWorksheets.length){var oWsBefore=this.aWorksheets[index-1];this.dependencyFormulas.changeSheet(this.dependencyFormulas.prepareChangeSheet(oWsBefore.getId(), Workbook.prototype._insertTablePartsName=function(sheet){if(sheet&&sheet.TableParts&&sheet.TableParts.length)for(var i=0;i<sheet.TableParts.length;i++){var tablePart=sheet.TableParts[i];this.dependencyFormulas.addTableName(sheet,tablePart);tablePart.buildDependencies()}};Workbook.prototype._insertWorksheetFormula=function(index){if(index>0&&index<this.aWorksheets.length){var oWsBefore=this.aWorksheets[index-1];this.dependencyFormulas.changeSheet(this.dependencyFormulas.prepareChangeSheet(oWsBefore.getId(),
{insert:index}))}};Workbook.prototype.replaceWorksheet=function(indexFrom,indexTo){if(indexFrom>=0&&indexFrom<this.aWorksheets.length&&indexTo>=0&&indexTo<this.aWorksheets.length){var wsActive=this.getActiveWs();var oWsFrom=this.aWorksheets[indexFrom];var tempW={wF:oWsFrom,wFI:indexFrom,wTI:indexTo};if(tempW.wFI<tempW.wTI)tempW.wTI++;this.dependencyFormulas.lockRecal();var prepared=this.dependencyFormulas.prepareChangeSheet(oWsFrom.getId(),{replace:tempW},null);var movedSheet=this.aWorksheets.splice(indexFrom, {insert:index}))}};Workbook.prototype.replaceWorksheet=function(indexFrom,indexTo){if(indexFrom>=0&&indexFrom<this.aWorksheets.length&&indexTo>=0&&indexTo<this.aWorksheets.length){var wsActive=this.getActiveWs();var oWsFrom=this.aWorksheets[indexFrom];var tempW={wF:oWsFrom,wFI:indexFrom,wTI:indexTo};if(tempW.wFI<tempW.wTI)tempW.wTI++;this.dependencyFormulas.lockRecal();var prepared=this.dependencyFormulas.prepareChangeSheet(oWsFrom.getId(),{replace:tempW},null);var movedSheet=this.aWorksheets.splice(indexFrom,

@ -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 // Support for growable heap + pthreads, where the buffer may change, so JS views
// must be updated. // must be updated.
function GROWABLE_HEAP_STORE_I8(ptr, value) { function GROWABLE_HEAP_STORE_I8(ptr, value) {
@ -1030,7 +1035,7 @@ if (ENVIRONMENT_IS_PTHREAD) {
"maximum": 1073741824 / WASM_PAGE_SIZE, "maximum": 1073741824 / WASM_PAGE_SIZE,
"shared": true "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"); 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) { if (ENVIRONMENT_HAS_NODE) {
console.log("(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and also use a recent version)"); 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) {}), receiveObjectTransfer: (function(data) {}),
allocateUnusedWorkers: (function(numWorkers, onFinishedLoading) { allocateUnusedWorkers: (function(numWorkers, onFinishedLoading) {
if (typeof SharedArrayBuffer === "undefined") return; if (!SUPPORTS_SHARED_MEMORY()) return;
var workers = []; var workers = [];
var numWorkersToCreate = numWorkers; var numWorkersToCreate = numWorkers;
if (PThread.preallocatedWorkers.length > 0) { if (PThread.preallocatedWorkers.length > 0) {
@ -2276,7 +2281,7 @@ var PThread = {
} }
}), }),
createNewWorkers: (function(numWorkers) { createNewWorkers: (function(numWorkers) {
if (typeof SharedArrayBuffer === "undefined") return []; if (!SUPPORTS_SHARED_MEMORY()) return [];
var pthreadMainJs = "x2t.worker.js"; var pthreadMainJs = "x2t.worker.js";
pthreadMainJs = locateFile(pthreadMainJs); pthreadMainJs = locateFile(pthreadMainJs);
var newWorkers = []; var newWorkers = [];
@ -5683,7 +5688,7 @@ function _emscripten_get_sbrk_ptr() {
} }
Module["_emscripten_get_sbrk_ptr"] = _emscripten_get_sbrk_ptr; Module["_emscripten_get_sbrk_ptr"] = _emscripten_get_sbrk_ptr;
function _emscripten_has_threading_support() { function _emscripten_has_threading_support() {
return typeof SharedArrayBuffer !== "undefined"; return SUPPORTS_SHARED_MEMORY();
} }
Module["_emscripten_has_threading_support"] = _emscripten_has_threading_support; Module["_emscripten_has_threading_support"] = _emscripten_has_threading_support;
function _emscripten_is_main_browser_thread() { function _emscripten_is_main_browser_thread() {
@ -6761,7 +6766,7 @@ function _pthread_self() {
} }
Module["_pthread_self"] = _pthread_self; Module["_pthread_self"] = _pthread_self;
function _pthread_create(pthread_ptr, attr, start_routine, arg) { 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!"); err("Current environment does not support SharedArrayBuffer, pthreads are not available!");
return 6; return 6;
} }

@ -112,6 +112,7 @@ define([
Store.set = function (clientId, data, cb) { Store.set = function (clientId, data, cb) {
var s = getStore(data.teamId); var s = getStore(data.teamId);
if (!s) { return void cb({ error: 'ENOTFOUND' }); } if (!s) { return void cb({ error: 'ENOTFOUND' }); }
if (!s.proxy) { return void cb({ error: 'ENODRIVE' }); }
var path = data.key.slice(); var path = data.key.slice();
var key = path.pop(); var key = path.pop();
var obj = Util.find(s.proxy, path); var obj = Util.find(s.proxy, path);
@ -273,9 +274,9 @@ define([
} }
var pads = data.pads || data; var pads = data.pads || data;
s.rpc.pin(pads, function (e, hash) { s.rpc.pin(pads, function (e) {
if (e) { return void cb({error: e}); } if (e) { return void cb({error: e}); }
cb({hash: hash}); cb({});
}); });
}; };
@ -288,9 +289,9 @@ define([
if (!s.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!s.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var pads = data.pads || data; var pads = data.pads || data;
s.rpc.unpin(pads, function (e, hash) { s.rpc.unpin(pads, function (e) {
if (e) { return void cb({error: e}); } if (e) { return void cb({error: e}); }
cb({hash: hash}); cb({});
}); });
}; };
@ -393,9 +394,9 @@ define([
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var list = getCanonicalChannelList(false); var list = getCanonicalChannelList(false);
store.rpc.reset(list, function (e, hash) { store.rpc.reset(list, function (e) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
cb(null, hash); cb(null);
}); });
}; };
@ -629,6 +630,7 @@ define([
if (!proxy.uid) { if (!proxy.uid) {
store.noDriveUid = store.noDriveUid || Hash.createChannelId(); store.noDriveUid = store.noDriveUid || Hash.createChannelId();
} }
var metadata = { var metadata = {
// "user" is shared with everybody via the userlist // "user" is shared with everybody via the userlist
user: { user: {
@ -655,7 +657,7 @@ define([
accountName: proxy.login_name || '', accountName: proxy.login_name || '',
offline: store.proxy && store.offline, offline: store.proxy && store.offline,
teams: teams, teams: teams,
plan: account.plan plan: account.plan,
} }
}; };
cb(JSON.parse(JSON.stringify(metadata))); cb(JSON.parse(JSON.stringify(metadata)));
@ -1226,7 +1228,7 @@ define([
}); });
// Add the pad if it does not exist in our drive // Add the pad if it does not exist in our drive
if (!contains) { // || (ownedByMe && !inMyDrive)) { if (!contains || (data.forceSave && !inMyDrive)) {
var autoStore = Util.find(store.proxy, ['settings', 'general', 'autostore']); var autoStore = Util.find(store.proxy, ['settings', 'general', 'autostore']);
if (autoStore !== 1 && !data.forceSave && !data.path) { if (autoStore !== 1 && !data.forceSave && !data.path) {
// send event to inner to display the corner popup // send event to inner to display the corner popup
@ -1267,7 +1269,8 @@ define([
}); });
// Let inner know that dropped files shouldn't trigger the popup // Let inner know that dropped files shouldn't trigger the popup
postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", { postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", {
stored: true stored: true,
inMyDrive: inMyDrive
}); });
nThen(function (waitFor) { nThen(function (waitFor) {
sendTo.forEach(function (teamId) { sendTo.forEach(function (teamId) {
@ -1303,9 +1306,14 @@ define([
getAllStores().forEach(function (s) { getAllStores().forEach(function (s) {
s.manager.getSecureFilesList(where).forEach(function (obj) { s.manager.getSecureFilesList(where).forEach(function (obj) {
var data = obj.data; var data = obj.data;
if (channels.indexOf(data.channel) !== -1) { return; } if (channels.indexOf(data.channel || data.id) !== -1) { return; }
var id = obj.id; 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); var parsed = Hash.parsePadUrl(data.href || data.roHref);
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) && if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) &&
!isFiltered(parsed.type, data)) { !isFiltered(parsed.type, data)) {
@ -2050,8 +2058,17 @@ define([
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
// Tell all the owners that the pad was deleted from the server // 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) { m.forEach(function (obj) {
var mb = JSON.parse(obj); var mb = JSON.parse(obj);
if (mb.curvePublic === curvePublic) { return; } if (mb.curvePublic === curvePublic) { return; }
@ -2139,11 +2156,23 @@ define([
if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); } if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); }
if (!data.command) { return void cb({ error: 'EINVAL' }); } if (!data.command) { return void cb({ error: 'EINVAL' }); }
var s = getStore(data.teamId); var s = getStore(data.teamId);
var otherChannels = data.channels;
delete data.channels;
s.rpc.setMetadata(data, function (err, res) { s.rpc.setMetadata(data, function (err, res) {
if (err) { return void cb({ error: err }); } if (err) { return void cb({ error: err }); }
if (!Array.isArray(res) || !res.length) { return void cb({}); } if (!Array.isArray(res) || !res.length) { return void cb({}); }
cb(res[0]); cb(res[0]);
}); });
// If we have other related channels, send the command for them too
if (Array.isArray(otherChannels)) {
otherChannels.forEach(function (chan) {
var _d = Util.clone(data);
_d.channel = chan;
Store.setPadMetadata(clientId, _d, function () {
});
});
}
}; };
// GET_FULL_HISTORY from sframe-common-outer // GET_FULL_HISTORY from sframe-common-outer
@ -2696,7 +2725,12 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
if (!proxy.settings) { proxy.settings = NEW_USER_SETTINGS; } if (!proxy.settings) { proxy.settings = NEW_USER_SETTINGS; }
if (!proxy.forms) { proxy.forms = {}; }
if (!proxy.friends_pending) { proxy.friends_pending = {}; } if (!proxy.friends_pending) { proxy.friends_pending = {}; }
// Form seed is used to generate a box encryption keypair when
// answering a form anonymously
if (!proxy.form_seed) { proxy.form_seed = Hash.createChannelId(); }
// Call onCacheReady if the manager is not yet defined // Call onCacheReady if the manager is not yet defined
if (!manager) { if (!manager) {
@ -3167,6 +3201,10 @@ define([
initialized = false; initialized = false;
} }
var redirect = Constants.prefersDriveRedirectKey;
var redirectPreference = Util.find(store, [ 'proxy', 'settings', 'general', redirect, ]);
ret[redirect] = redirectPreference;
callback(ret); callback(ret);
}); });

@ -49,7 +49,10 @@ define([
} }
cb(null, obj.c); cb(null, obj.c);
obj.t = +new Date(); obj.t = +new Date();
cache.setItem(id, obj); cache.setItem(id, obj, function (err) {
if (!err) { return; }
console.error(err);
});
}); });
}); });
}; };
@ -81,7 +84,10 @@ define([
} }
cb(null, obj); cb(null, obj);
obj.t = +new Date(); obj.t = +new Date();
cache.setItem(id, obj); cache.setItem(id, obj, function (err) {
if (!err) { return; }
console.error(err);
});
}); });
}); });
}; };
@ -91,7 +97,7 @@ define([
var checkCheckpoints = function (array) { var checkCheckpoints = function (array) {
if (!Array.isArray(array)) { return; } if (!Array.isArray(array)) { return; }
// Keep the last 100 messages // Keep the last 100 messages
if (array.length > 100) { if (array.length > 100) { // XXX 4.10.0
array.splice(0, array.length - 100); array.splice(0, array.length - 100);
} }
// Remove every message before the first checkpoint // Remove every message before the first checkpoint

@ -76,6 +76,16 @@ define([
return window.CP_logged_in || typeof getUserHash() === "string"; return window.CP_logged_in || typeof getUserHash() === "string";
}; };
LocalStore.getDriveRedirectPreference = function () {
try {
return JSON.parse(localStorage[Constants.redirectToDriveKey]);
} catch (err) { return; }
};
LocalStore.setDriveRedirectPreference = function (bool) {
localStorage.setItem(Constants.redirectToDriveKey, Boolean(bool));
};
LocalStore.login = function (hash, name, cb) { LocalStore.login = function (hash, name, cb) {
if (!hash) { throw new Error('expected a user hash'); } if (!hash) { throw new Error('expected a user hash'); }
if (!name) { throw new Error('expected a user name'); } if (!name) { throw new Error('expected a user name'); }

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

@ -195,7 +195,7 @@ define([
Pinpad.create(ctx.store.network, data, function (e, call) { Pinpad.create(ctx.store.network, data, function (e, call) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
team.rpc = call; team.rpc = call;
team.onRpcReadyEvt.fire(); if (team && team.onRpcReadyEvt) { team.onRpcReadyEvt.fire(); }
cb(); cb();
}, Cache); }, Cache);
}); });
@ -986,47 +986,60 @@ define([
var state = team.roster.getState() || {}; var state = team.roster.getState() || {};
var members = state.members || {}; var members = state.members || {};
// Get pending owners var md;
var md = team.listmap.metadata || {}; nThen(function (waitFor) {
if (Array.isArray(md.pending_owners)) { // Get pending owners
// Get the members associated to the pending_owners' edPublic and mark them as such ctx.Store.getPadMetadata(null, {
md.pending_owners.forEach(function (ed) { channel: teamData.channel
var member; }, waitFor(function (obj) {
Object.keys(members).some(function (curve) { if (obj && obj.error) {
if (members[curve].edPublic === ed) { md = team.listmap.metadata || {};
member = members[curve];
return true;
}
});
if (!member && teamData.owner) {
var removeOwnership = function (chan) {
ctx.Store.setPadMetadata(null, {
channel: chan,
command: 'RM_PENDING_OWNERS',
value: [ed],
}, function () {});
};
removeOwnership(teamData.channel);
removeOwnership(Util.find(teamData, ['keys', 'roster', 'channel']));
removeOwnership(Util.find(teamData, ['keys', 'chat', 'channel']));
return; return;
} }
member.pendingOwner = true; md = obj;
}); }));
} }).nThen(function () {
ctx.pending_owners = md.pending_owners;
if (Array.isArray(md.pending_owners)) {
// Get the members associated to the pending_owners' edPublic and mark them as such
md.pending_owners.forEach(function (ed) {
var member;
Object.keys(members).some(function (curve) {
if (members[curve].edPublic === ed) {
member = members[curve];
return true;
}
});
if (!member && teamData.owner) {
var removeOwnership = function (chan) {
ctx.Store.setPadMetadata(null, {
channel: chan,
command: 'RM_PENDING_OWNERS',
value: [ed],
}, function () {});
};
removeOwnership(teamData.channel);
removeOwnership(Util.find(teamData, ['keys', 'roster', 'channel']));
removeOwnership(Util.find(teamData, ['keys', 'chat', 'channel']));
return;
}
member.pendingOwner = true;
});
}
// Add online status (using messenger data) // Add online status (using messenger data)
if (ctx.store.messenger) { if (ctx.store.messenger) {
var chatData = team.getChatData(); var chatData = team.getChatData();
var online = ctx.store.messenger.getOnlineList(chatData.channel) || []; var online = ctx.store.messenger.getOnlineList(chatData.channel) || [];
online.forEach(function (curve) { online.forEach(function (curve) {
if (members[curve]) { if (members[curve]) {
members[curve].online = true; members[curve].online = true;
} }
}); });
} }
cb(members); cb(members);
});
}; };
// Return folders with edit rights available to everybody (decrypted pad href) // Return folders with edit rights available to everybody (decrypted pad href)
@ -1144,8 +1157,7 @@ define([
if (!teamData) { return void cb ({error: 'ENOENT'}); } if (!teamData) { return void cb ({error: 'ENOENT'}); }
var team = ctx.teams[teamId]; var team = ctx.teams[teamId];
if (!team) { return void cb ({error: 'ENOENT'}); } if (!team) { return void cb ({error: 'ENOENT'}); }
var md = team.listmap.metadata || {}; var isPendingOwner = user.pendingOwner;
var isPendingOwner = (md.pending_owners || []).indexOf(user.edPublic) !== -1;
nThen(function (waitFor) { nThen(function (waitFor) {
var cmd = isPendingOwner ? 'RM_PENDING_OWNERS' : 'RM_OWNERS'; var cmd = isPendingOwner ? 'RM_PENDING_OWNERS' : 'RM_OWNERS';
@ -1364,42 +1376,60 @@ define([
var describeUser = function (ctx, data, cId, cb) { var describeUser = function (ctx, data, cId, cb) {
var teamId = data.teamId; var teamId = data.teamId;
if (!teamId) { return void cb({error: 'EINVAL'}); } if (!teamId) { return void cb({error: 'EINVAL'}); }
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', teamId]);
var team = ctx.teams[teamId]; var team = ctx.teams[teamId];
if (!team) { return void cb ({error: 'ENOENT'}); } if (!teamData || !team) { return void cb ({error: 'ENOENT'}); }
if (!team.roster) { return void cb({error: 'NO_ROSTER'}); } if (!team.roster) { return void cb({error: 'NO_ROSTER'}); }
if (!data.curvePublic || !data.data) { return void cb({error: 'MISSING_DATA'}); } if (!data.curvePublic || !data.data) { return void cb({error: 'MISSING_DATA'}); }
var state = team.roster.getState(); var state = team.roster.getState();
var user = state.members[data.curvePublic]; var user = state.members[data.curvePublic];
// It it is an ownership revocation, we have to set it in pad metadata first var md;
if (user.role === "OWNER" && data.data.role !== "OWNER") { nThen(function (waitFor) {
revokeOwnership(ctx, teamId, user, function (err) { // Get pending owners
if (!err) { return void cb(); } ctx.Store.getPadMetadata(null, {
console.error(err); channel: teamData.channel
return void cb({error: err}); }, waitFor(function (obj) {
}); if (obj && obj.error) {
return; md = team.listmap.metadata || {};
} return;
}
md = obj;
}));
}).nThen(function () {
user.pendingOwner = Array.isArray(md.pending_owners) &&
md.pending_owners.indexOf(user.edPublic) !== -1;
// Viewer to editor // It it is an ownership revocation, we have to set it in pad metadata first
if (user.role === "VIEWER" && data.data.role !== "VIEWER") { if (user.role === "OWNER" && data.data.role !== "OWNER") {
changeEditRights(ctx, teamId, user, true, function (obj) { revokeOwnership(ctx, teamId, user, function (err) {
return void cb(obj); if (!err) { return void cb(); }
}); console.error(err);
} return void cb({error: err});
});
return;
}
// Editor to viewer // Viewer to editor
if (user.role !== "VIEWER" && data.data.role === "VIEWER") { if (user.role === "VIEWER" && data.data.role !== "VIEWER") {
changeEditRights(ctx, teamId, user, false, function (obj) { changeEditRights(ctx, teamId, user, true, function (obj) {
return void cb(obj); return void cb(obj);
}); });
} }
var obj = {}; // Editor to viewer
obj[data.curvePublic] = data.data; if (user.role !== "VIEWER" && data.data.role === "VIEWER") {
team.roster.describe(obj, function (err) { changeEditRights(ctx, teamId, user, false, function (obj) {
if (err) { return void cb({error: err}); } return void cb(obj);
cb(); });
}
var obj = {};
obj[data.curvePublic] = data.data;
team.roster.describe(obj, function (err) {
if (err) { return void cb({error: err}); }
cb();
});
}); });
}; };
@ -2010,9 +2040,16 @@ define([
if (['drive', 'teams', 'settings'].indexOf(app) !== -1) { safe = true; } if (['drive', 'teams', 'settings'].indexOf(app) !== -1) { safe = true; }
Object.keys(teams).forEach(function (id) { Object.keys(teams).forEach(function (id) {
if (!ctx.teams[id]) { return; } if (!ctx.teams[id]) { return; }
var proxy = ctx.teams[id].proxy || {};
var nPads = proxy.drive && Object.keys(proxy.drive.filesData || {}).length;
var nSf = proxy.drive && Object.keys(proxy.drive.sharedFolders || {}).length;
t[id] = { t[id] = {
owner: teams[id].owner, owner: teams[id].owner,
name: teams[id].metadata.name, name: teams[id].metadata.name,
channel: teams[id].channel,
numberPads: nPads,
numberSf: nSf,
roster: Util.find(teams[id], ['keys', 'roster', 'channel']),
edPublic: Util.find(teams[id], ['keys', 'drive', 'edPublic']), edPublic: Util.find(teams[id], ['keys', 'drive', 'edPublic']),
avatar: Util.find(teams[id], ['metadata', 'avatar']), avatar: Util.find(teams[id], ['metadata', 'avatar']),
viewer: !Util.find(teams[id], ['keys', 'drive', 'edPrivate']), viewer: !Util.find(teams[id], ['keys', 'drive', 'edPrivate']),

@ -20,6 +20,7 @@ define([
var ROOT = exp.ROOT; var ROOT = exp.ROOT;
var FILES_DATA = exp.FILES_DATA; var FILES_DATA = exp.FILES_DATA;
var STATIC_DATA = exp.STATIC_DATA;
var OLD_FILES_DATA = exp.OLD_FILES_DATA; var OLD_FILES_DATA = exp.OLD_FILES_DATA;
var UNSORTED = exp.UNSORTED; var UNSORTED = exp.UNSORTED;
var TRASH = exp.TRASH; var TRASH = exp.TRASH;
@ -78,6 +79,14 @@ define([
files[FILES_DATA][id] = data; files[FILES_DATA][id] = data;
cb(null, id); 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) { exp.pushSharedFolder = function (_data, cb) {
if (typeof cb !== "function") { cb = function () {}; } if (typeof cb !== "function") { cb = function () {}; }
@ -136,7 +145,7 @@ define([
var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]); var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
var toClean = []; 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) { if (filesList.indexOf(id) === -1) {
var fd = exp.isSharedFolder(id) ? files[SHARED_FOLDERS][id] : exp.getFileData(id); var fd = exp.isSharedFolder(id) ? files[SHARED_FOLDERS][id] : exp.getFileData(id);
var channelId = fd.channel; var channelId = fd.channel;
@ -146,6 +155,8 @@ define([
if (exp.isSharedFolder(id)) { if (exp.isSharedFolder(id)) {
delete files[SHARED_FOLDERS][id]; delete files[SHARED_FOLDERS][id];
if (config.removeProxy) { config.removeProxy(id); } if (config.removeProxy) { config.removeProxy(id); }
} else if (files[STATIC_DATA][id]) {
delete files[STATIC_DATA][id];
} else { } else {
spliceFileData(id); spliceFileData(id);
} }
@ -242,6 +253,12 @@ define([
id = Number(id); id = Number(id);
// Find and maybe update existing pads with the same channel id // Find and maybe update existing pads with the same channel id
var d = data[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 we were given an edit link, encrypt its value if needed
if (d.href) { d.href = exp.cryptor.encrypt(d.href); } if (d.href) { d.href = exp.cryptor.encrypt(d.href); }
var found = false; var found = false;
@ -398,7 +415,7 @@ define([
if (!loggedIn && !config.testMode) { return; } if (!loggedIn && !config.testMode) { return; }
id = Number(id); 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; } if (!data || typeof(data) !== "object") { return; }
var newPath = path, parentEl; var newPath = path, parentEl;
if (path && !Array.isArray(path)) { if (path && !Array.isArray(path)) {
@ -599,13 +616,18 @@ define([
var element = elem || files[ROOT]; var element = elem || files[ROOT];
if (!element) { return console.error("Invalid element in root"); } if (!element) { return console.error("Invalid element in root"); }
var nbMetadataFolders = 0; 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) { for (var el in element) {
if (element[el] === null) { element_el = element[el];
if (element_el === null) {
console.error('element[%s] is null', el); console.error('element[%s] is null', el);
delete element[el]; delete element[el];
continue; continue;
} }
if (exp.isFolderData(element[el])) { if (exp.isFolderData(element_el)) {
if (nbMetadataFolders !== 0) { if (nbMetadataFolders !== 0) {
debug("Multiple metadata files in folder"); debug("Multiple metadata files in folder");
delete element[el]; delete element[el];
@ -613,30 +635,30 @@ define([
nbMetadataFolders++; nbMetadataFolders++;
continue; continue;
} }
if (!exp.isFile(element[el], true) && !exp.isFolder(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]); debug("An element in ROOT was not a folder nor a file. ", element_el);
delete element[el]; delete element[el];
continue; continue;
} }
if (exp.isFolder(element[el])) { if (exp.isFolder(element_el)) {
fixRoot(element[el]); fixRoot(element_el);
continue; continue;
} }
if (typeof element[el] === "string") { if (typeof element_el === "string") {
// We have an old file (href) which is not in filesData: add it // We have an old file (href) which is not in filesData: add it
var id = Util.createRandomInteger(); var id = Util.createRandomInteger();
var key = Hash.createChannelId(); var key = Hash.createChannelId();
files[FILES_DATA][id] = { files_data[id] = {
href: exp.cryptor.encrypt(element[el]), href: exp.cryptor.encrypt(element_el),
filename: el filename: el
}; };
element[key] = id; element[key] = id;
delete element[el]; delete element[el];
} }
if (typeof element[el] === "number") { if (typeof element_el === "number") {
var data = files[FILES_DATA][element[el]]; var data = files_data[element_el] || static_data[element_el];
if (!data) { 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]; delete element[el];
} }
} }
@ -845,6 +867,26 @@ define([
toClean.forEach(function (id) { toClean.forEach(function (id) {
spliceFileData(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 () { var fixSharedFolders = function () {
if (sharedFolder) { return; } 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 () { var fixDrive = function () {
@ -900,6 +948,7 @@ define([
}); });
}; };
fixStaticData();
fixRoot(); fixRoot();
fixTrashRoot(); fixTrashRoot();
fixTemplate(); fixTemplate();

@ -5355,10 +5355,12 @@ var PDFFunction = function PDFFunctionClosure() {
var domain = IR[1]; var domain = IR[1];
var range = IR[2]; var range = IR[2];
var code = IR[3]; var code = IR[3];
/*
var compiled = new PostScriptCompiler().compile(code, domain, range); var compiled = new PostScriptCompiler().compile(code, domain, range);
if (compiled) { if (compiled) {
return new Function('src', 'srcOffset', 'dest', 'destOffset', compiled); return new Function('src', 'srcOffset', 'dest', 'destOffset', compiled);
} }
*/
(0, _util.info)('Unable to compile PS function'); (0, _util.info)('Unable to compile PS function');
var numOutputs = range.length >> 1; var numOutputs = range.length >> 1;
var numInputs = domain.length >> 1; var numInputs = domain.length >> 1;
@ -38545,4 +38547,4 @@ if (typeof PDFJS === 'undefined' || !PDFJS.compatibilityChecked) {
/***/ }) /***/ })
/******/ ]); /******/ ]);
}); });
//# sourceMappingURL=pdf.worker.js.map //# sourceMappingURL=pdf.worker.js.map

@ -26,23 +26,19 @@ var factory = function (Util, Rpc) {
exp.send = rpc.send; exp.send = rpc.send;
// you can ask the server to pin a particular channel for you // you can ask the server to pin a particular channel for you
exp.pin = function (channels, cb) { exp.pin = function (channels, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (!Array.isArray(channels)) { if (!Array.isArray(channels)) {
setTimeout(function () { return void cb('[TypeError] pin expects an array');
cb('[TypeError] pin expects an array');
});
return;
} }
rpc.send('PIN', channels, cb); rpc.send('PIN', channels, cb);
}; };
// you can also ask to unpin a particular channel // you can also ask to unpin a particular channel
exp.unpin = function (channels, cb) { exp.unpin = function (channels, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (!Array.isArray(channels)) { if (!Array.isArray(channels)) {
setTimeout(function () { return void cb('[TypeError] pin expects an array');
cb('[TypeError] pin expects an array');
});
return;
} }
rpc.send('UNPIN', channels, cb); rpc.send('UNPIN', channels, cb);
}; };
@ -70,23 +66,12 @@ var factory = function (Util, Rpc) {
}; };
// if local and remote hashes don't match, send a reset // if local and remote hashes don't match, send a reset
exp.reset = function (channels, cb) { exp.reset = function (channels, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (!Array.isArray(channels)) { if (!Array.isArray(channels)) {
setTimeout(function () { return void cb('[TypeError] pin expects an array');
cb('[TypeError] pin expects an array');
});
return;
} }
rpc.send('RESET', channels, function (e, response) { rpc.send('RESET', channels, cb);
if (e) {
return void cb(e);
}
if (!response.length) {
console.log(response);
return void cb('INVALID_RESPONSE');
}
cb(e, response[0]);
});
}; };
// get the combined size of all channels (in bytes) for all the // get the combined size of all channels (in bytes) for all the

@ -22,11 +22,11 @@ define([
// a cached version // a cached version
if (Env.folders[id].offline && !lm.cache) { if (Env.folders[id].offline && !lm.cache) {
Env.folders[id].offline = false; Env.folders[id].offline = false;
if (Env.folders[id].userObject.fixFiles) { Env.folders[id].userObject.fixFiles(); }
Env.Store.refreshDriveUI(); Env.Store.refreshDriveUI();
} }
return; return;
} }
if (Env.folders[id]) { console.warn(Env.folders[id]); }
var cfg = getConfig(Env); var cfg = getConfig(Env);
cfg.sharedFolder = true; cfg.sharedFolder = true;
cfg.id = id; cfg.id = id;
@ -237,7 +237,15 @@ define([
var getSharedFolderData = function (Env, id) { var getSharedFolderData = function (Env, id) {
if (!Env.folders[id]) { return {}; } if (!Env.folders[id]) { return {}; }
var obj = Env.folders[id].proxy.metadata || {}; var proxy = Env.folders[id].proxy;
// Clean deprecated values
if (Object.keys(proxy.metadata || {}).length > 1) {
proxy.metadata = { title: proxy.metadata.title };
}
var obj = Util.clone(proxy.metadata || {});
for (var k in Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {}) { for (var k in Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {}) {
if (typeof(Env.user.proxy[UserObject.SHARED_FOLDERS][id][k]) === "undefined") { // XXX "deleted folder" for restricted shared folders when viewer in a team if (typeof(Env.user.proxy[UserObject.SHARED_FOLDERS][id][k]) === "undefined") { // XXX "deleted folder" for restricted shared folders when viewer in a team
continue; continue;
@ -576,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 _restoreSharedFolder = function (Env, _data, cb) {
var fId = _data.id; var fId = _data.id;
@ -810,6 +836,7 @@ define([
_findChannels(Env, toUnpin).forEach(function (id) { _findChannels(Env, toUnpin).forEach(function (id) {
var data = _getFileData(Env, id); var data = _getFileData(Env, id);
var arr = [data.channel]; var arr = [data.channel];
if (data.answersChannel) { arr.push(data.answersChannel); }
if (data.rtChannel) { arr.push(data.rtChannel); } if (data.rtChannel) { arr.push(data.rtChannel); }
if (data.lastVersion) { arr.push(Hash.hrefToHexChannelId(data.lastVersion)); } if (data.lastVersion) { arr.push(Hash.hrefToHexChannelId(data.lastVersion)); }
Array.prototype.push.apply(toKeep, arr); Array.prototype.push.apply(toKeep, arr);
@ -1010,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 onCommand = function (Env, cmdData, cb) {
var cmd = cmdData.cmd; var cmd = cmdData.cmd;
var data = cmdData.data || {}; var data = cmdData.data || {};
switch (cmd) { var method = COMMANDS[cmd];
case 'move':
_move(Env, data, cb); break; if (typeof(method) === 'function') {
case 'restore': return void method(Env, data, cb);
_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();
} }
// if the command was not handled then call back
cb();
}; };
// Set the value everywhere the given pad is stored (main and shared folders) // Set the value everywhere the given pad is stored (main and shared folders)
@ -1120,8 +1152,8 @@ define([
data: uo.getFileData(id) data: uo.getFileData(id)
}; };
}).filter(function (d) { }).filter(function (d) {
if (channels.indexOf(d.data.channel) === -1) { if (channels.indexOf(d.data.channel || d.id) === -1) {
channels.push(d.data.channel); channels.push(d.data.channel || d.id);
return true; return true;
} }
}); });
@ -1176,6 +1208,10 @@ define([
result.push(otherChan); result.push(otherChan);
} }
} }
// Pin form answers channels
if (data.answersChannel && result.indexOf(data.answersChannel) === -1) {
result.push(data.answersChannel);
}
// Pin onlyoffice realtime patches // Pin onlyoffice realtime patches
if (data.rtChannel && result.indexOf(data.rtChannel) === -1) { if (data.rtChannel && result.indexOf(data.rtChannel) === -1) {
result.push(data.rtChannel); result.push(data.rtChannel);
@ -1220,7 +1256,10 @@ define([
Array.prototype.push.apply(result, sfChannels); 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) { var addPad = function (Env, path, pad, cb) {
@ -1370,6 +1409,16 @@ define([
} }
}, cb); }, 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) { var restoreSharedFolderInner = function (Env, fId, password, cb) {
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", { return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "restoreSharedFolder", cmd: "restoreSharedFolder",
@ -1420,6 +1469,14 @@ define([
}, cb); }, cb);
}; };
var updateStaticAccessInner = function (Env, id, cb) {
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
cmd: "updateStaticAccess",
data: id
}, cb);
};
/* Tools */ /* Tools */
var findChannels = _findChannels; var findChannels = _findChannels;
@ -1437,6 +1494,11 @@ define([
return String(uo.getTitle(id, type)); 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 isReadOnlyFile = function (Env, id) {
var uo = _getUserObjectFromId(Env, id); var uo = _getUserObjectFromId(Env, id);
return uo.isReadOnlyFile(id); return uo.isReadOnlyFile(id);
@ -1478,7 +1540,7 @@ define([
var files = []; var files = [];
var userObjects = _getUserObjects(Env); var userObjects = _getUserObjects(Env);
userObjects.forEach(function (uo) { 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)]; return [Number(id), uo.getFileData(id)];
}); });
Array.prototype.push.apply(files, data); Array.prototype.push.apply(files, data);
@ -1595,17 +1657,20 @@ define([
emptyTrash: callWithEnv(emptyTrashInner), emptyTrash: callWithEnv(emptyTrashInner),
addFolder: callWithEnv(addFolderInner), addFolder: callWithEnv(addFolderInner),
addSharedFolder: callWithEnv(addSharedFolderInner), addSharedFolder: callWithEnv(addSharedFolderInner),
addLink: callWithEnv(addLinkInner),
restoreSharedFolder: callWithEnv(restoreSharedFolderInner), restoreSharedFolder: callWithEnv(restoreSharedFolderInner),
convertFolderToSharedFolder: callWithEnv(convertFolderToSharedFolderInner), convertFolderToSharedFolder: callWithEnv(convertFolderToSharedFolderInner),
delete: callWithEnv(deleteInner), delete: callWithEnv(deleteInner),
deleteOwned: callWithEnv(deleteOwnedInner), deleteOwned: callWithEnv(deleteOwnedInner),
restore: callWithEnv(restoreInner), restore: callWithEnv(restoreInner),
setFolderData: callWithEnv(setFolderDataInner), setFolderData: callWithEnv(setFolderDataInner),
updateStaticAccess: callWithEnv(updateStaticAccessInner),
// Tools // Tools
getFileData: callWithEnv(getFileData), getFileData: callWithEnv(getFileData),
find: callWithEnv(find), find: callWithEnv(find),
getTitle: callWithEnv(getTitle), getTitle: callWithEnv(getTitle),
isReadOnlyFile: callWithEnv(isReadOnlyFile), isReadOnlyFile: callWithEnv(isReadOnlyFile),
isStaticFile: callWithEnv(isStaticFile),
getFiles: callWithEnv(getFiles), getFiles: callWithEnv(getFiles),
search: callWithEnv(search), search: callWithEnv(search),
getRecentPads: callWithEnv(getRecentPads), getRecentPads: callWithEnv(getRecentPads),

@ -572,7 +572,9 @@ define([
if (!readOnly) { onLocal(); } if (!readOnly) { onLocal(); }
evOnReady.fire(newPad); evOnReady.fire(newPad);
common.openPadChat(onLocal); // In forms, only editors can see the chat
if (!readOnly || type !== 'form') { common.openPadChat(onLocal); }
if (!readOnly && cursorGetter) { if (!readOnly && cursorGetter) {
common.openCursorChannel(onLocal); common.openCursorChannel(onLocal);
cursor = common.createCursor(onLocal); cursor = common.createCursor(onLocal);
@ -731,11 +733,20 @@ define([
if (!common.isLoggedIn()) { return; } if (!common.isLoggedIn()) { return; }
$embedButton = common.createButton('mediatag', true).click(function () { $embedButton = common.createButton('mediatag', true).click(function () {
var cfg = { var cfg = {
types: ['file'], types: ['file', 'link'],
where: ['root'] where: ['root']
}; };
if ($embedButton.data('filter')) { cfg.filter = $embedButton.data('filter'); } if ($embedButton.data('filter')) { cfg.filter = $embedButton.data('filter'); }
common.openFilePicker(cfg, function (data) { 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') { if (data.type !== 'file') {
console.log("Unexpected data type picked " + data.type); console.log("Unexpected data type picked " + data.type);
return; return;
@ -746,8 +757,8 @@ define([
var privateDat = cpNfInner.metadataMgr.getPrivateData(); var privateDat = cpNfInner.metadataMgr.getPrivateData();
var origin = privateDat.fileHost || privateDat.origin; var origin = privateDat.fileHost || privateDat.origin;
var src = data.src = data.src.slice(0,1) === '/' ? origin + data.src : data.src; var src = data.src = data.src.slice(0,1) === '/' ? origin + data.src : data.src;
mediaTagEmbedder($('<media-tag src="' + src + var mt = UI.mediaTag(src, data.key);
'" data-crypto-key="cryptpad:' + data.key + '"></media-tag>'), data); mediaTagEmbedder($(mt), data);
}); });
}).appendTo(toolbar.$bottomL).hide(); }).appendTo(toolbar.$bottomL).hide();
}; };
@ -925,7 +936,9 @@ define([
} }
var $importTemplateButton = common.createButton('importtemplate', true); var $importTemplateButton = common.createButton('importtemplate', true);
toolbar.$drawer.append($importTemplateButton); if (!readOnly) {
toolbar.$drawer.append($importTemplateButton);
}
/* add a forget button */ /* add a forget button */
toolbar.$drawer.append(common.createButton('forget', true, {}, function (err) { toolbar.$drawer.append(common.createButton('forget', true, {}, function (err) {

@ -73,6 +73,8 @@ define([
else { else {
editor.setSelection(posToCursor(selects[0], remoteDoc), posToCursor(selects[1], remoteDoc)); editor.setSelection(posToCursor(selects[0], remoteDoc), posToCursor(selects[1], remoteDoc));
} }
editor.scrollTo(scroll.left, scroll.top);
}; };
module.handleImagePaste = function (editor) { module.handleImagePaste = function (editor) {

@ -570,7 +570,7 @@ define([
var defaultTitle = Utils.UserObject.getDefaultName(parsed); var defaultTitle = Utils.UserObject.getDefaultName(parsed);
var edPublic, curvePublic, notifications, isTemplate; var edPublic, curvePublic, notifications, isTemplate;
var settings = {}; var settings = {};
var isSafe = ['debug', 'profile', 'drive', 'teams', 'calendar'].indexOf(currentPad.app) !== -1; var isSafe = ['debug', 'profile', 'drive', 'teams', 'calendar', 'file'].indexOf(currentPad.app) !== -1;
var isDeleted = isNewFile && currentPad.hash.length > 0; var isDeleted = isNewFile && currentPad.hash.length > 0;
if (isDeleted) { if (isDeleted) {
@ -615,6 +615,7 @@ define([
newTemplate: Array.isArray(Cryptpad.initialPath) newTemplate: Array.isArray(Cryptpad.initialPath)
&& Cryptpad.initialPath[0] === "template", && Cryptpad.initialPath[0] === "template",
feedbackAllowed: Utils.Feedback.state, feedbackAllowed: Utils.Feedback.state,
prefersDriveRedirect: Utils.LocalStore.getDriveRedirectPreference(),
isPresent: parsed.hashData && parsed.hashData.present, isPresent: parsed.hashData && parsed.hashData.present,
isEmbed: parsed.hashData && parsed.hashData.embed, isEmbed: parsed.hashData && parsed.hashData.embed,
oldVersionHash: parsed.hashData && parsed.hashData.version < 2, // password oldVersionHash: parsed.hashData && parsed.hashData.version < 2, // password
@ -1477,28 +1478,45 @@ define([
return 'thumbnail-' + parsed.type + '-' + channel; return 'thumbnail-' + parsed.type + '-' + channel;
}; };
sframeChan.on('Q_CREATE_TEMPLATES', function (type, cb) { sframeChan.on('Q_CREATE_TEMPLATES', function (type, cb) {
Cryptpad.getSecureFilesList({ var templates;
types: [type], nThen(function (waitFor) {
where: ['template'] var next = waitFor();
}, function (err, data) { require([
// NOTE: Never return data directly! '/'+type+'/templates.js'
if (err) { return void cb({error: err}); } ], function (Templates) {
templates = Templates;
var res = []; next();
nThen(function (waitFor) { }, function () {
Object.keys(data).map(function (el) { next();
var k = getKey(data[el].href, data[el].channel); });
Utils.LocalStore.getThumbnail(k, waitFor(function (e, thumb) { }).nThen(function () {
res.push({ Cryptpad.getSecureFilesList({
id: el, types: [type],
name: data[el].filename || data[el].title || '?', where: ['template']
thumbnail: thumb, }, function (err, data) {
used: data[el].used || 0 // NOTE: Never return data directly!
if (err) { return void cb({error: err}); }
var res = [];
nThen(function (waitFor) {
Object.keys(data).map(function (el) {
var k = getKey(data[el].href, data[el].channel);
Utils.LocalStore.getThumbnail(k, waitFor(function (e, thumb) {
res.push({
id: el,
name: data[el].filename || data[el].title || '?',
thumbnail: thumb,
used: data[el].used || 0
});
}));
});
}).nThen(function () {
if (Array.isArray(templates)) {
templates.forEach(function (obj) {
res.push(obj);
}); });
})); }
cb({data: res});
}); });
}).nThen(function () {
cb({data: res});
}); });
}); });
}); });
@ -1891,6 +1909,7 @@ define([
Utils.rtConfig = rtConfig; Utils.rtConfig = rtConfig;
var templatePw; var templatePw;
nThen(function(waitFor) { nThen(function(waitFor) {
if (data.templateContent) { return; }
if (data.templateId) { if (data.templateId) {
if (data.templateId === -1) { if (data.templateId === -1) {
isTemplate = true; isTemplate = true;
@ -1904,6 +1923,13 @@ define([
} }
}).nThen(function () { }).nThen(function () {
var cryptputCfg = $.extend(true, {}, rtConfig, {password: password}); var cryptputCfg = $.extend(true, {}, rtConfig, {password: password});
if (data.templateContent) {
Cryptget.put(currentPad.hash, JSON.stringify(data.templateContent), function () {
startRealtime();
cb();
}, cryptputCfg);
return;
}
if (data.template) { if (data.template) {
// Start OO with a template... // Start OO with a template...
// Cryptget and give href, password and content to inner // Cryptget and give href, password and content to inner
@ -1977,6 +2003,8 @@ define([
sframeChan.on('EV_BURN_AFTER_READING', function () { sframeChan.on('EV_BURN_AFTER_READING', function () {
startRealtime(); startRealtime();
// feedback fails for users in noDrive mode
Utils.Feedback.send("BURN_AFTER_READING", Boolean(cfg.noDrive));
}); });
sframeChan.ready(); sframeChan.ready();

@ -23,6 +23,12 @@ define([
var $title; var $title;
exp.setToolbar = function (toolbar) { exp.setToolbar = function (toolbar) {
$title = toolbar && (toolbar.title || toolbar.pageTitle); $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; }; exp.getTitle = function () { return exp.title; };

@ -145,8 +145,7 @@ define([
var hexFileName = secret.channel; var hexFileName = secret.channel;
var origin = data.fileHost || data.origin; var origin = data.fileHost || data.origin;
var src = origin + Hash.getBlobPathFromHex(hexFileName); var src = origin + Hash.getBlobPathFromHex(hexFileName);
return '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '">' + return UI.mediaTag(src, key).outerHTML;
'</media-tag>';
} }
return; return;
}; };
@ -448,13 +447,18 @@ define([
} }
}; };
funcs.createPad = function (cfg, cb) { funcs.createPad = function (cfg, cb) {
//var priv = ctx.metadataMgr.getPrivateData();
if (AppConfig.disableAnonymousPadCreation && !funcs.isLoggedIn()) {
return void UI.errorLoadingScreen(Messages.mustLogin);
}
ctx.sframeChan.query("Q_CREATE_PAD", { ctx.sframeChan.query("Q_CREATE_PAD", {
owned: cfg.owned, owned: cfg.owned,
expire: cfg.expire, expire: cfg.expire,
password: cfg.password, password: cfg.password,
team: cfg.team, team: cfg.team,
template: cfg.template, template: cfg.template,
templateId: cfg.templateId templateId: cfg.templateId,
templateContent: cfg.templateContent
}, cb); }, cb);
}; };
@ -917,7 +921,7 @@ define([
}); });
ctx.sframeChan.on('EV_WORKER_TIMEOUT', function () { 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(''); funcs.gotoURL('');
}); });
}); });

@ -166,6 +166,7 @@ MessengerUI, Messages, Pages) {
var showColors = false; var showColors = false;
var updateUserList = function (toolbar, config, forceOffline) { var updateUserList = function (toolbar, config, forceOffline) {
if (!config.displayed || config.displayed.indexOf('userlist') === -1) { return; } if (!config.displayed || config.displayed.indexOf('userlist') === -1) { return; }
if (toolbar.isAlone) { return; }
// Make sure the elements are displayed // Make sure the elements are displayed
var $userButtons = toolbar.userlist; var $userButtons = toolbar.userlist;
var $userlistContent = toolbar.userlistContent; var $userlistContent = toolbar.userlistContent;
@ -553,11 +554,13 @@ MessengerUI, Messages, Pages) {
if (toolbar.isDeleted) { if (toolbar.isDeleted) {
return void UI.warn(Messages.deletedFromServer); return void UI.warn(Messages.deletedFromServer);
} }
var privateData = config.metadataMgr.getPrivateData();
var title = (config.title && config.title.getTitle && config.title.getTitle()) var title = (config.title && config.title.getTitle && config.title.getTitle())
|| (config.title && config.title.defaultName) || (config.title && config.title.defaultName)
|| ""; || "";
Common.getSframeChannel().event('EV_SHARE_OPEN', { Common.getSframeChannel().event('EV_SHARE_OPEN', {
title: title title: title,
auditorHash: privateData.form_auditorHash
}); });
}); });
@ -864,10 +867,6 @@ MessengerUI, Messages, Pages) {
'class': "cp-toolbar-link-logo" 'class': "cp-toolbar-link-logo"
}).append(UIElements.getSvgLogo()); }).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) { var onClick = function (e) {
e.preventDefault(); e.preventDefault();
if (e.ctrlKey) { if (e.ctrlKey) {
@ -1214,6 +1213,7 @@ MessengerUI, Messages, Pages) {
if (!config.metadataMgr) { return; } if (!config.metadataMgr) { return; }
var metadataMgr = config.metadataMgr; var metadataMgr = config.metadataMgr;
var notify = function(type, name, oldname) { var notify = function(type, name, oldname) {
if (toolbar.isAlone) { return; }
// type : 1 (+1 user), 0 (rename existing user), -1 (-1 user) // type : 1 (+1 user), 0 (rename existing user), -1 (-1 user)
if (typeof name === "undefined") { return; } if (typeof name === "undefined") { return; }
name = name || Messages.anonymous; name = name || Messages.anonymous;
@ -1513,6 +1513,15 @@ MessengerUI, Messages, Pages) {
} }
}; };
// disable notification, userlist and chat
toolbar.alone = function () {
toolbar.userlist.hide();
toolbar.chat.hide();
$('.cp-toolbar-userlist-drawer').remove();
$('.cp-toolbar-chat-drawer').remove();
toolbar.isAlone = true;
};
// On log out, remove permanently the realtime elements of the toolbar // On log out, remove permanently the realtime elements of the toolbar
Common.onLogout(function () { Common.onLogout(function () {
failed(); failed();

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

Loading…
Cancel
Save