summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMuhammet Kara <muhammet.kara@pardus.org.tr>2018-10-05 03:35:43 +0300
committerMuhammet Kara <muhammet.kara@pardus.org.tr>2018-10-08 09:39:28 +0200
commit6c97feec872e82711f58cce61b7796ed594e552c (patch)
tree94ea759dfc1556d43dd39570ca9c33e7fef12e5f
parent1aae63b62b8a745ba3345d73e59107c63f904c02 (diff)
Modernize personas under-the-hood - The search
* Upgrade the used mozilla API from v1.5 to v3 * Ditch Neon for curl * Get rid of the get-HTML-then-search-within craziness It is much faster and smarter now: * Fetches the search results at once in JSON format, instead of repetitively making http calls for each persona, and relying on an ever-changing HTML design * Doesn't redownload and overwrite files each time, so it is much faster for the second time of the same search query This patch handles the search part. A follow-up patch will handle the apply part. Change-Id: I703fc7b510799e8c205566cf5ffad2a81f12c4ea Reviewed-on: https://gerrit.libreoffice.org/61449 Tested-by: Jenkins Reviewed-by: Heiko Tietze <tietze.heiko@gmail.com> Tested-by: Heiko Tietze <tietze.heiko@gmail.com> Reviewed-by: Muhammet Kara <muhammet.kara@pardus.org.tr>
-rw-r--r--cui/Library_cui.mk3
-rw-r--r--cui/source/options/personalization.cxx547
2 files changed, 345 insertions, 205 deletions
diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk
index 1c6185488f36..26900dec8e3a 100644
--- a/cui/Library_cui.mk
+++ b/cui/Library_cui.mk
@@ -66,8 +66,11 @@ $(eval $(call gb_Library_use_externals,cui,\
boost_headers \
$(call gb_Helper_optional,OPENCL,\
clew) \
+ curl \
icuuc \
icu_headers \
+ orcus-parser \
+ orcus \
))
ifeq ($(DISABLE_GUI),)
$(eval $(call gb_Library_use_externals,cui,\
diff --git a/cui/source/options/personalization.cxx b/cui/source/options/personalization.cxx
index d30c1c1d2347..bbaa03e8d3a1 100644
--- a/cui/source/options/personalization.cxx
+++ b/cui/source/options/personalization.cxx
@@ -40,11 +40,131 @@
#include <ucbhelper/content.hxx>
#include <comphelper/simplefileaccessinteraction.hxx>
+#include <curl/curl.h>
+
+#include <orcus/json_document_tree.hpp>
+#include <orcus/config.hpp>
+#include <orcus/pstring.hpp>
+
+#include <vector>
+
using namespace com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::beans;
+#ifdef UNX
+static const char kUserAgent[] = "LibreOffice PersonaDownloader/1.0 (Linux)";
+#else
+static const char kUserAgent[] = "LibreOffice PersonaDownloader/1.0 (unknown platform)";
+#endif
+
+struct PersonaInfo
+{
+ OUString sSlug;
+ OUString sName;
+ OUString sPreviewURL;
+ OUString sHeaderURL;
+ OUString sFooterURL;
+ OUString sTextColor;
+ OUString sAccentColor;
+};
+
+namespace {
+
+// Callback to get the response data from server.
+size_t WriteCallback(void *ptr, size_t size,
+ size_t nmemb, void *userp)
+{
+ if (!userp)
+ return 0;
+
+ std::string* response = static_cast<std::string *>(userp);
+ size_t real_size = size * nmemb;
+ response->append(static_cast<char *>(ptr), real_size);
+ return real_size;
+}
+
+// Callback to get the response data from server to a file.
+size_t WriteCallbackFile(void *ptr, size_t size,
+ size_t nmemb, void *userp)
+{
+ if (!userp)
+ return 0;
+
+ SvStream* response = static_cast<SvStream *>(userp);
+ size_t real_size = size * nmemb;
+ response->WriteBytes(ptr, real_size);
+ return real_size;
+}
+
+// Gets the content of the given URL and returns as a standard string
+std::string curlGet(const OString& rURL)
+{
+ CURL* curl = curl_easy_init();
+
+ if (!curl)
+ return std::string();
+
+ curl_easy_setopt(curl, CURLOPT_URL, rURL.getStr());
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
+
+ std::string response_body;
+
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA,
+ static_cast<void *>(&response_body));
+
+ CURLcode cc = curl_easy_perform(curl);
+ long http_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+
+ if (http_code != 200)
+ {
+ SAL_WARN("cui.options", "Download failed. Error code: " << http_code);
+ }
+
+ if (cc != CURLE_OK)
+ {
+ SAL_WARN("cui.options", "curl error: " << cc);
+ }
+
+ return response_body;
+}
+
+// Downloads and saves the file at the given rURL to to a local path (sFileURL)
+void curlDownload(const OString& rURL, const OUString& sFileURL)
+{
+ CURL* curl = curl_easy_init();
+ SvFileStream aFile( sFileURL, StreamMode::WRITE );
+
+ if (!curl)
+ return;
+
+ curl_easy_setopt(curl, CURLOPT_URL, rURL.getStr());
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
+
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallbackFile);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA,
+ static_cast<void *>(&aFile));
+
+ CURLcode cc = curl_easy_perform(curl);
+ long http_code = 0;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
+
+ if (http_code != 200)
+ {
+ SAL_WARN("cui.options", "Download failed. Error code: " << http_code);
+ }
+
+ if (cc != CURLE_OK)
+ {
+ SAL_WARN("cui.options", "curl error: " << cc);
+ }
+}
+
+} //End of anonymous namespace
+
SelectPersonaDialog::SelectPersonaDialog( vcl::Window *pParent )
: ModalDialog( pParent, "SelectPersonaDialog", "cui/ui/select_persona_dialog.ui" )
{
@@ -134,6 +254,12 @@ IMPL_LINK( SelectPersonaDialog, SearchPersonas, Button*, pButton, void )
* These strings should be in sync with the strings of
* RID_SVXSTR_PERSONA_CATEGORIES in personalization.hrc
*/
+ /* FIXME: These categories are actual categories of Mozilla themes/personas,
+ * but we are using them just as regular search terms, and bringing the first
+ * 9 (MAX_RESULTS) personas which have all the fields set. We should instead bring
+ * results from the actual categories; maybe the most downloaded one, or the ones with
+ * the highest ratings.
+ */
static const OUStringLiteral vSuggestionCategories[] =
{"LibreOffice", "Abstract", "Color", "Music", "Nature", "Solid"};
@@ -159,16 +285,19 @@ IMPL_LINK( SelectPersonaDialog, SearchPersonas, Button*, pButton, void )
if( searchTerm.isEmpty( ) )
return;
- // 15 results so that invalid and duplicate search results whose names can't be retrieved can be skipped
- OUString rSearchURL = "https://services.addons.mozilla.org/en-US/firefox/api/1.5/search/" + searchTerm + "/9/15";
-
if ( searchTerm.startsWith( "https://addons.mozilla.org/" ) )
{
- searchTerm = "https://addons.mozilla.org/en-US/" + searchTerm.copy( searchTerm.indexOf( "firefox" ) );
- m_pSearchThread = new SearchAndParseThread( this, searchTerm, true );
+ OUString sSlug = searchTerm.getToken( 6, '/' );
+
+ m_pSearchThread = new SearchAndParseThread( this, sSlug, true );
}
else
+ {
+ // 15 results so that invalid and duplicate search results whose names, textcolors etc. are null can be skipped
+ OUString rSearchURL = "https://addons.mozilla.org/api/v3/addons/search/?q=" + searchTerm + "&type=persona&page_size=15";
+
m_pSearchThread = new SearchAndParseThread( this, rSearchURL, false );
+ }
m_pSearchThread->launch();
}
@@ -216,15 +345,11 @@ IMPL_LINK( SelectPersonaDialog, SelectPersona, Button*, pButton, void )
if( !m_vPersonaSettings[index].isEmpty() )
{
m_aSelectedPersona = m_vPersonaSettings[index];
+
// get the persona name from the setting variable to show in the progress.
- sal_Int32 nSlugIndex, nNameIndex;
- OUString aName, aProgress;
-
- // Skip the slug
- nSlugIndex = m_aSelectedPersona.indexOf( ';' );
- nNameIndex = m_aSelectedPersona.indexOf( ';', nSlugIndex );
- aName = m_aSelectedPersona.copy( nSlugIndex + 1, nNameIndex );
- aProgress = CuiResId(RID_SVXSTR_SELECTEDPERSONA) + aName;
+ OUString aName( m_aSelectedPersona.getToken( 1, ';' ) );
+ OUString aProgress( CuiResId(RID_SVXSTR_SELECTEDPERSONA) + aName );
+
SetProgress( aProgress );
}
break;
@@ -579,68 +704,6 @@ IMPL_LINK_NOARG( SvxPersonalizationTabPage, SelectInstalledPersona, ListBox&, vo
m_pExtensionPersonaPreview->SetModeImage( Image( aBmp ) );
}
-/// Find the value on the Persona page, and convert it to a usable form.
-static OUString searchValue( const OString &rBuffer, sal_Int32 from, const OString &rIdentifier )
-{
- sal_Int32 where = rBuffer.indexOf( rIdentifier, from );
- if ( where < 0 )
- return OUString();
-
- where += rIdentifier.getLength();
-
- sal_Int32 end = rBuffer.indexOf( "\"", where );
- if ( end < 0 )
- return OUString();
-
- OString aOString( rBuffer.copy( where, end - where ) );
- OUString aString( aOString.getStr(), aOString.getLength(), RTL_TEXTENCODING_UTF8, OSTRING_TO_OUSTRING_CVTFLAGS );
-
- return aString.replaceAll( "\\u002F", "/" );
-}
-
-/// Parse the Persona web page, and find where to get the bitmaps + the color values.
-static bool parsePersonaInfo( const OString &rBufferArg, OUString *pHeaderURL, OUString *pFooterURL,
- OUString *pTextColor, OUString *pAccentColor, OUString *pPreviewURL,
- OUString *pName, OUString *pSlug )
-{
- // tdf#115417: buffer retrieved from html response can contain &quot; or &#34;
- // let's replace the whole buffer with last one so we can treat it easily
- OString rBuffer = rBufferArg.replaceAll(OString("&quot;"), OString("&#34;"));
- // it is the first attribute that contains "persona="
- sal_Int32 persona = rBuffer.indexOf( "\"type\":\"persona\"" );
- if ( persona < 0 )
- return false;
-
- // now search inside
- *pHeaderURL = searchValue( rBuffer, persona, "\"headerURL\":\"" );
- if ( pHeaderURL->isEmpty() )
- return false;
-
- *pFooterURL = searchValue( rBuffer, persona, "\"footerURL\":\"" );
- if ( pFooterURL->isEmpty() )
- return false;
-
- *pTextColor = searchValue( rBuffer, persona, "\"textcolor\":\"" );
- if ( pTextColor->isEmpty() )
- return false;
-
- *pAccentColor = searchValue( rBuffer, persona, "\"accentcolor\":\"" );
- if ( pAccentColor->isEmpty() )
- return false;
-
- *pPreviewURL = searchValue( rBuffer, persona, "\"previewURL\":\"" );
- if ( pPreviewURL->isEmpty() )
- return false;
-
- *pName = searchValue( rBuffer, persona, "\"name\":\"" );
- if ( pName->isEmpty() )
- return false;
-
- *pSlug = searchValue( rBuffer, persona, "\"bySlug\":{\"" );
-
- return !pSlug->isEmpty();
-}
-
SearchAndParseThread::SearchAndParseThread( SelectPersonaDialog* pDialog,
const OUString& rURL, bool bDirectURL ) :
Thread( "cuiPersonasSearchThread" ),
@@ -657,191 +720,265 @@ SearchAndParseThread::~SearchAndParseThread()
namespace {
-bool getPreviewFile( const OUString& rURL, OUString *pPreviewFile, OUString *pPersonaSetting )
+bool getPreviewFile( const PersonaInfo& aPersonaInfo, OUString& pPreviewFile )
{
uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
- if ( !xFileAccess.is() )
- return false;
-
Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
- uno::Reference< io::XInputStream > xStream;
- try {
- css:: uno::Reference< task::XInteractionHandler > xIH(
- css::task::InteractionHandler::createWithParent( xContext, nullptr ) );
- xFileAccess->setInteractionHandler( new comphelper::SimpleFileAccessInteraction( xIH ) );
- xStream = xFileAccess->openFileRead( rURL );
+ // copy the images to the user's gallery
+ OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
+ rtl::Bootstrap::expandMacros( gallery );
+ gallery += "/user/gallery/personas/" + aPersonaInfo.sSlug + "/";
+
+ OUString aPreviewFile( INetURLObject( aPersonaInfo.sPreviewURL ).getName() );
+ OString aPreviewURL = OUStringToOString( aPersonaInfo.sPreviewURL, RTL_TEXTENCODING_UTF8 );
- if( !xStream.is() )
- return false;
+ try {
+ osl::Directory::createPath( gallery );
+
+ if ( !xFileAccess->exists( gallery + aPreviewFile ) )
+ curlDownload(aPreviewURL, gallery + aPreviewFile);
}
- catch (...)
+ catch ( const uno::Exception & )
{
return false;
}
+ pPreviewFile = gallery + aPreviewFile;
+ return true;
+}
- // read the persona specification
- // NOTE: Parsing for real is an overkill here; and worse - I tried, and
- // the HTML the site provides is not 100% valid ;-)
- const sal_Int32 BUF_LEN = 8000;
- uno::Sequence< sal_Int8 > buffer( BUF_LEN );
- OStringBuffer aBuffer( 64000 );
+void parseResponse(const std::string& rResponse, std::vector<PersonaInfo> & aPersonas)
+{
+ orcus::json::document_tree aJsonDoc;
+ orcus::json_config aConfig;
- sal_Int32 nRead = 0;
- while ( ( nRead = xStream->readBytes( buffer, BUF_LEN ) ) == BUF_LEN )
- aBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
+ if (rResponse.empty())
+ return;
- if ( nRead > 0 )
- aBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead );
+ aJsonDoc.load(rResponse, aConfig);
- xStream->closeInput();
+ auto aDocumentRoot = aJsonDoc.get_document_root();
+ if (aDocumentRoot.type() != orcus::json::node_t::object)
+ {
+ SAL_WARN("cui.options", "invalid root entries: " << rResponse);
+ return;
+ }
- // get the important bits of info
- OUString aHeaderURL, aFooterURL, aTextColor, aAccentColor, aPreviewURL, aName, aSlug;
+ auto resultsArray = aDocumentRoot.child("results");
- if ( !parsePersonaInfo( aBuffer.makeStringAndClear(), &aHeaderURL, &aFooterURL, &aTextColor, &aAccentColor, &aPreviewURL, &aName, &aSlug ) )
- return false;
+ for (size_t i = 0; i < resultsArray.child_count(); ++i)
+ {
+ auto arrayElement = resultsArray.child(i);
- // copy the images to the user's gallery
- OUString gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}";
- rtl::Bootstrap::expandMacros( gallery );
- gallery += "/user/gallery/personas/" + aSlug + "/";
+ try
+ {
+ PersonaInfo aNewPersona = {
+ OStringToOUString( OString(arrayElement.child("slug").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(arrayElement.child("name").child("en-US").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(arrayElement.child("theme_data").child("previewURL").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(arrayElement.child("theme_data").child("headerURL").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(arrayElement.child("theme_data").child("footerURL").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(arrayElement.child("theme_data").child("textcolor").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(arrayElement.child("theme_data").child("accentcolor").string_value().get()),
+ RTL_TEXTENCODING_UTF8 )
+ };
+
+ aPersonas.push_back(aNewPersona);
+ }
+ catch(orcus::json::document_error& e)
+ {
+ // This usually happens when one of the values is null (type() == orcus::json::node_t::null)
+ // TODO: Allow null values in personas.
+ SAL_WARN("cui.options", "Persona JSON parse error: " << e.what());
+ }
+ }
+}
- OUString aPreviewFile( INetURLObject( aPreviewURL ).getName() );
+PersonaInfo parseSingleResponse(const std::string& rResponse)
+{
+ orcus::json::document_tree aJsonDoc;
+ orcus::json_config aConfig;
- try {
- osl::Directory::createPath( gallery );
+ if (rResponse.empty())
+ return PersonaInfo();
- if ( !xFileAccess->exists( gallery + aPreviewFile ) )
- xFileAccess->copy( aPreviewURL, gallery + aPreviewFile );
+ aJsonDoc.load(rResponse, aConfig);
+
+ auto aDocumentRoot = aJsonDoc.get_document_root();
+ if (aDocumentRoot.type() != orcus::json::node_t::object)
+ {
+ SAL_WARN("cui.options", "invalid root entries: " << rResponse);
+ return PersonaInfo();
}
- catch ( const uno::Exception & )
+
+ auto theme_data = aDocumentRoot.child("theme_data");
+
+ try
{
- return false;
+ PersonaInfo aNewPersona = {
+ OUString(),
+ OStringToOUString( OString(theme_data.child("name").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(theme_data.child("previewURL").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(theme_data.child("headerURL").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(theme_data.child("footerURL").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(theme_data.child("textcolor").string_value().get()),
+ RTL_TEXTENCODING_UTF8 ),
+ OStringToOUString( OString(theme_data.child("accentcolor").string_value().get()),
+ RTL_TEXTENCODING_UTF8 )
+ };
+
+ return aNewPersona;
+ }
+ catch(orcus::json::document_error& e)
+ {
+ // This usually happens when one of the values is null (type() == orcus::json::node_t::null)
+ // TODO: Allow null values in personas.
+ // TODO: Give a message to the user
+ SAL_WARN("cui.options", "Persona JSON parse error: " << e.what());
+ return PersonaInfo();
}
- *pPreviewFile = gallery + aPreviewFile;
- *pPersonaSetting = aSlug + ";" + aName + ";" + aHeaderURL + ";" + aFooterURL + ";" + aTextColor + ";" + aAccentColor;
- return true;
}
-}
+} //End of anonymous namespace
void SearchAndParseThread::execute()
{
m_pPersonaDialog->ClearSearchResults();
- OUString sProgress( CuiResId( RID_SVXSTR_SEARCHING ) ), sError;
+ OUString sProgress( CuiResId( RID_SVXSTR_SEARCHING ) );
m_pPersonaDialog->SetProgress( sProgress );
- PersonasDocHandler* pHandler = new PersonasDocHandler();
- Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
- Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(xContext);
- Reference< xml::sax::XDocumentHandler > xDocHandler = pHandler;
uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY );
- uno::Reference< io::XInputStream > xStream;
- xParser->setDocumentHandler( xDocHandler );
- if( !m_bDirectURL )
+ if (!m_bDirectURL)
{
- if ( !xFileAccess.is() )
- return;
+ OString rURL = OUStringToOString( m_aURL, RTL_TEXTENCODING_UTF8 );
+ std::string sResponse = curlGet(rURL);
- try {
- css:: uno::Reference< task::XInteractionHandler > xIH(
- css::task::InteractionHandler::createWithParent( xContext, nullptr ) );
+ std::vector<PersonaInfo> personaInfos;
- xFileAccess->setInteractionHandler( new comphelper::SimpleFileAccessInteraction( xIH ) );
+ parseResponse(sResponse, personaInfos);
- xStream = xFileAccess->openFileRead( m_aURL );
- if( !xStream.is() )
+ if ( personaInfos.empty() )
+ {
+ sProgress = CuiResId( RID_SVXSTR_NORESULTS );
+ m_pPersonaDialog->SetProgress( sProgress );
+ return;
+ }
+ else
+ {
+ //Get Preview Files
+ sal_Int32 nIndex = 0;
+ for (const auto & personaInfo : personaInfos)
{
- // in case of a returned CommandFailedException
- // SimpleFileAccess serves it, returning an empty stream
+ if( !m_bExecute )
+ return;
+
+ OUString aPreviewFile;
+ bool bResult = getPreviewFile(personaInfo, aPreviewFile);
+
+ if (!bResult)
+ {
+ SAL_INFO("cui.options", "Couldn't get the preview file. Skipping: " << aPreviewFile);
+ continue;
+ }
+
+
+ GraphicFilter aFilter;
+ Graphic aGraphic;
+
+ INetURLObject aURLObj( aPreviewFile );
+
+ OUString aPersonaSetting = personaInfos[nIndex].sSlug
+ + ";" + personaInfos[nIndex].sName
+ + ";" + personaInfos[nIndex].sHeaderURL
+ + ";" + personaInfos[nIndex].sFooterURL
+ + ";" + personaInfos[nIndex].sTextColor
+ + ";" + personaInfos[nIndex].sAccentColor;
+
+ m_pPersonaDialog->AddPersonaSetting( aPersonaSetting );
+
+ // for VCL to be able to create bitmaps / do visual changes in the thread
SolarMutexGuard aGuard;
- sError = CuiResId(RID_SVXSTR_SEARCHERROR);
- sError = sError.replaceAll("%1", m_aURL);
- m_pPersonaDialog->SetProgress( OUString() );
-
- std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
- VclMessageType::Error, VclButtonsType::Ok,
- sError));
- xBox->run();
- return;
+ aFilter.ImportGraphic( aGraphic, aURLObj );
+ BitmapEx aBmp = aGraphic.GetBitmapEx();
+
+ m_pPersonaDialog->SetImages( Image( aBmp ), nIndex++ );
+ m_pPersonaDialog->setOptimalLayoutSize();
+
+ if (nIndex >= MAX_RESULTS)
+ break;
}
+
+ //TODO: Give a message to the user if nIndex == 0
}
- catch (...)
- {
- // a catch all clause, in case the exception is not
- // served elsewhere
- SolarMutexGuard aGuard;
- sError = CuiResId(RID_SVXSTR_SEARCHERROR);
- sError = sError.replaceAll("%1", m_aURL);
- m_pPersonaDialog->SetProgress( OUString() );
- std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
- VclMessageType::Error, VclButtonsType::Ok,
- sError));
- xBox->run();
- return;
- }
+ }
+ else
+ {
+ //Now we have a slug instead a search term in m_aURL
+ OString rURL = "https://addons.mozilla.org/api/v3/addons/addon/"
+ + OUStringToOString( m_aURL, RTL_TEXTENCODING_UTF8 )
+ + "/";
+ std::string sResponse = curlGet(rURL);
- xml::sax::InputSource aParserInput;
- aParserInput.aInputStream = xStream;
- xParser->parseStream( aParserInput );
+ PersonaInfo aPersonaInfo = parseSingleResponse(sResponse);
+ aPersonaInfo.sSlug = m_aURL;
- if( !pHandler->hasResults() )
+ if ( aPersonaInfo.sName.isEmpty() )
{
+ //TODO: Give error message to user
sProgress = CuiResId( RID_SVXSTR_NORESULTS );
m_pPersonaDialog->SetProgress( sProgress );
return;
}
- }
-
- std::vector<OUString> vLearnmoreURLs;
- sal_Int32 nIndex = 0;
- GraphicFilter aFilter;
- Graphic aGraphic;
+ else
+ {
+ //Get the preview file
+ if( !m_bExecute )
+ return;
- if( !m_bDirectURL )
- vLearnmoreURLs = pHandler->getLearnmoreURLs();
- else
- vLearnmoreURLs.push_back( m_aURL );
+ OUString aPreviewFile;
+ bool bResult = getPreviewFile(aPersonaInfo, aPreviewFile);
- for (auto const& learnMoreUrl : vLearnmoreURLs)
- {
- OUString sPreviewFile, aPersonaSetting;
- bool bResult = getPreviewFile( learnMoreUrl, &sPreviewFile, &aPersonaSetting );
- // parsing is buggy at times, as HTML is not proper. Skip it.
- if(aPersonaSetting.isEmpty() || !bResult)
- {
- if( m_bDirectURL )
+ if (!bResult)
{
- SolarMutexGuard aGuard;
- sError = CuiResId(RID_SVXSTR_SEARCHERROR);
- sError = sError.replaceAll("%1", m_aURL);
- m_pPersonaDialog->SetProgress( OUString() );
- std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
- VclMessageType::Error, VclButtonsType::Ok,
- sError));
- xBox->run();
+ //TODO: Inform the user
+ SAL_WARN("cui.options", "Couldn't get the preview file: " << aPreviewFile);
return;
}
- continue;
- }
- INetURLObject aURLObj( sPreviewFile );
- // Stop the thread if requested -- before taking the solar mutex.
- if( !m_bExecute )
- return;
+ GraphicFilter aFilter;
+ Graphic aGraphic;
- // for VCL to be able to create bitmaps / do visual changes in the thread
- SolarMutexGuard aGuard;
- aFilter.ImportGraphic( aGraphic, aURLObj );
- BitmapEx aBmp = aGraphic.GetBitmapEx();
+ INetURLObject aURLObj( aPreviewFile );
+
+ OUString aPersonaSetting = aPersonaInfo.sSlug
+ + ";" + aPersonaInfo.sName
+ + ";" + aPersonaInfo.sHeaderURL
+ + ";" + aPersonaInfo.sFooterURL
+ + ";" + aPersonaInfo.sTextColor
+ + ";" + aPersonaInfo.sAccentColor;
+
+ m_pPersonaDialog->AddPersonaSetting( aPersonaSetting );
+
+ // for VCL to be able to create bitmaps / do visual changes in the thread
+ SolarMutexGuard aGuard;
+ aFilter.ImportGraphic( aGraphic, aURLObj );
+ BitmapEx aBmp = aGraphic.GetBitmapEx();
+
+ m_pPersonaDialog->SetImages( Image( aBmp ), 0 );
+ m_pPersonaDialog->setOptimalLayoutSize();
+ }
- m_pPersonaDialog->SetImages( Image( aBmp ), nIndex++ );
- m_pPersonaDialog->setOptimalLayoutSize();
- m_pPersonaDialog->AddPersonaSetting( aPersonaSetting );
- if (nIndex >= MAX_RESULTS)
- break;
}
if( !m_bExecute )