/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::ucbhelper; using namespace ::com::sun::star::task; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::ucb; using namespace ::com::sun::star::io; using namespace ::com::sun::star::util; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::container; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::document; using namespace ::com::sun::star::sdb; using namespace ::com::sun::star::embed; using namespace ::com::sun::star::ui::dialogs; using ::com::sun::star::awt::XWindow; using ::com::sun::star::sdb::application::NamedDatabaseObject; namespace dbaxml { namespace { class DBTypeDetection : public ::cppu::WeakImplHelper< XExtendedFilterDetection, XServiceInfo> { const Reference< XComponentContext > m_aContext; public: explicit DBTypeDetection(const Reference< XComponentContext >&); // XServiceInfo OUString SAL_CALL getImplementationName() override; sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; Sequence< OUString > SAL_CALL getSupportedServiceNames() override; virtual OUString SAL_CALL detect( css::uno::Sequence< css::beans::PropertyValue >& Descriptor ) override; }; } DBTypeDetection::DBTypeDetection(const Reference< XComponentContext >& _rxContext) :m_aContext( _rxContext ) { } OUString SAL_CALL DBTypeDetection::detect( css::uno::Sequence< css::beans::PropertyValue >& Descriptor ) { try { ::comphelper::NamedValueCollection aMedia( Descriptor ); bool bStreamFromDescr = false; OUString sURL = aMedia.getOrDefault( "URL", OUString() ); Reference< XInputStream > xInStream( aMedia.getOrDefault( "InputStream", Reference< XInputStream >() ) ); Reference< XPropertySet > xStorageProperties; if ( xInStream.is() ) { bStreamFromDescr = true; xStorageProperties.set( ::comphelper::OStorageHelper::GetStorageFromInputStream( xInStream, m_aContext ), UNO_QUERY ); } else { OUString sSalvagedURL( aMedia.getOrDefault( "SalvagedFile", OUString() ) ); OUString sFileLocation( sSalvagedURL.isEmpty() ? sURL : sSalvagedURL ); if ( !sFileLocation.isEmpty() ) { xStorageProperties.set( ::comphelper::OStorageHelper::GetStorageFromURL( sFileLocation, ElementModes::READ, m_aContext ), UNO_QUERY ); } } if ( xStorageProperties.is() ) { OUString sMediaType; xStorageProperties->getPropertyValue( INFO_MEDIATYPE ) >>= sMediaType; if ( sMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII || sMediaType == MIMETYPE_VND_SUN_XML_BASE_ASCII ) { if ( bStreamFromDescr && !sURL.startsWith( "private:stream" ) ) { // After fixing of the i88522 issue ( use the new file locking for database files ) the stream from the type detection can be used further // for now the file should be reopened to have read/write access aMedia.remove( "InputStream" ); aMedia.remove( "Stream" ); aMedia >>= Descriptor; try { ::comphelper::disposeComponent(xStorageProperties); if ( xInStream.is() ) xInStream->closeInput(); } catch( Exception& ) { DBG_UNHANDLED_EXCEPTION("dbaccess"); } } return "StarBase"; } ::comphelper::disposeComponent(xStorageProperties); } } catch(Exception&){} return OUString(); } // XServiceInfo OUString SAL_CALL DBTypeDetection::getImplementationName() { return "org.openoffice.comp.dbflt.DBTypeDetection"; } // XServiceInfo sal_Bool SAL_CALL DBTypeDetection::supportsService(const OUString& ServiceName) { return cppu::supportsService(this, ServiceName); } // XServiceInfo Sequence< OUString > SAL_CALL DBTypeDetection::getSupportedServiceNames() { return { "com.sun.star.document.ExtendedTypeDetection" }; } } // namespace dbaxml extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* org_openoffice_comp_dbflt_DBTypeDetection_get_implementation( css::uno::XComponentContext* context, css::uno::Sequence const& ) { return cppu::acquire(new ::dbaxml::DBTypeDetection(context)); } namespace dbaxml { namespace { class DBContentLoader : public ::cppu::WeakImplHelper< XFrameLoader, XServiceInfo> { private: const Reference< XComponentContext > m_aContext; Reference< XFrameLoader > m_xMySelf; OUString m_sCurrentURL; ImplSVEvent * m_nStartWizard; DECL_LINK( OnStartTableWizard, void*, void ); public: explicit DBContentLoader(const Reference< XComponentContext >&); // XServiceInfo OUString SAL_CALL getImplementationName() override; sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; Sequence< OUString > SAL_CALL getSupportedServiceNames() override; // XLoader virtual void SAL_CALL load( const Reference< XFrame > & _rFrame, const OUString& _rURL, const Sequence< PropertyValue >& _rArgs, const Reference< XLoadEventListener > & _rListener) override; virtual void SAL_CALL cancel() override; private: bool impl_executeNewDatabaseWizard( Reference< XModel > const & _rxModel, bool& _bShouldStartTableWizard ); }; } DBContentLoader::DBContentLoader(const Reference< XComponentContext >& _rxFactory) :m_aContext( _rxFactory ) ,m_nStartWizard(nullptr) { } // XServiceInfo OUString SAL_CALL DBContentLoader::getImplementationName() { return "org.openoffice.comp.dbflt.DBContentLoader2"; } // XServiceInfo sal_Bool SAL_CALL DBContentLoader::supportsService(const OUString& ServiceName) { return cppu::supportsService(this, ServiceName); } // XServiceInfo Sequence< OUString > SAL_CALL DBContentLoader::getSupportedServiceNames() { return { "com.sun.star.frame.FrameLoader" }; } namespace { bool lcl_urlAllowsInteraction( const Reference & _rContext, const OUString& _rURL ) { bool bDoesAllow = false; try { Reference< XURLTransformer > xTransformer( URLTransformer::create(_rContext) ); URL aURL; aURL.Complete = _rURL; xTransformer->parseStrict( aURL ); bDoesAllow = aURL.Arguments == "Interactive"; } catch( const Exception& ) { TOOLS_WARN_EXCEPTION( "dbaccess", "lcl_urlAllowsInteraction: caught an exception while analyzing the URL!" ); } return bDoesAllow; } Reference< XWindow > lcl_getTopMostWindow( const Reference & _rxContext ) { Reference< XWindow > xWindow; // get the top most window Reference < XDesktop2 > xDesktop = Desktop::create(_rxContext); Reference < XFrame > xActiveFrame = xDesktop->getActiveFrame(); if ( xActiveFrame.is() ) { xWindow = xActiveFrame->getContainerWindow(); Reference xFrame = xActiveFrame; while ( xFrame.is() && !xFrame->isTop() ) xFrame = xFrame->getCreator(); if ( xFrame.is() ) xWindow = xFrame->getContainerWindow(); } return xWindow; } } bool DBContentLoader::impl_executeNewDatabaseWizard( Reference< XModel > const & _rxModel, bool& _bShouldStartTableWizard ) { Sequence aWizardArgs(comphelper::InitAnyPropertySequence( { {"ParentWindow", Any(lcl_getTopMostWindow( m_aContext ))}, {"InitialSelection", Any(_rxModel)} })); // create the dialog Reference< XExecutableDialog > xAdminDialog( m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.sdb.DatabaseWizardDialog", aWizardArgs, m_aContext), UNO_QUERY_THROW); // execute it if ( RET_OK != xAdminDialog->execute() ) return false; Reference xProp(xAdminDialog,UNO_QUERY); bool bSuccess = false; xProp->getPropertyValue("OpenDatabase") >>= bSuccess; xProp->getPropertyValue("StartTableWizard") >>= _bShouldStartTableWizard; return bSuccess; } void SAL_CALL DBContentLoader::load(const Reference< XFrame > & rFrame, const OUString& _rURL, const Sequence< PropertyValue >& rArgs, const Reference< XLoadEventListener > & rListener) { // first check if preview is true, if so return without creating a controller. Preview is not supported ::comphelper::NamedValueCollection aMediaDesc( rArgs ); bool bPreview = aMediaDesc.getOrDefault( "Preview", false ); if ( bPreview ) { if (rListener.is()) rListener->loadCancelled(this); return; } Reference< XModel > xModel = aMediaDesc.getOrDefault( "Model", Reference< XModel >() ); OUString sSalvagedURL = aMediaDesc.getOrDefault( "SalvagedFile", _rURL ); bool bCreateNew = false; // does the URL denote the private:factory URL? bool bStartTableWizard = false; // start the table wizard after everything was loaded successfully? bool bSuccess = true; // If there's no interaction handler in the media descriptor, put one. // By definition, loading via loadComponentFromURL (and thus via the content loader here) // is allowed to raise UI. To not burden every place inside the document with creating // a default handler, we simply ensure there is one. // If a handler is present in the media descriptor, even if it is NULL, we will // not touch it. if ( !aMediaDesc.has( "InteractionHandler" ) ) { Reference< XInteractionHandler2 > xHandler( InteractionHandler::createWithParent(m_aContext, nullptr) ); aMediaDesc.put( "InteractionHandler", xHandler ); } // it's allowed to pass an existing document Reference< XOfficeDatabaseDocument > xExistentDBDoc; xModel.set( aMediaDesc.getOrDefault( "Model", xExistentDBDoc ), UNO_QUERY ); aMediaDesc.remove( "Model" ); // also, it's allowed to specify the type of view which should be created OUString sViewName = aMediaDesc.getOrDefault( "ViewName", OUString( "Default" ) ); aMediaDesc.remove( "ViewName" ); // this needs to stay alive for duration of this method Reference< XDatabaseContext > xDatabaseContext; sal_Int32 nInitialSelection = -1; if ( !xModel.is() ) { xDatabaseContext = DatabaseContext::create(m_aContext); OUString sFactoryName = SvtModuleOptions().GetFactoryEmptyDocumentURL(SvtModuleOptions::EFactory::DATABASE); bCreateNew = sFactoryName.match(_rURL); Reference< XDocumentDataSource > xDocumentDataSource; bool bNewAndInteractive = false; if ( bCreateNew ) { bNewAndInteractive = lcl_urlAllowsInteraction( m_aContext, _rURL ); xDocumentDataSource.set( xDatabaseContext->createInstance(), UNO_QUERY_THROW ); } else { ::comphelper::NamedValueCollection aCreationArgs; aCreationArgs.put( INFO_POOLURL, sSalvagedURL ); xDocumentDataSource.set( xDatabaseContext->createInstanceWithArguments( aCreationArgs.getWrappedNamedValues() ), UNO_QUERY_THROW ); } xModel.set( xDocumentDataSource->getDatabaseDocument(), UNO_QUERY ); if ( bCreateNew && xModel.is() ) { if ( bNewAndInteractive ) { bSuccess = impl_executeNewDatabaseWizard( xModel, bStartTableWizard ); } else { try { Reference< XLoadable > xLoad( xModel, UNO_QUERY_THROW ); xLoad->initNew(); bSuccess = true; } catch( const Exception& ) { bSuccess = false; } } // initially select the "Tables" category (will be done below) nInitialSelection = css::sdb::application::DatabaseObjectContainer::TABLES; } } if ( !xModel.is() ) { if ( rListener.is() ) rListener->loadCancelled(this); return; } if ( !bCreateNew ) { // We need to XLoadable::load the document if it does not yet have a URL. // If it already *does* have a URL, then it was either passed in the arguments, or a previous incarnation // of that model existed before (which can happen if a model is closed, but an associated DataSource is kept // alive 'til loading the document again). bool bNeedLoad = xModel->getURL().isEmpty(); try { aMediaDesc.put( "FileName", _rURL ); Sequence< PropertyValue > aResource( aMediaDesc.getPropertyValues() ); if ( bNeedLoad ) { Reference< XLoadable > xLoad( xModel, UNO_QUERY_THROW ); xLoad->load( aResource ); } // always attach the resource, even if the document has not been freshly loaded xModel->attachResource( _rURL, aResource ); } catch(const Exception&) { DBG_UNHANDLED_EXCEPTION("dbaccess"); bSuccess = false; } } if ( bSuccess ) { try { Reference< XModel2 > xModel2( xModel, UNO_QUERY_THROW ); Reference< XController2 > xController( xModel2->createViewController( sViewName, Sequence< PropertyValue >(), rFrame ), UNO_SET_THROW ); xController->attachModel( xModel ); xModel->connectController( xController ); rFrame->setComponent( xController->getComponentWindow(), xController ); xController->attachFrame( rFrame ); xModel->setCurrentController( xController ); bSuccess = true; } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("dbaccess"); bSuccess = false; } } if (bSuccess) { if ( rListener.is() ) rListener->loadFinished(this); if ( nInitialSelection != -1 ) { Reference< css::view::XSelectionSupplier > xDocView( xModel->getCurrentController(), UNO_QUERY ); if ( xDocView.is() ) { NamedDatabaseObject aSelection; aSelection.Type = nInitialSelection; xDocView->select( Any( aSelection ) ); } } if ( bStartTableWizard ) { // reset the data of the previous async drop (if any) if ( m_nStartWizard ) Application::RemoveUserEvent(m_nStartWizard); m_sCurrentURL = xModel->getURL(); m_xMySelf = this; m_nStartWizard = Application::PostUserEvent(LINK(this, DBContentLoader, OnStartTableWizard)); } } else { if ( rListener.is() ) rListener->loadCancelled( this ); } if ( !bSuccess ) ::comphelper::disposeComponent(xModel); } void DBContentLoader::cancel() { } IMPL_LINK_NOARG( DBContentLoader, OnStartTableWizard, void*, void ) { m_nStartWizard = nullptr; try { Sequence aWizArgs(comphelper::InitAnyPropertySequence( { {"DatabaseLocation", Any(m_sCurrentURL)} })); SolarMutexGuard aGuard; Reference< XJobExecutor > xTableWizard( m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.wizards.table.CallTableWizard", aWizArgs, m_aContext), UNO_QUERY); if ( xTableWizard.is() ) xTableWizard->trigger("start"); } catch(const Exception&) { TOOLS_WARN_EXCEPTION( "dbaccess", "caught an exception while starting the table wizard!"); } m_xMySelf = nullptr; } } extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* org_openoffice_comp_dbflt_DBContentLoader2_get_implementation( css::uno::XComponentContext* context, css::uno::Sequence const& ) { return cppu::acquire(new ::dbaxml::DBContentLoader(context)); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */