1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 // include own header
21 #include <jobs/helponstartup.hxx>
22 #include <loadenv/targethelper.hxx>
23 #include <services.h>
24 #include <targets.h>
25 
26 // include others
27 #include <comphelper/configurationhelper.hxx>
28 #include <comphelper/sequenceashashmap.hxx>
29 #include <unotools/configmgr.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/help.hxx>
32 #include <rtl/ustrbuf.hxx>
33 
34 // include interfaces
35 #include <com/sun/star/frame/FrameSearchFlag.hpp>
36 #include <com/sun/star/frame/ModuleManager.hpp>
37 #include <com/sun/star/frame/XFramesSupplier.hpp>
38 #include <com/sun/star/frame/Desktop.hpp>
39 
40 namespace framework{
41 
DEFINE_XSERVICEINFO_MULTISERVICE_2(HelpOnStartup,::cppu::OWeakObject,SERVICENAME_JOB,IMPLEMENTATIONNAME_HELPONSTARTUP)42 DEFINE_XSERVICEINFO_MULTISERVICE_2(HelpOnStartup                   ,
43                                       ::cppu::OWeakObject             ,
44                                       SERVICENAME_JOB                 ,
45                                       IMPLEMENTATIONNAME_HELPONSTARTUP)
46 
47 DEFINE_INIT_SERVICE(HelpOnStartup,
48                     {
49                         /*  Attention
50                             I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
51                             to create a new instance of this class by our own supported service factory.
52                             see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further information!
53                         */
54                         // create some needed uno services and cache it
55                         m_xModuleManager = css::frame::ModuleManager::create( m_xContext );
56 
57                         m_xDesktop = css::frame::Desktop::create(m_xContext);
58 
59                         m_xConfig.set(
60                             ::comphelper::ConfigurationHelper::openConfig(
61                                 m_xContext,
62                                 "/org.openoffice.Setup/Office/Factories",
63                                 ::comphelper::EConfigurationModes::ReadOnly),
64                             css::uno::UNO_QUERY_THROW);
65 
66                         // ask for office locale
67                         ::comphelper::ConfigurationHelper::readDirectKey(
68                             m_xContext,
69                             "/org.openoffice.Setup",
70                             "L10N",
71                             "ooLocale",
72                             ::comphelper::EConfigurationModes::ReadOnly) >>= m_sLocale;
73 
74                         // detect system
75                         ::comphelper::ConfigurationHelper::readDirectKey(
76                             m_xContext,
77                             "/org.openoffice.Office.Common",
78                             "Help",
79                             "System",
80                             ::comphelper::EConfigurationModes::ReadOnly) >>= m_sSystem;
81 
82                         // Start listening for disposing events of these services,
83                         // so we can react e.g. for an office shutdown
84                         css::uno::Reference< css::lang::XComponent > xComponent;
85                         xComponent.set(m_xModuleManager, css::uno::UNO_QUERY);
86                         if (xComponent.is())
87                             xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
88                         if (m_xDesktop.is())
89                             m_xDesktop->addEventListener(static_cast< css::lang::XEventListener* >(this));
90                         xComponent.set(m_xConfig, css::uno::UNO_QUERY);
91                         if (xComponent.is())
92                             xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
93                     }
94                    )
95 
96 HelpOnStartup::HelpOnStartup(const css::uno::Reference< css::uno::XComponentContext >& xContext)
97     : m_xContext    (xContext)
98 {
99 }
100 
~HelpOnStartup()101 HelpOnStartup::~HelpOnStartup()
102 {
103 }
104 
105 // css.task.XJob
execute(const css::uno::Sequence<css::beans::NamedValue> & lArguments)106 css::uno::Any SAL_CALL HelpOnStartup::execute(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
107 {
108     // Analyze the given arguments; try to locate a model there and
109     // classify it's used application module.
110     OUString sModule = its_getModuleIdFromEnv(lArguments);
111 
112     // Attention: we are bound to events for opening any document inside the office.
113     // That includes e.g. the help module itself. But we have to do nothing then!
114     if (sModule.isEmpty())
115         return css::uno::Any();
116 
117     // check current state of the help module
118     // a) help isn't open                       => show default page for the detected module
119     // b) help shows any other default page(!) => show default page for the detected module
120     // c) help shows any other content         => do nothing (user travelled to any other content and leaved the set of default pages)
121     OUString sCurrentHelpURL                = its_getCurrentHelpURL();
122     bool        bCurrentHelpURLIsAnyDefaultURL = its_isHelpUrlADefaultOne(sCurrentHelpURL);
123     bool        bShowIt                        = false;
124 
125     // a)
126     if (sCurrentHelpURL.isEmpty())
127         bShowIt = true;
128     // b)
129     else if (bCurrentHelpURLIsAnyDefaultURL)
130         bShowIt = true;
131 
132     if (bShowIt)
133     {
134         // retrieve the help URL for the detected application module
135         OUString sModuleDependentHelpURL = its_checkIfHelpEnabledAndGetURL(sModule);
136         if (!sModuleDependentHelpURL.isEmpty())
137         {
138             // Show this help page.
139             // Note: The help window brings itself to front ...
140             Help* pHelp = Application::GetHelp();
141             if (pHelp)
142                 pHelp->Start(sModuleDependentHelpURL, static_cast<vcl::Window*>(nullptr));
143         }
144     }
145 
146     return css::uno::Any();
147 }
148 
disposing(const css::lang::EventObject & aEvent)149 void SAL_CALL HelpOnStartup::disposing(const css::lang::EventObject& aEvent)
150 {
151     osl::MutexGuard g(m_mutex);
152     if (aEvent.Source == m_xModuleManager)
153         m_xModuleManager.clear();
154     else if (aEvent.Source == m_xDesktop)
155         m_xDesktop.clear();
156     else if (aEvent.Source == m_xConfig)
157         m_xConfig.clear();
158 }
159 
its_getModuleIdFromEnv(const css::uno::Sequence<css::beans::NamedValue> & lArguments)160 OUString HelpOnStartup::its_getModuleIdFromEnv(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
161 {
162     ::comphelper::SequenceAsHashMap lArgs        (lArguments);
163     ::comphelper::SequenceAsHashMap lEnvironment = lArgs.getUnpackedValueOrDefault("Environment", css::uno::Sequence< css::beans::NamedValue >());
164 
165     // check for right environment.
166     // If it's not a DocumentEvent, which triggered this job,
167     // we can't work correctly! => return immediately and do nothing
168     OUString sEnvType = lEnvironment.getUnpackedValueOrDefault("EnvType", OUString());
169     if (sEnvType != "DOCUMENTEVENT")
170         return OUString();
171 
172     css::uno::Reference< css::frame::XModel > xDoc = lEnvironment.getUnpackedValueOrDefault("Model", css::uno::Reference< css::frame::XModel >());
173     if (!xDoc.is())
174         return OUString();
175 
176     // be sure that we work on top level documents only, which are registered
177     // on the desktop instance. Ignore e.g. life previews, which are top frames too ...
178     // but not registered at this global desktop instance.
179     css::uno::Reference< css::frame::XDesktop >    xDesktopCheck;
180     css::uno::Reference< css::frame::XFrame >      xFrame;
181     css::uno::Reference< css::frame::XController > xController  = xDoc->getCurrentController();
182     if (xController.is())
183         xFrame = xController->getFrame();
184     if (xFrame.is() && xFrame->isTop())
185         xDesktopCheck.set(xFrame->getCreator(), css::uno::UNO_QUERY);
186     if (!xDesktopCheck.is())
187         return OUString();
188 
189     // OK - now we are sure this document is a top level document.
190     // Classify it.
191     // SAFE ->
192     osl::ClearableMutexGuard aLock(m_mutex);
193     css::uno::Reference< css::frame::XModuleManager2 > xModuleManager = m_xModuleManager;
194     aLock.clear();
195     // <- SAFE
196 
197     OUString sModuleId;
198     try
199     {
200         sModuleId = xModuleManager->identify(xDoc);
201     }
202     catch(const css::uno::RuntimeException&)
203         { throw; }
204     catch(const css::uno::Exception&)
205         { sModuleId.clear(); }
206 
207     return sModuleId;
208 }
209 
its_getCurrentHelpURL()210 OUString HelpOnStartup::its_getCurrentHelpURL()
211 {
212     // SAFE ->
213     osl::ClearableMutexGuard aLock(m_mutex);
214     css::uno::Reference< css::frame::XDesktop2 > xDesktop = m_xDesktop;
215     aLock.clear();
216     // <- SAFE
217 
218     if (!xDesktop.is())
219         return OUString();
220 
221     css::uno::Reference< css::frame::XFrame > xHelp = xDesktop->findFrame(SPECIALTARGET_HELPTASK, css::frame::FrameSearchFlag::CHILDREN);
222     if (!xHelp.is())
223         return OUString();
224 
225     OUString sCurrentHelpURL;
226     try
227     {
228         css::uno::Reference< css::frame::XFramesSupplier >  xHelpRoot  (xHelp                 , css::uno::UNO_QUERY_THROW);
229         css::uno::Reference< css::container::XIndexAccess > xHelpChildren(xHelpRoot->getFrames(), css::uno::UNO_QUERY_THROW);
230 
231         css::uno::Reference< css::frame::XFrame >      xHelpChild;
232         css::uno::Reference< css::frame::XController > xHelpView;
233         css::uno::Reference< css::frame::XModel >      xHelpContent;
234 
235         xHelpChildren->getByIndex(0) >>= xHelpChild;
236         if (xHelpChild.is())
237             xHelpView = xHelpChild->getController();
238         if (xHelpView.is())
239             xHelpContent = xHelpView->getModel();
240         if (xHelpContent.is())
241             sCurrentHelpURL = xHelpContent->getURL();
242     }
243     catch(const css::uno::RuntimeException&)
244         { throw; }
245     catch(const css::uno::Exception&)
246         { sCurrentHelpURL.clear(); }
247 
248     return sCurrentHelpURL;
249 }
250 
its_isHelpUrlADefaultOne(const OUString & sHelpURL)251 bool HelpOnStartup::its_isHelpUrlADefaultOne(const OUString& sHelpURL)
252 {
253     if (sHelpURL.isEmpty())
254         return false;
255 
256     // SAFE ->
257     osl::ClearableMutexGuard aLock(m_mutex);
258     css::uno::Reference< css::container::XNameAccess >     xConfig = m_xConfig;
259     OUString                                        sLocale = m_sLocale;
260     OUString                                        sSystem = m_sSystem;
261     aLock.clear();
262     // <- SAFE
263 
264     if (!xConfig.is())
265         return false;
266 
267     // check given help url against all default ones
268     const css::uno::Sequence< OUString > lModules = xConfig->getElementNames();
269     const OUString*                      pModules = lModules.getConstArray();
270     ::sal_Int32                          c        = lModules.getLength();
271     ::sal_Int32                          i        = 0;
272 
273     for (i=0; i<c; ++i)
274     {
275         try
276         {
277             css::uno::Reference< css::container::XNameAccess > xModuleConfig;
278             xConfig->getByName(pModules[i]) >>= xModuleConfig;
279             if (!xModuleConfig.is())
280                 continue;
281 
282             OUString sHelpBaseURL;
283             xModuleConfig->getByName("ooSetupFactoryHelpBaseURL") >>= sHelpBaseURL;
284             OUString sHelpURLForModule = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
285             if (sHelpURL == sHelpURLForModule)
286                 return true;
287         }
288         catch(const css::uno::RuntimeException&)
289             { throw; }
290         catch(const css::uno::Exception&)
291             {}
292     }
293 
294     return false;
295 }
296 
its_checkIfHelpEnabledAndGetURL(const OUString & sModule)297 OUString HelpOnStartup::its_checkIfHelpEnabledAndGetURL(const OUString& sModule)
298 {
299     // SAFE ->
300     osl::ClearableMutexGuard aLock(m_mutex);
301     css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig;
302     OUString                                    sLocale = m_sLocale;
303     OUString                                    sSystem = m_sSystem;
304     aLock.clear();
305     // <- SAFE
306 
307     OUString sHelpURL;
308 
309     try
310     {
311         css::uno::Reference< css::container::XNameAccess > xModuleConfig;
312         if (xConfig.is())
313             xConfig->getByName(sModule) >>= xModuleConfig;
314 
315         bool bHelpEnabled = false;
316         if (xModuleConfig.is())
317             xModuleConfig->getByName("ooSetupFactoryHelpOnOpen") >>= bHelpEnabled;
318 
319         if (bHelpEnabled)
320         {
321             OUString sHelpBaseURL;
322             xModuleConfig->getByName("ooSetupFactoryHelpBaseURL") >>= sHelpBaseURL;
323             sHelpURL = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
324         }
325     }
326     catch(const css::uno::RuntimeException&)
327         { throw; }
328     catch(const css::uno::Exception&)
329         { sHelpURL.clear(); }
330 
331     return sHelpURL;
332 }
333 
ist_createHelpURL(const OUString & sBaseURL,const OUString & sLocale,const OUString & sSystem)334 OUString HelpOnStartup::ist_createHelpURL(const OUString& sBaseURL,
335                                                  const OUString& sLocale ,
336                                                  const OUString& sSystem )
337 {
338     return sBaseURL + "?Language=" + sLocale + "&System=" + sSystem;
339 }
340 
341 } // namespace framework
342 
343 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
344