1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "submission.hxx"
23 #include "binding.hxx"
25 #include "evaluationcontext.hxx"
26 #include "submission/submission_put.hxx"
27 #include "submission/submission_post.hxx"
28 #include "submission/submission_get.hxx"
30 #include <rtl/ustring.hxx>
31 #include <com/sun/star/lang/NoSupportException.hpp>
32 #include <com/sun/star/uno/Sequence.hxx>
33 #include <com/sun/star/uno/Reference.hxx>
34 #include <com/sun/star/xforms/XModel.hpp>
35 #include <com/sun/star/uno/RuntimeException.hpp>
36 #include <com/sun/star/xml/dom/XNodeList.hpp>
37 #include <com/sun/star/xml/dom/XDocument.hpp>
38 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
39 #include <com/sun/star/xml/dom/XDocumentFragment.hpp>
40 #include <com/sun/star/xml/dom/NodeType.hpp>
41 #include <com/sun/star/task/XInteractionHandler.hpp>
42 #include <com/sun/star/task/XInteractionRequest.hpp>
43 #include <com/sun/star/task/XInteractionContinuation.hpp>
44 #include <com/sun/star/xforms/InvalidDataOnSubmitException.hpp>
45 #include <com/sun/star/form/runtime/XFormController.hpp>
46 #include <com/sun/star/frame/XFrame.hpp>
47 #include <cppuhelper/exc_hlp.hxx>
48 #include <comphelper/interaction.hxx>
49 #include <comphelper/processfactory.hxx>
50 #include <comphelper/servicehelper.hxx>
51 #include <vcl/svapp.hxx>
52 #include <vcl/weld.hxx>
53 #include <frm_resource.hxx>
54 #include <strings.hrc>
56 #include <string_view>
58 using com::sun::star::util::VetoException
;
59 using com::sun::star::form::submission::XSubmissionVetoListener
;
60 using com::sun::star::lang::WrappedTargetException
;
61 using com::sun::star::lang::NoSupportException
;
62 using com::sun::star::task::XInteractionHandler
;
63 using com::sun::star::xforms::XModel
;
64 using com::sun::star::xforms::InvalidDataOnSubmitException
;
65 using com::sun::star::xml::xpath::XXPathObject
;
66 using com::sun::star::frame::XFrame
;
67 using xforms::Submission
;
71 using namespace com::sun::star::uno
;
72 using namespace com::sun::star::lang
;
73 using namespace com::sun::star::xml::dom
;
75 Submission::Submission() :
77 mbOmitXmlDeclaration(),
81 initializePropertySet();
84 Submission::~Submission() noexcept
89 void Submission::setModel( const Reference
<XModel
>& xModel
)
91 mxModel
= dynamic_cast<Model
*>(xModel
.get());
92 assert(bool(mxModel
)==bool(xModel
) && "we only support an instance of Model here");
96 void Submission::setID( const OUString
& sID
)
102 void Submission::setBind( const OUString
& sBind
)
107 OUString
Submission::getRef() const
109 return maRef
.getExpression();
112 void Submission::setRef( const OUString
& sRef
)
114 maRef
.setExpression( sRef
);
118 void Submission::setAction( const OUString
& sAction
)
124 void Submission::setMethod( const OUString
& sMethod
)
130 void Submission::setVersion( const OUString
& sVersion
)
132 msVersion
= sVersion
;
136 void Submission::setIndent( bool bIndent
)
142 void Submission::setMediaType( const OUString
& sMediaType
)
144 msMediaType
= sMediaType
;
148 void Submission::setEncoding( const OUString
& sEncoding
)
150 msEncoding
= sEncoding
;
154 void Submission::setOmitXmlDeclaration( bool bOmitXmlDeclaration
)
156 mbOmitXmlDeclaration
= bOmitXmlDeclaration
;
160 void Submission::setStandalone( bool bStandalone
)
162 mbStandalone
= bStandalone
;
166 void Submission::setCDataSectionElement( const OUString
& sCDataSectionElement
)
168 msCDataSectionElement
= sCDataSectionElement
;
172 void Submission::setReplace( const OUString
& sReplace
)
174 msReplace
= sReplace
;
178 void Submission::setSeparator( const OUString
& sSeparator
)
180 msSeparator
= sSeparator
;
184 void Submission::setIncludeNamespacePrefixes( const Sequence
< OUString
>& rIncludeNamespacePrefixes
)
186 msIncludeNamespacePrefixes
= rIncludeNamespacePrefixes
;
189 bool Submission::doSubmit( const Reference
< XInteractionHandler
>& xHandler
)
193 // construct XXPathObject for submission doc; use bind in preference of ref
194 EvaluationContext aEvalContext
;
195 ComputedExpression aExpression
;
196 if( !msBind
.isEmpty() )
198 Binding
* pBinding
= comphelper::getFromUnoTunnel
<Binding
>( mxModel
->getBinding(msBind
) );
199 if( pBinding
!= nullptr )
201 aExpression
.setExpression( pBinding
->getBindingExpression() );
202 aEvalContext
= pBinding
->getEvaluationContext();
204 // TODO: else: illegal binding name -> raise error
206 else if( !maRef
.getExpression().isEmpty() )
208 aExpression
.setExpression( maRef
.getExpression() );
209 aEvalContext
= mxModel
->getEvaluationContext();
213 aExpression
.setExpression( "/" );
214 aEvalContext
= mxModel
->getEvaluationContext();
216 aExpression
.evaluate( aEvalContext
);
217 Reference
<XXPathObject
> xResult
= aExpression
.getXPath();
218 OSL_ENSURE( xResult
.is(), "no result?" );
220 // early out if we have not obtained any result
225 // Reference< XNodeList > aList = xResult->getNodeList();
226 OUString aMethod
= getMethod();
228 // strip whitespace-only text node for get submission
229 Reference
< XDocumentFragment
> aFragment
= createSubmissionDocument(
230 xResult
, aMethod
.equalsIgnoreAsciiCase("get"));
232 // submit result; set encoding, etc.
233 std::unique_ptr
<CSubmission
> xSubmission
;
234 if (aMethod
.equalsIgnoreAsciiCase("PUT"))
235 xSubmission
.reset(new CSubmissionPut( getAction(), aFragment
));
236 else if (aMethod
.equalsIgnoreAsciiCase("post"))
237 xSubmission
.reset(new CSubmissionPost( getAction(), aFragment
));
238 else if (aMethod
.equalsIgnoreAsciiCase("get"))
239 xSubmission
.reset(new CSubmissionGet( getAction(), aFragment
));
242 OSL_FAIL("Unsupported xforms submission method");
246 const INetURLObject
& rURLObject
= xSubmission
->GetURLObject();
247 INetProtocol eProtocol
= rURLObject
.GetProtocol();
248 // tdf#154337 continue to allow submitting to http[s]: without further
249 // interaction. Don't allow for other protocols, except for file:
250 // where the user has to agree first.
251 if (eProtocol
!= INetProtocol::Http
&& eProtocol
!= INetProtocol::Https
)
253 if (eProtocol
!= INetProtocol::File
)
257 Reference
<css::form::runtime::XFormController
> xFormController(xHandler
, UNO_QUERY
);
258 Reference
<css::awt::XControl
> xContainerControl(xFormController
? xFormController
->getContainer() : nullptr, UNO_QUERY
);
259 Reference
<css::awt::XWindow
> xParent(xContainerControl
? xContainerControl
->getPeer() : nullptr, UNO_QUERY
);
261 OUString
aFileName(rURLObject
.getFSysPath(FSysStyle::Detect
));
262 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(Application::GetFrameWeld(xParent
),
263 VclMessageType::Question
, VclButtonsType::YesNo
,
264 frm::ResourceManager::loadString(RID_STR_XFORMS_WARN_TARGET_IS_FILE
).replaceFirst("$", aFileName
)));
265 xQueryBox
->set_default_response(RET_NO
);
267 if (xQueryBox
->run() != RET_YES
)
272 CSubmission::SubmissionResult aResult
= xSubmission
->submit( xHandler
);
274 if (aResult
== CSubmission::SUCCESS
)
276 Reference
< XDocument
> aInstanceDoc
= getInstanceDocument(xResult
);
277 aResult
= xSubmission
->replace(getReplace(), aInstanceDoc
, Reference
< XFrame
>());
280 return ( aResult
== CSubmission::SUCCESS
);
283 void Submission::liveCheck()
285 bool bValid
= mxModel
.is();
288 throw RuntimeException();
291 css::uno::Reference
<XModel
> Submission::getModel() const
297 // Property-Set implementation
301 #define HANDLE_Bind 1
303 #define HANDLE_Action 3
304 #define HANDLE_Method 4
305 #define HANDLE_Version 5
306 #define HANDLE_Indent 6
307 #define HANDLE_MediaType 7
308 #define HANDLE_Encoding 8
309 #define HANDLE_OmitXmlDeclaration 9
310 #define HANDLE_Standalone 10
311 #define HANDLE_CDataSectionElement 11
312 #define HANDLE_Replace 12
313 #define HANDLE_Separator 13
314 #define HANDLE_IncludeNamespacePrefixes 14
315 #define HANDLE_Model 15
317 void Submission::initializePropertySet()
319 registerProperty( css::beans::Property("ID", HANDLE_ID
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
320 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setID
, &Submission::getID
) );
322 registerProperty( css::beans::Property("Bind", HANDLE_Bind
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
323 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setBind
, &Submission::getBind
) );
325 registerProperty( css::beans::Property("Ref", HANDLE_Ref
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
326 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setRef
, &Submission::getRef
) );
328 registerProperty( css::beans::Property("Action", HANDLE_Action
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
329 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setAction
, &Submission::getAction
) );
331 registerProperty( css::beans::Property("Method", HANDLE_Method
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
332 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setMethod
, &Submission::getMethod
) );
334 registerProperty( css::beans::Property("Version", HANDLE_Version
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
335 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setVersion
, &Submission::getVersion
) );
337 registerProperty( css::beans::Property("Indent", HANDLE_Indent
, cppu::UnoType
<bool>::get(), css::beans::PropertyAttribute::BOUND
),
338 new BooleanPropertyAccessor
< Submission
>(this, &Submission::setIndent
, &Submission::getIndent
));
340 registerProperty( css::beans::Property("MediaType", HANDLE_MediaType
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
341 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setMediaType
, &Submission::getMediaType
) );
343 registerProperty( css::beans::Property("Encoding", HANDLE_Encoding
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
344 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setEncoding
, &Submission::getEncoding
) );
346 registerProperty( css::beans::Property("OmitXmlDeclaration", HANDLE_OmitXmlDeclaration
, cppu::UnoType
<bool>::get(), css::beans::PropertyAttribute::BOUND
),
347 new BooleanPropertyAccessor
< Submission
>(this, &Submission::setOmitXmlDeclaration
, &Submission::getOmitXmlDeclaration
));
349 registerProperty( css::beans::Property("Standalone", HANDLE_Standalone
, cppu::UnoType
<bool>::get(), css::beans::PropertyAttribute::BOUND
),
350 new BooleanPropertyAccessor
< Submission
>(this, &Submission::setStandalone
, &Submission::getStandalone
));
352 registerProperty( css::beans::Property("CDataSectionElement", HANDLE_CDataSectionElement
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
353 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setCDataSectionElement
, &Submission::getCDataSectionElement
) );
355 registerProperty( css::beans::Property("Replace", HANDLE_Replace
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
356 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setReplace
, &Submission::getReplace
) );
358 registerProperty( css::beans::Property("Separator", HANDLE_Separator
, cppu::UnoType
<OUString
>::get(), css::beans::PropertyAttribute::BOUND
),
359 new DirectPropertyAccessor
< Submission
, OUString
>(this, &Submission::setSeparator
, &Submission::getSeparator
) );
361 registerProperty( css::beans::Property("IncludeNamespacePrefixes", HANDLE_IncludeNamespacePrefixes
, cppu::UnoType
<Sequence
<OUString
>>::get(), css::beans::PropertyAttribute::BOUND
),
362 new DirectPropertyAccessor
< Submission
, Sequence
<OUString
> >(this, &Submission::setIncludeNamespacePrefixes
, &Submission::getIncludeNamespacePrefixes
) );
364 registerProperty( css::beans::Property("Model", HANDLE_Model
, cppu::UnoType
<Reference
<XModel
>>::get(), css::beans::PropertyAttribute::BOUND
),
365 new DirectPropertyAccessor
< Submission
, Reference
<XModel
> >(this, &Submission::setModel
, &Submission::getModel
) );
367 initializePropertyValueCache( HANDLE_Indent
);
368 initializePropertyValueCache( HANDLE_OmitXmlDeclaration
);
369 initializePropertyValueCache( HANDLE_Standalone
);
372 sal_Bool SAL_CALL
Submission::convertFastPropertyValue(
373 Any
& rConvertedValue
, Any
& rOldValue
, sal_Int32 nHandle
, const Any
& rValue
)
375 if ( nHandle
== HANDLE_IncludeNamespacePrefixes
)
377 // for convenience reasons (????), we accept a string which contains
378 // a comma-separated list of namespace prefixes
380 if ( rValue
>>= sTokenList
)
382 std::vector
< OUString
> aPrefixes
;
385 aPrefixes
.push_back( sTokenList
.getToken( 0, ',', p
) );
387 Sequence
< OUString
> aConvertedPrefixes( aPrefixes
.data(), aPrefixes
.size() );
388 return PropertySetBase::convertFastPropertyValue( rConvertedValue
, rOldValue
, nHandle
, Any( aConvertedPrefixes
) );
392 return PropertySetBase::convertFastPropertyValue( rConvertedValue
, rOldValue
, nHandle
, rValue
);
395 OUString SAL_CALL
Submission::getName()
400 void SAL_CALL
Submission::setName( const OUString
& sID
)
406 static OUString
lcl_message( std::u16string_view rID
, std::u16string_view rText
)
408 OUString aMessage
= OUString::Concat("XForms submission '") + rID
+ "' failed" + rText
+ ".";
412 void SAL_CALL
Submission::submitWithInteraction(
413 const Reference
<XInteractionHandler
>& _rxHandler
)
415 // as long as this class is not really threadsafe, we need to copy
416 // the members we're interested in
417 rtl::Reference
< Model
> xModel( mxModel
);
418 OUString
sID( msID
);
420 if ( !xModel
.is() || msID
.isEmpty() )
421 throw RuntimeException(
422 "This is not a valid submission object.",
426 // #i36765# #i47248# warning on submission of illegal data
427 // check for validity (and query user if invalid)
428 bool bValid
= xModel
->isValid();
431 InvalidDataOnSubmitException
aInvalidDataException(
432 lcl_message(sID
, u
" due to invalid data" ), *this );
434 if( _rxHandler
.is() )
436 // laboriously create interaction request
437 rtl::Reference
<comphelper::OInteractionRequest
> pRequest
438 = new comphelper::OInteractionRequest(
439 Any( aInvalidDataException
) );
441 rtl::Reference
<comphelper::OInteractionApprove
> pContinue
442 = new comphelper::OInteractionApprove();
443 pRequest
->addContinuation( pContinue
);
445 rtl::Reference
<comphelper::OInteractionDisapprove
> pCancel
446 = new comphelper::OInteractionDisapprove();
447 pRequest
->addContinuation( pCancel
);
449 // ask the handler...
450 _rxHandler
->handle( pRequest
);
451 OSL_ENSURE( pContinue
->wasSelected() || pCancel
->wasSelected(),
452 "handler didn't select" );
454 // and continue, if user chose 'continue'
455 if( pContinue
->wasSelected() )
459 // abort if invalid (and user didn't tell us to continue)
461 throw aInvalidDataException
;
464 // attempt submission
465 bool bResult
= false;
468 bResult
= doSubmit( _rxHandler
);
470 catch( const VetoException
& )
472 OSL_FAIL( "Model::submit: Hmm. How can a single submission have a veto right?" );
476 catch( const Exception
& )
478 css::uno::Any anyEx
= cppu::getCaughtException();
479 // exception caught: re-throw as wrapped target exception
480 throw WrappedTargetException(
481 lcl_message( sID
, u
" due to exception being thrown" ),
487 // other failure: throw wrapped target exception, too.
488 throw WrappedTargetException(
489 lcl_message( sID
, std::u16string_view() ), *this, Any() );
494 void SAL_CALL
Submission::submit( )
496 submitWithInteraction( nullptr );
499 void SAL_CALL
Submission::addSubmissionVetoListener( const Reference
< XSubmissionVetoListener
>& /*listener*/ )
502 throw NoSupportException();
505 void SAL_CALL
Submission::removeSubmissionVetoListener( const Reference
< XSubmissionVetoListener
>& /*listener*/ )
508 throw NoSupportException();
511 static bool isIgnorable(const Reference
< XNode
>& aNode
)
513 // ignore whitespace-only textnodes
514 if (aNode
->getNodeType() == NodeType_TEXT_NODE
)
516 OUString aTrimmedValue
= aNode
->getNodeValue().trim();
517 if (aTrimmedValue
.isEmpty()) return true;
523 // recursively copy relevant nodes from A to B
524 static void cloneNodes(Model
& aModel
, const Reference
< XNode
>& dstParent
, const Reference
< XNode
>& source
, bool bRemoveWSNodes
)
526 if (!source
.is()) return;
528 Reference
< XNode
> cur
= source
;
529 Reference
< XDocument
> dstDoc
= dstParent
->getOwnerDocument();
530 Reference
< XNode
> imported
;
535 // is this node relevant?
536 MIP mip
= aModel
.queryMIP(cur
);
537 if(mip
.isRelevant() && !(bRemoveWSNodes
&& isIgnorable(cur
)))
539 imported
= dstDoc
->importNode(cur
, false);
540 imported
= dstParent
->appendChild(imported
);
541 // append source children to new imported parent
542 for( cur
= cur
->getFirstChild(); cur
.is(); cur
= cur
->getNextSibling() )
543 cloneNodes(aModel
, imported
, cur
, bRemoveWSNodes
);
546 Reference
< XDocument
> Submission::getInstanceDocument(const Reference
< XXPathObject
>& aObj
)
548 using namespace css::xml::xpath
;
550 Reference
< XDocument
> aDocument
;
552 if (aObj
->getObjectType() == XPathObjectType_XPATH_NODESET
)
554 Reference
< XNodeList
> aList
= aObj
->getNodeList();
555 if (aList
->getLength() > 0)
556 aDocument
= aList
->item(0)->getOwnerDocument();
561 Reference
< XDocumentFragment
> Submission::createSubmissionDocument(const Reference
< XXPathObject
>& aObj
, bool bRemoveWSNodes
)
563 using namespace css::xml::xpath
;
564 Reference
< XDocumentBuilder
> aDocBuilder
= DocumentBuilder::create(comphelper::getProcessComponentContext());
565 Reference
< XDocument
> aDocument
= aDocBuilder
->newDocument();
566 Reference
< XDocumentFragment
> aFragment
= aDocument
->createDocumentFragment();
569 if (aObj
->getObjectType() == XPathObjectType_XPATH_NODESET
)
571 Reference
< XNodeList
> aList
= aObj
->getNodeList();
572 Reference
< XNode
> aListItem
;
573 for (sal_Int32 i
=0; i
< aList
->getLength(); i
++)
575 aListItem
= aList
->item(i
);
576 if (aListItem
->getNodeType()==NodeType_DOCUMENT_NODE
)
577 aListItem
= (Reference
< XDocument
>(aListItem
, UNO_QUERY
))->getDocumentElement();
578 // copy relevant nodes from instance into fragment
579 cloneNodes(*getModelImpl(), aFragment
, aListItem
, bRemoveWSNodes
);
585 // some forwarding: XPropertySet is implemented in our base class,
586 // but also available as base of XSubmission
587 Reference
< css::beans::XPropertySetInfo
> SAL_CALL
Submission::getPropertySetInfo( )
589 return PropertySetBase::getPropertySetInfo();
591 void SAL_CALL
Submission::setPropertyValue( const OUString
& aPropertyName
, const Any
& aValue
)
593 PropertySetBase::setPropertyValue( aPropertyName
, aValue
);
595 Any SAL_CALL
Submission::getPropertyValue( const OUString
& PropertyName
)
597 return PropertySetBase::getPropertyValue( PropertyName
);
599 void SAL_CALL
Submission::addPropertyChangeListener( const OUString
& aPropertyName
, const Reference
< css::beans::XPropertyChangeListener
>& xListener
)
601 PropertySetBase::addPropertyChangeListener( aPropertyName
, xListener
);
603 void SAL_CALL
Submission::removePropertyChangeListener( const OUString
& aPropertyName
, const Reference
< css::beans::XPropertyChangeListener
>& aListener
)
605 PropertySetBase::removePropertyChangeListener( aPropertyName
, aListener
);
607 void SAL_CALL
Submission::addVetoableChangeListener( const OUString
& PropertyName
, const Reference
< css::beans::XVetoableChangeListener
>& aListener
)
609 PropertySetBase::addVetoableChangeListener( PropertyName
, aListener
);
611 void SAL_CALL
Submission::removeVetoableChangeListener( const OUString
& PropertyName
, const Reference
< css::beans::XVetoableChangeListener
>& aListener
)
613 PropertySetBase::removeVetoableChangeListener( PropertyName
, aListener
);
616 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */