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