mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-04-16 11:01:39 +02:00
fix: appendText API now attributes text to the specified author (#7446)
* fix: appendText API now attributes text to the specified author spliceText() was calling makeSplice() without passing author attributes, so inserted text had no authorship attribution in the changeset — even though the authorId was recorded in the revision metadata. Now passes [['author', authorId]] and the pool to makeSplice() so the changeset ops carry the author attribute, making the text show the author's color in the editor and appear in listAuthorsOfPad. Also fixed the same issue in pad init (first changeset creation) and updated PadType interface to include the authorId parameter. Fixes #6873 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: assert API response code on createPad and anonymous appendText Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
928eef8978
commit
f7e4100aba
@ -294,7 +294,8 @@ class Pad {
|
||||
(!ins && start > 0 && orig[start - 1] === '\n');
|
||||
if (!willEndWithNewline) ins += '\n';
|
||||
if (ndel === 0 && ins.length === 0) return;
|
||||
const changeset = makeSplice(orig, start, ndel, ins);
|
||||
const attribs = authorId ? [['author', authorId] as [string, string]] : undefined;
|
||||
const changeset = makeSplice(orig, start, ndel, ins, attribs, this.pool);
|
||||
await this.appendRevision(changeset, authorId);
|
||||
}
|
||||
|
||||
@ -394,7 +395,8 @@ class Pad {
|
||||
if (context.type !== 'text') throw new Error(`unsupported content type: ${context.type}`);
|
||||
text = exports.cleanText(context.content);
|
||||
}
|
||||
const firstChangeset = makeSplice('\n', 0, 0, text);
|
||||
const firstAttribs = authorId ? [['author', authorId] as [string, string]] : undefined;
|
||||
const firstChangeset = makeSplice('\n', 0, 0, text, firstAttribs, this.pool);
|
||||
await this.appendRevision(firstChangeset, authorId);
|
||||
}
|
||||
await hooks.aCallAll('padLoad', {pad: this});
|
||||
|
||||
@ -15,7 +15,7 @@ export type PadType = {
|
||||
remove: ()=>Promise<void>,
|
||||
text: ()=>string,
|
||||
setText: (text: string, authorId?: string)=>Promise<void>,
|
||||
appendText: (text: string)=>Promise<void>,
|
||||
appendText: (text: string, authorId?: string)=>Promise<void>,
|
||||
getHeadRevisionNumber: ()=>number,
|
||||
getRevisionDate: (rev: number)=>Promise<number>,
|
||||
getRevisionChangeset: (rev: number)=>Promise<AChangeSet>,
|
||||
|
||||
96
src/tests/backend/specs/api/appendTextAuthor.ts
Normal file
96
src/tests/backend/specs/api/appendTextAuthor.ts
Normal file
@ -0,0 +1,96 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert').strict;
|
||||
const common = require('../../common');
|
||||
|
||||
let agent: any;
|
||||
let apiVersion = 1;
|
||||
const testPadId = `appendTextAuthor_${makeid()}`;
|
||||
|
||||
const endPoint = (point: string) => `/api/${apiVersion}/${point}`;
|
||||
|
||||
function makeid() {
|
||||
let text = '';
|
||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
for (let i = 0; i < 10; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
describe(__filename, function () {
|
||||
let authorId: string;
|
||||
|
||||
before(async function () {
|
||||
agent = await common.init();
|
||||
const res = await agent.get('/api/')
|
||||
.expect(200)
|
||||
.expect('Content-Type', /json/);
|
||||
apiVersion = res.body.currentVersion;
|
||||
assert(apiVersion);
|
||||
|
||||
// Create an author
|
||||
const authorRes = await agent.get(`${endPoint('createAuthor')}?name=TestAuthor`)
|
||||
.set('Authorization', await common.generateJWTToken())
|
||||
.expect(200);
|
||||
assert.equal(authorRes.body.code, 0);
|
||||
authorId = authorRes.body.data.authorID;
|
||||
assert(authorId);
|
||||
|
||||
// Create a pad
|
||||
await agent.get(`${endPoint('createPad')}?padID=${testPadId}`)
|
||||
.set('Authorization', await common.generateJWTToken())
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('appendText with authorId attributes the text to that author', async function () {
|
||||
// Append text with an authorId
|
||||
const res = await agent.post(endPoint('appendText'))
|
||||
.set('Authorization', await common.generateJWTToken())
|
||||
.send({padID: testPadId, text: 'authored text', authorId})
|
||||
.expect(200)
|
||||
.expect('Content-Type', /json/);
|
||||
assert.equal(res.body.code, 0);
|
||||
|
||||
// Verify the author appears in the pad's author list
|
||||
const authorsRes = await agent.get(
|
||||
`${endPoint('listAuthorsOfPad')}?padID=${testPadId}`)
|
||||
.set('Authorization', await common.generateJWTToken())
|
||||
.expect(200);
|
||||
assert.equal(authorsRes.body.code, 0);
|
||||
assert(authorsRes.body.data.authorIDs.includes(authorId),
|
||||
`Expected authorId ${authorId} in pad authors: ${authorsRes.body.data.authorIDs}`);
|
||||
});
|
||||
|
||||
it('appendText without authorId does not attribute to any author', async function () {
|
||||
const newPadId = `appendTextNoAuthor_${makeid()}`;
|
||||
const createRes = await agent.get(`${endPoint('createPad')}?padID=${newPadId}`)
|
||||
.set('Authorization', await common.generateJWTToken())
|
||||
.expect(200);
|
||||
assert.equal(createRes.body.code, 0);
|
||||
|
||||
const appendRes = await agent.post(endPoint('appendText'))
|
||||
.set('Authorization', await common.generateJWTToken())
|
||||
.send({padID: newPadId, text: 'anonymous text'})
|
||||
.expect(200);
|
||||
assert.equal(appendRes.body.code, 0);
|
||||
|
||||
const authorsRes = await agent.get(
|
||||
`${endPoint('listAuthorsOfPad')}?padID=${newPadId}`)
|
||||
.set('Authorization', await common.generateJWTToken())
|
||||
.expect(200);
|
||||
assert.equal(authorsRes.body.code, 0);
|
||||
// No authors should be listed for anonymous text
|
||||
assert.equal(authorsRes.body.data.authorIDs.length, 0);
|
||||
|
||||
await agent.get(`${endPoint('deletePad')}?padID=${newPadId}`)
|
||||
.set('Authorization', await common.generateJWTToken())
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await agent.get(`${endPoint('deletePad')}?padID=${testPadId}`)
|
||||
.set('Authorization', await common.generateJWTToken())
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user