include/sfx2/classificationhelper.hxx | 10 +++ sfx2/source/view/classificationhelper.cxx | 29 +++++++++ sw/qa/extras/rtfimport/data/custom-doc-props.rtf | 10 +++ sw/qa/extras/rtfimport/rtfimport.cxx | 10 +++ sw/qa/extras/uiwriter/uiwriter.cxx | 27 +++++++++ sw/source/uibase/dochdl/swdtflvr.cxx | 29 +++------ writerfilter/source/rtftok/rtfcontrolwords.hxx | 3 + writerfilter/source/rtftok/rtfdocumentimpl.cxx | 68 +++++++++++++++++++++++ writerfilter/source/rtftok/rtfdocumentimpl.hxx | 5 + 9 files changed, 173 insertions(+), 18 deletions(-)
New commits: commit 8ff32dc66e3adb00209c82bddebfbeb47a275066 Author: Miklos Vajna <[email protected]> Date: Wed Mar 9 12:21:36 2016 +0100 RTF import: handle \staticval With this, user-defined document properties are imported from RTF. Change-Id: I8dfb8e802bd26906827620550d6f5d88f047d364 diff --git a/sw/qa/extras/rtfimport/data/custom-doc-props.rtf b/sw/qa/extras/rtfimport/data/custom-doc-props.rtf new file mode 100644 index 0000000..b36d864 --- /dev/null +++ b/sw/qa/extras/rtfimport/data/custom-doc-props.rtf @@ -0,0 +1,10 @@ +{\rtf1 +{\*\userprops +{\propname urn:bails:IntellectualProperty:Authorization:StartValidity} +\proptype30 +{\staticval 2016-03-08T10:55:18,531376147} +{\propname urn:bails:IntellectualProperty:Authorization:StopValidity} +\proptype30 +{\staticval None} +} +} diff --git a/sw/qa/extras/rtfimport/rtfimport.cxx b/sw/qa/extras/rtfimport/rtfimport.cxx index d98b9b9..6ee8729 100644 --- a/sw/qa/extras/rtfimport/rtfimport.cxx +++ b/sw/qa/extras/rtfimport/rtfimport.cxx @@ -2525,6 +2525,16 @@ DECLARE_RTFIMPORT_TEST(testTdf87034, "tdf87034.rtf") CPPUNIT_ASSERT_EQUAL(OUString("A1B3C4D"), getParagraph(1)->getString()); } +DECLARE_RTFIMPORT_TEST(testCustomDocProps, "custom-doc-props.rtf") +{ + // Custom document properties were not improved, this resulted in a beans::UnknownPropertyException. + uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<document::XDocumentProperties> xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties(); + uno::Reference<beans::XPropertyContainer> xUserDefinedProperties = xDocumentProperties->getUserDefinedProperties(); + CPPUNIT_ASSERT_EQUAL(OUString("2016-03-08T10:55:18,531376147"), getProperty<OUString>(xUserDefinedProperties, "urn:bails:IntellectualProperty:Authorization:StartValidity")); + CPPUNIT_ASSERT_EQUAL(OUString("None"), getProperty<OUString>(xUserDefinedProperties, "urn:bails:IntellectualProperty:Authorization:StopValidity")); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfcontrolwords.hxx b/writerfilter/source/rtftok/rtfcontrolwords.hxx index dab8196..80a2d1e 100644 --- a/writerfilter/source/rtftok/rtfcontrolwords.hxx +++ b/writerfilter/source/rtftok/rtfcontrolwords.hxx @@ -158,6 +158,7 @@ enum class Destination TOCENTRY, USERPROPS, PROPNAME, + STATICVAL, }; enum RTFKeyword diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx index 1fa5cbc..6dd561b 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx @@ -1291,6 +1291,7 @@ void RTFDocumentImpl::text(OUString& rString) case Destination::INDEXENTRY: case Destination::TOCENTRY: case Destination::PROPNAME: + case Destination::STATICVAL: m_aStates.top().pDestinationText->append(rString); break; default: @@ -2059,6 +2060,9 @@ RTFError RTFDocumentImpl::dispatchDestination(RTFKeyword nKeyword) case RTF_PROPNAME: m_aStates.top().eDestination = Destination::PROPNAME; break; + case RTF_STATICVAL: + m_aStates.top().eDestination = Destination::STATICVAL; + break; default: { // Check if it's a math token. @@ -5108,6 +5112,14 @@ void RTFDocumentImpl::resetAttributes() m_aStates.top().aParagraphAttributes.clear(); } +bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties, const OUString& rName) +{ + return std::find_if(rProperties.begin(), rProperties.end(), [&](const beans::Property& rProperty) + { + return rProperty.Name == rName; + }) != rProperties.end(); +} + RTFError RTFDocumentImpl::popState() { //SAL_INFO("writerfilter", OSL_THIS_FUNC << " before pop: m_pTokenizer->getGroup() " << m_pTokenizer->getGroup() << @@ -5897,6 +5909,35 @@ RTFError RTFDocumentImpl::popState() break; // not for nested group aState.aPropName = m_aStates.top().pDestinationText->makeStringAndClear(); break; + case Destination::STATICVAL: + if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText) + break; // not for nested group + if (m_xDocumentProperties.is()) + { + // Find out what is the key, value type and value we want to set. + uno::Reference<beans::XPropertyContainer> xPropertyContainer = m_xDocumentProperties->getUserDefinedProperties(); + uno::Reference<beans::XPropertySet> xPropertySet(xPropertyContainer, uno::UNO_QUERY); + uno::Sequence<beans::Property> aProperties = xPropertySet->getPropertySetInfo()->getProperties(); + const OUString& rKey = m_aStates.top().aPropName; + OUString aStaticVal = m_aStates.top().pDestinationText->makeStringAndClear(); + uno::Any aAny; + if (m_aStates.top().aPropType == cppu::UnoType<OUString>::get()) + aAny = uno::makeAny(aStaticVal); + + // Set it. + try + { + if (lcl_containsProperty(aProperties, rKey)) + xPropertySet->setPropertyValue(rKey, aAny); + else + xPropertyContainer->addProperty(rKey, beans::PropertyAttribute::REMOVABLE, aAny); + } + catch (const uno::Exception& rException) + { + SAL_WARN("writerfilter", "failed to set property " << rKey << ": " << rException.Message); + } + } + break; default: break; } commit d11b43fe02a6daf2384d50de14f98fbfd55c180e Author: Miklos Vajna <[email protected]> Date: Wed Mar 9 12:05:17 2016 +0100 RTF import: handle \proptype Change-Id: I398785ff0ac47a678e3171982119a85b107404b4 diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx index 7d7ee39..1fa5cbc 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx @@ -4808,6 +4808,16 @@ RTFError RTFDocumentImpl::dispatchValue(RTFKeyword nKeyword, int nParam) lcl_putNestedSprm(m_aStates.top().aTableRowSprms, NS_ooxml::LN_CT_TblPrBase_tblCellMar, NS_ooxml::LN_CT_TblCellMar_right, std::make_shared<RTFValue>(aAttributes)); } break; + case RTF_PROPTYPE: + { + switch (nParam) + { + case 30: + m_aStates.top().aPropType = cppu::UnoType<OUString>::get(); + break; + } + } + break; default: { SAL_INFO("writerfilter", "TODO handle value '" << lcl_RtfToString(nKeyword) << "'"); diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.hxx b/writerfilter/source/rtftok/rtfdocumentimpl.hxx index 9445ac9..fe239a4 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.hxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.hxx @@ -300,6 +300,8 @@ public: /// User-defined property: key name. OUString aPropName; + /// User-defined property: value type. + css::uno::Type aPropType; }; /// An RTF stack is similar to std::stack, except that it has an operator[]. commit cf773edad7033b23c53b5e9a60ecc0b56d7f83f9 Author: Miklos Vajna <[email protected]> Date: Wed Mar 9 11:59:14 2016 +0100 RTF import: handle \userprops and \propname Change-Id: I3fa0be5186603006e671779933625efff5d31867 diff --git a/writerfilter/source/rtftok/rtfcontrolwords.hxx b/writerfilter/source/rtftok/rtfcontrolwords.hxx index dfbbabf..dab8196 100644 --- a/writerfilter/source/rtftok/rtfcontrolwords.hxx +++ b/writerfilter/source/rtftok/rtfcontrolwords.hxx @@ -156,6 +156,8 @@ enum class Destination FOOTNOTESEPARATOR, INDEXENTRY, TOCENTRY, + USERPROPS, + PROPNAME, }; enum RTFKeyword diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx index 187eb5a..7d7ee39 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx @@ -1290,6 +1290,7 @@ void RTFDocumentImpl::text(OUString& rString) case Destination::MGROW: case Destination::INDEXENTRY: case Destination::TOCENTRY: + case Destination::PROPNAME: m_aStates.top().pDestinationText->append(rString); break; default: @@ -2051,6 +2052,13 @@ RTFError RTFDocumentImpl::dispatchDestination(RTFKeyword nKeyword) m_aStates.top().eDestination = Destination::FOOTNOTESEPARATOR; m_aStates.top().aCharacterAttributes.set(NS_ooxml::LN_CT_FtnEdn_type, std::make_shared<RTFValue>(NS_ooxml::LN_Value_doc_ST_FtnEdn_separator)); break; + case RTF_USERPROPS: + // Container of all user-defined properties. + m_aStates.top().eDestination = Destination::USERPROPS; + break; + case RTF_PROPNAME: + m_aStates.top().eDestination = Destination::PROPNAME; + break; default: { // Check if it's a math token. @@ -5874,6 +5882,11 @@ RTFError RTFDocumentImpl::popState() if (aState.bCreatedShapeGroup) m_pSdrImport->popParent(); break; + case Destination::PROPNAME: + if (&m_aStates.top().aDestinationText != m_aStates.top().pDestinationText) + break; // not for nested group + aState.aPropName = m_aStates.top().pDestinationText->makeStringAndClear(); + break; default: break; } @@ -6143,6 +6156,10 @@ RTFError RTFDocumentImpl::popState() m_aStates.top().aDrawingObject.nBottom = aState.aDrawingObject.nBottom; } break; + case Destination::PROPNAME: + if (m_aStates.top().eDestination == Destination::USERPROPS) + m_aStates.top().aPropName = aState.aPropName; + break; default: { if (!m_aStates.empty() && m_aStates.top().eDestination == Destination::PICT) diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.hxx b/writerfilter/source/rtftok/rtfdocumentimpl.hxx index 37727d8..9445ac9 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.hxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.hxx @@ -297,6 +297,9 @@ public: bool bInShape; ///< If we're inside a \shp group. bool bCreatedShapeGroup; ///< A GroupShape was created and pushed to the parent stack. bool bStartedTrackchange; ///< Track change is started, need to end it before popping. + + /// User-defined property: key name. + OUString aPropName; }; /// An RTF stack is similar to std::stack, except that it has an operator[]. commit 38dd218425b3e4a56d71af7082eea1dda24b48da Author: Miklos Vajna <[email protected]> Date: Wed Mar 9 11:32:12 2016 +0100 CppunitTest_sw_uiwriter: add classification copy/paste testcase Change-Id: Ic3ac02fe28bee0ea3189fb2c9d3548f5ca13fe5c diff --git a/sw/qa/extras/uiwriter/uiwriter.cxx b/sw/qa/extras/uiwriter/uiwriter.cxx index 96e8cd5..fced549 100644 --- a/sw/qa/extras/uiwriter/uiwriter.cxx +++ b/sw/qa/extras/uiwriter/uiwriter.cxx @@ -89,6 +89,7 @@ #include <txtfrm.hxx> #include <editeng/svxenum.hxx> #include <comphelper/propertysequence.hxx> +#include <sfx2/classificationhelper.hxx> #include <LibreOfficeKit/LibreOfficeKitEnums.h> static const char* DATA_DIRECTORY = "/sw/qa/extras/uiwriter/data/"; @@ -187,6 +188,7 @@ public: void testTdf96536(); void testTdf96479(); void testTdf96961(); + void testClassificationPaste(); CPPUNIT_TEST_SUITE(SwUiWriterTest); CPPUNIT_TEST(testReplaceForward); @@ -277,6 +279,7 @@ public: CPPUNIT_TEST(testTdf96536); CPPUNIT_TEST(testTdf96479); CPPUNIT_TEST(testTdf96961); + CPPUNIT_TEST(testClassificationPaste); CPPUNIT_TEST_SUITE_END(); private: @@ -3246,6 +3249,30 @@ void SwUiWriterTest::testTdf96961() CPPUNIT_ASSERT(nLast > nOther); } +void SwUiWriterTest::testClassificationPaste() +{ + SwDocShell* pSourceShell = createDoc()->GetDocShell(); + uno::Reference<lang::XComponent> xSourceComponent = mxComponent; + mxComponent.clear(); + + SwDocShell* pDestinationShell = createDoc()->GetDocShell(); + + // Not classified source, not classified destination. + CPPUNIT_ASSERT_EQUAL(int(SfxClassificationCheckPasteResult::None), int(SfxClassificationHelper::CheckPaste(*pSourceShell, *pDestinationShell))); + + // Classified source, not classified destination. + uno::Sequence<beans::PropertyValue> aInternalOnly = comphelper::InitPropertySequence({{"Name", uno::makeAny(OUString("Internal Only"))}}); + lcl_dispatchCommand(xSourceComponent, ".uno:ClassificationApply", aInternalOnly); + CPPUNIT_ASSERT_EQUAL(int(SfxClassificationCheckPasteResult::TargetDocNotClassified), int(SfxClassificationHelper::CheckPaste(*pSourceShell, *pDestinationShell))); + + // Classified source and classified destination -- internal only has a higher level than confidential. + uno::Sequence<beans::PropertyValue> aConfidential = comphelper::InitPropertySequence({{"Name", uno::makeAny(OUString("Confidential"))}}); + lcl_dispatchCommand(mxComponent, ".uno:ClassificationApply", aConfidential); + CPPUNIT_ASSERT_EQUAL(int(SfxClassificationCheckPasteResult::DocClassificationTooLow), int(SfxClassificationHelper::CheckPaste(*pSourceShell, *pDestinationShell))); + + xSourceComponent->dispose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SwUiWriterTest); CPPUNIT_PLUGIN_IMPLEMENT(); commit 76e2cede5a415df8d3e7a874f56be7a0b5953e12 Author: Miklos Vajna <[email protected]> Date: Wed Mar 9 10:58:25 2016 +0100 Move copy/paste classification check from sw to sfx2 So that it's easy to unit test it and other apps can use it as well in the future. Change-Id: I38d601924b7fbb17615ff6e9c031a71b40777c4c diff --git a/include/sfx2/classificationhelper.hxx b/include/sfx2/classificationhelper.hxx index 5c49db7..5a58fcc 100644 --- a/include/sfx2/classificationhelper.hxx +++ b/include/sfx2/classificationhelper.hxx @@ -23,6 +23,14 @@ namespace basegfx class BColor; } +/// Return code of SfxClassificationHelper::CheckPaste(). +enum class SfxClassificationCheckPasteResult +{ + None = 1, + TargetDocNotClassified = 2, + DocClassificationTooLow = 3 +}; + /// Shared code to handle Business Authorization Identification and Labeling Scheme (BAILS) properties. class SFX2_DLLPUBLIC SfxClassificationHelper { @@ -32,6 +40,8 @@ class SFX2_DLLPUBLIC SfxClassificationHelper public: /// Does the document have any BAILS properties? static bool IsClassified(SfxObjectShell& rObjectShell); + /// Checks if pasting from rSource to rDestination would leak information. + static SfxClassificationCheckPasteResult CheckPaste(SfxObjectShell& rSource, SfxObjectShell& rDestination); SfxClassificationHelper(SfxObjectShell& rObjectShell); ~SfxClassificationHelper(); diff --git a/sfx2/source/view/classificationhelper.cxx b/sfx2/source/view/classificationhelper.cxx index ab913b5..6950048 100644 --- a/sfx2/source/view/classificationhelper.cxx +++ b/sfx2/source/view/classificationhelper.cxx @@ -426,6 +426,35 @@ bool SfxClassificationHelper::IsClassified(SfxObjectShell& rObjectShell) return false; } +SfxClassificationCheckPasteResult SfxClassificationHelper::CheckPaste(SfxObjectShell& rSource, SfxObjectShell& rDestination) +{ + bool bSourceClassified = SfxClassificationHelper::IsClassified(rSource); + if (!bSourceClassified) + // No classification on the source side. Return early, regardless the + // state of the destination side. + return SfxClassificationCheckPasteResult::None; + + bool bDestinationClassified = SfxClassificationHelper::IsClassified(rDestination); + if (bSourceClassified && !bDestinationClassified) + { + // Paste from a classified document to a non-classified one -> deny. + return SfxClassificationCheckPasteResult::TargetDocNotClassified; + } + + // Remaining case: paste between two classified documents. + SfxClassificationHelper aSource(rSource); + SfxClassificationHelper aDestination(rDestination); + if (aSource.GetImpactScale() != aDestination.GetImpactScale()) + // It's possible to compare them if they have the same scale. + return SfxClassificationCheckPasteResult::None; + + if (aSource.GetImpactLevel() > aDestination.GetImpactLevel()) + // Paste from a doc that has higher classification -> deny. + return SfxClassificationCheckPasteResult::DocClassificationTooLow; + + return SfxClassificationCheckPasteResult::None; +} + SfxClassificationHelper::SfxClassificationHelper(SfxObjectShell& rObjectShell) : m_pImpl(o3tl::make_unique<Impl>(rObjectShell)) { diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index 04c3ae1..fb46de91 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -3222,33 +3222,26 @@ bool lcl_checkClassification(SwDoc* pSourceDoc, SwDoc* pDestinationDoc) if (!pSourceShell || !pDestinationShell) return true; - bool bSourceClassified = SfxClassificationHelper::IsClassified(*pSourceShell); - if (!bSourceClassified) - // No classification on the source side. Return early, regardless the - // state of the destination side. + switch (SfxClassificationHelper::CheckPaste(*pSourceShell, *pDestinationShell)) + { + case SfxClassificationCheckPasteResult::None: + { return true; - - bool bDestinationClassified = SfxClassificationHelper::IsClassified(*pDestinationShell); - if (bSourceClassified && !bDestinationClassified) + } + break; + case SfxClassificationCheckPasteResult::TargetDocNotClassified: { - // Paste from a classified document to a non-classified one -> deny. ScopedVclPtrInstance<MessageDialog>::Create(nullptr, SW_RES(STR_TARGET_DOC_NOT_CLASSIFIED), VCL_MESSAGE_INFO)->Execute(); return false; } - - // Remaining case: paste between two classified documents. - SfxClassificationHelper aSource(*pSourceShell); - SfxClassificationHelper aDestination(*pDestinationShell); - if (aSource.GetImpactScale() != aDestination.GetImpactScale()) - // It's possible to compare them if they have the same scale. - return true; - - if (aSource.GetImpactLevel() > aDestination.GetImpactLevel()) + break; + case SfxClassificationCheckPasteResult::DocClassificationTooLow: { - // Paste from a doc that has higher classification -> deny. ScopedVclPtrInstance<MessageDialog>::Create(nullptr, SW_RES(STR_DOC_CLASSIFICATION_TOO_LOW), VCL_MESSAGE_INFO)->Execute(); return false; } + break; + } return true; } _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
