bump product version to 7.6.3.2-android
[LibreOffice.git] / forms / source / xforms / submission.cxx
blobe0d312aa63f0682c0bf09d1e2ece6b3feb0b03fa
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
22 #include "model.hxx"
23 #include "binding.hxx"
24 #include "mip.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>
55 #include <memory>
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;
68 using xforms::Model;
69 using xforms::MIP;
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() :
76 mbIndent(),
77 mbOmitXmlDeclaration(),
78 mbStandalone(),
79 msReplace( "none" )
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 )
98 msID = sID;
102 void Submission::setBind( const OUString& sBind )
104 msBind = 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 )
120 msAction = sAction;
124 void Submission::setMethod( const OUString& sMethod )
126 msMethod = sMethod;
130 void Submission::setVersion( const OUString& sVersion )
132 msVersion = sVersion;
136 void Submission::setIndent( bool bIndent )
138 mbIndent = 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 )
191 liveCheck();
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();
211 else
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
221 if( ! xResult.is() )
222 return false;
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));
240 else
242 OSL_FAIL("Unsupported xforms submission method");
243 return false;
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)
254 return false;
255 else
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)
268 return false;
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();
287 if( ! bValid )
288 throw RuntimeException();
291 css::uno::Reference<XModel> Submission::getModel() const
293 return mxModel;
297 // Property-Set implementation
300 #define HANDLE_ID 0
301 #define HANDLE_Bind 1
302 #define HANDLE_Ref 2
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
379 OUString sTokenList;
380 if ( rValue >>= sTokenList )
382 std::vector< OUString > aPrefixes;
383 sal_Int32 p = 0;
384 while ( p >= 0 )
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()
397 return getID();
400 void SAL_CALL Submission::setName( const OUString& sID )
402 setID( sID );
406 static OUString lcl_message( std::u16string_view rID, std::u16string_view rText )
408 OUString aMessage = OUString::Concat("XForms submission '") + rID + "' failed" + rText + ".";
409 return aMessage;
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.",
423 *this
426 // #i36765# #i47248# warning on submission of illegal data
427 // check for validity (and query user if invalid)
428 bool bValid = xModel->isValid();
429 if( ! bValid )
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() )
456 bValid = true;
459 // abort if invalid (and user didn't tell us to continue)
460 if( ! bValid )
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?" );
473 // allowed to leave
474 throw;
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" ),
482 *this, anyEx );
485 if( !bResult )
487 // other failure: throw wrapped target exception, too.
488 throw WrappedTargetException(
489 lcl_message( sID, std::u16string_view() ), *this, Any() );
491 mxModel->rebuild();
494 void SAL_CALL Submission::submit( )
496 submitWithInteraction( nullptr );
499 void SAL_CALL Submission::addSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ )
501 // TODO
502 throw NoSupportException();
505 void SAL_CALL Submission::removeSubmissionVetoListener( const Reference< XSubmissionVetoListener >& /*listener*/ )
507 // TODO
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;
520 return false;
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;
532 if (!cur.is())
533 return;
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;
549 // result
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();
558 return aDocument;
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);
582 return aFragment;
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: */