desktop/qa/data/three-changes.fodt          |   40 ++++++
 desktop/qa/desktop_lib/test_desktop_lib.cxx |  162 +++++++++++++++++++++++++++-
 desktop/source/lib/init.cxx                 |   13 ++
 include/LibreOfficeKit/LibreOfficeKit.h     |    3 
 include/LibreOfficeKit/LibreOfficeKit.hxx   |   10 +
 include/sfx2/lokhelper.hxx                  |    2 
 include/sfx2/viewsh.hxx                     |    7 -
 sfx2/source/control/dispatch.cxx            |   16 ++
 sfx2/source/view/lokhelper.cxx              |    9 +
 9 files changed, 259 insertions(+), 3 deletions(-)

New commits:
commit c4f3d75567d5664259e01ff14a75e6f3e952ac9a
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Wed Jun 18 14:22:17 2025 +0500
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Jun 20 08:57:41 2025 +0200

    LOK: Introduce "read-only mode with tracked changes management"
    
    When it is enabled, most of the editing commands in a read-only view
    are disabled, as usual; but the commands to manage tracked changes
    are enabled, and allow user to accept / reject / comment on changes.
    Saving is enabled, too.
    
    Change-Id: Ic8bafc335c47ecd1b897c428a804cea2bc677cb3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186686
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/desktop/qa/data/three-changes.fodt 
b/desktop/qa/data/three-changes.fodt
new file mode 100644
index 000000000000..0c7072b244a0
--- /dev/null
+++ b/desktop/qa/data/three-changes.fodt
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:dc="http://purl.org/dc/elements/1.1/"; 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:automatic-styles>
+  <style:style style:name="T1" style:family="text">
+   <style:text-properties fo:font-weight="bold"/>
+  </style:style>
+ </office:automatic-styles>
+ <office:body>
+  <office:text>
+   <text:tracked-changes text:track-changes="false">
+    <text:changed-region xml:id="ct2412392131792" text:id="ct2412392131792">
+     <text:deletion>
+      <office:change-info>
+       <dc:creator>Mike</dc:creator>
+       <dc:date>2025-06-16T14:08:27</dc:date>
+      </office:change-info>
+     </text:deletion>
+    </text:changed-region>
+    <text:changed-region xml:id="ct2412428113776" text:id="ct2412428113776">
+     <text:format-change>
+      <office:change-info>
+       <dc:creator>Mike</dc:creator>
+       <dc:date>2025-06-17T12:41:00</dc:date>
+      </office:change-info>
+     </text:format-change>
+    </text:changed-region>
+    <text:changed-region xml:id="ct2412428117232" text:id="ct2412428117232">
+     <text:insertion>
+      <office:change-info>
+       <dc:creator>Mike</dc:creator>
+       <dc:date>2025-06-17T12:41:19</dc:date>
+      </office:change-info>
+     </text:insertion>
+    </text:changed-region>
+   </text:tracked-changes>
+   <text:p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum 
consequat mi quis pretium semper. Proin luctus orci ac neque venenatis, quis 
commodo dolor posuere. Curabitur dignissim sapien quis cursus egestas. 
<text:change-start text:change-id="ct2412392131792"/>Donec<text:change-end 
text:change-id="ct2412392131792"/> blandit auctor arcu, nec <text:change-start 
text:change-id="ct2412428113776"/><text:span 
text:style-name="T1">pellentesque</text:span><text:change-end 
text:change-id="ct2412428113776"/> eros molestie eget. In consectetur aliquam 
hendrerit. Sed cursus mauris vitae ligula pellentesque, non pellentesque urna 
aliquet. Fusce placerat mauris enim, nec rutrum purus semper vel. Praesent 
tincidunt neque eu pellentesque pharetra. Fusce pellentesque est 
orci.<text:change-start text:change-id="ct2412428117232"/> Sapienti 
sat.<text:change-end text:change-id="ct2412428117232"/></text:p>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx 
b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 903b99cb17f1..49fa3e5a4a5d 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -196,6 +196,7 @@ public:
     void testCommentsCallbacksWriter();
     void testCommentsAddEditDeleteDraw();
     void testCommentsInReadOnlyMode();
