ep_comments_page 0.1.81 → 0.1.85
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/oc.json +15 -0
- package/package.json +10 -9
- package/static/js/commentBoxes.js +0 -8
- 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/oc.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@metadata": {
|
|
3
|
+
"authors": [
|
|
4
|
+
"Quentí"
|
|
5
|
+
]
|
|
6
|
+
},
|
|
7
|
+
"ep_comments_page.comment": "Comentari",
|
|
8
|
+
"ep_comments_page.comments": "Comentaris",
|
|
9
|
+
"ep_comments_page.add_comment.title": "Apondre un comentari novèl sus la seleccion",
|
|
10
|
+
"ep_comments_page.comments_template.accept_change.value": "Acceptar la modificacion",
|
|
11
|
+
"ep_comments_page.comments_template.cancel.value": "Anullar",
|
|
12
|
+
"ep_comments_page.comments_template.reply.value": "Respondre",
|
|
13
|
+
"ep_comments_page.comments_template.edit_comment.save": "enregistrar",
|
|
14
|
+
"ep_comments_page.comments_template.edit_comment.cancel": "anullar"
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"description": "Adds comments on sidebar and link it to the text. For no-skin use ep_page_view.",
|
|
3
3
|
"name": "ep_comments_page",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.85",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nicolas Lescop",
|
|
7
7
|
"email": "limplementeur@gmail.com"
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"underscore": "^1.13.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"eslint": "^7.
|
|
31
|
-
"eslint-config-etherpad": "^2.0.
|
|
32
|
-
"eslint-plugin-cypress": "^2.
|
|
30
|
+
"eslint": "^7.32.0",
|
|
31
|
+
"eslint-config-etherpad": "^2.0.2",
|
|
32
|
+
"eslint-plugin-cypress": "^2.12.1",
|
|
33
33
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
34
34
|
"eslint-plugin-mocha": "^9.0.0",
|
|
35
35
|
"eslint-plugin-node": "^11.1.0",
|
|
36
36
|
"eslint-plugin-prefer-arrow": "^1.2.3",
|
|
37
|
-
"eslint-plugin-promise": "^5.1.
|
|
38
|
-
"eslint-plugin-you-dont-need-lodash-underscore": "^6.
|
|
37
|
+
"eslint-plugin-promise": "^5.1.1",
|
|
38
|
+
"eslint-plugin-you-dont-need-lodash-underscore": "^6.12.0",
|
|
39
39
|
"socket.io-client": "^2.3.0",
|
|
40
40
|
"superagent": "^6.1.0"
|
|
41
41
|
},
|
|
@@ -48,10 +48,11 @@
|
|
|
48
48
|
]
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
|
-
"lint": "eslint ."
|
|
51
|
+
"lint": "eslint .",
|
|
52
|
+
"lint:fix": "eslint --fix ."
|
|
52
53
|
},
|
|
53
54
|
"engines": {
|
|
54
|
-
"node": ">=
|
|
55
|
+
"node": ">=12.13.0"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
57
58
|
"ep_etherpad-lite": ">=1.8.6"
|
|
@@ -62,6 +63,6 @@
|
|
|
62
63
|
},
|
|
63
64
|
"funding": {
|
|
64
65
|
"type": "individual",
|
|
65
|
-
"url": "
|
|
66
|
+
"url": "https://etherpad.org/"
|
|
66
67
|
}
|
|
67
68
|
}
|
|
@@ -11,13 +11,6 @@ const getCommentsContainer = () => getPadOuter().find('#comments');
|
|
|
11
11
|
|
|
12
12
|
/* ***** Public methods: ***** */
|
|
13
13
|
|
|
14
|
-
const showComment = (commentId, e) => {
|
|
15
|
-
const commentElm = getCommentsContainer().find(`#${commentId}`);
|
|
16
|
-
commentElm.show();
|
|
17
|
-
|
|
18
|
-
highlightComment(commentId, e);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
14
|
const hideComment = (commentId, hideCommentTitle) => {
|
|
22
15
|
const commentElm = getCommentsContainer().find(`#${commentId}`);
|
|
23
16
|
commentElm.removeClass('full-display');
|
|
@@ -122,7 +115,6 @@ const shouldNotCloseComment = (e) => {
|
|
|
122
115
|
return false;
|
|
123
116
|
};
|
|
124
117
|
|
|
125
|
-
exports.showComment = showComment;
|
|
126
118
|
exports.hideComment = hideComment;
|
|
127
119
|
exports.hideAllComments = hideAllComments;
|
|
128
120
|
exports.highlightComment = highlightComment;
|
|
@@ -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
|
+
});
|