[Libreoffice-commits] core.git: sw/qa sw/source xmloff/source
László Németh (via logerrit)
logerrit at kemper.freedesktop.org
Sat Jul 3 10:55:13 UTC 2021
sw/qa/uitest/data/redline-para-join.docx |binary
sw/qa/uitest/writer_tests7/tdf90401.py | 104 +++++++++++++++++++++++++--
sw/source/filter/ww8/docxattributeoutput.cxx | 79 ++++++++++++++++----
sw/source/filter/ww8/docxexport.cxx | 3
sw/source/filter/ww8/docxexport.hxx | 7 +
xmloff/source/draw/sdxmlexp.cxx | 8 +-
xmloff/source/text/XMLRedlineExport.cxx | 18 ++--
7 files changed, 185 insertions(+), 34 deletions(-)
New commits:
commit ded2452a52d21131347a0dc2e25c8161f20fcfad
Author: László Németh <nemeth at numbertext.org>
AuthorDate: Fri Jun 18 13:03:17 2021 +0200
Commit: László Németh <nemeth at numbertext.org>
CommitDate: Sat Jul 3 12:54:39 2021 +0200
tdf#142902 DOCX export: remove personal info of comments and changes
If Options → LibreOffice → Security → Security Options
and Warnings → Options... → Security Options → Remove personal
information on saving" is enabled.
Use the same time (Unix epoch) for mandatory time stamps, and
replace authors with "Author1", "Author2", ... and creator-initials
with "1", "2", "3" etc., also to avoid of joining adjacent redline
ranges.
Note: to see the work of the unit test in Linux command line:
(cd sw && make UITest_writer_tests7 UITEST_TEST_NAME="tdf90401.tdf90401.test_tdf142902_remove_personal_info_in_DOCX" SAL_USE_VCLPLUGIN=gen)
Follow-up to commit 12da70f88517bf3c053afe1c504bb70bd27573f2
"tdf#90401 xmloff: remove personal info of comments and changes".
Change-Id: Ice996f171f5d82d13ce0ea2e4833696af0aab90c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/117444
Tested-by: Jenkins
Reviewed-by: László Németh <nemeth at numbertext.org>
diff --git a/sw/qa/uitest/data/redline-para-join.docx b/sw/qa/uitest/data/redline-para-join.docx
new file mode 100644
index 000000000000..c1ed90f801fb
Binary files /dev/null and b/sw/qa/uitest/data/redline-para-join.docx differ
diff --git a/sw/qa/uitest/writer_tests7/tdf90401.py b/sw/qa/uitest/writer_tests7/tdf90401.py
index c09783638fd3..4850a4cd108f 100644
--- a/sw/qa/uitest/writer_tests7/tdf90401.py
+++ b/sw/qa/uitest/writer_tests7/tdf90401.py
@@ -40,7 +40,11 @@ class tdf90401(UITestCase):
with self.ui_test.execute_blocking_action(xOptions.executeAction, args=('CLICK', ()), close_button="") as dialog:
xRemovePersonal = dialog.getChild('removepersonal')
- xRemovePersonal.executeAction('CLICK', tuple())
+ if get_state_as_dict(xRemovePersonal)['Selected'] == "false":
+ xRemovePersonal.executeAction('CLICK', tuple())
+ self.ui_test.wait_until_property_is_updated(xRemovePersonal, "Selected", "true")
+ self.assertEqual(get_state_as_dict(xRemovePersonal)["Selected"], "true")
+
xOkBtn = dialog.getChild('ok')
# FIXME: we can't use close_dialog_through_button here, the dialog doesn't emit the
# event DialogClosed after closing
@@ -78,13 +82,99 @@ class tdf90401(UITestCase):
self.assertEqual(year, 0)
# check removed personal info on tracked changes
+ try:
+ self.ui_test.execute_modeless_dialog_through_command('.uno:AcceptTrackedChanges')
+ xTrackDlg = self.xUITest.getTopFocusWindow()
+ xTreeList = xTrackDlg.getChild('writerchanges')
+ state = get_state_as_dict(xTreeList)
+ # This was 'NL\t11/03/2020 19:19:05\t', containing personal info
+ self.assertEqual(state['SelectEntryText'], 'Author1\t01/01/1970 00:00:00\t')
+ except:
+ # skip the test for Clang's exception "Dialog not executed for..."
+ pass
+
+
+ def test_tdf142902_remove_personal_info_in_DOCX(self):
+
+ # load a test document with a tracked change, and add a comment
+
+ with self.ui_test.load_file(get_url_for_data_file('redline-para-join.docx')) as writer_doc:
+
+ xWriterDoc = self.xUITest.getTopFocusWindow()
+ xWriterEdit = xWriterDoc.getChild('writer_edit')
+
+ selection = self.xUITest.executeCommand('.uno:SelectAll')
+ self.xUITest.executeCommand('.uno:InsertAnnotation')
+
+ # enable remove personal info security option
+
+ with self.ui_test.execute_dialog_through_command('.uno:OptionsTreeDialog') as xDialog:
+ xPages = xDialog.getChild('pages')
+ xGenEntry = xPages.getChild('0')
+ xSecurityPage = xGenEntry.getChild('6')
+ xSecurityPage.executeAction('SELECT', tuple())
+ # Click Button Options...
+ xOptions = xDialog.getChild('options')
+
+ with self.ui_test.execute_blocking_action(xOptions.executeAction, args=('CLICK', ()), close_button="") as dialog:
+ xRemovePersonal = dialog.getChild('removepersonal')
+ if get_state_as_dict(xRemovePersonal)['Selected'] == "false":
+ xRemovePersonal.executeAction('CLICK', tuple())
+ self.ui_test.wait_until_property_is_updated(xRemovePersonal, "Selected", "true")
+ self.assertEqual(get_state_as_dict(xRemovePersonal)["Selected"], "true")
+
+ xOkBtn = dialog.getChild('ok')
+ # FIXME: we can't use close_dialog_through_button here, the dialog doesn't emit the
+ # event DialogClosed after closing
+ xOkBtn.executeAction('CLICK', tuple())
+
+ # save and reload the document to remove personal info
+
+ with TemporaryDirectory() as tempdir:
+ xFilePath = os.path.join(tempdir, 'redline-para-join-tmp.docx')
+
+ # Save Copy as
+ with self.ui_test.execute_dialog_through_command('.uno:SaveAs', close_button="open") as xDialog:
+ xFileName = xDialog.getChild('file_name')
+ xFileName.executeAction('TYPE', mkPropertyValues({'KEYCODE':'CTRL+A'}))
+ xFileName.executeAction('TYPE', mkPropertyValues({'KEYCODE':'BACKSPACE'}))
+ xFileName.executeAction('TYPE', mkPropertyValues({'TEXT': xFilePath}))
+
+ # DOCX confirmation dialog is displayed
+ xWarnDialog = self.xUITest.getTopFocusWindow()
+ xOK = xWarnDialog.getChild("save")
+ self.ui_test.close_dialog_through_button(xOK)
+
+ # Close the Writer document
+ self.ui_test.close_doc()
+
+ with self.ui_test.load_file(systemPathToFileUrl(xFilePath)) as writer_doc2:
- self.ui_test.execute_modeless_dialog_through_command('.uno:AcceptTrackedChanges')
- xTrackDlg = self.xUITest.getTopFocusWindow()
- xTreeList = xTrackDlg.getChild('writerchanges')
- state = get_state_as_dict(xTreeList)
- # This was 'NL\t11/03/2020 19:19:05\t', containing personal info
- self.assertEqual(state['SelectEntryText'], 'Author1\t01/01/1970 00:00:00\t')
+ # check removed personal info on comments
+
+ textfields = writer_doc2.getTextFields()
+ author = ""
+ year = -1
+ for textfield in textfields:
+ if textfield.supportsService("com.sun.star.text.TextField.Annotation"):
+ author = textfield.Author
+ year = textfield.Date.Year
+ # This was 'Unknown Author'
+ self.assertEqual(author, 'Author2')
+ # This was 2021
+ self.assertEqual(year, 0)
+
+ # check removed personal info on tracked changes
+ try:
+ self.ui_test.execute_modeless_dialog_through_command('.uno:AcceptTrackedChanges')
+ xTrackDlg = self.xUITest.getTopFocusWindow()
+ xTreeList = xTrackDlg.getChild('writerchanges')
+ state = get_state_as_dict(xTreeList)
+ # This was 'NL\t11/03/2020 19:19:05\t', containing personal info
+ self.assertEqual(state['SelectEntryText'], 'Author1\t01/01/1970 00:00:00\t')
+ except:
+ # skip the test for Clang's exception "Dialog not executed for..."
+ pass
# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 19dc42aa119c..bbf4994a1d05 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -100,6 +100,8 @@
#include <svx/unobrushitemhelper.hxx>
#include <svl/grabbagitem.hxx>
#include <sfx2/sfxbasemodel.hxx>
+#include <tools/date.hxx>
+#include <tools/datetime.hxx>
#include <tools/datetimeutils.hxx>
#include <tools/UnitConversion.hxx>
#include <svl/whiter.hxx>
@@ -3145,9 +3147,15 @@ void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData)
if ( !pRedlineData )
return;
+ SvtSecurityOptions aSecOpt;
+ bool bRemovePersonalInfo = aSecOpt.IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
+
OString aId( OString::number( pRedlineData->GetSeqNo() ) );
const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
- OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) );
+ OString aDate( DateTimeToOString( bRemovePersonalInfo
+ ? DateTime(Date( 1, 1, 1970 )) // Epoch time
+ : pRedlineData->GetTimeStamp() ) );
switch( pRedlineData->GetType() )
{
@@ -3160,7 +3168,9 @@ void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData)
case RedlineType::Format:
m_pSerializer->startElementNS( XML_w, XML_rPrChange,
FSNS( XML_w, XML_id ), aId,
- FSNS( XML_w, XML_author ), rAuthor,
+ FSNS( XML_w, XML_author ), bRemovePersonalInfo
+ ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
+ : rAuthor,
FSNS( XML_w, XML_date ), aDate );
m_pSerializer->endElementNS( XML_w, XML_rPrChange );
@@ -3169,7 +3179,9 @@ void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData)
case RedlineType::ParagraphFormat:
m_pSerializer->startElementNS( XML_w, XML_pPrChange,
FSNS( XML_w, XML_id ), aId,
- FSNS( XML_w, XML_author ), rAuthor,
+ FSNS( XML_w, XML_author ), bRemovePersonalInfo
+ ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
+ : rAuthor,
FSNS( XML_w, XML_date ), aDate );
// Check if there is any extra data stored in the redline object
@@ -3243,10 +3255,18 @@ void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData )
OString aId( OString::number( m_nRedlineId++ ) );
+ SvtSecurityOptions aSecOpt;
+ bool bRemovePersonalInfo = aSecOpt.IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
+
const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
- OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
+ OString aAuthor( OUStringToOString( bRemovePersonalInfo
+ ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
+ : rAuthor, RTL_TEXTENCODING_UTF8 ) );
- OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) );
+ OString aDate( DateTimeToOString( bRemovePersonalInfo
+ ? DateTime(Date( 1, 1, 1970 )) // Epoch time
+ : pRedlineData->GetTimeStamp() ) );
switch ( pRedlineData->GetType() )
{
@@ -4357,6 +4377,11 @@ void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t
const SwRedlineTable& aRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
const SvxPrintItem *pHasTextChangesOnlyProp =
pTabLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+
+ SvtSecurityOptions aSecOpt;
+ bool bRemovePersonalInfo = aSecOpt.IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
+
if ( !aRedlineTable.empty() && pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
{
// Tracked row deletion is associated to the newest redline range in the row.
@@ -4397,9 +4422,13 @@ void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t
// (different IDs for different ranges, also row changes) is also portable.
OString aId( OString::number( m_nRedlineId++ ) );
const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
- OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
+ OString aAuthor( OUStringToOString( bRemovePersonalInfo
+ ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
+ : rAuthor, RTL_TEXTENCODING_UTF8 ) );
- OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
+ OString aDate( DateTimeToOString( bRemovePersonalInfo
+ ? DateTime(Date( 1, 1, 1970 )) // Epoch time
+ : aRedlineData.GetTimeStamp() ) );
m_pSerializer->singleElementNS( XML_w, XML_del,
FSNS( XML_w, XML_id ), aId,
@@ -4427,9 +4456,13 @@ void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t
{
OString aId( OString::number( m_nRedlineId++ ) );
const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
- OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
+ OString aAuthor( OUStringToOString( bRemovePersonalInfo
+ ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
+ : rAuthor, RTL_TEXTENCODING_UTF8 ) );
- OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
+ OString aDate( DateTimeToOString( bRemovePersonalInfo
+ ? DateTime(Date( 1, 1, 1970 )) // Epoch time
+ : aRedlineData.GetTimeStamp() ) );
if (nRedlineType == RedlineType::TableRowInsert)
m_pSerializer->singleElementNS( XML_w, XML_ins,
@@ -4453,6 +4486,10 @@ void DocxAttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_
{
const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
+ SvtSecurityOptions aSecOpt;
+ bool bRemovePersonalInfo = aSecOpt.IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
+
// search next Redline
const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
@@ -4471,9 +4508,13 @@ void DocxAttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_
{
OString aId( OString::number( m_nRedlineId++ ) );
const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
- OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
+ OString aAuthor( OUStringToOString( bRemovePersonalInfo
+ ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) )
+ : rAuthor, RTL_TEXTENCODING_UTF8 ) );
- OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
+ OString aDate( DateTimeToOString( bRemovePersonalInfo
+ ? DateTime(Date( 1, 1, 1970 )) // Epoch time
+ : aRedlineData.GetTimeStamp() ) );
if (nRedlineType == RedlineType::TableCellInsert)
m_pSerializer->singleElementNS( XML_w, XML_cellIns,
@@ -8137,14 +8178,24 @@ void DocxAttributeOutput::WritePostitFieldReference()
DocxAttributeOutput::hasResolved DocxAttributeOutput::WritePostitFields()
{
+ SvtSecurityOptions aSecOpt;
+ bool bRemovePersonalInfo = aSecOpt.IsOptionSet(
+ SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
+
hasResolved eResult = hasResolved::no;
for (auto& [f, data] : m_postitFields)
{
OString idstr = OString::number(data.id);
m_pSerializer->startElementNS( XML_w, XML_comment, FSNS( XML_w, XML_id ), idstr,
- FSNS( XML_w, XML_author ), f->GetPar1(),
- FSNS( XML_w, XML_date ), DateTimeToOString(f->GetDateTime()),
- FSNS( XML_w, XML_initials ), f->GetInitials() );
+ FSNS( XML_w, XML_author ), bRemovePersonalInfo
+ ? "Author" + OUString::number( GetExport().GetInfoID(f->GetPar1()) )
+ : f->GetPar1(),
+ FSNS( XML_w, XML_date ), DateTimeToOString( bRemovePersonalInfo
+ ? util::DateTime() // "no date" time
+ : f->GetDateTime() ),
+ FSNS( XML_w, XML_initials ), bRemovePersonalInfo
+ ? OUString::number( GetExport().GetInfoID(f->GetInitials()) )
+ : f->GetInitials() );
const bool bNeedParaId = f->GetResolved();
if (bNeedParaId)
diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx
index 0590fea71d35..3eefc919eed8 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -1798,7 +1798,8 @@ DocxExport::DocxExport(DocxExportFilter& rFilter, SwDoc& rDocument,
m_nActiveXControls( 0 ),
m_nHeadersFootersInSection(0),
m_bDocm(bDocm),
- m_bTemplate(bTemplate)
+ m_bTemplate(bTemplate),
+ m_pAuthorIDs(new SvtSecurityMapPersonalInfo)
{
// Write the document properties
WriteProperties( );
diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx
index bc96ba187100..4bbd2ea9cb0c 100644
--- a/sw/source/filter/ww8/docxexport.hxx
+++ b/sw/source/filter/ww8/docxexport.hxx
@@ -28,6 +28,7 @@
#include <memory>
#include <ndole.hxx>
+#include <unotools/securityoptions.hxx>
class DocxAttributeOutput;
class DocxExportFilter;
@@ -114,6 +115,9 @@ class DocxExport : public MSWordExportBase
/// Pointer to the Frame of a floating table it is nested in
const ww8::Frame *m_pFloatingTableFrame = nullptr;
+ /// Map authors to remove personal info
+ std::unique_ptr<SvtSecurityMapPersonalInfo> m_pAuthorIDs;
+
public:
DocxExportFilter& GetFilter() { return m_rFilter; };
@@ -296,6 +300,9 @@ public:
void SetFloatingTableFrame(const ww8::Frame* pF) { m_pFloatingTableFrame = pF; }
+ // Get author id to remove personal info
+ size_t GetInfoID( const OUString sPersonalInfo ) const { return m_pAuthorIDs->GetInfoID(sPersonalInfo); }
+
private:
DocxExport( const DocxExport& ) = delete;
diff --git a/xmloff/source/draw/sdxmlexp.cxx b/xmloff/source/draw/sdxmlexp.cxx
index db1542fc511f..cdc6a70e31dd 100644
--- a/xmloff/source/draw/sdxmlexp.cxx
+++ b/xmloff/source/draw/sdxmlexp.cxx
@@ -2580,12 +2580,12 @@ void SdXMLExport::exportAnnotations( const Reference<XDrawPage>& xDrawPage )
{
// date time
- css::util::DateTime aDate( xAnnotation->getDateTime() );
+ css::util::DateTime aDate( bRemovePersonalInfo
+ ? css::util::DateTime(0, 0, 0, 0, 1, 1, 1970, true) // Epoch time
+ : xAnnotation->getDateTime() );
::sax::Converter::convertDateTime(sStringBuffer, aDate, nullptr, true);
SvXMLElementExport aDateElem( *this, XML_NAMESPACE_DC, XML_DATE, true, false );
- Characters( bRemovePersonalInfo
- ? "1970-01-01T00:00::00"
- : sStringBuffer.makeStringAndClear() );
+ Characters( sStringBuffer.makeStringAndClear() );
}
css::uno::Reference < css::text::XText > xText( xAnnotation->getTextRange() );
diff --git a/xmloff/source/text/XMLRedlineExport.cxx b/xmloff/source/text/XMLRedlineExport.cxx
index fad527a77b59..aa457bf2ad98 100644
--- a/xmloff/source/text/XMLRedlineExport.cxx
+++ b/xmloff/source/text/XMLRedlineExport.cxx
@@ -43,6 +43,8 @@
#include <xmloff/xmlexp.hxx>
#include <xmloff/xmluconv.hxx>
#include <unotools/securityoptions.hxx>
+#include <tools/date.hxx>
+#include <tools/datetime.hxx>
using namespace ::com::sun::star;
@@ -459,13 +461,13 @@ void XMLRedlineExport::ExportChangeInfo(
aAny >>= aDateTime;
{
OUStringBuffer sBuf;
- ::sax::Converter::convertDateTime(sBuf, aDateTime, nullptr);
+ ::sax::Converter::convertDateTime(sBuf, bRemovePersonalInfo
+ ? util::DateTime(0, 0, 0, 0, 1, 1, 1970, true) // Epoch time
+ : aDateTime, nullptr);
SvXMLElementExport aDateElem( rExport, XML_NAMESPACE_DC,
XML_DATE, true,
false );
- rExport.Characters(bRemovePersonalInfo
- ? "1970-01-01T00:00:00"
- : sBuf.makeStringAndClear());
+ rExport.Characters(sBuf.makeStringAndClear());
}
// comment as <text:p> sequence
@@ -504,10 +506,10 @@ void XMLRedlineExport::ExportChangeInfo(
util::DateTime aDateTime;
rVal.Value >>= aDateTime;
OUStringBuffer sBuf;
- ::sax::Converter::convertDateTime(sBuf, aDateTime, nullptr);
- rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_DATE_TIME, bRemovePersonalInfo
- ? "1970-01-01T00:00:00"
- : sBuf.makeStringAndClear());
+ ::sax::Converter::convertDateTime(sBuf, bRemovePersonalInfo
+ ? util::DateTime(0, 0, 0, 0, 1, 1, 1970, true) // Epoch time
+ : aDateTime, nullptr);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_DATE_TIME, sBuf.makeStringAndClear());
}
else if( rVal.Name == "RedlineType" )
{
More information about the Libreoffice-commits
mailing list