Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion browser/src/canvas/sections/CommentSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export class Comment extends CanvasSectionObject {
this.sectionProperties.acceptButton = null;
this.sectionProperties.rejectButton = null;
this.sectionProperties.menu = null;
this.sectionProperties.menuBarCell = null;
this.sectionProperties.captionNode = null;
this.sectionProperties.captionText = null;

Expand Down Expand Up @@ -228,7 +229,11 @@ export class Comment extends CanvasSectionObject {
this.createTrackChangeButtons();
}

if (this.sectionProperties.noMenu !== true && app.isCommentEditingAllowed()) {
// Always create the menu if allowed; its visibility is kept in sync with
// the current edit permission by updateEditability() so the Viewing/
// Editing toggle can show or hide the Edit/Reply/Delete affordances
// without re-rendering the comment.
if (this.sectionProperties.noMenu !== true) {
this.createMenu();
}

Expand Down Expand Up @@ -273,6 +278,35 @@ export class Comment extends CanvasSectionObject {

if (!(<any>window).mode.isMobile())
document.getElementById('document-container').appendChild(this.sectionProperties.container);

this.updateEditability();
this.sectionProperties.onUpdatePermissionBound = this.updateEditability.bind(this);
app.events.on('updatepermission', this.sectionProperties.onUpdatePermissionBound);
}

// Syncs visible edit affordances with the current comment-editing
// permission. The editable=true branch only re-shows the menubar
// cell: nodeModify/nodeReply remain hidden until the user explicitly
// triggers edit()/reply(), so we deliberately do NOT auto-reopen a
// previously open edit/reply pane when permission is restored.
private makeEditable (editable: boolean): void {
const props = this.sectionProperties;
if (props.menuBarCell?.style)
props.menuBarCell.style.display = editable ? '' : 'none';
if (editable) return;
if (props.nodeModify?.style) props.nodeModify.style.display = 'none';
if (props.nodeReply?.style) props.nodeReply.style.display = 'none';
if (props.contentNode?.style) props.contentNode.style.display = '';
props.container.classList.remove('modify-annotation-container');
props.container.classList.remove('reply-annotation-container');
this.cachedIsEdit = false;
}

// Invoked at init and on every updatepermission event, so toggling
// between Viewing and Editing mode immediately hides or restores the
// per-comment controls.
private updateEditability (): void {
this.makeEditable(app.isCommentEditingAllowed());
}

private createContainerAndWrapper (): void {
Expand Down Expand Up @@ -331,6 +365,7 @@ export class Comment extends CanvasSectionObject {

private createMenu (): void {
var tdMenu = window.L.DomUtil.create('td', 'cool-annotation-menubar', this.sectionProperties.authorRow);
this.sectionProperties.menuBarCell = tdMenu;
const edit = window.L.DomUtil.create('div', 'cool-annotation-menu-edit', tdMenu);
edit.id = 'comment-annotation-menu-edit-' + this.sectionProperties.data.id;
edit.tabIndex = 0;
Expand Down Expand Up @@ -1703,6 +1738,11 @@ export class Comment extends CanvasSectionObject {
public onRemove (): void {
this.sectionProperties.commentContainerRemoved = true;

if (this.sectionProperties.onUpdatePermissionBound) {
app.events.off('updatepermission', this.sectionProperties.onUpdatePermissionBound);
this.sectionProperties.onUpdatePermissionBound = null;
}

if (this.sectionProperties.commentListSection.sectionProperties.selectedComment === this)
this.sectionProperties.commentListSection.sectionProperties.selectedComment = null;

Expand Down
27 changes: 27 additions & 0 deletions browser/src/control/Permission.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,22 @@ window.L.Map.include({
}
},

// Tell core whether this view is read-only, and flip the client-side
// comment/redline gates the UI checks via isCommentEditingAllowed()/
// isRedlineManagementAllowed(). Only meaningful for users that actually
// have WOPI write permission: users without write permission already
// get read-only set up server-side at session start, and their
// app.file.editComment / allowManageRedlines flags already reflect the
// real doc state (e.g. comment-only PDFs) and must not be touched.
_applyViewReadOnly: function (readOnly) {
if (!this['wopi'] || !this['wopi'].UserCanWrite)
return;
if (app.socket)
app.socket.sendMessage('setviewreadonly value=' + readOnly);
app.file.editComment = !readOnly;
app.file.allowManageRedlines = !readOnly;
},

_enterEditMode: function (perm) {
this._permission = perm;

Expand All @@ -220,6 +236,11 @@ window.L.Map.include({
if (app.map['stateChangeHandler'].getItemValue('EditDoc') === 'false')
app.map.sendUnoCommand('.uno:EditDoc?Editable:bool=true');

// Re-enable direct-canvas interactions (shape drag, arrow-key
// shape move) that the matching _enterReadOnlyMode branch
// disabled.
this._applyViewReadOnly(false);

app.events.fire('updatepermission', {perm : perm});

if (this._docLayer._docType === 'text') {
Expand All @@ -241,6 +262,12 @@ window.L.Map.include({
this._docLayer._onUpdateCursor();
this._docLayer._clearSelections();
}

// Block direct-canvas interactions (shape drag, arrow-key shape
// move) server-side and hide per-comment edit/redline controls
// in the UI.
this._applyViewReadOnly(true);

app.events.fire('updatepermission', {perm : perm});
this.fire('closemobilewizard');
this.fire('closealldialogs');
Expand Down
28 changes: 28 additions & 0 deletions kit/ChildSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ bool ChildSession::_handleInput(const char *buffer, int length)
tokens.equals(0, "formfieldevent") ||
tokens.equals(0, "traceeventrecording") ||
tokens.equals(0, "sallogoverride") ||
tokens.equals(0, "setviewreadonly") ||
tokens.equals(0, "rendersearchresult") ||
tokens.equals(0, "contentcontrolevent") ||
tokens.equals(0, "a11ystate") ||
Expand Down Expand Up @@ -848,6 +849,33 @@ bool ChildSession::_handleInput(const char *buffer, int length)
getLOKit()->setOption("sallogoverride", tokens[1].c_str());
}
}
else if (tokens.equals(0, "setviewreadonly"))
{
// Propagate the browser-side Viewing/Editing toggle to core so it can
// block direct-canvas interactions (shape drag, arrow-key move) and
// gate comment/redline commands via the dispatch filter.
bool readOnly = false;
std::string value;
if (tokens.size() > 1 && getTokenString(tokens[1], "value", value))
readOnly = (value == "true");

if (getLOKitDocument())
{
getLOKitDocument()->setView(_viewId);
getLOKitDocument()->setViewReadOnly(_viewId, readOnly);

// Browser only sends setviewreadonly when the user has WOPI
// write permission, so this path is the Viewing/Editing toggle
// on a fully editable doc. Block comments and redline management
// in Viewing mode too - comment-only docs (e.g. PDFs) are set
// up separately at session start and never reach this branch.
getLOKitDocument()->setAllowChangeComments(_viewId, !readOnly);
getLOKitDocument()->setAllowManageRedlines(_viewId, !readOnly);

LOG_DBG("setviewreadonly: viewId=" << _viewId
<< " readOnly=" << readOnly);
}
}
else if (tokens.equals(0, "rendersearchresult"))
{
return renderSearchResult(buffer, length, tokens);
Expand Down
1 change: 1 addition & 0 deletions wsd/ClientSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
}
else if (tokens.equals(0, "formfieldevent") ||
tokens.equals(0, "sallogoverride") ||
tokens.equals(0, "setviewreadonly") ||
tokens.equals(0, "contentcontrolevent"))
{
return forwardToChild(firstLine, docBroker);
Expand Down
Loading