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 :
14 : #include <comphelper/processfactory.hxx>
15 : #include <officecfg/Office/Common.hxx>
16 : #include <osl/file.hxx>
17 : #include <rtl/bootstrap.hxx>
18 : #include <tools/urlobj.hxx>
19 : #include <vcl/edit.hxx>
20 : #include <vcl/msgbox.hxx>
21 : #include <vcl/svapp.hxx>
22 : #include <vcl/settings.hxx>
23 :
24 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
25 : #include <com/sun/star/system/SystemShellExecute.hpp>
26 : #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
27 : #include <com/sun/star/ucb/SimpleFileAccess.hpp>
28 : #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
29 : #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
30 : #include <com/sun/star/ui/dialogs/FilePicker.hpp>
31 : #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
32 : #include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
33 : #include <com/sun/star/ui/dialogs/XFilterManager.hpp>
34 :
35 : using namespace com::sun::star;
36 :
37 : /** Dialog that will allow the user to choose a Persona to use.
38 :
39 : So far there is no better possibility than just to paste the URL from
40 : https://addons.mozilla.org/firefox/themes ...
41 : */
42 0 : class SelectPersonaDialog : public ModalDialog
43 : {
44 : private:
45 : Edit *m_pEdit; ///< The input line for the Persona URL
46 :
47 : public:
48 : SelectPersonaDialog( Window *pParent );
49 :
50 : /// Get the URL from the Edit field.
51 : OUString GetPersonaURL() const;
52 :
53 : private:
54 : /// Handle the [Visit Firefox Personas] button
55 : DECL_LINK( VisitPersonas, PushButton* );
56 : };
57 :
58 0 : SelectPersonaDialog::SelectPersonaDialog( Window *pParent )
59 0 : : ModalDialog( pParent, "SelectPersonaDialog", "cui/ui/select_persona_dialog.ui" )
60 : {
61 : PushButton *pButton;
62 0 : get( pButton, "visit_personas" );
63 0 : pButton->SetClickHdl( LINK( this, SelectPersonaDialog, VisitPersonas ) );
64 :
65 0 : get( m_pEdit, "persona_url" );
66 0 : m_pEdit->SetPlaceholderText( "https://addons.mozilla.org/firefox/themes/" );
67 0 : }
68 :
69 0 : OUString SelectPersonaDialog::GetPersonaURL() const
70 : {
71 0 : OUString aText( m_pEdit->GetText() );
72 :
73 0 : if ( aText.startsWith( "https://addons.mozilla.org/" ) )
74 0 : return aText;
75 :
76 0 : return OUString();
77 : }
78 :
79 0 : IMPL_LINK( SelectPersonaDialog, VisitPersonas, PushButton*, /*pButton*/ )
80 : {
81 0 : uno::Reference< com::sun::star::system::XSystemShellExecute > xSystemShell( com::sun::star::system::SystemShellExecute::create( ::comphelper::getProcessComponentContext() ) );
82 :
83 0 : xSystemShell->execute( "https://addons.mozilla.org/firefox/themes/", OUString(), com::sun::star::system::SystemShellExecuteFlags::URIS_ONLY );
84 :
85 0 : return 0;
86 : }
87 :
88 0 : SvxPersonalizationTabPage::SvxPersonalizationTabPage( Window *pParent, const SfxItemSet &rSet )
89 0 : : SfxTabPage( pParent, "PersonalizationTabPage", "cui/ui/personalization_tab.ui", rSet )
90 : {
91 : // persona
92 0 : get( m_pNoPersona, "no_persona" );
93 0 : get( m_pDefaultPersona, "default_persona" );
94 :
95 0 : get( m_pOwnPersona, "own_persona" );
96 0 : m_pOwnPersona->SetClickHdl( LINK( this, SvxPersonalizationTabPage, ForceSelect ) );
97 :
98 0 : get( m_pSelectPersona, "select_persona" );
99 0 : m_pSelectPersona->SetClickHdl( LINK( this, SvxPersonalizationTabPage, SelectPersona ) );
100 0 : }
101 :
102 0 : SvxPersonalizationTabPage::~SvxPersonalizationTabPage()
103 : {
104 0 : }
105 :
106 0 : SfxTabPage* SvxPersonalizationTabPage::Create( Window *pParent, const SfxItemSet &rSet )
107 : {
108 0 : return new SvxPersonalizationTabPage( pParent, rSet );
109 : }
110 :
111 0 : bool SvxPersonalizationTabPage::FillItemSet( SfxItemSet & )
112 : {
113 : // persona
114 0 : OUString aPersona( "default" );
115 0 : if ( m_pNoPersona->IsChecked() )
116 0 : aPersona = "no";
117 0 : else if ( m_pOwnPersona->IsChecked() )
118 0 : aPersona = "own";
119 :
120 0 : bool bModified = false;
121 0 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
122 0 : if ( xContext.is() &&
123 0 : ( aPersona != officecfg::Office::Common::Misc::Persona::get( xContext ) ||
124 0 : m_aPersonaSettings != officecfg::Office::Common::Misc::PersonaSettings::get( xContext ) ) )
125 : {
126 0 : bModified = true;
127 : }
128 :
129 : // write
130 0 : boost::shared_ptr< comphelper::ConfigurationChanges > batch( comphelper::ConfigurationChanges::create() );
131 :
132 0 : officecfg::Office::Common::Misc::Persona::set( aPersona, batch );
133 0 : officecfg::Office::Common::Misc::PersonaSettings::set( m_aPersonaSettings, batch );
134 :
135 0 : batch->commit();
136 :
137 0 : if ( bModified )
138 : {
139 : // broadcast the change
140 0 : DataChangedEvent aDataChanged( DATACHANGED_SETTINGS, NULL, SETTINGS_STYLE );
141 0 : Application::NotifyAllWindows( aDataChanged );
142 : }
143 :
144 0 : return bModified;
145 : }
146 :
147 0 : void SvxPersonalizationTabPage::Reset( const SfxItemSet & )
148 : {
149 0 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
150 :
151 : // persona
152 0 : OUString aPersona( "default" );
153 0 : if ( xContext.is() )
154 : {
155 0 : aPersona = officecfg::Office::Common::Misc::Persona::get( xContext );
156 0 : m_aPersonaSettings = officecfg::Office::Common::Misc::PersonaSettings::get( xContext );
157 : }
158 :
159 0 : if ( aPersona == "no" )
160 0 : m_pNoPersona->Check();
161 0 : else if ( aPersona == "own" )
162 0 : m_pOwnPersona->Check();
163 : else
164 0 : m_pDefaultPersona->Check();
165 0 : }
166 :
167 0 : IMPL_LINK( SvxPersonalizationTabPage, SelectPersona, PushButton*, /*pButton*/ )
168 : {
169 0 : SelectPersonaDialog aDialog( NULL );
170 :
171 0 : while ( aDialog.Execute() == RET_OK )
172 : {
173 0 : OUString aURL( aDialog.GetPersonaURL() );
174 0 : if ( !aURL.isEmpty() )
175 : {
176 0 : if ( CopyPersonaToGallery( aURL ) )
177 0 : m_pOwnPersona->Check();
178 0 : break;
179 : }
180 : // else TODO msgbox that the URL did not match
181 0 : }
182 :
183 0 : return 0;
184 : }
185 :
186 0 : IMPL_LINK( SvxPersonalizationTabPage, ForceSelect, RadioButton*, pButton )
187 : {
188 0 : if ( pButton == m_pOwnPersona && m_aPersonaSettings.isEmpty() )
189 0 : SelectPersona( m_pSelectPersona );
190 :
191 0 : return 0;
192 : }
193 :
194 : /// Find the value on the Persona page, and convert it to a usable form.
195 0 : static OUString searchValue( const OString &rBuffer, sal_Int32 from, const OString &rIdentifier )
196 : {
197 0 : sal_Int32 where = rBuffer.indexOf( rIdentifier, from );
198 0 : if ( where < 0 )
199 0 : return OUString();
200 :
201 0 : where += rIdentifier.getLength();
202 :
203 0 : sal_Int32 end = rBuffer.indexOf( """, where );
204 0 : if ( end < 0 )
205 0 : return OUString();
206 :
207 0 : OString aOString( rBuffer.copy( where, end - where ) );
208 0 : OUString aString( aOString.getStr(), aOString.getLength(), RTL_TEXTENCODING_UTF8, OSTRING_TO_OUSTRING_CVTFLAGS );
209 :
210 0 : return aString.replaceAll( "\\/", "/" );
211 : }
212 :
213 : /// Parse the Persona web page, and find where to get the bitmaps + the color values.
214 0 : static bool parsePersonaInfo( const OString &rBuffer, OUString *pHeaderURL, OUString *pFooterURL, OUString *pTextColor, OUString *pAccentColor )
215 : {
216 : // it is the first attribute that contains "persona="
217 0 : sal_Int32 persona = rBuffer.indexOf( "data-browsertheme=\"{" );
218 0 : if ( persona < 0 )
219 0 : return false;
220 :
221 : // now search inside
222 0 : *pHeaderURL = searchValue( rBuffer, persona, ""headerURL":"" );
223 0 : if ( pHeaderURL->isEmpty() )
224 0 : return false;
225 :
226 0 : *pFooterURL = searchValue( rBuffer, persona, ""footerURL":"" );
227 0 : if ( pFooterURL->isEmpty() )
228 0 : return false;
229 :
230 0 : *pTextColor = searchValue( rBuffer, persona, ""textcolor":"" );
231 0 : if ( pTextColor->isEmpty() )
232 0 : return false;
233 :
234 0 : *pAccentColor = searchValue( rBuffer, persona, ""accentcolor":"" );
235 0 : if ( pAccentColor->isEmpty() )
236 0 : return false;
237 :
238 0 : return true;
239 : }
240 :
241 0 : bool SvxPersonalizationTabPage::CopyPersonaToGallery( const OUString &rURL )
242 : {
243 : // init the input stream
244 0 : uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
245 0 : if ( !xFileAccess.is() )
246 0 : return false;
247 :
248 0 : uno::Reference< io::XInputStream > xStream;
249 : try {
250 0 : xStream = xFileAccess->openFileRead( rURL );
251 : }
252 0 : catch (...)
253 : {
254 0 : return false;
255 : }
256 0 : if ( !xStream.is() )
257 0 : return false;
258 :
259 : // read the persona specification
260 : // NOTE: Parsing for real is an overkill here; and worse - I tried, and
261 : // the HTML the site provides is not 100% valid ;-)
262 0 : const sal_Int32 BUF_LEN = 8000;
263 0 : uno::Sequence< sal_Int8 > buffer( BUF_LEN );
264 0 : OStringBuffer aBuffer( 64000 );
265 :
266 0 : sal_Int32 nRead = 0;
267 0 : while ( ( nRead = xStream->readBytes( buffer, BUF_LEN ) ) == BUF_LEN )
268 0 : aBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
269 :
270 0 : if ( nRead > 0 )
271 0 : aBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
272 :
273 0 : xStream->closeInput();
274 :
275 : // get the important bits of info
276 0 : OUString aHeaderURL, aFooterURL, aTextColor, aAccentColor;
277 :
278 0 : if ( !parsePersonaInfo( aBuffer.makeStringAndClear(), &aHeaderURL, &aFooterURL, &aTextColor, &aAccentColor ) )
279 0 : return false;
280 :
281 : // copy the images to the user's gallery
282 0 : OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
283 0 : rtl::Bootstrap::expandMacros( gallery );
284 0 : gallery += "/user/gallery/personas/";
285 0 : osl::Directory::createPath( gallery );
286 :
287 0 : OUString aHeaderFile( INetURLObject( aHeaderURL ).getName() );
288 0 : OUString aFooterFile( INetURLObject( aFooterURL ).getName() );
289 :
290 : try {
291 0 : xFileAccess->copy( aHeaderURL, gallery + aHeaderFile );
292 0 : xFileAccess->copy( aFooterURL, gallery + aFooterFile );
293 : }
294 0 : catch ( const uno::Exception & )
295 : {
296 0 : return false;
297 : }
298 :
299 0 : m_aPersonaSettings = aHeaderFile + ";" + aFooterFile + ";" + aTextColor + ";" + aAccentColor;
300 :
301 0 : return true;
302 0 : }
303 :
304 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|