ep_comments_page 0.1.84 → 0.1.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +27 -6
- package/locales/diq.json +14 -0
- package/locales/tr.json +3 -0
- package/package.json +1 -1
- package/static/tests/backend/specs/readOnlyPad.js +134 -0
package/index.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const AttributePool = require('ep_etherpad-lite/static/js/AttributePool');
|
|
4
|
+
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
|
3
5
|
const eejs = require('ep_etherpad-lite/node/eejs/');
|
|
4
6
|
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
|
5
7
|
const formidable = require('formidable');
|
|
6
8
|
const commentManager = require('./commentManager');
|
|
7
9
|
const apiUtils = require('./apiUtils');
|
|
8
10
|
const _ = require('underscore');
|
|
11
|
+
const padMessageHandler = require('ep_etherpad-lite/node/handler/PadMessageHandler');
|
|
9
12
|
const readOnlyManager = require('ep_etherpad-lite/node/db/ReadOnlyManager.js');
|
|
10
13
|
|
|
11
14
|
let io;
|
|
@@ -26,13 +29,31 @@ exports.padCopy = async (hookName, context) => {
|
|
|
26
29
|
]);
|
|
27
30
|
};
|
|
28
31
|
|
|
29
|
-
exports.handleMessageSecurity = (hookName,
|
|
30
|
-
const {
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
exports.handleMessageSecurity = async (hookName, {message, client: socket}) => {
|
|
33
|
+
const {type: mtype, data: {type: dtype, apool, changeset} = {}} = message;
|
|
34
|
+
if (mtype !== 'COLLABROOM') return;
|
|
35
|
+
if (dtype !== 'USER_CHANGES') return;
|
|
36
|
+
// Nothing needs to be done if the user already has write access.
|
|
37
|
+
if (!padMessageHandler.sessioninfos[socket.id].readonly) return;
|
|
38
|
+
const pool = new AttributePool().fromJsonable(apool);
|
|
39
|
+
const cs = Changeset.unpack(changeset);
|
|
40
|
+
const opIter = Changeset.opIterator(cs.ops);
|
|
41
|
+
while (opIter.hasNext()) {
|
|
42
|
+
const op = opIter.next();
|
|
43
|
+
// Only operations that manipulate the 'comment' attribute on existing text are allowed.
|
|
44
|
+
if (op.opcode !== '=') return;
|
|
45
|
+
const forbiddenAttrib = new Error();
|
|
46
|
+
try {
|
|
47
|
+
Changeset.eachAttribNumber(op.attribs, (n) => {
|
|
48
|
+
// Use an exception to break out of the iteration early.
|
|
49
|
+
if (pool.getAttribKey(n) !== 'comment') throw forbiddenAttrib;
|
|
50
|
+
});
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err !== forbiddenAttrib) throw err;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
34
55
|
}
|
|
35
|
-
return
|
|
56
|
+
return true;
|
|
36
57
|
};
|
|
37
58
|
|
|
38
59
|
exports.socketio = (hookName, args, cb) => {
|
package/locales/diq.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@metadata": {
|
|
3
|
+
"authors": [
|
|
4
|
+
"Mirzali"
|
|
5
|
+
]
|
|
6
|
+
},
|
|
7
|
+
"ep_comments_page.comment": "Mışewre",
|
|
8
|
+
"ep_comments_page.comments_template.comment.value": "Mışewre",
|
|
9
|
+
"ep_comments_page.comments_template.cancel.value": "Bıtexelne",
|
|
10
|
+
"ep_comments_page.comments_template.reply.value": "Cewab bıde",
|
|
11
|
+
"ep_comments_page.comments_template.reply.placeholder": "Cewab bıde",
|
|
12
|
+
"ep_comments_page.comments_template.edit_comment.save": "qeyd ke",
|
|
13
|
+
"ep_comments_page.comments_template.edit_comment.cancel": "bıtexelne"
|
|
14
|
+
}
|
package/locales/tr.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@metadata": {
|
|
3
3
|
"authors": [
|
|
4
|
+
"Can",
|
|
4
5
|
"Erdemkose",
|
|
5
6
|
"Hedda"
|
|
6
7
|
]
|
|
@@ -17,6 +18,8 @@
|
|
|
17
18
|
"ep_comments_page.comments_template.from": "Gönderen",
|
|
18
19
|
"ep_comments_page.comments_template.accept_change.value": "Değişikliği Kabul Et",
|
|
19
20
|
"ep_comments_page.comments_template.revert_change.value": "Değişikliği Geri Al",
|
|
21
|
+
"ep_comments_page.comments_template.suggested_change_from": "\"{{changeFrom}}\"'dan/den, {{changeTo}}'ye/ya önerilen değişiklik",
|
|
22
|
+
"ep_comments_page.comments_template.suggest_change_from": "\"{{changeFrom}}\"'den/dan şuraya önerilen değişiklik",
|
|
20
23
|
"ep_comments_page.comments_template.to": "Alıcı",
|
|
21
24
|
"ep_comments_page.comments_template.include_suggestion": "Önerilen değişikliği dahil et",
|
|
22
25
|
"ep_comments_page.comments_template.comment.value": "Yorum",
|
package/package.json
CHANGED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const AttributePool = require('ep_etherpad-lite/static/js/AttributePool');
|
|
4
|
+
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
|
5
|
+
const assert = require('assert').strict;
|
|
6
|
+
const common = require('ep_etherpad-lite/tests/backend/common');
|
|
7
|
+
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
|
8
|
+
const readOnlyManager = require('ep_etherpad-lite/node/db/ReadOnlyManager');
|
|
9
|
+
const shared = require('../../../js/shared.js');
|
|
10
|
+
|
|
11
|
+
describe(__filename, function () {
|
|
12
|
+
let agent;
|
|
13
|
+
let pad;
|
|
14
|
+
let padId;
|
|
15
|
+
let roPadId;
|
|
16
|
+
let socket;
|
|
17
|
+
|
|
18
|
+
const makeUserChanges = (opcode, attribs) => {
|
|
19
|
+
const oldLen = pad.text().length;
|
|
20
|
+
assert(oldLen > 0);
|
|
21
|
+
const apool = new AttributePool();
|
|
22
|
+
const op = Changeset.newOp(opcode);
|
|
23
|
+
op.chars = 1;
|
|
24
|
+
op.attribs = Changeset.makeAttribsString(opcode, attribs, apool);
|
|
25
|
+
const assem = Changeset.smartOpAssembler();
|
|
26
|
+
assem.append(op);
|
|
27
|
+
const cs = assem.toString();
|
|
28
|
+
const newLen = oldLen + assem.getLengthChange();
|
|
29
|
+
const changeset = Changeset.pack(oldLen, newLen, cs, opcode === '+' ? 'x' : '');
|
|
30
|
+
return {baseRev: pad.head, changeset, apool: apool.toJsonable()};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
before(async function () {
|
|
34
|
+
agent = await common.init();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
beforeEach(async function () {
|
|
38
|
+
padId = `testpad${common.randomString()}`;
|
|
39
|
+
assert(!await padManager.doesPadExist(padId));
|
|
40
|
+
pad = await padManager.getPad(padId, 'text');
|
|
41
|
+
assert(pad.text().startsWith('text'));
|
|
42
|
+
roPadId = await readOnlyManager.getReadOnlyId(padId);
|
|
43
|
+
const res = await agent.get(`/p/${roPadId}`).expect(200);
|
|
44
|
+
socket = await common.connect(res);
|
|
45
|
+
const {type, data: clientVars} = await common.handshake(socket, roPadId);
|
|
46
|
+
assert.equal(type, 'CLIENT_VARS');
|
|
47
|
+
assert(clientVars.readonly);
|
|
48
|
+
assert.equal(clientVars.readOnlyId, roPadId);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterEach(async function () {
|
|
52
|
+
if (socket != null) socket.close();
|
|
53
|
+
socket = null;
|
|
54
|
+
if (pad != null) await pad.remove();
|
|
55
|
+
pad = null;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('comment-only changes are accepted', function () {
|
|
59
|
+
it('add/change comment attribute', async function () {
|
|
60
|
+
await Promise.all([
|
|
61
|
+
common.waitForAcceptCommit(socket, pad.head + 1),
|
|
62
|
+
common.sendUserChanges(
|
|
63
|
+
socket, makeUserChanges('=', [['comment', shared.generateCommentId()]])),
|
|
64
|
+
]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('remove comment attribute', async function () {
|
|
68
|
+
await Promise.all([
|
|
69
|
+
common.waitForAcceptCommit(socket, pad.head + 1),
|
|
70
|
+
common.sendUserChanges(
|
|
71
|
+
socket, makeUserChanges('=', [['comment', shared.generateCommentId()]])),
|
|
72
|
+
]);
|
|
73
|
+
await Promise.all([
|
|
74
|
+
common.waitForAcceptCommit(socket, pad.head + 1),
|
|
75
|
+
common.sendUserChanges(socket, makeUserChanges('=', [['comment', '']])),
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('other changes are rejected', function () {
|
|
81
|
+
const testCases = [
|
|
82
|
+
{
|
|
83
|
+
desc: 'keep with non-comment attrib add/change',
|
|
84
|
+
opcode: '=',
|
|
85
|
+
attribs: [['bold', 'true']],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
desc: 'keep with non-comment attrib removal',
|
|
89
|
+
opcode: '=',
|
|
90
|
+
attribs: [['bold', '']],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
desc: 'keep with comment and non-comment attrib adds/changes',
|
|
94
|
+
opcode: '=',
|
|
95
|
+
attribs: [['comment', shared.generateCommentId()], ['bold', 'true']],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
desc: 'insert with no attribs',
|
|
99
|
+
opcode: '+',
|
|
100
|
+
attribs: [],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
desc: 'insert with comment attrib',
|
|
104
|
+
opcode: '+',
|
|
105
|
+
attribs: [['comment', shared.generateCommentId()]],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
desc: 'insert with non-comment attrib',
|
|
109
|
+
opcode: '+',
|
|
110
|
+
attribs: [['bold', 'true']],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
desc: 'insert with comment and non-comment attribs',
|
|
114
|
+
opcode: '+',
|
|
115
|
+
attribs: [['comment', shared.generateCommentId()], ['bold', 'true']],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
desc: 'remove',
|
|
119
|
+
opcode: '-',
|
|
120
|
+
attribs: [],
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
for (const {desc, opcode, attribs} of testCases) {
|
|
125
|
+
it(desc, async function () {
|
|
126
|
+
const head = pad.head;
|
|
127
|
+
await common.sendUserChanges(socket, makeUserChanges(opcode, attribs));
|
|
128
|
+
// common.sendUserChanges() waits for message ack, so if the message was accepted then head
|
|
129
|
+
// should have already incremented by the time we get here.
|
|
130
|
+
assert.equal(pad.head, head);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|