539 lines
17 KiB
C++
539 lines
17 KiB
C++
/* -*- 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 <utility>
|
|
#include <vcl/svapp.hxx>
|
|
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/configuration/theDefaultProvider.hpp>
|
|
#include <com/sun/star/deployment/DeploymentException.hpp>
|
|
#include <com/sun/star/deployment/ExtensionManager.hpp>
|
|
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
|
|
#include <com/sun/star/frame/Desktop.hpp>
|
|
#include <com/sun/star/frame/TerminationVetoException.hpp>
|
|
#include <com/sun/star/ucb/CommandAbortedException.hpp>
|
|
#include <com/sun/star/ucb/CommandFailedException.hpp>
|
|
#include <comphelper/propertysequence.hxx>
|
|
#include <cppuhelper/exc_hlp.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
|
|
#include "dp_gui_dialog2.hxx"
|
|
#include "dp_gui_extensioncmdqueue.hxx"
|
|
#include "dp_gui_theextmgr.hxx"
|
|
#include <dp_misc.h>
|
|
#include <dp_update.hxx>
|
|
|
|
constexpr OUStringLiteral USER_PACKAGE_MANAGER = u"user";
|
|
constexpr OUString SHARED_PACKAGE_MANAGER = u"shared"_ustr;
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
namespace dp_gui {
|
|
|
|
|
|
::rtl::Reference< TheExtensionManager > TheExtensionManager::s_ExtMgr;
|
|
|
|
|
|
// TheExtensionManager
|
|
|
|
|
|
TheExtensionManager::TheExtensionManager( uno::Reference< awt::XWindow > xParent,
|
|
const uno::Reference< uno::XComponentContext > &xContext ) :
|
|
m_xContext( xContext ),
|
|
m_xParent(std::move( xParent )),
|
|
m_bModified(false),
|
|
m_bExtMgrDialogExecuting(false)
|
|
{
|
|
m_xExtensionManager = deployment::ExtensionManager::get( xContext );
|
|
m_xExtensionManager->addModifyListener( this );
|
|
|
|
uno::Reference< lang::XMultiServiceFactory > xConfig(
|
|
configuration::theDefaultProvider::get(xContext));
|
|
uno::Sequence<uno::Any> args(comphelper::InitAnyPropertySequence(
|
|
{
|
|
{"nodepath", uno::Any(u"/org.openoffice.Office.OptionsDialog/Nodes"_ustr)}
|
|
}));
|
|
m_xNameAccessNodes.set(
|
|
xConfig->createInstanceWithArguments( u"com.sun.star.configuration.ConfigurationAccess"_ustr, args),
|
|
uno::UNO_QUERY_THROW);
|
|
|
|
// get the 'get more extensions here' url
|
|
uno::Sequence<uno::Any> args2(comphelper::InitAnyPropertySequence(
|
|
{
|
|
{"nodepath", uno::Any(u"/org.openoffice.Office.ExtensionManager/ExtensionRepositories"_ustr)}
|
|
}));
|
|
uno::Reference< container::XNameAccess > xNameAccessRepositories;
|
|
xNameAccessRepositories.set(
|
|
xConfig->createInstanceWithArguments( u"com.sun.star.configuration.ConfigurationAccess"_ustr, args2),
|
|
uno::UNO_QUERY_THROW);
|
|
try
|
|
{ //throws css::container::NoSuchElementException, css::lang::WrappedTargetException
|
|
uno::Any value = xNameAccessRepositories->getByName(u"WebsiteLink"_ustr);
|
|
m_sGetExtensionsURL = value.get< OUString > ();
|
|
}
|
|
catch ( const uno::Exception& )
|
|
{}
|
|
|
|
if ( dp_misc::office_is_running() )
|
|
{
|
|
// the registration should be done after the construction has been ended
|
|
// otherwise an exception prevents object creation, but it is registered as a listener
|
|
m_xDesktop.set( frame::Desktop::create(xContext), uno::UNO_SET_THROW );
|
|
m_xDesktop->addTerminateListener( this );
|
|
}
|
|
}
|
|
|
|
TheExtensionManager::~TheExtensionManager()
|
|
{
|
|
if (m_xUpdReqDialog)
|
|
m_xUpdReqDialog->response(RET_CANCEL);
|
|
assert(!m_xUpdReqDialog);
|
|
if (m_xExtMgrDialog)
|
|
{
|
|
if (m_bExtMgrDialogExecuting)
|
|
m_xExtMgrDialog->response(RET_CANCEL);
|
|
else
|
|
{
|
|
m_xExtMgrDialog->Close();
|
|
m_xExtMgrDialog.reset();
|
|
}
|
|
}
|
|
assert(!m_xExtMgrDialog);
|
|
}
|
|
|
|
void TheExtensionManager::createDialog( const bool bCreateUpdDlg )
|
|
{
|
|
const SolarMutexGuard guard;
|
|
|
|
if ( bCreateUpdDlg )
|
|
{
|
|
if ( !m_xUpdReqDialog )
|
|
{
|
|
m_xUpdReqDialog.reset(new UpdateRequiredDialog(Application::GetFrameWeld(m_xParent), this));
|
|
m_xExecuteCmdQueue.reset( new ExtensionCmdQueue( m_xUpdReqDialog.get(), this, m_xContext ) );
|
|
createPackageList();
|
|
}
|
|
}
|
|
else if ( !m_xExtMgrDialog )
|
|
{
|
|
m_xExtMgrDialog = std::make_shared<ExtMgrDialog>(Application::GetFrameWeld(m_xParent), this);
|
|
m_xExecuteCmdQueue.reset( new ExtensionCmdQueue( m_xExtMgrDialog.get(), this, m_xContext ) );
|
|
m_xExtMgrDialog->setGetExtensionsURL( m_sGetExtensionsURL );
|
|
createPackageList();
|
|
}
|
|
}
|
|
|
|
void TheExtensionManager::Show()
|
|
{
|
|
const SolarMutexGuard guard;
|
|
|
|
m_bExtMgrDialogExecuting = true;
|
|
|
|
weld::DialogController::runAsync(m_xExtMgrDialog, [this](sal_Int32 /*nResult*/) {
|
|
m_bExtMgrDialogExecuting = false;
|
|
auto xExtMgrDialog = m_xExtMgrDialog;
|
|
m_xExtMgrDialog.reset();
|
|
xExtMgrDialog->Close();
|
|
});
|
|
}
|
|
|
|
void TheExtensionManager::SetText( const OUString &rTitle )
|
|
{
|
|
const SolarMutexGuard guard;
|
|
|
|
if (weld::Window* pDialog = getDialog())
|
|
pDialog->set_title( rTitle );
|
|
}
|
|
|
|
|
|
void TheExtensionManager::ToTop()
|
|
{
|
|
const SolarMutexGuard guard;
|
|
|
|
if (weld::Window* pDialog = getDialog())
|
|
pDialog->present();
|
|
}
|
|
|
|
void TheExtensionManager::Close()
|
|
{
|
|
if (m_xExtMgrDialog)
|
|
{
|
|
if (m_bExtMgrDialogExecuting)
|
|
m_xExtMgrDialog->response(RET_CANCEL);
|
|
else
|
|
m_xExtMgrDialog->Close();
|
|
}
|
|
else if (m_xUpdReqDialog)
|
|
m_xUpdReqDialog->response(RET_CANCEL);
|
|
}
|
|
|
|
sal_Int16 TheExtensionManager::execute()
|
|
{
|
|
sal_Int16 nRet = 0;
|
|
|
|
if ( m_xUpdReqDialog )
|
|
{
|
|
nRet = m_xUpdReqDialog->run();
|
|
m_xUpdReqDialog.reset();
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
bool TheExtensionManager::isVisible()
|
|
{
|
|
weld::Window* pDialog = getDialog();
|
|
return pDialog && pDialog->get_visible();
|
|
}
|
|
|
|
void TheExtensionManager::checkUpdates()
|
|
{
|
|
std::vector< uno::Reference< deployment::XPackage > > vEntries;
|
|
uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
|
|
|
|
try {
|
|
xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
|
|
uno::Reference< ucb::XCommandEnvironment >() );
|
|
} catch ( const deployment::DeploymentException & ) {
|
|
return;
|
|
} catch ( const ucb::CommandFailedException & ) {
|
|
return;
|
|
} catch ( const ucb::CommandAbortedException & ) {
|
|
return;
|
|
} catch ( const lang::IllegalArgumentException & e ) {
|
|
css::uno::Any anyEx = cppu::getCaughtException();
|
|
throw css::lang::WrappedTargetRuntimeException( e.Message,
|
|
e.Context, anyEx );
|
|
}
|
|
|
|
for (auto const& i : xAllPackages)
|
|
{
|
|
uno::Reference< deployment::XPackage > xPackage = dp_misc::getExtensionWithHighestVersion(i);
|
|
OSL_ASSERT(xPackage.is());
|
|
if ( xPackage.is() )
|
|
{
|
|
vEntries.push_back( xPackage );
|
|
}
|
|
}
|
|
|
|
m_xExecuteCmdQueue->checkForUpdates( std::move(vEntries) );
|
|
}
|
|
|
|
|
|
bool TheExtensionManager::installPackage( const OUString &rPackageURL, bool bWarnUser )
|
|
{
|
|
if ( rPackageURL.isEmpty() )
|
|
return false;
|
|
|
|
createDialog( false );
|
|
|
|
bool bInstall = true;
|
|
bool bInstallForAll = false;
|
|
|
|
// DV! missing function is read only repository from extension manager
|
|
if ( !bWarnUser && ! m_xExtensionManager->isReadOnlyRepository( SHARED_PACKAGE_MANAGER ) )
|
|
bInstall = getDialogHelper()->installForAllUsers( bInstallForAll );
|
|
|
|
if ( !bInstall )
|
|
return false;
|
|
|
|
if ( bInstallForAll )
|
|
m_xExecuteCmdQueue->addExtension( rPackageURL, SHARED_PACKAGE_MANAGER, false );
|
|
else
|
|
m_xExecuteCmdQueue->addExtension( rPackageURL, USER_PACKAGE_MANAGER, bWarnUser );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void TheExtensionManager::terminateDialog()
|
|
{
|
|
if ( dp_misc::office_is_running() )
|
|
return;
|
|
|
|
const SolarMutexGuard guard;
|
|
if (m_xExtMgrDialog)
|
|
{
|
|
if (m_bExtMgrDialogExecuting)
|
|
m_xExtMgrDialog->response(RET_CANCEL);
|
|
else
|
|
{
|
|
m_xExtMgrDialog->Close();
|
|
m_xExtMgrDialog.reset();
|
|
}
|
|
}
|
|
assert(!m_xExtMgrDialog);
|
|
if (m_xUpdReqDialog)
|
|
m_xUpdReqDialog->response(RET_CANCEL);
|
|
assert(!m_xUpdReqDialog);
|
|
Application::Quit();
|
|
}
|
|
|
|
|
|
void TheExtensionManager::createPackageList()
|
|
{
|
|
uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
|
|
|
|
try {
|
|
xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
|
|
uno::Reference< ucb::XCommandEnvironment >() );
|
|
} catch ( const deployment::DeploymentException & ) {
|
|
return;
|
|
} catch ( const ucb::CommandFailedException & ) {
|
|
return;
|
|
} catch ( const ucb::CommandAbortedException & ) {
|
|
return;
|
|
} catch ( const lang::IllegalArgumentException & e ) {
|
|
css::uno::Any anyEx = cppu::getCaughtException();
|
|
throw css::lang::WrappedTargetRuntimeException( e.Message,
|
|
e.Context, anyEx );
|
|
}
|
|
|
|
for (uno::Sequence<uno::Reference<deployment::XPackage>> const& xPackageList : xAllPackages)
|
|
{
|
|
for ( uno::Reference< deployment::XPackage > const & xPackage : xPackageList )
|
|
{
|
|
if ( xPackage.is() )
|
|
{
|
|
PackageState eState = getPackageState( xPackage );
|
|
getDialogHelper()->addPackageToList( xPackage );
|
|
// When the package is enabled, we can stop here, otherwise we have to look for
|
|
// another version of this package
|
|
if ( ( eState == REGISTERED ) || ( eState == NOT_AVAILABLE ) )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const uno::Sequence< uno::Reference< deployment::XPackage > > xNoLicPackages = m_xExtensionManager->getExtensionsWithUnacceptedLicenses( SHARED_PACKAGE_MANAGER,
|
|
uno::Reference< ucb::XCommandEnvironment >() );
|
|
for ( uno::Reference< deployment::XPackage > const & xPackage : xNoLicPackages )
|
|
{
|
|
if ( xPackage.is() )
|
|
{
|
|
getDialogHelper()->addPackageToList( xPackage, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PackageState TheExtensionManager::getPackageState( const uno::Reference< deployment::XPackage > &xPackage )
|
|
{
|
|
try {
|
|
beans::Optional< beans::Ambiguous< sal_Bool > > option(
|
|
xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
|
|
uno::Reference< ucb::XCommandEnvironment >() ) );
|
|
if ( option.IsPresent )
|
|
{
|
|
::beans::Ambiguous< sal_Bool > const & reg = option.Value;
|
|
if ( reg.IsAmbiguous )
|
|
return AMBIGUOUS;
|
|
else
|
|
return reg.Value ? REGISTERED : NOT_REGISTERED;
|
|
}
|
|
else
|
|
return NOT_AVAILABLE;
|
|
}
|
|
catch ( const uno::RuntimeException & ) {
|
|
throw;
|
|
}
|
|
catch (const uno::Exception &) {
|
|
TOOLS_WARN_EXCEPTION( "desktop", "" );
|
|
return NOT_AVAILABLE;
|
|
}
|
|
}
|
|
|
|
|
|
bool TheExtensionManager::isReadOnly( const uno::Reference< deployment::XPackage > &xPackage ) const
|
|
{
|
|
if ( m_xExtensionManager.is() && xPackage.is() )
|
|
{
|
|
return m_xExtensionManager->isReadOnlyRepository( xPackage->getRepositoryName() );
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
|
|
// The function investigates if the extension supports options.
|
|
bool TheExtensionManager::supportsOptions( const uno::Reference< deployment::XPackage > &xPackage ) const
|
|
{
|
|
bool bOptions = false;
|
|
|
|
if ( ! xPackage->isBundle() )
|
|
return false;
|
|
|
|
beans::Optional< OUString > aId = xPackage->getIdentifier();
|
|
|
|
//a bundle must always have an id
|
|
OSL_ASSERT( aId.IsPresent );
|
|
|
|
//iterate over all available nodes
|
|
const uno::Sequence< OUString > seqNames = m_xNameAccessNodes->getElementNames();
|
|
|
|
for ( OUString const & nodeName : seqNames )
|
|
{
|
|
uno::Any anyNode = m_xNameAccessNodes->getByName( nodeName );
|
|
//If we have a node then it must contain the set of leaves. This is part of OptionsDialog.xcs
|
|
uno::Reference< XInterface> xIntNode = anyNode.get< uno::Reference< XInterface > >();
|
|
uno::Reference< container::XNameAccess > xNode( xIntNode, uno::UNO_QUERY_THROW );
|
|
|
|
uno::Any anyLeaves = xNode->getByName(u"Leaves"_ustr);
|
|
uno::Reference< XInterface > xIntLeaves = anyLeaves.get< uno::Reference< XInterface > >();
|
|
uno::Reference< container::XNameAccess > xLeaves( xIntLeaves, uno::UNO_QUERY_THROW );
|
|
|
|
//iterate over all available leaves
|
|
const uno::Sequence< OUString > seqLeafNames = xLeaves->getElementNames();
|
|
for ( OUString const & leafName : seqLeafNames )
|
|
{
|
|
uno::Any anyLeaf = xLeaves->getByName( leafName );
|
|
uno::Reference< XInterface > xIntLeaf = anyLeaf.get< uno::Reference< XInterface > >();
|
|
uno::Reference< beans::XPropertySet > xLeaf( xIntLeaf, uno::UNO_QUERY_THROW );
|
|
//investigate the Id property if it matches the extension identifier which
|
|
//has been passed in.
|
|
uno::Any anyValue = xLeaf->getPropertyValue(u"Id"_ustr);
|
|
|
|
OUString sId = anyValue.get< OUString >();
|
|
if ( sId == aId.Value )
|
|
{
|
|
bOptions = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( bOptions )
|
|
break;
|
|
}
|
|
return bOptions;
|
|
}
|
|
|
|
|
|
// XEventListener
|
|
void TheExtensionManager::disposing( lang::EventObject const & rEvt )
|
|
{
|
|
bool shutDown = (rEvt.Source == m_xDesktop);
|
|
|
|
if ( shutDown && m_xDesktop.is() )
|
|
{
|
|
m_xDesktop->removeTerminateListener( this );
|
|
m_xDesktop.clear();
|
|
}
|
|
|
|
if ( !shutDown )
|
|
return;
|
|
|
|
if ( dp_misc::office_is_running() )
|
|
{
|
|
const SolarMutexGuard guard;
|
|
if (m_xExtMgrDialog)
|
|
{
|
|
if (m_bExtMgrDialogExecuting)
|
|
m_xExtMgrDialog->response(RET_CANCEL);
|
|
else
|
|
{
|
|
m_xExtMgrDialog->Close();
|
|
m_xExtMgrDialog.reset();
|
|
}
|
|
}
|
|
assert(!m_xExtMgrDialog);
|
|
if (m_xUpdReqDialog)
|
|
m_xUpdReqDialog->response(RET_CANCEL);
|
|
assert(!m_xUpdReqDialog);
|
|
}
|
|
s_ExtMgr.clear();
|
|
}
|
|
|
|
// XTerminateListener
|
|
void TheExtensionManager::queryTermination( ::lang::EventObject const & )
|
|
{
|
|
DialogHelper *pDialogHelper = getDialogHelper();
|
|
|
|
if ( m_xExecuteCmdQueue->isBusy() || ( pDialogHelper && pDialogHelper->isBusy() ) )
|
|
{
|
|
ToTop();
|
|
throw frame::TerminationVetoException(
|
|
u"The office cannot be closed while the Extension Manager is running"_ustr,
|
|
static_cast<frame::XTerminateListener*>(this));
|
|
}
|
|
else
|
|
{
|
|
clearModified();
|
|
if (m_xExtMgrDialog)
|
|
{
|
|
if (m_bExtMgrDialogExecuting)
|
|
m_xExtMgrDialog->response(RET_CANCEL);
|
|
else
|
|
{
|
|
m_xExtMgrDialog->Close();
|
|
m_xExtMgrDialog.reset();
|
|
}
|
|
}
|
|
if (m_xUpdReqDialog)
|
|
m_xUpdReqDialog->response(RET_CANCEL);
|
|
}
|
|
}
|
|
|
|
void TheExtensionManager::notifyTermination( ::lang::EventObject const & rEvt )
|
|
{
|
|
disposing( rEvt );
|
|
}
|
|
|
|
// XModifyListener
|
|
void TheExtensionManager::modified( ::lang::EventObject const & /*rEvt*/ )
|
|
{
|
|
m_bModified = true;
|
|
DialogHelper *pDialogHelper = getDialogHelper();
|
|
if (!pDialogHelper)
|
|
return;
|
|
pDialogHelper->prepareChecking();
|
|
createPackageList();
|
|
pDialogHelper->checkEntries();
|
|
}
|
|
|
|
|
|
::rtl::Reference< TheExtensionManager > TheExtensionManager::get( const uno::Reference< uno::XComponentContext > &xContext,
|
|
const uno::Reference< awt::XWindow > &xParent,
|
|
const OUString & extensionURL )
|
|
{
|
|
if ( s_ExtMgr.is() )
|
|
{
|
|
OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
|
|
if ( !extensionURL.isEmpty() )
|
|
s_ExtMgr->installPackage( extensionURL, true );
|
|
return s_ExtMgr;
|
|
}
|
|
|
|
::rtl::Reference<TheExtensionManager> that( new TheExtensionManager( xParent, xContext ) );
|
|
|
|
const SolarMutexGuard guard;
|
|
if ( ! s_ExtMgr.is() )
|
|
{
|
|
OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
|
|
s_ExtMgr = std::move(that);
|
|
}
|
|
|
|
if ( !extensionURL.isEmpty() )
|
|
s_ExtMgr->installPackage( extensionURL, true );
|
|
|
|
return s_ExtMgr;
|
|
}
|
|
|
|
} //namespace dp_gui
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|