+    void testRedlinesInReadOnlyMode();
     void testCalcValidityDropdown();
     void testCalcValidityDropdownInReadonlyMode();
     void testRunMacro();
@@ -272,6 +273,7 @@ public:
     CPPUNIT_TEST(testCommentsCallbacksWriter);
     CPPUNIT_TEST(testCommentsAddEditDeleteDraw);
     CPPUNIT_TEST(testCommentsInReadOnlyMode);
+    CPPUNIT_TEST(testRedlinesInReadOnlyMode);
     CPPUNIT_TEST(testCalcValidityDropdown);
     CPPUNIT_TEST(testCalcValidityDropdownInReadonlyMode);
     CPPUNIT_TEST(testRunMacro);
@@ -2207,6 +2209,49 @@ void DesktopLOKTest::testRedlineCalc()
 
 namespace {
 
+struct RedlineInfo
+{
+    std::string action;
+    std::string index;
+    std::string author;
+    std::string type;
+    std::string comment;
+    std::string description;
+    std::string dateTime;
+};
+
+std::vector<RedlineInfo> getRedlineInfo(const boost::property_tree::ptree& 
redlineNode)
+{
+    std::vector<RedlineInfo> result;
+    result.reserve(redlineNode.size());
+    for (const auto& redline : redlineNode)
+    {
+        result.emplace_back();
+        result.back().index = redline.second.get<std::string>("index");
+        result.back().author = redline.second.get<std::string>("author");
+        result.back().type = redline.second.get<std::string>("type");
+        result.back().comment = redline.second.get<std::string>("comment");
+        result.back().description = 
redline.second.get<std::string>("description");
+        result.back().dateTime = redline.second.get<std::string>("dateTime");
+        if (auto oAction = redline.second.get_optional<std::string>("action"))
+            result.back().action = *oAction;
+    }
+
+    return result;
+}
+
+std::vector<RedlineInfo> getRedlineInfo(LibLODocument_Impl* pDocument)
+{
+    char* json
+        = pDocument->m_pDocumentClass->getCommandValues(pDocument, 
".uno:AcceptTrackedChanges");
+    std::stringstream stream(json);
+    free(json);
+    CPPUNIT_ASSERT(!stream.str().empty());
+    boost::property_tree::ptree tree;
+    boost::property_tree::read_json(stream, tree);
+    return getRedlineInfo(tree.get_child("redlines"));
+}
+
 class ViewCallback
 {
     LibLODocument_Impl* mpDocument;
@@ -2222,6 +2267,7 @@ public:
     tools::Rectangle m_aOwnCursor;
     boost::property_tree::ptree m_aCommentCallbackResult;
     boost::property_tree::ptree m_aColorPaletteCallbackResult;
+    RedlineInfo m_aLastRedlineInfo;
 
     ViewCallback(LibLODocument_Impl* pDocument)
         : mpDocument(pDocument),
@@ -2305,6 +2351,17 @@ public:
             boost::property_tree::read_json(aStream, m_JSONDialog);
         }
         break;
+        case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED:
+        case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED:
+        {
+            std::stringstream aStream(pPayload);
+            boost::property_tree::ptree tree;
+            boost::property_tree::read_json(aStream, tree);
+            auto redlines = getRedlineInfo(tree);
+            CPPUNIT_ASSERT_EQUAL(size_t(1), redlines.size());
+            m_aLastRedlineInfo = redlines[0];
+        }
+        break;
         }
     }
 };
@@ -2902,6 +2959,108 @@ void DesktopLOKTest::testCommentsInReadOnlyMode()
     //CPPUNIT_ASSERT_EQUAL(nCommentId, 
aView.m_aCommentCallbackResult.get<int>("id"));
 }
 
