sw/qa/extras/ooxmlexport/ooxmlexport12.cxx | 10 + sw/qa/extras/ooxmlexport/ooxmlexport16.cxx | 6 - sw/source/filter/ww8/docxattributeoutput.cxx | 147 ++++++++++++++++++--------- 3 files changed, 108 insertions(+), 55 deletions(-)
New commits: commit 64dcedcf7c073d1819794d68a33651b14877e1b5 Author: László Németh <nem...@numbertext.org> AuthorDate: Thu Mar 10 11:00:36 2022 +0100 Commit: László Németh <nem...@numbertext.org> CommitDate: Thu Mar 10 19:14:04 2022 +0100 tdf#147760 tdf#142902 DOCX export: anonymize date and moveFromRangeStart Anonymization exported a zeroed w:date in change tracking and commenting elements w:ins, w:del, w:moveFrom, w:moveTo, w:cellIns, w:cellDel, w:comment, w:moveFromRangeStart and w:moveToRangeStart instead of simply skipping it, according to OOXML. Also don't export w:date, if the imported DOCX was anonymized by Word's Document Inspector, i.e. it doesn't contain w:date. Fix also missing anonymization of author and date of moveFromRangeStart and moveToRangeStart elements. Follow-up to commit 2c51746997478ad5d0e7cc64aa6489769c473d43 "tdf#146171 DOCX: fix loss of change tracking, if no date", commit 9e1e88ad5cf2dc0e9b188c60930445652a6c7519 "tdf#145720 DOCX export: fix loss of tracked moving" and commit ded2452a52d21131347a0dc2e25c8161f20fcfad "tdf#142902 DOCX export: remove personal info of comments and changes". Change-Id: If79402e4723886ceab3b4b9c24e83793f87cd513 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131317 Tested-by: Jenkins Reviewed-by: László Németh <nem...@numbertext.org> diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx index c568f7228a7b..5d1af7159fcf 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx @@ -1222,10 +1222,12 @@ DECLARE_OOXMLEXPORT_TEST(testTdf145720, "tdf104797.docx") u"Tekijä"); assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveTo/w:moveToRangeStart", "author", u"Tekijä"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:moveFrom/w:moveFromRangeStart", "date", - "1970-01-01T00:00:00Z"); - assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveTo/w:moveToRangeStart", "date", - "1970-01-01T00:00:00Z"); + // no date (anonymized change) + // This failed, date was exported as w:date="0-00-00T00:00:00Z", and later "1970-01-01T00:00:00Z" + assertXPathNoAttribute(pXmlDoc, "/w:document/w:body/w:p[1]/w:moveFrom/w:moveFromRangeStart", + "date"); + assertXPathNoAttribute(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveTo/w:moveToRangeStart", + "date"); } } diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx index 543a7580bd79..b1637cfa3ad1 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx @@ -651,6 +651,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf146171_invalid_change_date) // false alarm? during ODF roundtrip: // 'Error: "1970-01-01" does not satisfy the "dateTime" type' // disable and check only the conversion of the invalid (zeroed) change date + // 0000-00-00T00:00:00Z, resulting loss of change tracking during ODF roundtrip // reload("writer8", "tdf146171.odt"); reload("Office Open XML Text", "tdf146171.docx"); @@ -659,8 +660,9 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf146171_invalid_change_date) assertXPath(pXmlDoc, "//w:ins", 4); // This was 0 assertXPath(pXmlDoc, "//w:del", 1); - // This was 0000-00-00T00:00:00Z, resulting loss of change tracking during ODF roundtrip - assertXPath(pXmlDoc, "//w:del", "date", "1970-01-01T00:00:00Z"); + // no date (anonymized change) + // This failed, date was exported as w:date="1970-01-01T00:00:00Z" before fixing tdf#147760 + assertXPathNoAttribute(pXmlDoc, "//w:del", "date"); } DECLARE_OOXMLEXPORT_TEST(testTdf139580, "tdf139580.odt") diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index a0fd9fd0f995..11e890706e7a 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -1914,16 +1914,32 @@ void DocxAttributeOutput::DoWriteBookmarkTagEnd(sal_Int32 const nId) void DocxAttributeOutput::DoWriteMoveRangeTagStart(const OString & bookmarkName, bool bFrom, const SwRedlineData* pRedlineData) { - const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) ); - OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) ); + bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet( + SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ); - m_pSerializer->singleElementNS(XML_w, bFrom + const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) ); + const DateTime aDateTime = pRedlineData->GetTimeStamp(); + bool bNoDate = bRemovePersonalInfo || + ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 ); + if ( bNoDate ) + m_pSerializer->singleElementNS(XML_w, bFrom ? XML_moveFromRangeStart : XML_moveToRangeStart, - FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId), - FSNS(XML_w, XML_author ), OUStringToOString(rAuthor, RTL_TEXTENCODING_UTF8), - FSNS(XML_w, XML_date ), aDate, - FSNS(XML_w, XML_name), bookmarkName); + FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId), + FSNS(XML_w, XML_author ), bRemovePersonalInfo + ? "Author" + OString::number( GetExport().GetInfoID(rAuthor) ) + : OUStringToOString(rAuthor, RTL_TEXTENCODING_UTF8), + FSNS(XML_w, XML_name), bookmarkName); + else + m_pSerializer->singleElementNS(XML_w, bFrom + ? XML_moveFromRangeStart + : XML_moveToRangeStart, + FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId), + FSNS(XML_w, XML_author ), bRemovePersonalInfo + ? "Author" + OString::number( GetExport().GetInfoID(rAuthor) ) + : OUStringToOString(rAuthor, RTL_TEXTENCODING_UTF8), + FSNS(XML_w, XML_date ), DateTimeToOString( aDateTime ), + FSNS(XML_w, XML_name), bookmarkName); } void DocxAttributeOutput::DoWriteMoveRangeTagEnd(sal_Int32 const nId, bool bFrom) @@ -3486,9 +3502,9 @@ void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData) OString aId( OString::number( pRedlineData->GetSeqNo() ) ); const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) ); - OString aDate( DateTimeToOString( bRemovePersonalInfo - ? DateTime(Date( 1, 1, 1970 )) // Epoch time - : pRedlineData->GetTimeStamp() ) ); + const DateTime aDateTime = pRedlineData->GetTimeStamp(); + bool bNoDate = bRemovePersonalInfo || + ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 ); switch( pRedlineData->GetType() ) { @@ -3499,12 +3515,19 @@ void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData) break; case RedlineType::Format: - m_pSerializer->startElementNS( XML_w, XML_rPrChange, + if ( bNoDate ) + m_pSerializer->startElementNS( XML_w, XML_rPrChange, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), bRemovePersonalInfo + ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) + : rAuthor ); + else + m_pSerializer->startElementNS( XML_w, XML_rPrChange, FSNS( XML_w, XML_id ), aId, FSNS( XML_w, XML_author ), bRemovePersonalInfo ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) : rAuthor, - FSNS( XML_w, XML_date ), aDate ); + FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) ); // Check if there is any extra data stored in the redline object if (pRedlineData->GetExtraData()) @@ -3538,12 +3561,19 @@ void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData) break; case RedlineType::ParagraphFormat: - m_pSerializer->startElementNS( XML_w, XML_pPrChange, + if ( bNoDate ) + m_pSerializer->startElementNS( XML_w, XML_pPrChange, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), bRemovePersonalInfo + ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) + : rAuthor ); + else + m_pSerializer->startElementNS( XML_w, XML_pPrChange, FSNS( XML_w, XML_id ), aId, FSNS( XML_w, XML_author ), bRemovePersonalInfo ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) : rAuthor, - FSNS( XML_w, XML_date ), aDate ); + FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) ); // Check if there is any extra data stored in the redline object if (pRedlineData->GetExtraData()) @@ -3624,27 +3654,29 @@ void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData ) ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) : rAuthor, RTL_TEXTENCODING_UTF8 ) ); - OString aDate( DateTimeToOString( bRemovePersonalInfo - ? DateTime(Date( 1, 1, 1970 )) // Epoch time - : pRedlineData->GetTimeStamp() ) ); - + const DateTime aDateTime = pRedlineData->GetTimeStamp(); + bool bNoDate = bRemovePersonalInfo || + ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 ); bool bMoved = pRedlineData->IsMoved(); switch ( pRedlineData->GetType() ) { case RedlineType::Insert: - m_pSerializer->startElementNS( XML_w, bMoved ? XML_moveTo : XML_ins, - FSNS( XML_w, XML_id ), aId, - FSNS( XML_w, XML_author ), aAuthor, - FSNS( XML_w, XML_date ), aDate ); - break; - case RedlineType::Delete: - m_pSerializer->startElementNS( XML_w, bMoved ? XML_moveFrom : XML_del, + { + sal_Int32 eElement = RedlineType::Insert == pRedlineData->GetType() + ? ( bMoved ? XML_moveTo : XML_ins ) + : ( bMoved ? XML_moveFrom : XML_del ); + if ( bNoDate ) + m_pSerializer->startElementNS( XML_w, eElement, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor ); + else + m_pSerializer->startElementNS( XML_w, eElement, FSNS( XML_w, XML_id ), aId, FSNS( XML_w, XML_author ), aAuthor, - FSNS( XML_w, XML_date ), aDate ); + FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) ); break; - + } case RedlineType::Format: SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRedline()" ); break; @@ -4745,15 +4777,21 @@ void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) : rAuthor, RTL_TEXTENCODING_UTF8 ) ); - OString aDate( DateTimeToOString( bRemovePersonalInfo - ? DateTime(Date( 1, 1, 1970 )) // Epoch time - : aRedlineData.GetTimeStamp() ) ); + const DateTime aDateTime = aRedlineData.GetTimeStamp(); + bool bNoDate = bRemovePersonalInfo || + ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 ); - m_pSerializer->singleElementNS( XML_w, + if ( bNoDate ) + m_pSerializer->singleElementNS( XML_w, + RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins, + FSNS( XML_w, XML_id ), aId, + FSNS( XML_w, XML_author ), aAuthor ); + else + m_pSerializer->singleElementNS( XML_w, RedlineType::Delete == pRedline->GetType() ? XML_del : XML_ins, FSNS( XML_w, XML_id ), aId, FSNS( XML_w, XML_author ), aAuthor, - FSNS( XML_w, XML_date ), aDate ); + FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) ); return; } } @@ -4787,20 +4825,21 @@ void DocxAttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_ ? "Author" + OUString::number( GetExport().GetInfoID(rAuthor) ) : rAuthor, RTL_TEXTENCODING_UTF8 ) ); - OString aDate( DateTimeToOString( bRemovePersonalInfo - ? DateTime(Date( 1, 1, 1970 )) // Epoch time - : aRedlineData.GetTimeStamp() ) ); - - if (nRedlineType == RedlineType::TableCellInsert) - m_pSerializer->singleElementNS( XML_w, XML_cellIns, + sal_Int32 nElement = nRedlineType == RedlineType::TableCellInsert + ? XML_cellIns + : XML_cellDel; + const DateTime aDateTime = aRedlineData.GetTimeStamp(); + bool bNoDate = bRemovePersonalInfo || ( aDateTime.GetYear() == 1970 && + aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 ); + if ( bNoDate ) + m_pSerializer->singleElementNS( XML_w, nElement, FSNS( XML_w, XML_id ), aId, - FSNS( XML_w, XML_author ), aAuthor, - FSNS( XML_w, XML_date ), aDate ); - else if (nRedlineType == RedlineType::TableCellDelete) - m_pSerializer->singleElementNS( XML_w, XML_cellDel, + FSNS( XML_w, XML_author ), aAuthor ); + else + m_pSerializer->singleElementNS( XML_w, nElement, FSNS( XML_w, XML_id ), aId, FSNS( XML_w, XML_author ), aAuthor, - FSNS( XML_w, XML_date ), aDate ); + FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ) ); } break; default: break; @@ -8499,14 +8538,24 @@ DocxAttributeOutput::hasResolved DocxAttributeOutput::WritePostitFields() 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 ), bRemovePersonalInfo + const DateTime aDateTime = f->GetDateTime(); + bool bNoDate = bRemovePersonalInfo || + ( aDateTime.GetYear() == 1970 && aDateTime.GetMonth() == 1 && aDateTime.GetDay() == 1 ); + if ( bNoDate ) + m_pSerializer->startElementNS( XML_w, XML_comment, FSNS( XML_w, XML_id ), idstr, + FSNS( XML_w, XML_author ), bRemovePersonalInfo + ? "Author" + OUString::number( GetExport().GetInfoID(f->GetPar1()) ) + : f->GetPar1(), + FSNS( XML_w, XML_initials ), bRemovePersonalInfo + ? OUString::number( GetExport().GetInfoID(f->GetInitials()) ) + : f->GetInitials() ); + else + m_pSerializer->startElementNS( XML_w, XML_comment, FSNS( XML_w, XML_id ), idstr, + 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 + FSNS( XML_w, XML_date ), DateTimeToOString( aDateTime ), + FSNS( XML_w, XML_initials ), bRemovePersonalInfo ? OUString::number( GetExport().GetInfoID(f->GetInitials()) ) : f->GetInitials() );