mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
Merge branch '2026-autumn-test-scalable' into notes-from-autumn-test
This commit is contained in:
commit
8b54310d60
6
.github/workflows/test-trigger.yml
vendored
6
.github/workflows/test-trigger.yml
vendored
@ -6,7 +6,7 @@ name: Trigger E2E tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, 2026.9-rc, 2026.9-test ]
|
||||
branches: [ staging, 2026-autumn-rc, 2026-autumn-test ]
|
||||
|
||||
jobs:
|
||||
trigger-tests:
|
||||
@ -18,9 +18,9 @@ jobs:
|
||||
|
||||
if [ "$branch" = "staging" ]; then
|
||||
event_type="trigger-e2e-tests-staging"
|
||||
elif [ "$branch" = "2026.9-rc" ]; then
|
||||
elif [ "$branch" = "2026-autumn-rc" ]; then
|
||||
event_type="trigger-e2e-tests-rc"
|
||||
elif [ "$branch" = "2026.9-test" ]; then
|
||||
elif [ "$branch" = "2026-autumn-test" ]; then
|
||||
event_type="trigger-e2e-tests-test"
|
||||
fi
|
||||
|
||||
|
||||
15
Dockerfile
15
Dockerfile
@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
# Multistage build to reduce image size and increase security
|
||||
FROM node:lts-alpine AS build
|
||||
FROM node:lts-slim AS build
|
||||
|
||||
# Create folder for CryptPad
|
||||
RUN mkdir /cryptpad
|
||||
@ -14,20 +14,25 @@ COPY . /cryptpad
|
||||
|
||||
RUN sed -i "s@//httpAddress: 'localhost'@httpAddress: '0.0.0.0'@" /cryptpad/config/config.example.js
|
||||
RUN sed -i "s@installMethod: 'unspecified'@installMethod: 'docker'@" /cryptpad/config/config.example.js
|
||||
RUN sed -i 's@host: "localhost"@host: "0.0.0.0"@' /cryptpad/config/infra.example.js
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install --production \
|
||||
&& npm run install:components
|
||||
|
||||
# Create actual CryptPad image
|
||||
FROM node:lts-alpine
|
||||
FROM node:lts-slim
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Create user and group for CryptPad so it does not run as root
|
||||
RUN addgroup -S cryptpad -g 4001 && adduser -S cryptpad -G cryptpad --uid 4001 -h /cryptpad
|
||||
RUN groupadd cryptpad -g 4001 && useradd cryptpad -u 4001 -g 4001 -d /cryptpad
|
||||
|
||||
# Install curl for healthcheck
|
||||
# Install git, rdfind and unzip for install-office.sh
|
||||
RUN apk add --no-cache ca-certificates git rdfind unzip bash curl
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
bash curl ca-certificates git rdfind unzip && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy cryptpad with installed modules
|
||||
COPY --from=build --chown=cryptpad /cryptpad /cryptpad
|
||||
@ -55,7 +60,7 @@ ENTRYPOINT ["/bin/bash", "/cryptpad/docker-entrypoint.sh"]
|
||||
HEALTHCHECK --interval=1m CMD curl -f http://localhost:3000/ || exit 1
|
||||
|
||||
# Ports
|
||||
EXPOSE 3000 3003
|
||||
EXPOSE 3000
|
||||
|
||||
# Run cryptpad on startup
|
||||
CMD ["npm", "start"]
|
||||
|
||||
@ -165,6 +165,7 @@ define(req, function(AppConfig, Default, Language) {
|
||||
Messages.settings_profileShare = 'Share profile';
|
||||
Messages.copy = 'Copy';
|
||||
Messages.oo_blockURLImageDrop = 'Dragging images from a browser is not supported.'; // XXX
|
||||
Messages.form_footerInfo = "This form and your answers are protected with end-to-end encryption<br>Only the form author can read the data<br>To learn more and create your own forms <a href='https://cryptpad.org/apps/form/' target='_blank'>visit the CryptPad website</a>"; // XXX
|
||||
|
||||
return Messages;
|
||||
|
||||
|
||||
@ -738,7 +738,8 @@
|
||||
.cp-toolbar-title-value {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-collapse: collapse;
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
&:focus {
|
||||
margin: 2px;
|
||||
border-radius: @variables_radius;
|
||||
|
||||
@ -82,6 +82,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.cp-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.cp-usergrid-user-avatar {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
@ -28,7 +28,6 @@ services:
|
||||
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "3003:3003"
|
||||
|
||||
ulimits:
|
||||
nofile:
|
||||
|
||||
@ -8,6 +8,17 @@
|
||||
# installation (http server by the Nodejs process). If you are using CryptPad
|
||||
# in production and require professional support please contact sales@cryptpad.fr
|
||||
|
||||
# CryptPad possesses several nodes that can answer websocket requests. The
|
||||
# following allows nginx to load-balance between them.
|
||||
# The different `server` corresponds to the location of the different front
|
||||
# nodes specified in your CryptPad `config/infra.js` file. If you change this
|
||||
# setting, don’t forget to update the following list of servers
|
||||
# The following works with config/infra.example.js:
|
||||
upstream fronts {
|
||||
server localhost:3010;
|
||||
server localhost:3011;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
@ -202,7 +213,7 @@ server {
|
||||
# Websocket traffic still needs to be handled by the main process, which means it needs
|
||||
# to be hosted on a different port. By default 3003 will be used, though this is configurable
|
||||
# via config.websocketPort
|
||||
proxy_pass http://localhost:3003;
|
||||
proxy_pass http://fronts;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
@ -76,15 +76,4 @@ server {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection upgrade;
|
||||
}
|
||||
|
||||
location ^~ /cryptpad_websocket {
|
||||
proxy_pass http://localhost:3003;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection upgrade;
|
||||
}
|
||||
}
|
||||
|
||||
830
package-lock.json
generated
830
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,6 @@
|
||||
"@mcrowe/minibloom": "^0.2.0",
|
||||
"@node-saml/node-saml": "^5.1.0",
|
||||
"alertify.js": "1.0.11",
|
||||
"body-parser": "^1.20.4",
|
||||
"bootstrap": "^4.0.0",
|
||||
"bootstrap-tokenfield": "^0.12.0",
|
||||
"chainpad": "^5.3.1",
|
||||
@ -28,10 +27,9 @@
|
||||
"connect-gzip-static": "^4.2.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"croppie": "^2.5.0",
|
||||
"cryptpad-server": "^0.0.1",
|
||||
"cryptpad-server": "^0.0.3",
|
||||
"dragula": "3.7.2",
|
||||
"drawio": "github:cryptpad/drawio-npm#npm-29.6.7+3",
|
||||
"express": "~4.22.1",
|
||||
"file-saver": "1.3.1",
|
||||
"fs-extra": "^7.0.0",
|
||||
"get-folder-size": "^2.0.1",
|
||||
|
||||
@ -7,7 +7,7 @@ var Fse = require("fs-extra");
|
||||
var Path = require("path");
|
||||
var OS = require("os");
|
||||
|
||||
var config = require("../lib/load-config");
|
||||
const { config, infra } = require("../lib/load-config");
|
||||
|
||||
var swap = function (s, o) {
|
||||
return s
|
||||
@ -108,7 +108,7 @@ var imagePath = `/customize/images/opengraph_preview/`;
|
||||
|
||||
var appImagePath = a => {
|
||||
var partial = previewExists(`og-${a}.png`) && `og-${a}.png` || `og-default.png`;
|
||||
return new URL(imagePath + partial, config.httpUnsafeOrigin).href;
|
||||
return new URL(imagePath + partial, infra.public.origin).href;
|
||||
};
|
||||
|
||||
var buildPath = Path.resolve('./customize');
|
||||
@ -218,7 +218,7 @@ appIndexesToBuild.forEach(function (app) {
|
||||
var type = types[app];
|
||||
var built = processPage(src.replace(patt, (current) => {
|
||||
return current + swap(ogData, {
|
||||
url: new URL(`/${app}/`, config.httpUnsafeOrigin).href,
|
||||
url: new URL(`/${app}/`, infra.public.origin).href,
|
||||
title: type && `Encrypted ${type}` || 'CryptPad',
|
||||
image: appImagePath(app),
|
||||
description: Messages.og_default,
|
||||
@ -237,7 +237,7 @@ appIndexesToBuild.forEach(function (app) {
|
||||
|
||||
var instance;
|
||||
try {
|
||||
instance = new URL(config.httpUnsafeOrigin).hostname;
|
||||
instance = new URL(infra.public.origin).hostname;
|
||||
} catch (err) {
|
||||
console.error("Failed to parse instance domain name\nAborting...");
|
||||
return void process.exit(1);
|
||||
@ -284,12 +284,12 @@ try {
|
||||
console.log(`Parsing ${srcPath}`);
|
||||
var src = Fs.readFileSync(srcPath, 'utf8');
|
||||
var patt = /<\/title>/;
|
||||
var href = new URL(obj.url, config.httpUnsafeOrigin).href;
|
||||
var href = new URL(obj.url, infra.public.origin).href;
|
||||
var built = processPage(src.replace(patt, (current) => {
|
||||
return current + swap(ogData, {
|
||||
url: href,
|
||||
title: obj.title || "CryptPad",
|
||||
image: new URL(imagePath + 'og-default.png', config.httpUnsafeOrigin).href,
|
||||
image: new URL(imagePath + 'og-default.png', infra.public.origin).href,
|
||||
description: Messages.og_default,
|
||||
});
|
||||
}));
|
||||
|
||||
@ -1319,10 +1319,10 @@ const factory = (UserObject, ProxyManager,
|
||||
var list = {};
|
||||
var types = query.types;
|
||||
var where = query.where;
|
||||
var filter = query.filter || {};
|
||||
var filter = query.filter || {};
|
||||
var isFiltered = function (type, data) {
|
||||
var filtered;
|
||||
var fType = filter.fileType || [];
|
||||
var fType = filter.fileType || [];
|
||||
if (type === 'file' && fType.length) {
|
||||
if (!data.fileType) { return true; }
|
||||
filtered = !fType.some(function (t) {
|
||||
@ -1353,6 +1353,22 @@ const factory = (UserObject, ProxyManager,
|
||||
cb(list);
|
||||
};
|
||||
|
||||
// Get all teams where a given pad is stored
|
||||
Store.getPadTeams = function (userId, data, cb) {
|
||||
var channel = data && data.channel;
|
||||
if (!channel) { return void cb({error: 'EINVAL'}); }
|
||||
|
||||
var teamIds = [];
|
||||
getAllStores().forEach(function (s) {
|
||||
if (!s.id) { return; } // Skip our own drive, we only want teams here
|
||||
var chans = s.manager.findChannel(channel, true);
|
||||
if (chans && chans.length) {
|
||||
teamIds.push(s.id);
|
||||
}
|
||||
});
|
||||
cb(teamIds);
|
||||
};
|
||||
|
||||
// Get the first pad we can find in any of our drives and return its file data
|
||||
// NOTE: This is currently only used for template: this won't search inside shared folders
|
||||
Store.getPadData = function (clientId, id, cb) {
|
||||
|
||||
@ -45,6 +45,7 @@ const factory = AStore => {
|
||||
LIST_ALL_TAGS: Store.listAllTags,
|
||||
GET_TEMPLATES: Store.getTemplates,
|
||||
GET_SECURE_FILES_LIST: Store.getSecureFilesList,
|
||||
GET_PAD_TEAMS: Store.getPadTeams,
|
||||
GET_PAD_DATA: Store.getPadData,
|
||||
GET_PAD_DATA_FROM_CHANNEL: Store.getPadDataFromChannel,
|
||||
GET_STRONGER_HASH: Store.getStrongerHash,
|
||||
|
||||
@ -161,9 +161,18 @@ const factory = (Util, Hash, UserObject, nThen) => {
|
||||
|
||||
hash = obj[0].hash;
|
||||
var messages = obj.map(function(data) {
|
||||
return data.msg;
|
||||
try {
|
||||
// Remove txid from received messages
|
||||
let parsed = JSON.parse(data.msg);
|
||||
parsed[0] = 0;
|
||||
return JSON.stringify(parsed);
|
||||
} catch (e) {
|
||||
// Invalid message in history: should not happen
|
||||
return data.msg;
|
||||
}
|
||||
});
|
||||
dataSize = messages.join('\n').length;
|
||||
// +1 is there for the last new line
|
||||
dataSize = messages.join('\n').length + 1;
|
||||
}), true);
|
||||
}
|
||||
// Metadata
|
||||
@ -172,7 +181,8 @@ const factory = (Util, Hash, UserObject, nThen) => {
|
||||
}, waitFor(function (obj) {
|
||||
if (obj && obj.error) { return; }
|
||||
if (!obj || typeof(obj) !== "object") { return; }
|
||||
metadata = JSON.stringify(obj).length;
|
||||
// +1 is there for the last new line
|
||||
metadata = JSON.stringify(obj).length + 1;
|
||||
if (!obj || !Array.isArray(obj.owners) ||
|
||||
obj.owners.indexOf(edPublic) === -1) {
|
||||
waitFor.abort();
|
||||
|
||||
@ -147,14 +147,11 @@ define([
|
||||
'active-pads',
|
||||
'open-files',
|
||||
'registered',
|
||||
'disk-usage',
|
||||
]
|
||||
},
|
||||
'performance' : { // Msg.admin_cat_performance
|
||||
icon : 'performance',
|
||||
content : [
|
||||
'refresh-performance',
|
||||
'performance-profiling',
|
||||
'enable-disk-measurements',
|
||||
'bytes-written',
|
||||
]
|
||||
@ -2985,53 +2982,6 @@ define([
|
||||
cb(pre);
|
||||
});
|
||||
|
||||
// Msg.admin_diskUsageHint, .admin_diskUsageTitle, .admin_diskUsageButton
|
||||
sidebar.addItem('disk-usage', function(cb){
|
||||
var button = blocks.button('primary', 'report', Messages.admin_diskUsageButton);
|
||||
var $button = $(button);
|
||||
var called = false;
|
||||
var nav = blocks.nav([button]);
|
||||
var content = blocks.table([], []);
|
||||
var form = blocks.form([
|
||||
content
|
||||
], nav);
|
||||
|
||||
Util.onClickEnter($button, function() {
|
||||
UI.confirm(Messages.admin_diskUsageWarning, function (yes) {
|
||||
if (!yes) { return; }
|
||||
$button.hide();
|
||||
if (called) { return; }
|
||||
called = true;
|
||||
sFrameChan.query('Q_ADMIN_RPC', {
|
||||
cmd: 'DISK_USAGE',
|
||||
}, function (e, data) {
|
||||
if (e) { return void console.error(e); }
|
||||
var obj = data[0];
|
||||
Object.keys(obj).forEach(function (key) {
|
||||
var val = obj[key];
|
||||
var unit = Util.magnitudeOfBytes(val);
|
||||
if (unit === 'GB') {
|
||||
obj[key] = Util.bytesToGigabytes(val) + ' GB';
|
||||
} else if (unit === 'MB') {
|
||||
obj[key] = Util.bytesToMegabytes(val) + ' MB';
|
||||
} else {
|
||||
obj[key] = Util.bytesToKilobytes(val) + ' KB';
|
||||
}
|
||||
});
|
||||
let attr = {'class': 'cp-strong'};
|
||||
let entries = Object.keys(obj).map(function (k) {
|
||||
return [
|
||||
{attr, content: (k === 'total' ? k : '/' + k)},
|
||||
obj[k]
|
||||
];
|
||||
});
|
||||
content.updateContent(entries);
|
||||
});
|
||||
});
|
||||
});
|
||||
cb(form);
|
||||
});
|
||||
|
||||
let onRefreshSupportEvt = Util.mkEvent();
|
||||
let refreshSupport = () => {
|
||||
let moderators, supportKey;
|
||||
@ -3836,67 +3786,6 @@ define([
|
||||
|
||||
});
|
||||
|
||||
var onRefreshPerformance = Util.mkEvent();
|
||||
|
||||
sidebar.addItem('refresh-performance', function(cb){
|
||||
var btn = blocks.button('primary', 'refresh', Messages.oo_refresh);
|
||||
Util.onClickEnter($(btn), function () {
|
||||
onRefreshPerformance.fire();
|
||||
});
|
||||
cb(btn);
|
||||
}, {
|
||||
noTitle: true,
|
||||
noHint: true
|
||||
});
|
||||
|
||||
// Msg.admin_performanceProfilingHint, .admin_performanceProfilingTitle
|
||||
sidebar.addItem('performance-profiling', function(cb){
|
||||
var header = [
|
||||
Messages.admin_performanceKeyHeading,
|
||||
Messages.admin_performanceTimeHeading,
|
||||
Messages.admin_performancePercentHeading
|
||||
];
|
||||
|
||||
var table = blocks.table(header, []);
|
||||
table.setAttribute('id', 'cp-admin-table');
|
||||
let div = blocks.block([table]);
|
||||
div.setAttribute('id', 'cp-admin-table-container');
|
||||
|
||||
const onRefresh = function () {
|
||||
sFrameChan.query('Q_ADMIN_RPC', {
|
||||
cmd: 'GET_WORKER_PROFILES',
|
||||
}, function (e, data) {
|
||||
if (e || data.error) {
|
||||
UI.warn(Messages.error);
|
||||
console.error(e, data);
|
||||
return;
|
||||
}
|
||||
|
||||
var o = data[0];
|
||||
var sorted = Object.keys(o).sort(function (a, b) {
|
||||
if (o[b] - o[a] <= 0) { return -1; }
|
||||
return 1;
|
||||
});
|
||||
|
||||
var total = 0;
|
||||
sorted.forEach(function (key) { total += o[key]; });
|
||||
|
||||
const newRows = sorted.map(function (key) {
|
||||
var percent = Math.floor((o[key] / total) * 1000) / 10;
|
||||
return [key, o[key], percent + '%'];
|
||||
});
|
||||
|
||||
table.updateContent(newRows);
|
||||
});
|
||||
};
|
||||
|
||||
onRefresh();
|
||||
onRefreshPerformance.reg(onRefresh);
|
||||
|
||||
cb(div);
|
||||
});
|
||||
|
||||
|
||||
// Msg.admin_enableDiskMeasurementsTitle.admin_enableDiskMeasurementsHint
|
||||
sidebar.addCheckboxItem({
|
||||
getState: function () {
|
||||
|
||||
@ -522,6 +522,13 @@ define([
|
||||
mkHelpMenu(framework);
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', function (e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'p') {
|
||||
e.preventDefault();
|
||||
$('.cp-toolbar-icon-print').click();
|
||||
}
|
||||
});
|
||||
|
||||
var evModeChange = Util.mkEvent();
|
||||
evModeChange.reg(previewPane.modeChange);
|
||||
evModeChange.reg(markdownTb.modeChange);
|
||||
|
||||
@ -791,6 +791,7 @@ define([
|
||||
message = dialog.message(msg);
|
||||
}
|
||||
|
||||
input.setAttribute('aria-label', message.textContent || message.innerText);
|
||||
var ok = dialog.okButton(opt.ok);
|
||||
var cancel = dialog.cancelButton(opt.cancel);
|
||||
var frame = dialog.frame([
|
||||
|
||||
@ -184,7 +184,7 @@ define([
|
||||
|
||||
el = h('div.cp-usergrid-user'+(data.selected?'.cp-selected':'')+(config.large?'.large':''), {
|
||||
'data-ed': data.edPublic,
|
||||
'data-teamid': data.teamId,
|
||||
'data-teamid': data.id,
|
||||
'data-curve': data.curvePublic || '',
|
||||
'data-name': name.toLowerCase(),
|
||||
'data-order': i,
|
||||
|
||||
@ -1246,6 +1246,11 @@ define([
|
||||
cb(void 0, list);
|
||||
});
|
||||
};
|
||||
common.getPadTeams = function (query, cb) {
|
||||
postMessage("GET_PAD_TEAMS", query, function (teams) {
|
||||
cb(teams);
|
||||
});
|
||||
};
|
||||
// Get a template href from its id
|
||||
common.getPadData = function (id, cb) {
|
||||
postMessage("GET_PAD_DATA", id, function (data) {
|
||||
|
||||
@ -82,13 +82,32 @@ define([
|
||||
}
|
||||
|
||||
if (Object.keys(teams).length) {
|
||||
var teamsList = UIElements.getUserGrid(Messages.share_linkTeam, {
|
||||
common: common,
|
||||
noFilter: true,
|
||||
large: true,
|
||||
data: teams
|
||||
}, refreshButtons);
|
||||
$div.append(teamsList.div);
|
||||
common.getPadTeams({channel: config.channel}, function (err, teamIds) {
|
||||
if (err) { console.error(err); }
|
||||
teamIds = teamIds || [];
|
||||
|
||||
var teamsList = UIElements.getUserGrid(Messages.share_linkTeam, {
|
||||
common: common,
|
||||
noFilter: true,
|
||||
large: true,
|
||||
data: teams
|
||||
}, refreshButtons);
|
||||
|
||||
teamsList.icons.forEach(function (icon) {
|
||||
var inTeam = teamIds.some(function (id) {
|
||||
return String(id) === icon.getAttribute('data-teamid');
|
||||
});
|
||||
if (inTeam) {
|
||||
$(icon).addClass('cp-disabled');
|
||||
$(icon).on('click', function (e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$div.append(teamsList.div);
|
||||
});
|
||||
}
|
||||
|
||||
var shareButton = {
|
||||
|
||||
@ -3001,13 +3001,13 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
}
|
||||
};
|
||||
|
||||
const loadHistoryCp = function(cp, keepQueue) {
|
||||
APP.history = true;
|
||||
APP.stopHistory = false;
|
||||
loadCheckpoint(cp, keepQueue);
|
||||
};
|
||||
|
||||
var loadTemplate = function (href, password, parsed) {
|
||||
var loadHistoryCp = function (cp, keepQueue) {
|
||||
APP.history = true;
|
||||
APP.stopHistory = false;
|
||||
loadCheckpoint(cp, keepQueue);
|
||||
};
|
||||
|
||||
APP.history = true;
|
||||
APP.template = true;
|
||||
var editor = getEditor();
|
||||
@ -3316,7 +3316,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
var histConfig = {
|
||||
onPatch: onPatch,
|
||||
onPatchBack: onPatchBack,
|
||||
docType: docType,
|
||||
docType: () => APP.ooconfig.documentType,
|
||||
loadCp: loadCheckpoint,
|
||||
loadHistoryCp: loadHistoryCp,
|
||||
onCheckpoint: onCheckpoint,
|
||||
|
||||
@ -1618,6 +1618,10 @@ define([
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
sframeChan.on('Q_GET_PAD_TEAMS', function (types, cb) {
|
||||
Cryptpad.getPadTeams(types, cb);
|
||||
});
|
||||
};
|
||||
addCommonRpc(sframeChan, isSafe);
|
||||
|
||||
|
||||
@ -689,6 +689,12 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
funcs.getPadTeams = function (query, cb) {
|
||||
ctx.sframeChan.query('Q_GET_PAD_TEAMS', query, function (err, data) {
|
||||
cb(err || data?.error, data);
|
||||
});
|
||||
};
|
||||
|
||||
funcs.getCache = function () {
|
||||
return ctx.cache;
|
||||
};
|
||||
|
||||
@ -856,16 +856,16 @@ MessengerUI, Messages, Pages, PadTypes, Icons) {
|
||||
|
||||
var createPageTitle = function (toolbar, config) {
|
||||
if (!config.pageTitle) { return; }
|
||||
var $titleContainer = $('<span>', {
|
||||
var $titleContainer = $('<div>', {
|
||||
'class': TITLE_CLS
|
||||
}).appendTo(toolbar.$top);
|
||||
|
||||
toolbar.$top.find('.filler').hide();
|
||||
|
||||
var $hoverable = $('<span>', {'class': 'cp-toolbar-title-hoverable'}).appendTo($titleContainer);
|
||||
var $hoverable = $('<div>', {'class': 'cp-toolbar-title-hoverable'}).appendTo($titleContainer);
|
||||
|
||||
// Buttons
|
||||
var $b = $('<span>', {
|
||||
var $b = $('<h1>', {
|
||||
'class': 'cp-toolbar-title-value cp-toolbar-title-value-page'
|
||||
}).appendTo($hoverable).text(config.pageTitle);
|
||||
|
||||
|
||||
2
www/common/worker.bundle.min.js
vendored
2
www/common/worker.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
@ -213,7 +213,26 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
padding: 20px 20px 150px 20px;
|
||||
.cp-form-view-footer-text {
|
||||
text-align: center;
|
||||
line-height: 180%;
|
||||
margin-bottom: 2rem;
|
||||
svg.cp-form-footer-icon {
|
||||
margin: 0 0.3rem 0.1rem 0;
|
||||
width: 1em;
|
||||
vertical-align: text-bottom;
|
||||
color: @cryptpad_color_link;
|
||||
}
|
||||
a {
|
||||
color: @cryptpad_color_link;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
div.cp-form-view-logo {
|
||||
padding: 10px;
|
||||
font-size: 40px;
|
||||
@ -915,7 +934,7 @@
|
||||
align-items: baseline;
|
||||
border-radius: @variables_radius;
|
||||
margin-top: 0.5rem;
|
||||
padding: 1px;
|
||||
padding: 1px;
|
||||
.CodeMirror {
|
||||
cursor: default;
|
||||
flex: 1;
|
||||
@ -1486,10 +1505,10 @@
|
||||
.cp-form-response-modal {
|
||||
.cp-form-markdown-editor-wrapper {
|
||||
border: 1px solid @cp_forms-border;
|
||||
border-radius: @variables_radius;
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
.CodeMirror {
|
||||
border-radius: 0 0 @variables_radius @variables_radius;
|
||||
border-radius: 0 0 @variables_radius @variables_radius;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1556,3 +1575,15 @@
|
||||
.charts_main();
|
||||
}
|
||||
|
||||
@media print {
|
||||
html,
|
||||
body {
|
||||
height: auto !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.cp-form-creator-container {
|
||||
max-height: none !important;
|
||||
overflow-y: visible !important
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,6 +125,14 @@ define([
|
||||
|
||||
return cancelBlock;
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', function (e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'p') {
|
||||
e.preventDefault();
|
||||
window.print();
|
||||
}
|
||||
});
|
||||
|
||||
var editTextOptions = function (opts, setCursorGetter, cb) {
|
||||
var evOnSave = Util.mkEvent();
|
||||
|
||||
@ -268,7 +276,7 @@ define([
|
||||
},
|
||||
content: Messages['form_poll_'+t]
|
||||
};
|
||||
});
|
||||
});
|
||||
var dropdownConfig = {
|
||||
text: '', // Button initial text
|
||||
options: options, // Entries displayed in the menu
|
||||
@ -312,7 +320,7 @@ define([
|
||||
dateFormat: dateFormat,
|
||||
defaultDate: val ? new Date(val) : undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// if this element was active before the remote change, restore cursor
|
||||
var setCursor = function () {
|
||||
@ -541,7 +549,7 @@ define([
|
||||
$(el).find('input').focus();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
$(add).click();
|
||||
return;
|
||||
} else {
|
||||
@ -557,12 +565,12 @@ define([
|
||||
$(element).find('input').focus();
|
||||
} else {
|
||||
$container.find('input')[i].value = selectedDates[i].toLocaleDateString();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ($container.find('input')[0]) {
|
||||
$container.find('input')[0].value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$container.find('input').each(function (i, input) {
|
||||
if (input._flatpickr) {
|
||||
@ -1191,7 +1199,7 @@ define([
|
||||
toggleRow,
|
||||
block
|
||||
]);
|
||||
|
||||
|
||||
return [
|
||||
outerWrapper,
|
||||
cancelBlock
|
||||
@ -1884,10 +1892,10 @@ define([
|
||||
});
|
||||
return res;
|
||||
},
|
||||
reset: function () {
|
||||
reset: function () {
|
||||
$(tag).find('input').each(function (i, input) {
|
||||
var $i = $(input);
|
||||
if (Util.isChecked($i)) {
|
||||
if (Util.isChecked($i)) {
|
||||
$i.prop('checked', false);
|
||||
}
|
||||
});
|
||||
@ -2020,10 +2028,10 @@ define([
|
||||
});
|
||||
return res;
|
||||
},
|
||||
reset: function () {
|
||||
reset: function () {
|
||||
$(tag).find('input').each(function (i, input) {
|
||||
var $i = $(input);
|
||||
if (Util.isChecked($i)) {
|
||||
if (Util.isChecked($i)) {
|
||||
$i.prop('checked', false);
|
||||
}
|
||||
});
|
||||
@ -2254,7 +2262,7 @@ define([
|
||||
reset: function () {
|
||||
$(tag).find('input').each(function (i, input) {
|
||||
var $i = $(input);
|
||||
if (Util.isChecked($i)) {
|
||||
if (Util.isChecked($i)) {
|
||||
$i.prop('checked', false);
|
||||
}
|
||||
});
|
||||
@ -2408,7 +2416,7 @@ define([
|
||||
lines.forEach(checkDisabled);
|
||||
$(tag).find('input').each(function (i, input) {
|
||||
var $i = $(input);
|
||||
if (Util.isChecked($i)) {
|
||||
if (Util.isChecked($i)) {
|
||||
$i.prop('checked', false);
|
||||
}
|
||||
});
|
||||
@ -2754,7 +2762,7 @@ define([
|
||||
tag: tag,
|
||||
isEmpty: function () {
|
||||
var v = this.getValue();
|
||||
if (!v || !v.values) { return true; }
|
||||
if (!v || !v.values) { return true; }
|
||||
// Poll is considered empty if user hasn't interacted with it
|
||||
return !hasInteracted;
|
||||
},
|
||||
@ -3348,7 +3356,9 @@ define([
|
||||
$(logo).click(function () {
|
||||
APP.framework._.sfCommon.gotoURL('/');
|
||||
});
|
||||
var footer = h('div.cp-form-view-footer', [logo]);
|
||||
var content = UI.setHTML(h('div.cp-form-view-footer-text'), Messages.form_footerInfo);
|
||||
content.insertBefore(Icons.get('lock', {'class': 'cp-form-footer-icon', 'aria-hidden': true}), content.firstChild);
|
||||
var footer = h('div.cp-form-view-footer', [content, logo]);
|
||||
return footer;
|
||||
};
|
||||
|
||||
@ -3424,7 +3434,7 @@ define([
|
||||
});
|
||||
sorted.forEach(function (uid) {
|
||||
var answer = answers[uid];
|
||||
|
||||
|
||||
var viewOnly = content.answers.cantEdit || APP.isClosed;
|
||||
|
||||
var action = h(viewOnly ? 'button.btn.btn-secondary' : 'button.btn.btn-primary', [
|
||||
@ -4309,7 +4319,7 @@ define([
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
content.form[_uid] = {
|
||||
q: content.form[uid].q,
|
||||
opts: opts,
|
||||
@ -4512,7 +4522,7 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
var shownContent = [];
|
||||
var shownPages = [];
|
||||
pgcontent.forEach(function(page) {
|
||||
@ -4529,7 +4539,7 @@ define([
|
||||
};
|
||||
|
||||
var pageContainer = h('div.cp-form-page-container');
|
||||
|
||||
|
||||
var $page = $(pageContainer);
|
||||
_content.push(pageContainer);
|
||||
var refreshPage = APP.refreshPage = function (current, direction) {
|
||||
@ -4587,7 +4597,7 @@ define([
|
||||
var shownContent = checkPages[0];
|
||||
var shownPages = checkPages[1];
|
||||
$(state).text(Messages._getKey('form_page', [shownPages.indexOf(_content[current-1])+1, shownContent.length]));
|
||||
|
||||
|
||||
});
|
||||
|
||||
if (shownPages.indexOf(_content[current-1])+1 === shownContent.length) { $(right).css('visibility', 'hidden'); }
|
||||
@ -5140,7 +5150,7 @@ define([
|
||||
var endDateStr = h('div.cp-form-status');
|
||||
var $endDate = $(endDateContainer);
|
||||
var $endDateStr = $(endDateStr);
|
||||
|
||||
|
||||
var refreshEndDate = function () {
|
||||
$endDate.empty();
|
||||
|
||||
@ -5379,7 +5389,7 @@ define([
|
||||
if (state) {
|
||||
$container.removeClass('cp-no-drag');
|
||||
} else {
|
||||
$container.addClass('cp-no-drag');
|
||||
$container.addClass('cp-no-drag');
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -5779,7 +5789,7 @@ define([
|
||||
]);
|
||||
var editButtons = h('div.cp-form-edit-buttons-container', [ preview, edit, del ]);
|
||||
|
||||
var toggleRow = h('div.cp-markdown-toggle-row');
|
||||
var toggleRow = h('div.cp-markdown-toggle-row');
|
||||
var markdownWrapper = h('div.cp-form-markdown-editor-wrapper', t);
|
||||
var editDiv, previewDiv;
|
||||
var div = h('div.cp-form-block.editable.nodrag.cp-form-submit-message', [
|
||||
|
||||
@ -430,10 +430,16 @@ define([
|
||||
]);
|
||||
UI.openCustomModal(UI.dialog.customModal(div, {buttons: []}));
|
||||
console.log('Trimming team history', APP.team, size);
|
||||
const removeModalsTimeout = setTimeout(() => {
|
||||
UI.removeModals();
|
||||
Feedback.send(`TRIM_HISTORY_TIMEOUT=${channels.map(obj => obj?.channel).join('|')}`, true);
|
||||
UI.warn(Messages.trimHistory_error);
|
||||
}, 120000);
|
||||
APP.history.execCommand('TRIM_HISTORY', {
|
||||
channels: channels
|
||||
}, function(obj) {
|
||||
if (obj && obj.error) { console.error(obj.error); }
|
||||
clearTimeout(removeModalsTimeout);
|
||||
UI.removeModals();
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user