+void DesktopLOKTest::testRedlinesInReadOnlyMode()
+{
+    // In AllowManageRedlines mode, it must be possible to perform redline 
editing commands,
+    // even in read-only mode.
+
+    using namespace std::string_literals;
+
+    LibLODocument_Impl* pDocument = loadDoc("three-changes.fodt");
+
+    int viewId = pDocument->m_pDocumentClass->createView(pDocument);
+    pDocument->m_pDocumentClass->setView(pDocument, viewId);
+    pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}");
+    ViewCallback aCallback(pDocument);
+    Scheduler::ProcessEventsToIdle();
+
+    CPPUNIT_ASSERT_EQUAL(size_t(3), getRedlineInfo(pDocument).size());
+
+    // Activate read-only mode
+    SfxLokHelper::setViewReadOnly(viewId, true);
+
+    // Go to the 1st tracked change: "Delete “Donec”"
+    pDocument->pClass->postUnoCommand(pDocument, ".uno:NextTrackedChange", {}, 
false);
+    Scheduler::ProcessEventsToIdle();
+
+    // Check that redline management commands don't work in pure read-only
+    // Try to reject current redline
+    pDocument->pClass->postUnoCommand(pDocument, ".uno:RejectTrackedChange", 
{}, false);
+    Scheduler::ProcessEventsToIdle();
+    // Nothing happened
+    CPPUNIT_ASSERT_EQUAL(size_t(3), getRedlineInfo(pDocument).size());
+    CPPUNIT_ASSERT_EQUAL(""s, aCallback.m_aLastRedlineInfo.action);
+    CPPUNIT_ASSERT_EQUAL(""s, aCallback.m_aLastRedlineInfo.author);
+    CPPUNIT_ASSERT_EQUAL(""s, aCallback.m_aLastRedlineInfo.type);
+    CPPUNIT_ASSERT_EQUAL(""s, aCallback.m_aLastRedlineInfo.comment);
+    CPPUNIT_ASSERT_EQUAL(""s, aCallback.m_aLastRedlineInfo.description);
+    CPPUNIT_ASSERT_EQUAL(""s, aCallback.m_aLastRedlineInfo.dateTime);
+
+    // Activate the AllowManageRedlines mode
+    SfxLokHelper::setAllowManageRedlines(viewId, true);
+
+    // Try to reject current redline
+    pDocument->pClass->postUnoCommand(pDocument, ".uno:RejectTrackedChange", 
{}, false);
+    Scheduler::ProcessEventsToIdle();
+    // One change gone; it is recorded "Remove"d in 
aCallback.m_aLastRedlineInfo
+    CPPUNIT_ASSERT_EQUAL(size_t(2), getRedlineInfo(pDocument).size());
+    CPPUNIT_ASSERT_EQUAL("Remove"s, aCallback.m_aLastRedlineInfo.action);
+    CPPUNIT_ASSERT_EQUAL("Mike"s, aCallback.m_aLastRedlineInfo.author);
+    CPPUNIT_ASSERT_EQUAL("Delete"s, aCallback.m_aLastRedlineInfo.type);
+    CPPUNIT_ASSERT_EQUAL(""s, aCallback.m_aLastRedlineInfo.comment);
+    CPPUNIT_ASSERT_EQUAL("Delete “Donec”"s, 
aCallback.m_aLastRedlineInfo.description);
+    CPPUNIT_ASSERT_EQUAL("2025-06-16T14:08:27"s, 
aCallback.m_aLastRedlineInfo.dateTime);
+
+    // Go to the 2nd tracked change: "Attributes changed"
+    pDocument->pClass->postUnoCommand(pDocument, ".uno:NextTrackedChange", {}, 
false);
+    Scheduler::ProcessEventsToIdle();
+
+    // Comment on it
+    pDocument->pClass->postUnoCommand(pDocument, ".uno:CommentChangeTracking",
+                                      
R"({"Text":{"type":"string","value":"Some comment"}})",
+                                      false);
+    Scheduler::ProcessEventsToIdle();
+    // One change got a comment; it is recorded "Modify"ed in 
aCallback.m_aLastRedlineInfo
+    CPPUNIT_ASSERT_EQUAL(size_t(2), getRedlineInfo(pDocument).size());
+    CPPUNIT_ASSERT_EQUAL("Modify"s, aCallback.m_aLastRedlineInfo.action);
+    CPPUNIT_ASSERT_EQUAL("Mike"s, aCallback.m_aLastRedlineInfo.author);
+    CPPUNIT_ASSERT_EQUAL("Format"s, aCallback.m_aLastRedlineInfo.type);
+    CPPUNIT_ASSERT_EQUAL("Some comment"s, 
aCallback.m_aLastRedlineInfo.comment);
+    CPPUNIT_ASSERT_EQUAL("Attributes changed"s, 
aCallback.m_aLastRedlineInfo.description);
+    CPPUNIT_ASSERT_EQUAL("2025-06-17T12:41:00"s, 
aCallback.m_aLastRedlineInfo.dateTime);
+
+    // Go to the 3rd tracked change: "Insert “ Sapienti sat.”"
+    pDocument->pClass->postUnoCommand(pDocument, ".uno:NextTrackedChange", {}, 
false);
+    Scheduler::ProcessEventsToIdle();
+
+    // Accept it
+    pDocument->pClass->postUnoCommand(pDocument, ".uno:AcceptTrackedChange", 
{}, false);
+    Scheduler::ProcessEventsToIdle();
+    // One change gone; it is recorded "Remove"d in 
aCallback.m_aLastRedlineInfo
+    CPPUNIT_ASSERT_EQUAL(size_t(1), getRedlineInfo(pDocument).size());
+    CPPUNIT_ASSERT_EQUAL("Remove"s, aCallback.m_aLastRedlineInfo.action);
+    CPPUNIT_ASSERT_EQUAL("Mike"s, aCallback.m_aLastRedlineInfo.author);
+    CPPUNIT_ASSERT_EQUAL("Insert"s, aCallback.m_aLastRedlineInfo.type);
+    CPPUNIT_ASSERT_EQUAL(""s, aCallback.m_aLastRedlineInfo.comment);
+    CPPUNIT_ASSERT_EQUAL("Insert “ Sapienti sat.”"s, 
aCallback.m_aLastRedlineInfo.description);
+    CPPUNIT_ASSERT_EQUAL("2025-06-17T12:41:19"s, 
aCallback.m_aLastRedlineInfo.dateTime);
+
+    // Make sure that another (unrelated to redline management) editing 
command is not working
+    pDocument->pClass->postUnoCommand(pDocument, ".uno:InsertAnnotation",
+                                      
R"({"Text":{"type":"string","value":"Comment"}})",
+                                      false);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT(aCallback.m_aCommentCallbackResult.empty());
+
+    // Check that the same command would succeed in AllowChangeComments mode
+    SfxLokHelper::setAllowChangeComments(viewId, true);
+    pDocument->pClass->postUnoCommand(pDocument, ".uno:InsertAnnotation",
+                                      
R"({"Text":{"type":"string","value":"Comment"}})",
+                                      false);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT(!aCallback.m_aCommentCallbackResult.empty());
+}
+
 void DesktopLOKTest::testCalcValidityDropdown()
 {
     LibLODocument_Impl* pDocument = loadDoc("validity.ods");
@@ -3982,9 +4141,10 @@ void DesktopLOKTest::testABI()
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(77), offsetof(struct 
_LibreOfficeKitDocumentClass, renderNextSlideLayer));
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(78), offsetof(struct 
_LibreOfficeKitDocumentClass, setViewOption));
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(79), offsetof(struct 
_LibreOfficeKitDocumentClass, setColorPreviewState));
+    CPPUNIT_ASSERT_EQUAL(documentClassOffset(80), offsetof(struct 
_LibreOfficeKitDocumentClass, setAllowManageRedlines));
 
     // As above
