2021-11-25 01:43:46 -05:00
|
|
|
'use strict';
|
|
|
|
|
2014-12-29 21:13:49 +01:00
|
|
|
/**
|
2014-12-30 00:12:26 +01:00
|
|
|
* 2014 John McLear (Etherpad Foundation / McLear Ltd)
|
2014-12-29 21:13:49 +01:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2021-11-27 18:04:26 -05:00
|
|
|
const AttributePool = require('../../static/js/AttributePool');
|
2021-11-27 17:49:25 -05:00
|
|
|
const {Pad} = require('../db/Pad');
|
2021-11-26 01:48:42 -05:00
|
|
|
const async = require('async');
|
2021-11-25 01:43:46 -05:00
|
|
|
const authorManager = require('../db/AuthorManager');
|
2020-11-23 13:24:19 -05:00
|
|
|
const db = require('../db/DB');
|
2021-01-21 21:06:52 +00:00
|
|
|
const hooks = require('../../static/js/pluginfw/hooks');
|
2021-03-29 20:06:33 -04:00
|
|
|
const log4js = require('log4js');
|
2021-03-02 17:14:47 +00:00
|
|
|
const supportedElems = require('../../static/js/contentcollector').supportedElems;
|
2022-04-19 16:23:56 -04:00
|
|
|
const ueberdb = require('ueberdb2');
|
2014-12-29 21:13:49 +01:00
|
|
|
|
2021-03-29 20:06:33 -04:00
|
|
|
const logger = log4js.getLogger('ImportEtherpad');
|
|
|
|
|
2022-02-17 00:01:07 -05:00
|
|
|
exports.setPadRaw = async (padId, r, authorId = '') => {
|
2021-01-21 21:06:52 +00:00
|
|
|
const records = JSON.parse(r);
|
2014-12-30 00:01:15 +01:00
|
|
|
|
2021-02-21 11:07:13 +00:00
|
|
|
// get supported block Elements from plugins, we will use this later.
|
|
|
|
hooks.callAll('ccRegisterBlockElements').forEach((element) => {
|
2021-03-29 19:40:03 -04:00
|
|
|
supportedElems.add(element);
|
2021-02-21 11:07:13 +00:00
|
|
|
});
|
|
|
|
|
2021-11-25 01:19:00 -05:00
|
|
|
// DB key prefixes for pad records. Each key is expected to have the form `${prefix}:${padId}` or
|
|
|
|
// `${prefix}:${padId}:${otherstuff}`.
|
|
|
|
const padKeyPrefixes = [
|
|
|
|
...await hooks.aCallAll('exportEtherpadAdditionalContent'),
|
|
|
|
'pad',
|
|
|
|
];
|
|
|
|
|
2021-11-25 18:14:38 -05:00
|
|
|
let originalPadId = null;
|
|
|
|
const checkOriginalPadId = (padId) => {
|
|
|
|
if (originalPadId == null) originalPadId = padId;
|
|
|
|
if (originalPadId !== padId) throw new Error('unexpected pad ID in record');
|
|
|
|
};
|
|
|
|
|
2021-11-26 01:48:42 -05:00
|
|
|
// Limit the number of in-flight database queries so that the queries do not time out when
|
|
|
|
// importing really large files.
|
|
|
|
const q = async.queue(async (task) => await task(), 100);
|
|
|
|
|
2021-11-26 01:35:02 -05:00
|
|
|
// First validate and transform values. Do not commit any records to the database yet in case
|
|
|
|
// there is a problem with the data.
|
|
|
|
|
2022-04-19 16:23:56 -04:00
|
|
|
const data = new Map();
|
2021-11-26 01:35:02 -05:00
|
|
|
const existingAuthors = new Set();
|
2022-04-19 16:23:56 -04:00
|
|
|
const padDb = new ueberdb.Database('memory', {data});
|
|
|
|
await padDb.init();
|
|
|
|
try {
|
|
|
|
await Promise.all(Object.entries(records).map(([key, value]) => q.pushAsync(async () => {
|
|
|
|
if (!value) return;
|
|
|
|
const keyParts = key.split(':');
|
|
|
|
const [prefix, id] = keyParts;
|
|
|
|
if (prefix === 'globalAuthor' && keyParts.length === 2) {
|
|
|
|
// In the database, the padIDs subkey is an object (which is used as a set) that records
|
|
|
|
// every pad the author has worked on. When exported, that object becomes a single string
|
|
|
|
// containing the exported pad's ID.
|
|
|
|
if (typeof value.padIDs !== 'string') {
|
|
|
|
throw new TypeError('globalAuthor padIDs subkey is not a string');
|
|
|
|
}
|
|
|
|
checkOriginalPadId(value.padIDs);
|
|
|
|
if (await authorManager.doesAuthorExist(id)) {
|
|
|
|
existingAuthors.add(id);
|
|
|
|
return;
|
2021-11-25 01:13:31 -05:00
|
|
|
}
|
2022-04-19 16:23:56 -04:00
|
|
|
value.padIDs = {[padId]: 1};
|
|
|
|
} else if (padKeyPrefixes.includes(prefix)) {
|
|
|
|
checkOriginalPadId(id);
|
|
|
|
if (prefix === 'pad' && keyParts.length === 2) {
|
|
|
|
const pool = new AttributePool().fromJsonable(value.pool);
|
|
|
|
const unsupportedElements = new Set();
|
|
|
|
pool.eachAttrib((k, v) => {
|
|
|
|
if (!supportedElems.has(k)) unsupportedElements.add(k);
|
|
|
|
});
|
|
|
|
if (unsupportedElements.size) {
|
|
|
|
logger.warn(`(pad ${padId}) unsupported attributes (try installing a plugin): ` +
|
|
|
|
`${[...unsupportedElements].join(', ')}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
keyParts[1] = padId;
|
|
|
|
key = keyParts.join(':');
|
|
|
|
} else {
|
|
|
|
logger.warn(`(pad ${padId}) Ignoring record with unsupported key: ${key}`);
|
|
|
|
return;
|
2021-02-21 11:07:13 +00:00
|
|
|
}
|
2022-04-19 16:23:56 -04:00
|
|
|
await padDb.set(key, value);
|
|
|
|
})));
|
2021-03-29 20:06:33 -04:00
|
|
|
|
2022-04-19 16:23:56 -04:00
|
|
|
const pad = new Pad(padId, padDb);
|
|
|
|
await pad.init(null, authorId);
|
|
|
|
await pad.check();
|
|
|
|
} finally {
|
|
|
|
await padDb.close();
|
|
|
|
}
|
2021-11-27 17:49:25 -05:00
|
|
|
|
2021-11-26 01:35:02 -05:00
|
|
|
await Promise.all([
|
2022-04-19 16:23:56 -04:00
|
|
|
...[...data].map(([k, v]) => q.pushAsync(() => db.set(k, v))),
|
2021-11-26 01:48:42 -05:00
|
|
|
...[...existingAuthors].map((a) => q.pushAsync(() => authorManager.addPad(a, padId))),
|
2021-11-26 01:35:02 -05:00
|
|
|
]);
|
2020-11-23 13:24:19 -05:00
|
|
|
};
|