1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_framework.hxx"
30 
31 //_______________________________________________
32 // include own header
33 #include <jobs/helponstartup.hxx>
34 #include <threadhelp/resetableguard.hxx>
35 #include <loadenv/targethelper.hxx>
36 #include <services.h>
37 
38 //_______________________________________________
39 // include others
40 #include <comphelper/configurationhelper.hxx>
41 #include <comphelper/sequenceashashmap.hxx>
42 #include <unotools/configmgr.hxx>
43 #include <vcl/svapp.hxx>
44 #include <vcl/help.hxx>
45 #include <rtl/ustrbuf.hxx>
46 
47 //_______________________________________________
48 // include interfaces
49 #include <com/sun/star/frame/FrameSearchFlag.hpp>
50 #include <com/sun/star/frame/XFramesSupplier.hpp>
51 #include <com/sun/star/frame/XDesktop.hpp>
52 
53 //_______________________________________________
54 // namespace
55 
56 namespace framework{
57 
58 //_______________________________________________
59 // definitions
60 
61 // path to module config
62 static ::rtl::OUString CFG_PACKAGE_MODULES      = ::rtl::OUString::createFromAscii("/org.openoffice.Setup/Office/Factories");
63 static ::rtl::OUString CFG_PACKAGE_SETUP        = ::rtl::OUString::createFromAscii("/org.openoffice.Setup"             );
64 static ::rtl::OUString CFG_PACKAGE_COMMON       = ::rtl::OUString::createFromAscii("/org.openoffice.Office.Common"     );
65 static ::rtl::OUString CFG_PATH_L10N            = ::rtl::OUString::createFromAscii("L10N"                              );
66 static ::rtl::OUString CFG_PATH_HELP            = ::rtl::OUString::createFromAscii("Help"                              );
67 static ::rtl::OUString CFG_KEY_LOCALE           = ::rtl::OUString::createFromAscii("ooLocale"                          );
68 static ::rtl::OUString CFG_KEY_HELPSYSTEM       = ::rtl::OUString::createFromAscii("System"                            );
69 
70 // props of job environment
71 static ::rtl::OUString PROP_ENVIRONMENT         = ::rtl::OUString::createFromAscii("Environment"                       );
72 static ::rtl::OUString PROP_JOBCONFIG           = ::rtl::OUString::createFromAscii("JobConfig"                         );
73 static ::rtl::OUString PROP_ENVTYPE             = ::rtl::OUString::createFromAscii("EnvType"                           );
74 static ::rtl::OUString PROP_MODEL               = ::rtl::OUString::createFromAscii("Model"                             );
75 
76 // props of module config
77 static ::rtl::OUString PROP_HELP_BASEURL        = ::rtl::OUString::createFromAscii("ooSetupFactoryHelpBaseURL"         );
78 static ::rtl::OUString PROP_AUTOMATIC_HELP      = ::rtl::OUString::createFromAscii("ooSetupFactoryHelpOnOpen"          );
79 
80 // special value of job environment
81 static ::rtl::OUString ENVTYPE_DOCUMENTEVENT    = ::rtl::OUString::createFromAscii("DOCUMENTEVENT"                     );
82 
83 //-----------------------------------------------
84 
85 DEFINE_XSERVICEINFO_MULTISERVICE(HelpOnStartup                   ,
86                                       ::cppu::OWeakObject             ,
87                                       SERVICENAME_JOB                 ,
88                                       IMPLEMENTATIONNAME_HELPONSTARTUP)
89 
90 DEFINE_INIT_SERVICE(HelpOnStartup,
91                     {
92                         /*  Attention
93                             I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
94                             to create a new instance of this class by our own supported service factory.
95                             see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations!
96                         */
97                         // create some needed uno services and cache it
98                         m_xModuleManager = css::uno::Reference< css::frame::XModuleManager >(
99                             m_xSMGR->createInstance(SERVICENAME_MODULEMANAGER),
100                             css::uno::UNO_QUERY_THROW);
101 
102                         m_xDesktop = css::uno::Reference< css::frame::XFrame >(
103                             m_xSMGR->createInstance(SERVICENAME_DESKTOP),
104                             css::uno::UNO_QUERY_THROW);
105 
106                         m_xConfig = css::uno::Reference< css::container::XNameAccess >(
107                             ::comphelper::ConfigurationHelper::openConfig(
108                                 m_xSMGR,
109                                 CFG_PACKAGE_MODULES,
110                                 ::comphelper::ConfigurationHelper::E_READONLY),
111                             css::uno::UNO_QUERY_THROW);
112 
113                         // ask for office locale
114                         ::comphelper::ConfigurationHelper::readDirectKey(
115                             m_xSMGR,
116                             CFG_PACKAGE_SETUP,
117                             CFG_PATH_L10N,
118                             CFG_KEY_LOCALE,
119                             ::comphelper::ConfigurationHelper::E_READONLY) >>= m_sLocale;
120 
121                         // detect system
122                         ::comphelper::ConfigurationHelper::readDirectKey(
123                             m_xSMGR,
124                             CFG_PACKAGE_COMMON,
125                             CFG_PATH_HELP,
126                             CFG_KEY_HELPSYSTEM,
127                             ::comphelper::ConfigurationHelper::E_READONLY) >>= m_sSystem;
128 
129                         // Start listening for disposing events of these services,
130                         // so we can react e.g. for an office shutdown
131                         css::uno::Reference< css::lang::XComponent > xComponent;
132                         xComponent = css::uno::Reference< css::lang::XComponent >(m_xModuleManager, css::uno::UNO_QUERY);
133                         if (xComponent.is())
134                             xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
135                         xComponent = css::uno::Reference< css::lang::XComponent >(m_xDesktop, css::uno::UNO_QUERY);
136                         if (xComponent.is())
137                             xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
138                         xComponent = css::uno::Reference< css::lang::XComponent >(m_xConfig, css::uno::UNO_QUERY);
139                         if (xComponent.is())
140                             xComponent->addEventListener(static_cast< css::lang::XEventListener* >(this));
141                     }
142                    )
143 
144 //-----------------------------------------------
145 HelpOnStartup::HelpOnStartup(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
146     : ThreadHelpBase(     )
147     , m_xSMGR       (xSMGR)
148 {
149 }
150 
151 //-----------------------------------------------
152 HelpOnStartup::~HelpOnStartup()
153 {
154 }
155 
156 //-----------------------------------------------
157 // css.task.XJob
158 css::uno::Any SAL_CALL HelpOnStartup::execute(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
159     throw(css::lang::IllegalArgumentException,
160           css::uno::Exception                ,
161           css::uno::RuntimeException         )
162 {
163     // Analyze the given arguments; try to locate a model there and
164     // classify it's used application module.
165     ::rtl::OUString sModule = its_getModuleIdFromEnv(lArguments);
166 
167     // Attention: We are bound to events for openeing any document inside the office.
168     // That includes e.g. the help module itself. But we have to do nothing then!
169     if (!sModule.getLength())
170         return css::uno::Any();
171 
172     // check current state of the help module
173     // a) help isnt open                       => show default page for the detected module
174     // b) help shows any other default page(!) => show default page for the detected module
175     // c) help shows any other content         => do nothing (user travelled to any other content and leaved the set of default pages)
176     ::rtl::OUString sCurrentHelpURL                = its_getCurrentHelpURL();
177     sal_Bool        bCurrentHelpURLIsAnyDefaultURL = its_isHelpUrlADefaultOne(sCurrentHelpURL);
178     sal_Bool        bShowIt                        = sal_False;
179 
180     // a)
181     if (!sCurrentHelpURL.getLength())
182         bShowIt = sal_True;
183     else
184     // b)
185     if (bCurrentHelpURLIsAnyDefaultURL)
186         bShowIt = sal_True;
187 
188     if (bShowIt)
189     {
190         // retrieve the help URL for the detected application module
191         ::rtl::OUString sModuleDependendHelpURL = its_checkIfHelpEnabledAndGetURL(sModule);
192         if (sModuleDependendHelpURL.getLength())
193         {
194             // Show this help page.
195             // Note: The help window brings itself to front ...
196             Help* pHelp = Application::GetHelp();
197             if (pHelp)
198                 pHelp->Start(sModuleDependendHelpURL, 0);
199         }
200     }
201 
202     return css::uno::Any();
203 }
204 
205 //-----------------------------------------------
206 void SAL_CALL HelpOnStartup::disposing(const css::lang::EventObject& aEvent)
207     throw(css::uno::RuntimeException)
208 {
209     // SAFE ->
210     ResetableGuard aLock(m_aLock);
211 
212     if (aEvent.Source == m_xModuleManager)
213         m_xModuleManager.clear();
214     else
215     if (aEvent.Source == m_xDesktop)
216         m_xDesktop.clear();
217     else
218     if (aEvent.Source == m_xConfig)
219         m_xConfig.clear();
220 
221     aLock.unlock();
222     // <- SAFE
223 }
224 
225 //-----------------------------------------------
226 ::rtl::OUString HelpOnStartup::its_getModuleIdFromEnv(const css::uno::Sequence< css::beans::NamedValue >& lArguments)
227 {
228     ::comphelper::SequenceAsHashMap lArgs        (lArguments);
229     ::comphelper::SequenceAsHashMap lEnvironment = lArgs.getUnpackedValueOrDefault(PROP_ENVIRONMENT, css::uno::Sequence< css::beans::NamedValue >());
230     ::comphelper::SequenceAsHashMap lJobConfig   = lArgs.getUnpackedValueOrDefault(PROP_JOBCONFIG  , css::uno::Sequence< css::beans::NamedValue >());
231 
232     // check for right environment.
233     // If its not a DocumentEvent, which triggered this job,
234     // we cant work correctly! => return immediatly and do nothing
235     ::rtl::OUString sEnvType = lEnvironment.getUnpackedValueOrDefault(PROP_ENVTYPE, ::rtl::OUString());
236     if (!sEnvType.equals(ENVTYPE_DOCUMENTEVENT))
237         return ::rtl::OUString();
238 
239     css::uno::Reference< css::frame::XModel > xDoc = lEnvironment.getUnpackedValueOrDefault(PROP_MODEL, css::uno::Reference< css::frame::XModel >());
240     if (!xDoc.is())
241         return ::rtl::OUString();
242 
243     // be sure that we work on top level documents only, which are registered
244     // on the desktop instance. Ignore e.g. life previews, which are top frames too ...
245     // but not registered at this global desktop instance.
246     css::uno::Reference< css::frame::XDesktop >    xDesktopCheck;
247     css::uno::Reference< css::frame::XFrame >      xFrame       ;
248     css::uno::Reference< css::frame::XController > xController  = xDoc->getCurrentController();
249     if (xController.is())
250         xFrame = xController->getFrame();
251     if (xFrame.is() && xFrame->isTop())
252         xDesktopCheck = css::uno::Reference< css::frame::XDesktop >(xFrame->getCreator(), css::uno::UNO_QUERY);
253     if (!xDesktopCheck.is())
254         return ::rtl::OUString();
255 
256     // OK - now we are sure this document is a top level document.
257     // Classify it.
258     // SAFE ->
259     ResetableGuard aLock(m_aLock);
260     css::uno::Reference< css::frame::XModuleManager > xModuleManager = m_xModuleManager;
261     aLock.unlock();
262     // <- SAFE
263 
264     if (!xModuleManager.is())
265         return ::rtl::OUString();
266 
267     ::rtl::OUString sModuleId;
268     try
269     {
270         sModuleId = xModuleManager->identify(xDoc);
271     }
272     catch(const css::uno::RuntimeException& exRun)
273         { throw exRun; }
274     catch(const css::uno::Exception&)
275         { sModuleId = ::rtl::OUString(); }
276 
277     return sModuleId;
278 }
279 
280 //-----------------------------------------------
281 ::rtl::OUString HelpOnStartup::its_getCurrentHelpURL()
282 {
283     // SAFE ->
284     ResetableGuard aLock(m_aLock);
285     css::uno::Reference< css::frame::XFrame > xDesktop = m_xDesktop;
286     aLock.unlock();
287     // <- SAFE
288 
289     if (!xDesktop.is())
290         return ::rtl::OUString();
291 
292     css::uno::Reference< css::frame::XFrame > xHelp = xDesktop->findFrame(SPECIALTARGET_HELPTASK, css::frame::FrameSearchFlag::CHILDREN);
293     if (!xHelp.is())
294         return ::rtl::OUString();
295 
296     ::rtl::OUString sCurrentHelpURL;
297     try
298     {
299         css::uno::Reference< css::frame::XFramesSupplier >  xHelpRoot  (xHelp                 , css::uno::UNO_QUERY_THROW);
300         css::uno::Reference< css::container::XIndexAccess > xHelpChilds(xHelpRoot->getFrames(), css::uno::UNO_QUERY_THROW);
301 
302         css::uno::Reference< css::frame::XFrame >      xHelpChild  ;
303         css::uno::Reference< css::frame::XController > xHelpView   ;
304         css::uno::Reference< css::frame::XModel >      xHelpContent;
305 
306         xHelpChilds->getByIndex(0) >>= xHelpChild;
307         if (xHelpChild.is())
308             xHelpView = xHelpChild->getController();
309         if (xHelpView.is())
310             xHelpContent = xHelpView->getModel();
311         if (xHelpContent.is())
312             sCurrentHelpURL = xHelpContent->getURL();
313     }
314     catch(css::uno::RuntimeException& exRun)
315         { throw exRun; }
316     catch(css::uno::Exception&)
317         { sCurrentHelpURL = ::rtl::OUString(); }
318 
319     return sCurrentHelpURL;
320 }
321 
322 //-----------------------------------------------
323 ::sal_Bool HelpOnStartup::its_isHelpUrlADefaultOne(const ::rtl::OUString& sHelpURL)
324 {
325     if (!sHelpURL.getLength())
326         return sal_False;
327 
328     // SAFE ->
329     ResetableGuard aLock(m_aLock);
330     css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR   (m_xSMGR, css::uno::UNO_QUERY_THROW);
331     css::uno::Reference< css::container::XNameAccess >     xConfig = m_xConfig;
332     ::rtl::OUString                                        sLocale = m_sLocale;
333     ::rtl::OUString                                        sSystem = m_sSystem;
334     aLock.unlock();
335     // <- SAFE
336 
337     if (!xConfig.is())
338         return sal_False;
339 
340     // check given help url against all default ones
341     const css::uno::Sequence< ::rtl::OUString > lModules = xConfig->getElementNames();
342     const ::rtl::OUString*                      pModules = lModules.getConstArray();
343           ::sal_Int32                           c        = lModules.getLength();
344           ::sal_Int32                           i        = 0;
345 
346     for (i=0; i<c; ++i)
347     {
348         try
349         {
350             css::uno::Reference< css::container::XNameAccess > xModuleConfig;
351             xConfig->getByName(pModules[i]) >>= xModuleConfig;
352             if (!xModuleConfig.is())
353                 continue;
354 
355             ::rtl::OUString sHelpBaseURL;
356             xModuleConfig->getByName(PROP_HELP_BASEURL) >>= sHelpBaseURL;
357             ::rtl::OUString sHelpURLForModule = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
358             if (sHelpURL.equals(sHelpURLForModule))
359                 return sal_True;
360         }
361         catch(const css::uno::RuntimeException& exRun)
362             { throw exRun; }
363         catch(const css::uno::Exception&)
364             {}
365     }
366 
367     return sal_False;
368 }
369 
370 //-----------------------------------------------
371 ::rtl::OUString HelpOnStartup::its_checkIfHelpEnabledAndGetURL(const ::rtl::OUString& sModule)
372 {
373     // SAFE ->
374     ResetableGuard aLock(m_aLock);
375     css::uno::Reference< css::container::XNameAccess > xConfig = m_xConfig;
376     ::rtl::OUString                                    sLocale = m_sLocale;
377     ::rtl::OUString                                    sSystem = m_sSystem;
378     aLock.unlock();
379     // <- SAFE
380 
381     ::rtl::OUString sHelpURL;
382 
383     try
384     {
385         css::uno::Reference< css::container::XNameAccess > xModuleConfig;
386         if (xConfig.is())
387             xConfig->getByName(sModule) >>= xModuleConfig;
388 
389         sal_Bool bHelpEnabled = sal_False;
390         if (xModuleConfig.is())
391             xModuleConfig->getByName(PROP_AUTOMATIC_HELP) >>= bHelpEnabled;
392 
393         if (bHelpEnabled)
394         {
395             ::rtl::OUString sHelpBaseURL;
396             xModuleConfig->getByName(PROP_HELP_BASEURL) >>= sHelpBaseURL;
397             sHelpURL = HelpOnStartup::ist_createHelpURL(sHelpBaseURL, sLocale, sSystem);
398         }
399     }
400     catch(const css::uno::RuntimeException& exRun)
401         { throw exRun; }
402     catch(const css::uno::Exception&)
403         { sHelpURL = ::rtl::OUString(); }
404 
405     return sHelpURL;
406 }
407 
408 //-----------------------------------------------
409 ::rtl::OUString HelpOnStartup::ist_createHelpURL(const ::rtl::OUString& sBaseURL,
410                                                  const ::rtl::OUString& sLocale ,
411                                                  const ::rtl::OUString& sSystem )
412 {
413     ::rtl::OUStringBuffer sHelpURL(256);
414     sHelpURL.append     (sBaseURL    );
415     sHelpURL.appendAscii("?Language=");
416     sHelpURL.append     (sLocale     );
417     sHelpURL.appendAscii("&System="  );
418     sHelpURL.append     (sSystem     );
419 
420     return sHelpURL.makeStringAndClear();
421 }
422 
423 } // namespace framework
424