-    CPPUNIT_ASSERT_EQUAL(documentClassOffset(80), sizeof(struct 
_LibreOfficeKitDocumentClass));
+    CPPUNIT_ASSERT_EQUAL(documentClassOffset(81), sizeof(struct 
_LibreOfficeKitDocumentClass));
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest);
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index da3ca5e06504..89b8b8287205 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -1328,6 +1328,8 @@ static void doc_setViewReadOnly(LibreOfficeKitDocument* 
pThis, int nId, const bo
 
 static void doc_setAllowChangeComments(LibreOfficeKitDocument* pThis, int nId, 
const bool allow);
 
+static void doc_setAllowManageRedlines(LibreOfficeKitDocument* pThis, int nId, 
bool allow);
+
 static void doc_setAccessibilityState(LibreOfficeKitDocument* pThis, int nId, 
bool bEnabled);
 
 static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis);
@@ -1547,6 +1549,7 @@ LibLODocument_Impl::LibLODocument_Impl(uno::Reference 
<css::lang::XComponent> xC
         m_pDocumentClass->setViewReadOnly = doc_setViewReadOnly;
 
         m_pDocumentClass->setAllowChangeComments = doc_setAllowChangeComments;
+        m_pDocumentClass->setAllowManageRedlines = doc_setAllowManageRedlines;
 
         m_pDocumentClass->getPresentationInfo = doc_getPresentationInfo;
         m_pDocumentClass->createSlideRenderer = doc_createSlideRenderer;
@@ -7611,6 +7614,16 @@ static void 
doc_setAllowChangeComments(SAL_UNUSED_PARAMETER LibreOfficeKitDocume
     SfxLokHelper::setAllowChangeComments(nId, allow);
 }
 
+static void doc_setAllowManageRedlines(SAL_UNUSED_PARAMETER 
LibreOfficeKitDocument* /*pThis*/, int nId, bool allow)
+{
+    comphelper::ProfileZone aZone("doc_setAllowManageRedlines");
+
+    SolarMutexGuard aGuard;
+    SetLastExceptionMsg();
+
+    SfxLokHelper::setAllowManageRedlines(nId, allow);
+}
+
 static void doc_setAccessibilityState(SAL_UNUSED_PARAMETER 
LibreOfficeKitDocument* pThis, int nId, bool nEnabled)
 {
     SolarMutexGuard aGuard;
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h 
b/include/LibreOfficeKit/LibreOfficeKit.h
index 7f933aa29a4b..c242d00e0bde 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -562,6 +562,9 @@ struct _LibreOfficeKitDocumentClass
     /// @see lok::Document::setColorPreviewState().
     void (*setColorPreviewState) (LibreOfficeKitDocument* pThis, int nId, bool 
nEnabled);
 
+    /// @see lok::Document::setAllowManageRedlines().
+    void (*setAllowManageRedlines)(LibreOfficeKitDocument* pThis, int nId, 
bool allow);
+
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx 
b/include/LibreOfficeKit/LibreOfficeKit.hxx
index 8de0898817fb..c4dac79c412c 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -879,6 +879,16 @@ public:
         mpDoc->pClass->setAllowChangeComments(mpDoc, nId, allow);
     }
 
+    /** Set if the view can manage redlines in readonly mode or not.
+     *
+     * @param nId view ID
+     * @param allow
+    */
+    void setAllowManageRedlines(int nId, bool allow)
+    {
+        mpDoc->pClass->setAllowManageRedlines(mpDoc, nId, allow);
+    }
+
     /**
      * Enable/Disable accessibility support for the window with the specified 
nId.
      *
diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx
index d8cc5af06214..a3db5bada7d6 100644
--- a/include/sfx2/lokhelper.hxx
+++ b/include/sfx2/lokhelper.hxx
@@ -125,6 +125,8 @@ public:
     static void setViewReadOnly(int nId, bool readOnly);
     // In readonly view, can user add / modify comments or not.
     static void setAllowChangeComments(int nId, bool allow);
+    // In readonly view, can user accept / reject tracked changes or not.
+    static void setAllowManageRedlines(int nId, bool allow);
     /// Get the language used by the loading view (used for all save 
operations).
     static const LanguageTag & getLoadLanguage();
     /// Set the language used by the loading view (used for all save 
operations).
diff --git a/include/sfx2/viewsh.hxx b/include/sfx2/viewsh.hxx
index dcd6619d4e0f..1e55b1594493 100644
--- a/include/sfx2/viewsh.hxx
+++ b/include/sfx2/viewsh.hxx
@@ -225,8 +225,9 @@ private:
 
     LOKDocumentFocusListener& GetLOKDocumentFocusListener();
     const LOKDocumentFocusListener& GetLOKDocumentFocusListener() const;
-    bool                        lokReadOnlyView = false; // When true, this is 
a LOK readonly view.
-    bool                        allowChangeComments = false; // When true, 
user can edit comments in readonly view mode.
+    bool lokReadOnlyView : 1 = false; // When true, this is a LOK readonly view
+    bool allowChangeComments : 1 = false; // Allow editing comments in 
readonly view mode
+    bool allowManageRedlines : 1 = false; // Allow accepting/rejecting changes 
in readonly view mode
 
 public:
 
@@ -254,6 +255,8 @@ public:
     bool                        IsLokReadOnlyView() const { return 
lokReadOnlyView; };
     void                        SetAllowChangeComments(bool allow) { 
allowChangeComments = allow; }
     bool                        IsAllowChangeComments() const { return 
allowChangeComments; }
+    void                        SetAllowManageRedlines(bool allow) { 
allowManageRedlines = allow; }
+    bool                        IsAllowManageRedlines() const { return 
allowManageRedlines; }
 
     // Misc
 
diff --git a/sfx2/source/control/dispatch.cxx b/sfx2/source/control/dispatch.cxx
index 40910ff6e949..11614d9ef688 100644
--- a/sfx2/source/control/dispatch.cxx
+++ b/sfx2/source/control/dispatch.cxx
@@ -1554,6 +1554,22 @@ static bool 
IsCommandAllowedInLokReadOnlyViewMode(std::u16string_view commandNam
         if (std::find(std::begin(allowed), std::end(allowed), commandName) != 
std::end(allowed))
             return true;
     }
+    if (viewShell.IsAllowManageRedlines())
+    {
+        static constexpr std::u16string_view allowed[] = {
+            u".uno:AcceptTrackedChange",
+            u".uno:RejectTrackedChange",
+            u".uno:AcceptAllTrackedChanges",
+            u".uno:RejectAllTrackedChanges",
+            u".uno:AcceptTrackedChangeToNext",
+            u".uno:RejectTrackedChangeToNext",
+            u".uno:CommentChangeTracking",
+            u".uno:Save",
+        };
+
+        if (std::find(std::begin(allowed), std::end(allowed), commandName) != 
std::end(allowed))
+            return true;
+    }
     return false;
 }
 
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
index b16b7c7ff292..d9184e33e5bb 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -382,6 +382,15 @@ void SfxLokHelper::setAllowChangeComments(int nId, bool 
allow)
     }
 }
 
+void SfxLokHelper::setAllowManageRedlines(int nId, bool allow)
+{
+    if (SfxViewShell* pViewShell = getViewOfId(nId))
+    {
+        LOK_INFO("lok.readonlyview", "SfxLokHelper::setAllowManageRedlines: 
view id: " << nId << ", allow: " << allow);
+        pViewShell->SetAllowManageRedlines(allow);
+    }
+}
+
 void SfxLokHelper::setAccessibilityState(int nId, bool nEnabled)
 {
     std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();

Reply via email to