Line data Source code
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 :
10 : #include <config_folders.h>
11 :
12 : #include "personalization.hxx"
13 : #include "personasdochandler.hxx"
14 :
15 : #include <comphelper/processfactory.hxx>
16 : #include <officecfg/Office/Common.hxx>
17 : #include <com/sun/star/container/XNameAccess.hpp>
18 : #include <com/sun/star/beans/XPropertySet.hpp>
19 : #include <osl/file.hxx>
20 : #include <rtl/bootstrap.hxx>
21 : #include <rtl/strbuf.hxx>
22 : #include <tools/urlobj.hxx>
23 : #include <vcl/edit.hxx>
24 : #include <vcl/msgbox.hxx>
25 : #include <vcl/lstbox.hxx>
26 : #include <vcl/svapp.hxx>
27 : #include <vcl/settings.hxx>
28 : #include <vcl/graphicfilter.hxx>
29 : #include <dialmgr.hxx>
30 : #include "cuires.hrc"
31 :
32 : #include <com/sun/star/ucb/SimpleFileAccess.hpp>
33 : #include <com/sun/star/xml/sax/XParser.hpp>
34 : #include <com/sun/star/xml/sax/Parser.hpp>
35 : #include "ucbhelper/content.hxx"
36 :
37 : using namespace com::sun::star;
38 : using namespace ::com::sun::star::uno;
39 : using namespace ::com::sun::star::ucb;
40 : using namespace ::com::sun::star::beans;
41 :
42 0 : SelectPersonaDialog::SelectPersonaDialog( vcl::Window *pParent )
43 0 : : ModalDialog( pParent, "SelectPersonaDialog", "cui/ui/select_persona_dialog.ui" )
44 : {
45 0 : get( m_pSearchButton, "search_personas" );
46 0 : m_pSearchButton->SetClickHdl( LINK( this, SelectPersonaDialog, SearchPersonas ) );
47 :
48 0 : get( m_vSearchSuggestions[0], "suggestion1" );
49 0 : m_vSearchSuggestions[0]->SetText( "LibreOffice" );
50 0 : m_vSearchSuggestions[0]->SetClickHdl( LINK( this, SelectPersonaDialog, SearchPersonas ) );
51 :
52 0 : get( m_vSearchSuggestions[1], "suggestion2" );
53 0 : m_vSearchSuggestions[1]->SetText( CUI_RES ( RID_SVXSTR_PERSONA_ABSTRACT ) );
54 0 : m_vSearchSuggestions[1]->SetClickHdl( LINK( this, SelectPersonaDialog, SearchPersonas ) );
55 :
56 0 : get( m_vSearchSuggestions[2], "suggestion3" );
57 0 : m_vSearchSuggestions[2]->SetText( CUI_RES ( RID_SVXSTR_PERSONA_COLOR ) );
58 0 : m_vSearchSuggestions[2]->SetClickHdl( LINK( this, SelectPersonaDialog, SearchPersonas ) );
59 :
60 0 : get( m_vSearchSuggestions[3], "suggestion4" );
61 0 : m_vSearchSuggestions[3]->SetText( CUI_RES( RID_SVXSTR_PERSONA_MUSIC ) );
62 0 : m_vSearchSuggestions[3]->SetClickHdl( LINK( this, SelectPersonaDialog, SearchPersonas ) );
63 :
64 0 : get( m_vSearchSuggestions[4], "suggestion5" );
65 0 : m_vSearchSuggestions[4]->SetText( CUI_RES( RID_SVXSTR_PERSONA_NATURE ) );
66 0 : m_vSearchSuggestions[4]->SetClickHdl( LINK( this, SelectPersonaDialog, SearchPersonas ) );
67 :
68 0 : get( m_pEdit, "search_term" );
69 0 : m_pEdit->SetPlaceholderText( CUI_RES( RID_SVXSTR_SEARCHTERM ) );
70 :
71 0 : get( m_pProgressLabel, "progress_label" );
72 :
73 0 : get( m_pOkButton, "ok" );
74 0 : m_pOkButton->SetClickHdl( LINK( this, SelectPersonaDialog, ActionOK ) );
75 :
76 0 : get( m_pCancelButton, "cancel" );
77 0 : m_pCancelButton->SetClickHdl( LINK( this, SelectPersonaDialog, ActionCancel ) );
78 0 : get( m_vResultList[0], "result1" );
79 0 : get( m_vResultList[1], "result2" );
80 0 : get( m_vResultList[2], "result3" );
81 0 : get( m_vResultList[3], "result4" );
82 0 : get( m_vResultList[4], "result5" );
83 0 : get( m_vResultList[5], "result6" );
84 0 : get( m_vResultList[6], "result7" );
85 0 : get( m_vResultList[7], "result8" );
86 0 : get( m_vResultList[8], "result9" );
87 :
88 0 : for (sal_Int32 nIndex = 0; nIndex < 9; ++nIndex)
89 : {
90 0 : m_vResultList[nIndex]->SetClickHdl( LINK( this, SelectPersonaDialog, SelectPersona ) );
91 0 : m_vResultList[nIndex]->Disable();
92 : }
93 0 : }
94 :
95 0 : SelectPersonaDialog::~SelectPersonaDialog()
96 : {
97 0 : disposeOnce();
98 0 : }
99 :
100 0 : void SelectPersonaDialog::dispose()
101 : {
102 0 : m_pEdit.clear();
103 0 : m_pSearchButton.clear();
104 0 : m_pProgressLabel.clear();
105 0 : for (VclPtr<PushButton> vp : m_vResultList)
106 0 : vp.clear();
107 0 : for (VclPtr<PushButton> vp : m_vSearchSuggestions)
108 0 : vp.clear();
109 0 : m_pOkButton.clear();
110 0 : m_pCancelButton.clear();
111 0 : ModalDialog::dispose();
112 0 : }
113 :
114 0 : OUString SelectPersonaDialog::GetSelectedPersona() const
115 : {
116 0 : if( !m_aSelectedPersona.isEmpty( ) )
117 0 : return m_aSelectedPersona;
118 :
119 0 : return OUString();
120 : }
121 :
122 0 : IMPL_LINK( SelectPersonaDialog, SearchPersonas, PushButton*, pButton )
123 : {
124 0 : OUString searchTerm;
125 0 : if( m_rSearchThread.is() )
126 0 : m_rSearchThread->StopExecution();
127 :
128 0 : if( pButton == m_pSearchButton )
129 0 : searchTerm = m_pEdit->GetText();
130 : else
131 : {
132 0 : for( sal_Int32 nIndex = 0; nIndex < 5; nIndex++ )
133 : {
134 0 : if( pButton == m_vSearchSuggestions[nIndex] )
135 : {
136 0 : searchTerm = m_vSearchSuggestions[nIndex]->GetDisplayText();
137 0 : break;
138 : }
139 : }
140 : }
141 :
142 0 : if( searchTerm.isEmpty( ) )
143 0 : return 0;
144 :
145 : // TODO FIXME!
146 : // Before the release, the allizom.org url shoud be changed to:
147 : // OUString rSearchURL = "https://services.addons.mozilla.org/en-US/firefox/api/1.5/search/" + searchTerm + "/9/9";
148 : // The problem why it cannot be done just now is that the SSL negotiation
149 : // with services.addons.mozilla.org fails very early - during an early
150 : // propfind, SSL returns X509_V_ERR_CERT_UNTRUSTED to neon, causing the
151 : // NE_SSL_UNTRUSTED being set in verify_callback in neon/src/ne_openssl.c
152 : //
153 : // This is not cleared anywhere during the init, and so later, even though
154 : // we have found the certificate, this triggers
155 : // NeonSession_CertificationNotify callback, that
156 : // causes that NE_SSL_UNTRUSTED is igored in cases when the condition
157 : // if ( pSession->isDomainMatch(
158 : // GetHostnamePart( xEECert.get()->getSubjectName() ) ) )
159 : // is true; but that is only when getSubjectName() actually returns a
160 : // wildcard, or the exact name.
161 : //
162 : // In the case of services.addons.mozilla.com, the certificate is for
163 : // versioncheck.addons.mozilla.com, but it also has
164 : // X509v3 Subject Alternative Name:
165 : // DNS:services.addons.mozilla.org, DNS:versioncheck-bg.addons.mozilla.org, DNS:pyrepo.addons.mozilla.org, DNS:versioncheck.addons.mozilla.org
166 : // So it is all valid; but the early X509_V_ERR_CERT_UNTRUSTED failure
167 : // described above just makes this being ignored.
168 : //
169 : // My suspicion is that this never actually worked, and the
170 : // if ( pSession->isDomainMatch(
171 : // GetHostnamePart( xEECert.get()->getSubjectName() ) ) )
172 : // works around the root cause that is there for years, and which makes it
173 : // work in most cases. I guess that we initialize something wrongly or
174 : // too late; but I have already spent few hours debugging, and
175 : // give up for the moment - need to return to this at some stage.
176 0 : OUString rSearchURL = "https://addons.allizom.org/en-US/firefox/api/1.5/search/" + searchTerm + "/9/9";
177 0 : m_rSearchThread = new SearchAndParseThread( this, rSearchURL );
178 0 : m_rSearchThread->launch();
179 0 : return 0;
180 : }
181 :
182 0 : IMPL_LINK( SelectPersonaDialog, ActionOK, PushButton*, /* pButton */ )
183 : {
184 0 : OUString aSelectedPersona = GetSelectedPersona();
185 :
186 0 : if( !aSelectedPersona.isEmpty() )
187 : {
188 0 : m_rSearchThread = new SearchAndParseThread( this, aSelectedPersona );
189 0 : m_rSearchThread->launch();
190 : }
191 :
192 : else
193 : {
194 0 : if( m_rSearchThread.is() )
195 0 : m_rSearchThread->StopExecution();
196 0 : EndDialog( RET_OK );
197 : }
198 0 : return 0;
199 : }
200 :
201 0 : IMPL_LINK( SelectPersonaDialog, ActionCancel, PushButton*, /* pButton */ )
202 : {
203 0 : if( m_rSearchThread.is() )
204 0 : m_rSearchThread->StopExecution();
205 :
206 0 : EndDialog( RET_CANCEL );
207 0 : return 0;
208 : }
209 :
210 0 : IMPL_LINK( SelectPersonaDialog, SelectPersona, PushButton*, pButton )
211 : {
212 0 : if( m_rSearchThread.is() )
213 0 : m_rSearchThread->StopExecution();
214 :
215 0 : for( sal_Int32 index = 0; index < 9; index++ )
216 : {
217 0 : if( pButton == m_vResultList[index] )
218 : {
219 0 : if( !m_vPersonaSettings[index].isEmpty() )
220 : {
221 0 : m_aSelectedPersona = m_vPersonaSettings[index];
222 : // get the persona name from the setting variable to show in the progress.
223 0 : sal_Int32 nNameIndex = m_aSelectedPersona.indexOf( ';' );
224 0 : OUString aName = m_aSelectedPersona.copy( 0, nNameIndex );
225 0 : OUString aProgress( CUI_RES( RID_SVXSTR_SELECTEDPERSONA ) );
226 0 : aProgress += aName;
227 0 : SetProgress( aProgress );
228 : }
229 0 : break;
230 : }
231 : }
232 0 : return 0;
233 : }
234 :
235 0 : void SelectPersonaDialog::SetAppliedPersonaSetting( OUString& rPersonaSetting )
236 : {
237 0 : m_aAppliedPersona = rPersonaSetting;
238 0 : }
239 :
240 0 : OUString SelectPersonaDialog::GetAppliedPersonaSetting() const
241 : {
242 0 : return m_aAppliedPersona;
243 : }
244 :
245 0 : void SelectPersonaDialog::SetProgress( OUString& rProgress )
246 : {
247 0 : if(rProgress.isEmpty())
248 0 : m_pProgressLabel->Hide();
249 : else
250 : {
251 0 : SolarMutexGuard aGuard;
252 0 : m_pProgressLabel->Show();
253 0 : m_pProgressLabel->SetText( rProgress );
254 0 : setOptimalLayoutSize();
255 : }
256 0 : }
257 :
258 0 : void SelectPersonaDialog::SetImages( Image aImage, sal_Int32 nIndex )
259 : {
260 0 : m_vResultList[nIndex]->Enable();
261 0 : m_vResultList[nIndex]->SetModeImage( aImage );
262 0 : }
263 :
264 0 : void SelectPersonaDialog::AddPersonaSetting( OUString& rPersonaSetting )
265 : {
266 0 : m_vPersonaSettings.push_back( rPersonaSetting );
267 0 : }
268 :
269 0 : void SelectPersonaDialog::ClearSearchResults()
270 : {
271 0 : m_vPersonaSettings.clear();
272 0 : m_aSelectedPersona.clear();
273 0 : for( sal_Int32 nIndex = 0; nIndex < 9; nIndex++ )
274 : {
275 0 : m_vResultList[nIndex]->Disable();
276 0 : m_vResultList[nIndex]->SetModeImage(Image());
277 : }
278 0 : }
279 :
280 0 : SvxPersonalizationTabPage::SvxPersonalizationTabPage( vcl::Window *pParent, const SfxItemSet &rSet )
281 0 : : SfxTabPage( pParent, "PersonalizationTabPage", "cui/ui/personalization_tab.ui", &rSet )
282 : {
283 : // persona
284 0 : get( m_pNoPersona, "no_persona" );
285 0 : get( m_pDefaultPersona, "default_persona" );
286 :
287 0 : get( m_pOwnPersona, "own_persona" );
288 0 : m_pOwnPersona->SetClickHdl( LINK( this, SvxPersonalizationTabPage, ForceSelect ) );
289 :
290 0 : get( m_pSelectPersona, "select_persona" );
291 0 : m_pSelectPersona->SetClickHdl( LINK( this, SvxPersonalizationTabPage, SelectPersona ) );
292 :
293 0 : get( m_vDefaultPersonaImages[0], "default1" );
294 0 : m_vDefaultPersonaImages[0]->SetClickHdl( LINK( this, SvxPersonalizationTabPage, DefaultPersona ) );
295 :
296 0 : get( m_vDefaultPersonaImages[1], "default2" );
297 0 : m_vDefaultPersonaImages[1]->SetClickHdl( LINK( this, SvxPersonalizationTabPage, DefaultPersona ) );
298 :
299 0 : get( m_vDefaultPersonaImages[2], "default3" );
300 0 : m_vDefaultPersonaImages[2]->SetClickHdl( LINK( this, SvxPersonalizationTabPage, DefaultPersona ) );
301 :
302 0 : get( m_pPersonaList, "installed_personas" );
303 0 : m_pPersonaList->SetSelectHdl( LINK( this, SvxPersonalizationTabPage, SelectInstalledPersona ) );
304 :
305 0 : get( m_pExtensionPersonaPreview, "persona_preview" );
306 :
307 0 : get ( m_pExtensionLabel, "extensions_label" );
308 :
309 0 : LoadDefaultImages();
310 0 : LoadExtensionThemes();
311 0 : }
312 :
313 0 : SvxPersonalizationTabPage::~SvxPersonalizationTabPage()
314 : {
315 0 : disposeOnce();
316 0 : }
317 :
318 0 : void SvxPersonalizationTabPage::dispose()
319 : {
320 0 : m_pNoPersona.clear();
321 0 : m_pDefaultPersona.clear();
322 0 : m_pOwnPersona.clear();
323 0 : m_pSelectPersona.clear();
324 0 : for (int i=0; i<3; ++i)
325 0 : m_vDefaultPersonaImages[i].clear();
326 0 : m_pExtensionPersonaPreview.clear();
327 0 : m_pPersonaList.clear();
328 0 : m_pExtensionLabel.clear();
329 0 : SfxTabPage::dispose();
330 0 : }
331 :
332 :
333 0 : VclPtr<SfxTabPage> SvxPersonalizationTabPage::Create( vcl::Window *pParent, const SfxItemSet *rSet )
334 : {
335 0 : return VclPtr<SvxPersonalizationTabPage>::Create( pParent, *rSet );
336 : }
337 :
338 0 : bool SvxPersonalizationTabPage::FillItemSet( SfxItemSet * )
339 : {
340 : // persona
341 0 : OUString aPersona( "default" );
342 0 : if ( m_pNoPersona->IsChecked() )
343 0 : aPersona = "no";
344 0 : else if ( m_pOwnPersona->IsChecked() )
345 0 : aPersona = "own";
346 :
347 0 : bool bModified = false;
348 0 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
349 0 : if ( xContext.is() &&
350 0 : ( aPersona != officecfg::Office::Common::Misc::Persona::get( xContext ) ||
351 0 : m_aPersonaSettings != officecfg::Office::Common::Misc::PersonaSettings::get( xContext ) ) )
352 : {
353 0 : bModified = true;
354 : }
355 :
356 : // write
357 0 : std::shared_ptr< comphelper::ConfigurationChanges > batch( comphelper::ConfigurationChanges::create() );
358 0 : if( aPersona == "no" )
359 0 : m_aPersonaSettings.clear();
360 0 : officecfg::Office::Common::Misc::Persona::set( aPersona, batch );
361 0 : officecfg::Office::Common::Misc::PersonaSettings::set( m_aPersonaSettings, batch );
362 0 : batch->commit();
363 :
364 0 : if ( bModified )
365 : {
366 : // broadcast the change
367 0 : DataChangedEvent aDataChanged( DataChangedEventType::SETTINGS, NULL, AllSettingsFlags::STYLE );
368 0 : Application::NotifyAllWindows( aDataChanged );
369 : }
370 :
371 0 : return bModified;
372 : }
373 :
374 0 : void SvxPersonalizationTabPage::Reset( const SfxItemSet * )
375 : {
376 0 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
377 :
378 : // persona
379 0 : OUString aPersona( "default" );
380 0 : if ( xContext.is() )
381 : {
382 0 : aPersona = officecfg::Office::Common::Misc::Persona::get( xContext );
383 0 : m_aPersonaSettings = officecfg::Office::Common::Misc::PersonaSettings::get( xContext );
384 : }
385 :
386 0 : if ( aPersona == "no" )
387 0 : m_pNoPersona->Check();
388 0 : else if ( aPersona == "own" )
389 0 : m_pOwnPersona->Check();
390 : else
391 0 : m_pDefaultPersona->Check();
392 0 : }
393 :
394 0 : void SvxPersonalizationTabPage::SetPersonaSettings( const OUString& aPersonaSettings )
395 : {
396 0 : m_aPersonaSettings = aPersonaSettings;
397 0 : m_pOwnPersona->Check();
398 0 : }
399 :
400 0 : void SvxPersonalizationTabPage::LoadDefaultImages()
401 : {
402 : // Load the pre saved personas
403 :
404 0 : OUString gallery( "" );
405 0 : gallery = "$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER;
406 0 : gallery += "/gallery/personas/";
407 0 : rtl::Bootstrap::expandMacros( gallery );
408 0 : OUString aPersonasList = gallery + "personas_list.txt";
409 0 : SvFileStream aStream( aPersonasList, StreamMode::READ );
410 0 : GraphicFilter aFilter;
411 0 : Graphic aGraphic;
412 0 : sal_Int32 nIndex = 0;
413 :
414 0 : while( aStream.IsOpen() && !aStream.IsEof() )
415 : {
416 0 : OString aLine;
417 0 : aStream.ReadLine( aLine );
418 0 : OUString aPersonaSetting( OStringToOUString( aLine, RTL_TEXTENCODING_UTF8 ) );
419 0 : OUString aPreviewFile;
420 0 : sal_Int32 nNewIndex = aPersonaSetting.indexOf( ';', 0 );
421 0 : if( nNewIndex < 0 )
422 0 : break;
423 0 : aPreviewFile = aPersonaSetting.copy( 0, nNewIndex );
424 0 : aPersonaSetting = aPersonaSetting.copy( nNewIndex + 1 );
425 0 : m_vDefaultPersonaSettings.push_back( aPersonaSetting );
426 :
427 0 : INetURLObject aURLObj( gallery + aPreviewFile );
428 0 : aFilter.ImportGraphic( aGraphic, aURLObj );
429 0 : Bitmap aBmp = aGraphic.GetBitmap();
430 0 : m_vDefaultPersonaImages[nIndex]->Show();
431 0 : m_vDefaultPersonaImages[nIndex++]->SetModeImage( Image( aBmp ) );
432 0 : }
433 0 : }
434 :
435 0 : void SvxPersonalizationTabPage::LoadExtensionThemes()
436 : {
437 : // See if any extensions are used to install personas. If yes, load them.
438 :
439 0 : css::uno::Sequence<OUString> installedPersonas( officecfg::Office::Common::Misc::PersonasList::get()->getElementNames() );
440 0 : sal_Int32 nLength = installedPersonas.getLength();
441 :
442 0 : if( nLength == 0 )
443 0 : return;
444 :
445 0 : m_pPersonaList->Show();
446 0 : m_pExtensionLabel->Show();
447 :
448 0 : for( sal_Int32 nIndex = 0; nIndex < nLength; nIndex++ )
449 : {
450 0 : Reference< XPropertySet > xPropertySet( officecfg::Office::Common::Misc::PersonasList::get()->getByName( installedPersonas[nIndex] ), UNO_QUERY_THROW );
451 0 : OUString aPersonaName, aPreviewFile, aHeaderFile, aFooterFile, aTextColor, aAccentColor, aPersonaSettings;
452 0 : Any aValue = xPropertySet->getPropertyValue( "Name" );
453 0 : aValue >>= aPersonaName;
454 0 : m_pPersonaList->InsertEntry( aPersonaName );
455 :
456 0 : aValue = xPropertySet->getPropertyValue( "Preview" );
457 0 : aValue >>= aPreviewFile;
458 :
459 0 : aValue = xPropertySet->getPropertyValue( "Header" );
460 0 : aValue >>= aHeaderFile;
461 :
462 0 : aValue = xPropertySet->getPropertyValue( "Footer" );
463 0 : aValue >>= aFooterFile;
464 :
465 0 : aValue = xPropertySet->getPropertyValue( "TextColor" );
466 0 : aValue >>= aTextColor;
467 :
468 0 : aValue = xPropertySet->getPropertyValue( "AccentColor" );
469 0 : aValue >>= aAccentColor;
470 :
471 0 : aPersonaSettings = aPreviewFile + ";" + aHeaderFile + ";" + aFooterFile + ";" + aTextColor + ";" + aAccentColor;
472 0 : rtl::Bootstrap::expandMacros( aPersonaSettings );
473 0 : m_vExtensionPersonaSettings.push_back( aPersonaSettings );
474 0 : }
475 : }
476 :
477 0 : IMPL_LINK( SvxPersonalizationTabPage, SelectPersona, PushButton*, /*pButton*/ )
478 : {
479 0 : ScopedVclPtrInstance< SelectPersonaDialog > aDialog(nullptr);
480 :
481 0 : if ( aDialog->Execute() == RET_OK )
482 : {
483 0 : OUString aPersonaSetting( aDialog->GetAppliedPersonaSetting() );
484 0 : if ( !aPersonaSetting.isEmpty() )
485 : {
486 0 : SetPersonaSettings( aPersonaSetting );
487 0 : }
488 : }
489 :
490 0 : return 0;
491 : }
492 :
493 0 : IMPL_LINK( SvxPersonalizationTabPage, ForceSelect, RadioButton*, pButton )
494 : {
495 0 : if ( pButton == m_pOwnPersona && m_aPersonaSettings.isEmpty() )
496 0 : SelectPersona( m_pSelectPersona );
497 :
498 0 : return 0;
499 : }
500 :
501 0 : IMPL_LINK( SvxPersonalizationTabPage, DefaultPersona, PushButton*, pButton )
502 : {
503 0 : m_pDefaultPersona->Check();
504 0 : for( sal_Int32 nIndex = 0; nIndex < 3; nIndex++ )
505 : {
506 0 : if( pButton == m_vDefaultPersonaImages[nIndex] )
507 0 : m_aPersonaSettings = m_vDefaultPersonaSettings[nIndex];
508 : }
509 :
510 0 : return 0;
511 : }
512 :
513 0 : IMPL_LINK( SvxPersonalizationTabPage, SelectInstalledPersona, ListBox*, )
514 : {
515 0 : m_pOwnPersona->Check();
516 :
517 : // Get the details of the selected theme.
518 0 : m_pExtensionPersonaPreview->Show();
519 0 : sal_Int32 nSelectedPos = m_pPersonaList->GetSelectEntryPos();
520 0 : OUString aSettings = m_vExtensionPersonaSettings[nSelectedPos];
521 0 : sal_Int32 nIndex = aSettings.indexOf( ';', 0 );
522 0 : OUString aPreviewFile = aSettings.copy( 0, nIndex );
523 0 : m_aPersonaSettings = aSettings.copy( nIndex + 1 );
524 :
525 : // Show the preview file in the button.
526 0 : GraphicFilter aFilter;
527 0 : Graphic aGraphic;
528 0 : INetURLObject aURLObj( aPreviewFile );
529 0 : aFilter.ImportGraphic( aGraphic, aURLObj );
530 0 : Bitmap aBmp = aGraphic.GetBitmap();
531 0 : m_pExtensionPersonaPreview->SetModeImage( Image( aBmp ) );
532 :
533 0 : return 0;
534 : }
535 :
536 : /// Find the value on the Persona page, and convert it to a usable form.
537 0 : static OUString searchValue( const OString &rBuffer, sal_Int32 from, const OString &rIdentifier )
538 : {
539 0 : sal_Int32 where = rBuffer.indexOf( rIdentifier, from );
540 0 : if ( where < 0 )
541 0 : return OUString();
542 :
543 0 : where += rIdentifier.getLength();
544 :
545 0 : sal_Int32 end = rBuffer.indexOf( """, where );
546 0 : if ( end < 0 )
547 0 : return OUString();
548 :
549 0 : OString aOString( rBuffer.copy( where, end - where ) );
550 0 : OUString aString( aOString.getStr(), aOString.getLength(), RTL_TEXTENCODING_UTF8, OSTRING_TO_OUSTRING_CVTFLAGS );
551 :
552 0 : return aString.replaceAll( "\\/", "/" );
553 : }
554 :
555 : /// Parse the Persona web page, and find where to get the bitmaps + the color values.
556 0 : static bool parsePersonaInfo( const OString &rBuffer, OUString *pHeaderURL, OUString *pFooterURL,
557 : OUString *pTextColor, OUString *pAccentColor, OUString *pPreviewURL,
558 : OUString *pName )
559 : {
560 : // it is the first attribute that contains "persona="
561 0 : sal_Int32 persona = rBuffer.indexOf( "data-browsertheme=\"{" );
562 0 : if ( persona < 0 )
563 0 : return false;
564 :
565 : // now search inside
566 0 : *pHeaderURL = searchValue( rBuffer, persona, ""headerURL":"" );
567 0 : if ( pHeaderURL->isEmpty() )
568 0 : return false;
569 :
570 0 : *pFooterURL = searchValue( rBuffer, persona, ""footerURL":"" );
571 0 : if ( pFooterURL->isEmpty() )
572 0 : return false;
573 :
574 0 : *pTextColor = searchValue( rBuffer, persona, ""textcolor":"" );
575 0 : if ( pTextColor->isEmpty() )
576 0 : return false;
577 :
578 0 : *pAccentColor = searchValue( rBuffer, persona, ""accentcolor":"" );
579 0 : if ( pAccentColor->isEmpty() )
580 0 : return false;
581 :
582 0 : *pPreviewURL = searchValue( rBuffer, persona, ""previewURL":"" );
583 0 : if ( pPreviewURL->isEmpty() )
584 0 : return false;
585 :
586 0 : *pName = searchValue( rBuffer, persona, ""name":"" );
587 0 : if ( pName->isEmpty() )
588 0 : return false;
589 :
590 0 : return true;
591 : }
592 :
593 0 : SearchAndParseThread::SearchAndParseThread( SelectPersonaDialog* pDialog,
594 : const OUString& rURL ) :
595 : Thread( "cuiPersonasSearchThread" ),
596 : m_pPersonaDialog( pDialog ),
597 : m_aURL( rURL ),
598 0 : m_bExecute( true )
599 : {
600 0 : }
601 :
602 0 : SearchAndParseThread::~SearchAndParseThread()
603 : {
604 0 : }
605 :
606 0 : void SearchAndParseThread::execute()
607 : {
608 0 : if( m_aURL.startsWith( "https://" ) )
609 : {
610 0 : m_pPersonaDialog->ClearSearchResults();
611 0 : OUString sProgress( CUI_RES( RID_SVXSTR_SEARCHING ) );
612 0 : m_pPersonaDialog->SetProgress( sProgress );
613 0 : Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
614 0 : Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(xContext);
615 0 : PersonasDocHandler* pHandler = new PersonasDocHandler();
616 0 : Reference< xml::sax::XDocumentHandler > xDocHandler = pHandler;
617 0 : uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
618 0 : uno::Reference< io::XInputStream > xStream;
619 0 : xParser->setDocumentHandler( xDocHandler );
620 :
621 0 : if ( !xFileAccess.is() )
622 0 : return;
623 :
624 : try {
625 0 : xStream = xFileAccess->openFileRead( m_aURL );
626 : }
627 0 : catch (...)
628 : {
629 0 : sProgress = CUI_RES(RID_SVXSTR_SEARCHERROR);
630 0 : sProgress = sProgress.replaceAll("%1", m_aURL);
631 0 : m_pPersonaDialog->SetProgress(sProgress);
632 0 : return;
633 : }
634 :
635 0 : xml::sax::InputSource aParserInput;
636 0 : aParserInput.aInputStream = xStream;
637 0 : xParser->parseStream( aParserInput );
638 :
639 0 : if( !pHandler->hasResults() )
640 : {
641 0 : sProgress = CUI_RES( RID_SVXSTR_NORESULTS );
642 0 : m_pPersonaDialog->SetProgress( sProgress );
643 0 : return;
644 : }
645 :
646 0 : std::vector<OUString> vLearnmoreURLs = pHandler->getLearnmoreURLs();
647 0 : std::vector<OUString>::iterator it;
648 0 : sal_Int32 nIndex = 0;
649 0 : GraphicFilter aFilter;
650 0 : Graphic aGraphic;
651 :
652 0 : for( it = vLearnmoreURLs.begin(); it!=vLearnmoreURLs.end(); ++it )
653 : {
654 0 : OUString sPreviewFile, aPersonaSetting;
655 0 : getPreviewFile( *it, &sPreviewFile, &aPersonaSetting );
656 0 : INetURLObject aURLObj( sPreviewFile );
657 0 : aFilter.ImportGraphic( aGraphic, aURLObj );
658 0 : Bitmap aBmp = aGraphic.GetBitmap();
659 :
660 0 : if( !m_bExecute )
661 0 : return;
662 :
663 : // for VCL to be able to do visual changes in the thread
664 0 : SolarMutexGuard aGuard;
665 0 : m_pPersonaDialog->SetImages( Image( aBmp ), nIndex++ );
666 0 : m_pPersonaDialog->setOptimalLayoutSize();
667 0 : m_pPersonaDialog->AddPersonaSetting( aPersonaSetting );
668 0 : }
669 :
670 0 : if( !m_bExecute )
671 0 : return;
672 :
673 0 : SolarMutexGuard aGuard;
674 0 : sProgress.clear();
675 0 : m_pPersonaDialog->SetProgress( sProgress );
676 0 : m_pPersonaDialog->setOptimalLayoutSize();
677 : }
678 :
679 : else
680 : {
681 0 : OUString sProgress( CUI_RES( RID_SVXSTR_APPLYPERSONA ) );
682 0 : m_pPersonaDialog->SetProgress( sProgress );
683 :
684 0 : uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
685 0 : if ( !xFileAccess.is() )
686 0 : return;
687 :
688 0 : OUString aName, aHeaderURL, aFooterURL, aTextColor, aAccentColor;
689 0 : OUString aPersonaSetting;
690 :
691 : // get the required fields from m_aURL
692 0 : sal_Int32 nOldIndex = 0;
693 0 : sal_Int32 nNewIndex = m_aURL.indexOf( ';', nOldIndex );
694 0 : aName = m_aURL.copy( nOldIndex, ( nNewIndex - nOldIndex ) );
695 :
696 0 : nOldIndex = nNewIndex + 1;
697 0 : nNewIndex = m_aURL.indexOf( ';', nOldIndex );
698 0 : aHeaderURL = m_aURL.copy(nOldIndex , ( nNewIndex - nOldIndex ) );
699 :
700 0 : nOldIndex = nNewIndex + 1;
701 0 : nNewIndex = m_aURL.indexOf( ';', nOldIndex );
702 0 : aFooterURL = m_aURL.copy( nOldIndex, ( nNewIndex - nOldIndex ) );
703 :
704 0 : nOldIndex = nNewIndex + 1;
705 0 : nNewIndex = m_aURL.indexOf( ';', nOldIndex );
706 0 : aTextColor = m_aURL.copy( nOldIndex, ( nNewIndex - nOldIndex ) );
707 :
708 0 : nOldIndex = nNewIndex + 1;
709 0 : nNewIndex = m_aURL.getLength();
710 0 : aAccentColor = m_aURL.copy( nOldIndex, ( nNewIndex - nOldIndex ) );
711 :
712 : // copy the images to the user's gallery
713 0 : OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
714 0 : rtl::Bootstrap::expandMacros( gallery );
715 0 : gallery += "/user/gallery/personas/";
716 0 : osl::Directory::createPath( gallery );
717 :
718 0 : OUString aHeaderFile( INetURLObject( aHeaderURL ).getName() );
719 0 : OUString aFooterFile( INetURLObject( aFooterURL ).getName() );
720 :
721 0 : aHeaderFile = aName + "/" + aHeaderFile;
722 0 : aFooterFile = aName + "/" + aFooterFile;
723 :
724 : try {
725 0 : xFileAccess->copy( aHeaderURL, gallery + aHeaderFile );
726 0 : xFileAccess->copy( aFooterURL, gallery + aFooterFile );
727 : }
728 0 : catch ( const uno::Exception & )
729 : {
730 0 : sProgress = CUI_RES( RID_SVXSTR_SEARCHERROR );
731 0 : sProgress = sProgress.replaceAll("%1", m_aURL);
732 0 : m_pPersonaDialog->SetProgress( sProgress );
733 0 : return;
734 : }
735 :
736 0 : if( !m_bExecute )
737 0 : return;
738 :
739 0 : SolarMutexGuard aGuard;
740 :
741 0 : aPersonaSetting = aHeaderFile + ";" + aFooterFile + ";" + aTextColor + ";" + aAccentColor;
742 0 : m_pPersonaDialog->SetAppliedPersonaSetting( aPersonaSetting );
743 0 : m_pPersonaDialog->EndDialog( RET_OK );
744 : }
745 : }
746 :
747 0 : void SearchAndParseThread::getPreviewFile( const OUString& rURL, OUString *pPreviewFile, OUString *pPersonaSetting )
748 : {
749 0 : uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
750 0 : if ( !xFileAccess.is() )
751 0 : return;
752 :
753 0 : uno::Reference< io::XInputStream > xStream;
754 : try {
755 0 : xStream = xFileAccess->openFileRead( rURL );
756 : }
757 0 : catch (...)
758 : {
759 0 : OUString sProgress( CUI_RES( RID_SVXSTR_SEARCHERROR ) );
760 0 : sProgress = sProgress.replaceAll("%1", m_aURL);
761 0 : m_pPersonaDialog->SetProgress( sProgress );
762 0 : return;
763 : }
764 0 : if ( !xStream.is() )
765 0 : return;
766 :
767 : // read the persona specification
768 : // NOTE: Parsing for real is an overkill here; and worse - I tried, and
769 : // the HTML the site provides is not 100% valid ;-)
770 0 : const sal_Int32 BUF_LEN = 8000;
771 0 : uno::Sequence< sal_Int8 > buffer( BUF_LEN );
772 0 : OStringBuffer aBuffer( 64000 );
773 :
774 0 : sal_Int32 nRead = 0;
775 0 : while ( ( nRead = xStream->readBytes( buffer, BUF_LEN ) ) == BUF_LEN )
776 0 : aBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
777 :
778 0 : if ( nRead > 0 )
779 0 : aBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
780 :
781 0 : xStream->closeInput();
782 :
783 : // get the important bits of info
784 0 : OUString aHeaderURL, aFooterURL, aTextColor, aAccentColor, aPreviewURL, aName;
785 :
786 0 : if ( !parsePersonaInfo( aBuffer.makeStringAndClear(), &aHeaderURL, &aFooterURL, &aTextColor, &aAccentColor, &aPreviewURL, &aName ) )
787 0 : return;
788 :
789 : // copy the images to the user's gallery
790 0 : OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
791 0 : rtl::Bootstrap::expandMacros( gallery );
792 0 : gallery += "/user/gallery/personas/";
793 0 : gallery += aName + "/";
794 0 : osl::Directory::createPath( gallery );
795 :
796 0 : OUString aPreviewFile( INetURLObject( aPreviewURL ).getName() );
797 :
798 : try {
799 0 : xFileAccess->copy( aPreviewURL, gallery + aPreviewFile );
800 : }
801 0 : catch ( const uno::Exception & )
802 : {
803 0 : return;
804 : }
805 0 : *pPreviewFile = gallery + aPreviewFile;
806 0 : *pPersonaSetting = aName + ";" + aHeaderURL + ";" + aFooterURL + ";" + aTextColor + ";" + aAccentColor;
807 0 : }
808 :
809 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|