Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / desktop / source / deployment / gui / dp_gui_extensioncmdqueue.cxx
blob31f29368708e840dc9d4187a6a885600655184fe
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 <sal/config.h>
22 #include <cstddef>
24 #include <com/sun/star/beans/NamedValue.hpp>
26 #include <com/sun/star/deployment/DependencyException.hpp>
27 #include <com/sun/star/deployment/LicenseException.hpp>
28 #include <com/sun/star/deployment/VersionException.hpp>
29 #include <com/sun/star/deployment/InstallException.hpp>
30 #include <com/sun/star/deployment/PlatformException.hpp>
32 #include <com/sun/star/deployment/ui/LicenseDialog.hpp>
33 #include <com/sun/star/deployment/DeploymentException.hpp>
34 #include <com/sun/star/deployment/UpdateInformationProvider.hpp>
35 #include <com/sun/star/deployment/XPackage.hpp>
37 #include <com/sun/star/task/InteractionHandler.hpp>
38 #include <com/sun/star/task/XAbortChannel.hpp>
39 #include <com/sun/star/task/XInteractionAbort.hpp>
40 #include <com/sun/star/task/XInteractionApprove.hpp>
42 #include <com/sun/star/ucb/CommandAbortedException.hpp>
43 #include <com/sun/star/ucb/CommandFailedException.hpp>
44 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
46 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
48 #include <com/sun/star/uno/Reference.hxx>
49 #include <com/sun/star/uno/RuntimeException.hpp>
50 #include <com/sun/star/uno/Sequence.hxx>
51 #include <com/sun/star/uno/XInterface.hpp>
52 #include <com/sun/star/uno/TypeClass.hpp>
53 #include <o3tl/any.hxx>
54 #include <osl/diagnose.h>
55 #include <osl/mutex.hxx>
56 #include <rtl/ref.hxx>
57 #include <rtl/ustring.h>
58 #include <rtl/ustring.hxx>
59 #include <sal/types.h>
60 #include <salhelper/thread.hxx>
61 #include <ucbhelper/content.hxx>
62 #include <cppuhelper/exc_hlp.hxx>
63 #include <cppuhelper/implbase.hxx>
64 #include <comphelper/anytostring.hxx>
65 #include <vcl/svapp.hxx>
66 #include <vcl/weld.hxx>
67 #include <toolkit/helper/vclunohelper.hxx>
69 #include "dp_gui.h"
70 #include "dp_gui_extensioncmdqueue.hxx"
71 #include "dp_gui_dependencydialog.hxx"
72 #include "dp_gui_dialog2.hxx"
73 #include <dp_shared.hxx>
74 #include <strings.hrc>
75 #include "dp_gui_theextmgr.hxx"
76 #include "dp_gui_updatedialog.hxx"
77 #include "dp_gui_updateinstalldialog.hxx"
78 #include <dp_dependencies.hxx>
79 #include <dp_identifier.hxx>
80 #include <dp_version.hxx>
82 #include <queue>
83 #include <memory>
85 #ifdef _WIN32
86 #if !defined WIN32_LEAN_AND_MEAN
87 # define WIN32_LEAN_AND_MEAN
88 #endif
89 #include <windows.h>
90 #include <objbase.h>
91 #endif
94 using namespace ::com::sun::star;
96 namespace {
98 OUString getVersion( OUString const & sVersion )
100 return ( sVersion.isEmpty() ) ? OUString( "0" ) : sVersion;
103 OUString getVersion( const uno::Reference< deployment::XPackage > &rPackage )
105 return getVersion( rPackage->getVersion());
110 namespace dp_gui {
113 class ProgressCmdEnv
114 : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment,
115 task::XInteractionHandler,
116 ucb::XProgressHandler >
118 uno::Reference< task::XInteractionHandler2> m_xHandler;
119 uno::Reference< uno::XComponentContext > m_xContext;
121 DialogHelper* m_pDialogHelper;
122 OUString m_sTitle;
123 bool m_bWarnUser;
124 sal_Int32 m_nCurrentProgress;
126 void updateProgress();
128 /// @throws uno::RuntimeException
129 void update_( uno::Any const & Status );
131 public:
132 /** When param bAskWhenInstalling = true, then the user is asked if he
133 agrees to install this extension. In case this extension is already installed
134 then the user is also notified and asked if he wants to replace that existing
135 extension. In first case an interaction request with an InstallException
136 will be handled and in the second case a VersionException will be handled.
139 ProgressCmdEnv( const uno::Reference< uno::XComponentContext >& rContext,
140 DialogHelper* pDialogHelper,
141 const OUString& rTitle )
142 : m_xContext( rContext )
143 , m_pDialogHelper( pDialogHelper )
144 , m_sTitle( rTitle )
145 , m_bWarnUser( false )
146 , m_nCurrentProgress(0)
149 weld::Window* activeDialog() { return m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr; }
151 void startProgress();
152 void stopProgress();
153 void progressSection( const OUString &rText,
154 const uno::Reference< task::XAbortChannel > &xAbortChannel );
155 void setWarnUser( bool bNewVal ) { m_bWarnUser = bNewVal; }
157 // XCommandEnvironment
158 virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() override;
159 virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler() override;
161 // XInteractionHandler
162 virtual void SAL_CALL handle( uno::Reference< task::XInteractionRequest > const & xRequest ) override;
164 // XProgressHandler
165 virtual void SAL_CALL push( uno::Any const & Status ) override;
166 virtual void SAL_CALL update( uno::Any const & Status ) override;
167 virtual void SAL_CALL pop() override;
171 struct ExtensionCmd
173 enum E_CMD_TYPE { ADD, ENABLE, DISABLE, REMOVE, CHECK_FOR_UPDATES, ACCEPT_LICENSE };
175 E_CMD_TYPE m_eCmdType;
176 bool m_bWarnUser;
177 OUString m_sExtensionURL;
178 OUString m_sRepository;
179 uno::Reference< deployment::XPackage > m_xPackage;
180 std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList;
182 ExtensionCmd( const E_CMD_TYPE eCommand,
183 const OUString &rExtensionURL,
184 const OUString &rRepository,
185 const bool bWarnUser )
186 : m_eCmdType( eCommand ),
187 m_bWarnUser( bWarnUser ),
188 m_sExtensionURL( rExtensionURL ),
189 m_sRepository( rRepository ) {};
190 ExtensionCmd( const E_CMD_TYPE eCommand,
191 const uno::Reference< deployment::XPackage > &rPackage )
192 : m_eCmdType( eCommand ),
193 m_bWarnUser( false ),
194 m_xPackage( rPackage ) {};
195 ExtensionCmd( const E_CMD_TYPE eCommand,
196 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
197 : m_eCmdType( eCommand ),
198 m_bWarnUser( false ),
199 m_vExtensionList( vExtensionList ) {};
202 typedef std::shared_ptr< ExtensionCmd > TExtensionCmd;
205 class ExtensionCmdQueue::Thread: public salhelper::Thread
207 public:
208 Thread( DialogHelper *pDialogHelper,
209 TheExtensionManager *pManager,
210 const uno::Reference< uno::XComponentContext > & rContext );
212 void addExtension( const OUString &rExtensionURL,
213 const OUString &rRepository,
214 const bool bWarnUser );
215 void removeExtension( const uno::Reference< deployment::XPackage > &rPackage );
216 void enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
217 const bool bEnable );
218 void checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList );
219 void acceptLicense( const uno::Reference< deployment::XPackage > &rPackage );
220 void stop();
221 bool isBusy();
223 private:
224 virtual ~Thread() override;
226 virtual void execute() override;
228 void _insert(const TExtensionCmd& rExtCmd);
230 void _addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
231 const OUString &rPackageURL,
232 const OUString &rRepository,
233 const bool bWarnUser );
234 void _removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
235 const uno::Reference< deployment::XPackage > &xPackage );
236 void _enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
237 const uno::Reference< deployment::XPackage > &xPackage );
238 void _disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
239 const uno::Reference< deployment::XPackage > &xPackage );
240 void _checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList );
241 void _acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
242 const uno::Reference< deployment::XPackage > &xPackage );
244 enum Input { NONE, START, STOP };
246 uno::Reference< uno::XComponentContext > m_xContext;
247 std::queue< TExtensionCmd > m_queue;
249 DialogHelper *m_pDialogHelper;
250 TheExtensionManager *m_pManager;
252 const OUString m_sEnablingPackages;
253 const OUString m_sDisablingPackages;
254 const OUString m_sAddingPackages;
255 const OUString m_sRemovingPackages;
256 const OUString m_sDefaultCmd;
257 const OUString m_sAcceptLicense;
258 osl::Condition m_wakeup;
259 osl::Mutex m_mutex;
260 Input m_eInput;
261 bool m_bStopped;
262 bool m_bWorking;
266 void ProgressCmdEnv::startProgress()
268 m_nCurrentProgress = 0;
270 if ( m_pDialogHelper )
271 m_pDialogHelper->showProgress( true );
275 void ProgressCmdEnv::stopProgress()
277 if ( m_pDialogHelper )
278 m_pDialogHelper->showProgress( false );
282 void ProgressCmdEnv::progressSection( const OUString &rText,
283 const uno::Reference< task::XAbortChannel > &xAbortChannel )
285 m_nCurrentProgress = 0;
286 if ( m_pDialogHelper )
288 m_pDialogHelper->updateProgress( rText, xAbortChannel );
289 m_pDialogHelper->updateProgress( 5 );
294 void ProgressCmdEnv::updateProgress()
296 long nProgress = ((m_nCurrentProgress*5) % 100) + 5;
297 if ( m_pDialogHelper )
298 m_pDialogHelper->updateProgress( nProgress );
301 // XCommandEnvironment
303 uno::Reference< task::XInteractionHandler > ProgressCmdEnv::getInteractionHandler()
305 return this;
309 uno::Reference< ucb::XProgressHandler > ProgressCmdEnv::getProgressHandler()
311 return this;
315 // XInteractionHandler
317 void ProgressCmdEnv::handle( uno::Reference< task::XInteractionRequest > const & xRequest )
319 uno::Any request( xRequest->getRequest() );
320 OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
321 dp_misc::TRACE( "[dp_gui_cmdenv.cxx] incoming request:\n"
322 + ::comphelper::anyToString(request) + "\n");
324 lang::WrappedTargetException wtExc;
325 deployment::DependencyException depExc;
326 deployment::LicenseException licExc;
327 deployment::VersionException verExc;
328 deployment::InstallException instExc;
329 deployment::PlatformException platExc;
331 // selections:
332 bool approve = false;
333 bool abort = false;
335 if (request >>= wtExc) {
336 // handable deployment error signalled, e.g.
337 // bundle item registration failed, notify cause only:
338 uno::Any cause;
339 deployment::DeploymentException dpExc;
340 if (wtExc.TargetException >>= dpExc)
341 cause = dpExc.Cause;
342 else {
343 ucb::CommandFailedException cfExc;
344 if (wtExc.TargetException >>= cfExc)
345 cause = cfExc.Reason;
346 else
347 cause = wtExc.TargetException;
349 update_( cause );
351 // ignore intermediate errors of legacy packages, i.e.
352 // former pkgchk behaviour:
353 const uno::Reference< deployment::XPackage > xPackage( wtExc.Context, uno::UNO_QUERY );
354 OSL_ASSERT( xPackage.is() );
355 if ( xPackage.is() )
357 const uno::Reference< deployment::XPackageTypeInfo > xPackageType( xPackage->getPackageType() );
358 OSL_ASSERT( xPackageType.is() );
359 if (xPackageType.is())
361 approve = ( xPackage->isBundle() &&
362 xPackageType->getMediaType().match(
363 "application/vnd.sun.star.legacy-package-bundle" ));
366 abort = !approve;
368 else if (request >>= depExc)
370 std::vector< OUString > deps;
371 deps.reserve(depExc.UnsatisfiedDependencies.getLength());
372 for (sal_Int32 i = 0; i < depExc.UnsatisfiedDependencies.getLength(); ++i)
374 deps.push_back(
375 dp_misc::Dependencies::getErrorText( depExc.UnsatisfiedDependencies[i]) );
378 SolarMutexGuard guard;
379 if (m_pDialogHelper)
380 m_pDialogHelper->incBusy();
381 DependencyDialog aDlg(activeDialog(), deps);
382 short n = aDlg.run();
383 if (m_pDialogHelper)
384 m_pDialogHelper->decBusy();
385 // Distinguish between closing the dialog and programmatically
386 // canceling the dialog (headless VCL):
387 approve = n == RET_OK
388 || (n == RET_CANCEL && !Application::IsDialogCancelEnabled());
391 else if (request >>= licExc)
393 SolarMutexGuard guard;
395 weld::Window *pTopLevel = activeDialog();
396 if (m_pDialogHelper)
397 m_pDialogHelper->incBusy();
398 uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
399 deployment::ui::LicenseDialog::create(
400 m_xContext, pTopLevel ? pTopLevel->GetXWindow() : nullptr,
401 licExc.ExtensionName, licExc.Text ) );
402 sal_Int16 res = xDialog->execute();
403 if (m_pDialogHelper)
404 m_pDialogHelper->decBusy();
405 if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
406 abort = true;
407 else if ( res == ui::dialogs::ExecutableDialogResults::OK )
408 approve = true;
409 else
411 OSL_ASSERT(false);
414 else if (request >>= verExc)
416 const char* id;
417 switch (dp_misc::compareVersions(
418 verExc.NewVersion, verExc.Deployed->getVersion() ))
420 case dp_misc::LESS:
421 id = RID_STR_WARNING_VERSION_LESS;
422 break;
423 case dp_misc::EQUAL:
424 id = RID_STR_WARNING_VERSION_EQUAL;
425 break;
426 default: // dp_misc::GREATER
427 id = RID_STR_WARNING_VERSION_GREATER;
428 break;
430 OSL_ASSERT( verExc.Deployed.is() );
431 bool bEqualNames = verExc.NewDisplayName ==
432 verExc.Deployed->getDisplayName();
434 SolarMutexGuard guard;
436 if (m_pDialogHelper)
437 m_pDialogHelper->incBusy();
439 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
440 VclMessageType::Warning, VclButtonsType::OkCancel, DpResId(id)));
441 OUString s;
442 if (bEqualNames)
444 s = xBox->get_primary_text();
446 else if (!strcmp(id, RID_STR_WARNING_VERSION_EQUAL))
448 //hypothetical: requires two instances of an extension with the same
449 //version to have different display names. Probably the developer forgot
450 //to change the version.
451 s = DpResId(RID_STR_WARNINGBOX_VERSION_EQUAL_DIFFERENT_NAMES);
453 else if (!strcmp(id, RID_STR_WARNING_VERSION_LESS))
455 s = DpResId(RID_STR_WARNINGBOX_VERSION_LESS_DIFFERENT_NAMES);
457 else if (!strcmp(id, RID_STR_WARNING_VERSION_GREATER))
459 s = DpResId(RID_STR_WARNINGBOX_VERSION_GREATER_DIFFERENT_NAMES);
461 s = s.replaceAll("$NAME", verExc.NewDisplayName);
462 s = s.replaceAll("$OLDNAME", verExc.Deployed->getDisplayName());
463 s = s.replaceAll("$NEW", getVersion(verExc.NewVersion));
464 s = s.replaceAll("$DEPLOYED", getVersion(verExc.Deployed));
465 xBox->set_primary_text(s);
466 approve = xBox->run() == RET_OK;
467 if (m_pDialogHelper)
468 m_pDialogHelper->decBusy();
469 abort = !approve;
472 else if (request >>= instExc)
474 if ( ! m_bWarnUser )
476 approve = true;
478 else
480 if ( m_pDialogHelper )
482 SolarMutexGuard guard;
484 approve = m_pDialogHelper->installExtensionWarn( instExc.displayName );
486 else
487 approve = false;
488 abort = !approve;
491 else if (request >>= platExc)
493 SolarMutexGuard guard;
494 OUString sMsg(DpResId(RID_STR_UNSUPPORTED_PLATFORM));
495 sMsg = sMsg.replaceAll("%Name", platExc.package->getDisplayName());
496 if (m_pDialogHelper)
497 m_pDialogHelper->incBusy();
498 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
499 VclMessageType::Warning, VclButtonsType::Ok, sMsg));
500 xBox->run();
501 if (m_pDialogHelper)
502 m_pDialogHelper->decBusy();
503 approve = true;
506 if (!approve && !abort)
508 // forward to UUI handler:
509 if (! m_xHandler.is()) {
510 // late init:
511 m_xHandler = task::InteractionHandler::createWithParentAndContext(m_xContext, nullptr, m_sTitle);
513 m_xHandler->handle( xRequest );
515 else
517 // select:
518 uno::Sequence< uno::Reference< task::XInteractionContinuation > > conts(
519 xRequest->getContinuations() );
520 uno::Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
521 sal_Int32 len = conts.getLength();
522 for ( sal_Int32 pos = 0; pos < len; ++pos )
524 if (approve) {
525 uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
526 if (xInteractionApprove.is()) {
527 xInteractionApprove->select();
528 // don't query again for ongoing continuations:
529 approve = false;
532 else if (abort) {
533 uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
534 if (xInteractionAbort.is()) {
535 xInteractionAbort->select();
536 // don't query again for ongoing continuations:
537 abort = false;
545 // XProgressHandler
547 void ProgressCmdEnv::push( uno::Any const & rStatus )
549 update_( rStatus );
553 void ProgressCmdEnv::update_( uno::Any const & rStatus )
555 OUString text;
556 if ( rStatus.hasValue() && !( rStatus >>= text) )
558 if ( auto e = o3tl::tryAccess<uno::Exception>(rStatus) )
559 text = e->Message;
560 if ( text.isEmpty() )
561 text = ::comphelper::anyToString( rStatus ); // fallback
563 const SolarMutexGuard aGuard;
564 if (m_pDialogHelper)
565 m_pDialogHelper->incBusy();
566 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
567 VclMessageType::Warning, VclButtonsType::Ok, text));
568 xBox->run();
569 if (m_pDialogHelper)
570 m_pDialogHelper->decBusy();
572 ++m_nCurrentProgress;
573 updateProgress();
577 void ProgressCmdEnv::update( uno::Any const & rStatus )
579 update_( rStatus );
583 void ProgressCmdEnv::pop()
585 update_( uno::Any() ); // no message
589 ExtensionCmdQueue::Thread::Thread( DialogHelper *pDialogHelper,
590 TheExtensionManager *pManager,
591 const uno::Reference< uno::XComponentContext > & rContext ) :
592 salhelper::Thread( "dp_gui_extensioncmdqueue" ),
593 m_xContext( rContext ),
594 m_pDialogHelper( pDialogHelper ),
595 m_pManager( pManager ),
596 m_sEnablingPackages( DpResId( RID_STR_ENABLING_PACKAGES ) ),
597 m_sDisablingPackages( DpResId( RID_STR_DISABLING_PACKAGES ) ),
598 m_sAddingPackages( DpResId( RID_STR_ADDING_PACKAGES ) ),
599 m_sRemovingPackages( DpResId( RID_STR_REMOVING_PACKAGES ) ),
600 m_sDefaultCmd( DpResId( RID_STR_ADD_PACKAGES ) ),
601 m_sAcceptLicense( DpResId( RID_STR_ACCEPT_LICENSE ) ),
602 m_eInput( NONE ),
603 m_bStopped( false ),
604 m_bWorking( false )
606 OSL_ASSERT( pDialogHelper );
610 void ExtensionCmdQueue::Thread::addExtension( const OUString &rExtensionURL,
611 const OUString &rRepository,
612 const bool bWarnUser )
614 if ( !rExtensionURL.isEmpty() )
616 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::ADD, rExtensionURL, rRepository, bWarnUser ) );
617 _insert( pEntry );
622 void ExtensionCmdQueue::Thread::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
624 if ( rPackage.is() )
626 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::REMOVE, rPackage ) );
627 _insert( pEntry );
632 void ExtensionCmdQueue::Thread::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
634 if ( rPackage.is() )
636 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::ACCEPT_LICENSE, rPackage ) );
637 _insert( pEntry );
642 void ExtensionCmdQueue::Thread::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
643 const bool bEnable )
645 if ( rPackage.is() )
647 TExtensionCmd pEntry( new ExtensionCmd( bEnable ? ExtensionCmd::ENABLE :
648 ExtensionCmd::DISABLE,
649 rPackage ) );
650 _insert( pEntry );
655 void ExtensionCmdQueue::Thread::checkForUpdates(
656 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
658 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::CHECK_FOR_UPDATES, vExtensionList ) );
659 _insert( pEntry );
663 //Stopping this thread will not abort the installation of extensions.
664 void ExtensionCmdQueue::Thread::stop()
666 osl::MutexGuard aGuard( m_mutex );
667 m_bStopped = true;
668 m_eInput = STOP;
669 m_wakeup.set();
673 bool ExtensionCmdQueue::Thread::isBusy()
675 osl::MutexGuard aGuard( m_mutex );
676 return m_bWorking;
680 ExtensionCmdQueue::Thread::~Thread() {}
683 void ExtensionCmdQueue::Thread::execute()
685 #ifdef _WIN32
686 //Needed for use of the service "com.sun.star.system.SystemShellExecute" in
687 //DialogHelper::openWebBrowser
688 CoUninitialize();
689 (void) CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
690 #endif
691 for (;;)
693 if ( m_wakeup.wait() != osl::Condition::result_ok )
695 dp_misc::TRACE( "dp_gui::ExtensionCmdQueue::Thread::run: ignored "
696 "osl::Condition::wait failure\n" );
698 m_wakeup.reset();
700 int nSize;
701 Input eInput;
703 osl::MutexGuard aGuard( m_mutex );
704 eInput = m_eInput;
705 m_eInput = NONE;
706 nSize = m_queue.size();
707 m_bWorking = false;
710 // If this thread has been woken up by anything else except start, stop
711 // then input is NONE and we wait again.
712 // We only install the extension which are currently in the queue.
713 // The progressbar will be set to show the progress of the current number
714 // of extensions. If we allowed to add extensions now then the progressbar may
715 // have reached the end while we still install newly added extensions.
716 if ( ( eInput == NONE ) || ( nSize == 0 ) )
717 continue;
718 if ( eInput == STOP )
719 break;
721 ::rtl::Reference< ProgressCmdEnv > currentCmdEnv( new ProgressCmdEnv( m_xContext, m_pDialogHelper, m_sDefaultCmd ) );
723 // Do not lock the following part with addExtension. addExtension may be called in the main thread.
724 // If the message box "Do you want to install the extension (or similar)" is shown and then
725 // addExtension is called, which then blocks the main thread, then we deadlock.
726 bool bStartProgress = true;
728 while ( --nSize >= 0 )
731 osl::MutexGuard aGuard( m_mutex );
732 m_bWorking = true;
737 TExtensionCmd pEntry;
739 ::osl::MutexGuard queueGuard( m_mutex );
740 pEntry = m_queue.front();
741 m_queue.pop();
744 if ( bStartProgress && ( pEntry->m_eCmdType != ExtensionCmd::CHECK_FOR_UPDATES ) )
746 currentCmdEnv->startProgress();
747 bStartProgress = false;
750 switch ( pEntry->m_eCmdType ) {
751 case ExtensionCmd::ADD :
752 _addExtension( currentCmdEnv, pEntry->m_sExtensionURL, pEntry->m_sRepository, pEntry->m_bWarnUser );
753 break;
754 case ExtensionCmd::REMOVE :
755 _removeExtension( currentCmdEnv, pEntry->m_xPackage );
756 break;
757 case ExtensionCmd::ENABLE :
758 _enableExtension( currentCmdEnv, pEntry->m_xPackage );
759 break;
760 case ExtensionCmd::DISABLE :
761 _disableExtension( currentCmdEnv, pEntry->m_xPackage );
762 break;
763 case ExtensionCmd::CHECK_FOR_UPDATES :
764 _checkForUpdates( pEntry->m_vExtensionList );
765 break;
766 case ExtensionCmd::ACCEPT_LICENSE :
767 _acceptLicense( currentCmdEnv, pEntry->m_xPackage );
768 break;
771 catch ( const ucb::CommandAbortedException & )
773 //This exception is thrown when the user clicks cancel on the progressbar.
774 //Then we cancel the installation of all extensions and remove them from
775 //the queue.
777 ::osl::MutexGuard queueGuard2(m_mutex);
778 while ( --nSize >= 0 )
779 m_queue.pop();
781 break;
783 catch ( const ucb::CommandFailedException & )
785 //This exception is thrown when a user clicked cancel in the messagebox which was
786 //started by the interaction handler. For example the user will be asked if he/she
787 //really wants to install the extension.
788 //These interaction are run for exactly one extension at a time. Therefore we continue
789 //with installing the remaining extensions.
790 continue;
792 catch ( const uno::Exception & )
794 //Todo display the user an error
795 //see also DialogImpl::SyncPushButton::Click()
796 uno::Any exc( ::cppu::getCaughtException() );
797 OUString msg;
798 deployment::DeploymentException dpExc;
799 if (exc >>= dpExc)
801 if (auto e = o3tl::tryAccess<uno::Exception>(dpExc.Cause))
803 // notify error cause only:
804 msg = e->Message;
807 if (msg.isEmpty()) // fallback for debugging purposes
808 msg = ::comphelper::anyToString(exc);
810 const SolarMutexGuard guard;
811 if (m_pDialogHelper)
812 m_pDialogHelper->incBusy();
814 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(currentCmdEnv->activeDialog(),
815 VclMessageType::Warning, VclButtonsType::Ok, msg));
816 if (m_pDialogHelper)
817 xBox->set_title(m_pDialogHelper->getFrameWeld()->get_title());
818 xBox->run();
819 if (m_pDialogHelper)
820 m_pDialogHelper->decBusy();
821 //Continue with installation of the remaining extensions
824 osl::MutexGuard aGuard( m_mutex );
825 m_bWorking = false;
830 // when leaving the while loop with break, we should set working to false, too
831 osl::MutexGuard aGuard( m_mutex );
832 m_bWorking = false;
835 if ( !bStartProgress )
836 currentCmdEnv->stopProgress();
838 //end for
839 #ifdef _WIN32
840 CoUninitialize();
841 #endif
845 void ExtensionCmdQueue::Thread::_addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
846 const OUString &rPackageURL,
847 const OUString &rRepository,
848 const bool bWarnUser )
850 //check if we have a string in anyTitle. For example "unopkg gui \" caused anyTitle to be void
851 //and anyTitle.get<OUString> throws as RuntimeException.
852 uno::Any anyTitle;
855 anyTitle = ::ucbhelper::Content( rPackageURL, rCmdEnv.get(), m_xContext ).getPropertyValue( "Title" );
857 catch ( const uno::Exception & )
859 return;
862 OUString sName;
863 if ( ! (anyTitle >>= sName) )
865 OSL_FAIL("Could not get file name for extension.");
866 return;
869 rCmdEnv->setWarnUser( bWarnUser );
870 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
871 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
872 OUString sTitle(
873 m_sAddingPackages.replaceAll("%EXTENSION_NAME", sName));
874 rCmdEnv->progressSection( sTitle, xAbortChannel );
878 xExtMgr->addExtension(rPackageURL, uno::Sequence<beans::NamedValue>(),
879 rRepository, xAbortChannel, rCmdEnv.get() );
881 catch ( const ucb::CommandFailedException & )
883 // When the extension is already installed we'll get a dialog asking if we want to overwrite. If we then press
884 // cancel this exception is thrown.
886 catch ( const ucb::CommandAbortedException & )
888 // User clicked the cancel button
889 // TODO: handle cancel
891 rCmdEnv->setWarnUser( false );
895 void ExtensionCmdQueue::Thread::_removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
896 const uno::Reference< deployment::XPackage > &xPackage )
898 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
899 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
900 OUString sTitle(
901 m_sRemovingPackages.replaceAll("%EXTENSION_NAME",
902 xPackage->getDisplayName()));
903 rCmdEnv->progressSection( sTitle, xAbortChannel );
905 OUString id( dp_misc::getIdentifier( xPackage ) );
908 xExtMgr->removeExtension( id, xPackage->getName(), xPackage->getRepositoryName(), xAbortChannel, rCmdEnv.get() );
910 catch ( const deployment::DeploymentException & )
912 catch ( const ucb::CommandFailedException & )
914 catch ( const ucb::CommandAbortedException & )
917 // Check, if there are still updates to be notified via menu bar icon
918 uno::Sequence< uno::Sequence< OUString > > aItemList;
919 UpdateDialog::createNotifyJob( false, aItemList );
923 void ExtensionCmdQueue::Thread::_checkForUpdates(
924 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
926 const SolarMutexGuard guard;
928 if (m_pDialogHelper)
929 m_pDialogHelper->incBusy();
931 std::vector< UpdateData > vData;
932 UpdateDialog aUpdateDialog(m_xContext, m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, vExtensionList, &vData);
934 aUpdateDialog.notifyMenubar( true, false ); // prepare the checking, if there updates to be notified via menu bar icon
936 bool bOk = aUpdateDialog.run() == RET_OK;
937 if (m_pDialogHelper)
938 m_pDialogHelper->decBusy();
940 if (bOk && !vData.empty())
942 // If there is at least one directly downloadable extension then we
943 // open the install dialog.
944 std::vector< UpdateData > dataDownload;
946 for (auto const& data : vData)
948 if ( data.sWebsiteURL.isEmpty() )
949 dataDownload.push_back(data);
952 short nDialogResult = RET_OK;
953 if ( !dataDownload.empty() )
955 if (m_pDialogHelper)
956 m_pDialogHelper->incBusy();
957 UpdateInstallDialog aDlg(m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, dataDownload, m_xContext);
958 nDialogResult = aDlg.run();
959 if (m_pDialogHelper)
960 m_pDialogHelper->decBusy();
961 aUpdateDialog.notifyMenubar( false, true ); // Check, if there are still pending updates to be notified via menu bar icon
963 else
964 aUpdateDialog.notifyMenubar( false, false ); // Check, if there are pending updates to be notified via menu bar icon
966 //Now start the webbrowser and navigate to the websites where we get the updates
967 if ( RET_OK == nDialogResult )
969 for (auto const& data : vData)
971 if ( m_pDialogHelper && ( !data.sWebsiteURL.isEmpty() ) )
972 m_pDialogHelper->openWebBrowser( data.sWebsiteURL, m_pDialogHelper->getFrameWeld()->get_title() );
976 else
977 aUpdateDialog.notifyMenubar( false, false ); // check if there updates to be notified via menu bar icon
981 void ExtensionCmdQueue::Thread::_enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
982 const uno::Reference< deployment::XPackage > &xPackage )
984 if ( !xPackage.is() )
985 return;
987 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
988 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
989 OUString sTitle(
990 m_sEnablingPackages.replaceAll("%EXTENSION_NAME",
991 xPackage->getDisplayName()));
992 rCmdEnv->progressSection( sTitle, xAbortChannel );
996 xExtMgr->enableExtension( xPackage, xAbortChannel, rCmdEnv.get() );
997 if ( m_pDialogHelper )
998 m_pDialogHelper->updatePackageInfo( xPackage );
1000 catch ( const ::ucb::CommandAbortedException & )
1005 void ExtensionCmdQueue::Thread::_disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
1006 const uno::Reference< deployment::XPackage > &xPackage )
1008 if ( !xPackage.is() )
1009 return;
1011 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1012 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1013 OUString sTitle(
1014 m_sDisablingPackages.replaceAll("%EXTENSION_NAME",
1015 xPackage->getDisplayName()));
1016 rCmdEnv->progressSection( sTitle, xAbortChannel );
1020 xExtMgr->disableExtension( xPackage, xAbortChannel, rCmdEnv.get() );
1021 if ( m_pDialogHelper )
1022 m_pDialogHelper->updatePackageInfo( xPackage );
1024 catch ( const ::ucb::CommandAbortedException & )
1029 void ExtensionCmdQueue::Thread::_acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
1030 const uno::Reference< deployment::XPackage > &xPackage )
1032 if ( !xPackage.is() )
1033 return;
1035 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
1036 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
1037 OUString sTitle(
1038 m_sAcceptLicense.replaceAll("%EXTENSION_NAME",
1039 xPackage->getDisplayName()));
1040 rCmdEnv->progressSection( sTitle, xAbortChannel );
1044 xExtMgr->checkPrerequisitesAndEnable( xPackage, xAbortChannel, rCmdEnv.get() );
1045 if ( m_pDialogHelper )
1046 m_pDialogHelper->updatePackageInfo( xPackage );
1048 catch ( const ::ucb::CommandAbortedException & )
1052 void ExtensionCmdQueue::Thread::_insert(const TExtensionCmd& rExtCmd)
1054 ::osl::MutexGuard aGuard( m_mutex );
1056 // If someone called stop then we do not process the command -> game over!
1057 if ( m_bStopped )
1058 return;
1060 m_queue.push( rExtCmd );
1061 m_eInput = START;
1062 m_wakeup.set();
1066 ExtensionCmdQueue::ExtensionCmdQueue( DialogHelper * pDialogHelper,
1067 TheExtensionManager *pManager,
1068 const uno::Reference< uno::XComponentContext > &rContext )
1069 : m_thread( new Thread( pDialogHelper, pManager, rContext ) )
1071 m_thread->launch();
1074 ExtensionCmdQueue::~ExtensionCmdQueue() {
1075 stop();
1078 void ExtensionCmdQueue::addExtension( const OUString & extensionURL,
1079 const OUString & repository,
1080 const bool bWarnUser )
1082 m_thread->addExtension( extensionURL, repository, bWarnUser );
1085 void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
1087 m_thread->removeExtension( rPackage );
1090 void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
1091 const bool bEnable )
1093 m_thread->enableExtension( rPackage, bEnable );
1096 void ExtensionCmdQueue::checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList )
1098 m_thread->checkForUpdates( vExtensionList );
1101 void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
1103 m_thread->acceptLicense( rPackage );
1106 void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext )
1108 dp_misc::syncRepositories( false, new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
1111 void ExtensionCmdQueue::stop()
1113 m_thread->stop();
1116 bool ExtensionCmdQueue::isBusy()
1118 return m_thread->isBusy();
1121 void handleInteractionRequest( const uno::Reference< uno::XComponentContext > & xContext,
1122 const uno::Reference< task::XInteractionRequest > & xRequest )
1124 ::rtl::Reference< ProgressCmdEnv > xCmdEnv( new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
1125 xCmdEnv->handle( xRequest );
1128 } //namespace dp_gui
1130 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */