16#include <config_folders.h>
24#include <com/sun/star/graphic/GraphicProvider.hpp>
25#include <com/sun/star/graphic/XGraphicProvider.hpp>
26#include <com/sun/star/ucb/NameClash.hpp>
27#include <com/sun/star/ucb/SimpleFileAccess.hpp>
28#include <osl/file.hxx>
29#include <rtl/bootstrap.hxx>
39#include <com/sun/star/util/SearchFlags.hpp>
40#include <com/sun/star/util/SearchAlgorithms2.hpp>
45#include <com/sun/star/deployment/DeploymentException.hpp>
46#include <com/sun/star/deployment/ExtensionManager.hpp>
47#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
48#include <com/sun/star/ucb/CommandAbortedException.hpp>
49#include <com/sun/star/ucb/CommandFailedException.hpp>
51#include <com/sun/star/task/XInteractionApprove.hpp>
53#include <orcus/json_document_tree.hpp>
54#include <orcus/json_parser.hpp>
55#include <orcus/config.hpp>
57#include <bitmaps.hlst>
62using ::com::sun::star::uno::Reference;
63using ::com::sun::star::uno::Exception;
64using ::com::sun::star::uno::Sequence;
74std::string ucbGet(
const OUString& rURL)
81 SAL_WARN(
"cui.dialogs",
"CreateStream <" << rURL <<
"> failed");
84 std::string response_body;
88 auto const n = s->ReadBytes(buf,
sizeof buf);
89 response_body.append(buf,
n);
93 SAL_WARN(
"cui.dialogs",
"Reading <" << rURL <<
"> failed with " << s->GetError());
98 catch (css::uno::Exception&)
106void ucbDownload(
const OUString& rURL,
const OUString& sFolderURL,
const OUString& fileName)
113 css::ucb::NameClash::OVERWRITE);
115 catch (css::uno::Exception&)
121void parseResponse(
const std::string& rResponse, std::vector<AdditionInfo>& aAdditions)
123 orcus::json::document_tree aJsonDoc;
124 orcus::json_config aConfig;
126 if (rResponse.empty())
131 aJsonDoc.load(rResponse, aConfig);
133 catch (
const orcus::parse_error&)
139 auto aDocumentRoot = aJsonDoc.get_document_root();
140 if (aDocumentRoot.type() != orcus::json::node_t::object)
142 SAL_WARN(
"cui.dialogs",
"invalid root entries: " << rResponse);
146 auto resultsArray = aDocumentRoot.child(
"extension");
148 for (
size_t i = 0;
i < resultsArray.child_count(); ++
i)
150 auto arrayElement = resultsArray.child(
i);
155 OStringToOUString(arrayElement.child(
"id").string_value(), RTL_TEXTENCODING_UTF8),
156 OStringToOUString(arrayElement.child(
"name").string_value(), RTL_TEXTENCODING_UTF8),
157 OStringToOUString(arrayElement.child(
"author").string_value(),
158 RTL_TEXTENCODING_UTF8),
159 OStringToOUString(arrayElement.child(
"url").string_value(), RTL_TEXTENCODING_UTF8),
160 OStringToOUString(arrayElement.child(
"screenshotURL").string_value(),
161 RTL_TEXTENCODING_UTF8),
162 OStringToOUString(arrayElement.child(
"extensionIntroduction").string_value(),
163 RTL_TEXTENCODING_UTF8),
164 OStringToOUString(arrayElement.child(
"extensionDescription").string_value(),
165 RTL_TEXTENCODING_UTF8),
167 arrayElement.child(
"releases").child(0).child(
"compatibility").string_value(),
168 RTL_TEXTENCODING_UTF8),
170 arrayElement.child(
"releases").child(0).child(
"releaseName").string_value(),
171 RTL_TEXTENCODING_UTF8),
173 arrayElement.child(
"releases").child(0).child(
"license").string_value(),
174 RTL_TEXTENCODING_UTF8),
175 OStringToOUString(arrayElement.child(
"commentNumber").string_value(),
176 RTL_TEXTENCODING_UTF8),
177 OStringToOUString(arrayElement.child(
"commentURL").string_value(),
178 RTL_TEXTENCODING_UTF8),
179 OStringToOUString(arrayElement.child(
"rating").string_value(),
180 RTL_TEXTENCODING_UTF8),
181 OStringToOUString(arrayElement.child(
"downloadNumber").string_value(),
182 RTL_TEXTENCODING_UTF8),
184 arrayElement.child(
"releases").child(0).child(
"downloadURL").string_value(),
185 RTL_TEXTENCODING_UTF8)
188 aAdditions.push_back(aNewAddition);
190 catch (orcus::json::document_error& e)
194 SAL_WARN(
"cui.dialogs",
"Additions JSON parse error: " << e.what());
199bool getPreviewFile(
const AdditionInfo& aAdditionInfo, OUString& sPreviewFile)
205 OUString userFolder =
"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
207 rtl::Bootstrap::expandMacros(userFolder);
208 userFolder +=
"/user/additions/" + aAdditionInfo.
sExtensionID +
"/";
215 osl::Directory::createPath(userFolder);
217 if (!xFileAccess->exists(userFolder + aPreviewFile))
218 ucbDownload(aPreviewURL, userFolder, aPreviewFile);
220 catch (
const uno::Exception&)
224 sPreviewFile = userFolder + aPreviewFile;
228void LoadImage(std::u16string_view rPreviewFile, std::shared_ptr<AdditionsItem> pCurrentItem)
243 Size aThumbSize(pCurrentItem->m_xImageScreenshot->get_size_request());
249 aScale =
static_cast<double>(aBmpSize.
Width()) / (aThumbSize.
Width() - 2 *
Margin);
261 xVirDev->SetOutputSizePixel(aThumbSize);
266 pCurrentItem->m_xImageScreenshot->set_image(xVirDev.
get());
273 : Thread(
"cuiAdditionsSearchThread")
274 , m_pAdditionsDialog(pDialog)
276 , m_bIsFirstLoading(isFirstLoading)
286 OUString aPreviewFile;
287 bool bResult = getPreviewFile(additionInfo, aPreviewFile);
291 SAL_INFO(
"cui.dialogs",
"Couldn't get the preview file. Skipping: " << aPreviewFile);
302 LoadImage(aPreviewFile, aCurrentItem);
309 aCurrentItem->m_xButtonShowMore->set_visible(
true);
319 size_t nIteration = 0;
325 OUString sExtensionName = rInfo.sName;
326 OUString sExtensionDescription = rInfo.sDescription;
349 if (!xAllPackages.hasElements())
352 OUString currentExtensionName;
354 for (
auto&
package : xAllPackages)
356 for (
auto& extensionVersion :
package)
358 if (extensionVersion.is())
360 currentExtensionName = extensionVersion->getName();
361 if (currentExtensionName.isEmpty())
369 OUString sExtensionDownloadURL = rInfo->m_sDownloadURL;
378 rInfo->m_xButtonInstall->set_sensitive(
false);
379 rInfo->m_xButtonInstall->set_label(
380 CuiResId(RID_CUISTR_ADDITIONS_INSTALLEDBUTTON));
392 sProgress =
CuiResId(RID_CUISTR_ADDITIONS_LOADING);
394 sProgress =
CuiResId(RID_CUISTR_ADDITIONS_SEARCHING);
422 : GenericDialogController(pParent,
"cui/ui/additionsdialog.ui",
"AdditionsDialog")
423 , m_aSearchDataTimer(
"AdditionsDialog SearchDataTimer")
424 , m_xEntrySearch(m_xBuilder->weld_entry(
"entrySearch"))
425 , m_xButtonClose(m_xBuilder->weld_button(
"buttonClose"))
426 , m_xContentWindow(m_xBuilder->weld_scrolled_window(
"contentWindow"))
427 , m_xContentGrid(m_xBuilder->weld_container(
"contentGrid"))
428 , m_xLabelProgress(m_xBuilder->weld_label(
"labelProgress"))
429 , m_xGearBtn(m_xBuilder->weld_menu_button(
"buttonGear"))
432 m_xGearBtn->set_item_active(
"gear_sort_voting",
true);
445 OUString titlePrefix =
CuiResId(RID_CUISTR_ADDITIONS_DIALOG_TITLE_PREFIX);
448 OUString sDialogTitle =
"";
449 if (sAdditionsTag ==
"Templates")
451 sDialogTitle =
CuiResId(RID_CUISTR_ADDITIONS_TEMPLATES);
453 else if (sAdditionsTag ==
"Dictionary")
455 sDialogTitle =
CuiResId(RID_CUISTR_ADDITIONS_DICTIONARY);
457 else if (sAdditionsTag ==
"Gallery")
459 sDialogTitle =
CuiResId(RID_CUISTR_ADDITIONS_GALLERY);
461 else if (sAdditionsTag ==
"Icons")
463 sDialogTitle =
CuiResId(RID_CUISTR_ADDITIONS_ICONS);
465 else if (sAdditionsTag ==
"Color Palette")
467 sDialogTitle =
CuiResId(RID_CUISTR_ADDITIONS_PALETTES);
481 OUString rURL =
"https://extensions.libreoffice.org/api/v0/" + sEncodedURLPart +
".json";
485 = deployment::ExtensionManager::get(::comphelper::getProcessComponentContext());
491 | css::util::SearchFlags::REG_NOT_ENDOFLINE);
519 catch (
const deployment::DeploymentException&)
523 catch (
const ucb::CommandFailedException&)
527 catch (
const ucb::CommandAbortedException&)
531 catch (
const lang::IllegalArgumentException& e)
534 throw css::lang::WrappedTargetRuntimeException(e.Message, e.Context, anyEx);
541 if (rProgress.isEmpty())
562 item->m_xContainer->hide();
564 this->m_aAdditionsItems.clear();
580 return a.sRating.toDouble() > b.
sRating.toDouble();
595 : m_xBuilder(
Application::CreateBuilder(pParent,
"cui/ui/additionsfragment.ui"))
596 ,
m_xContainer(m_xBuilder->weld_widget(
"additionsEntry"))
597 , m_xImageScreenshot(m_xBuilder->weld_image(
"imageScreenshot"))
598 , m_xButtonInstall(m_xBuilder->weld_button(
"buttonInstall"))
599 , m_xLinkButtonWebsite(m_xBuilder->weld_link_button(
"btnWebsite"))
600 , m_xLabelName(m_xBuilder->weld_label(
"lbName"))
601 , m_xLabelAuthor(m_xBuilder->weld_label(
"labelAuthor"))
602 , m_xLabelDescription(m_xBuilder->weld_label(
"labelDescription"))
603 , m_xLabelLicense(m_xBuilder->weld_label(
"lbLicenseText"))
604 , m_xLabelVersion(m_xBuilder->weld_label(
"lbVersionText"))
605 , m_xLinkButtonComments(m_xBuilder->weld_link_button(
"linkButtonComments"))
606 , m_xImageVoting1(m_xBuilder->weld_image(
"imageVoting1"))
607 , m_xImageVoting2(m_xBuilder->weld_image(
"imageVoting2"))
608 , m_xImageVoting3(m_xBuilder->weld_image(
"imageVoting3"))
609 , m_xImageVoting4(m_xBuilder->weld_image(
"imageVoting4"))
610 , m_xImageVoting5(m_xBuilder->weld_image(
"imageVoting5"))
611 , m_xLabelDownloadNumber(m_xBuilder->weld_label(
"labelDownloadNumber"))
612 , m_xButtonShowMore(m_xBuilder->weld_button(
"buttonShowMore"))
613 , m_pParentDialog(pParentDialog)
624 OUString sExtensionName;
625 const sal_Int32 maxExtensionNameLength = 30;
627 if (additionInfo.
sName.getLength() > maxExtensionNameLength)
629 std::u16string_view sShortName = additionInfo.
sName.subView(0, maxExtensionNameLength - 3);
630 sExtensionName = OUString::Concat(sShortName) +
"...";
634 sExtensionName = additionInfo.
sName;
639 double aExtensionRating = additionInfo.
sRating.toDouble();
640 switch (std::isnan(aExtensionRating) ? 0 :
int(std::clamp(aExtensionRating, 0.0, 5.0)))
662 if (!additionInfo.
sAuthorName.equalsIgnoreAsciiCase(
"null"))
685 OUString userFolder =
"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
687 rtl::Bootstrap::expandMacros(userFolder);
695 osl::Directory::createPath(userFolder);
697 if (!xFileAccess->exists(userFolder + aExtensionsFile))
698 ucbDownload(aExtensionsURL, userFolder, aExtensionsFile);
700 catch (
const uno::Exception&)
704 sExtensionFile = userFolder + aExtensionsFile;
712 m_aSearchDataTimer.Start();
717 if (m_aSearchDataTimer.IsActive())
719 m_aSearchDataTimer.Stop();
720 m_aSearchDataTimer.Invoke();
726 if (m_pSearchThread.is())
727 m_pSearchThread->StopExecution();
733 this->m_xButtonShowMore->set_visible(
false);
734 m_pParentDialog->m_nMaxItemCount +=
PAGE_SIZE;
735 if (m_pParentDialog->m_pSearchThread.is())
736 m_pParentDialog->m_pSearchThread->StopExecution();
738 m_pParentDialog->m_pSearchThread->launch();
743 m_xButtonInstall->set_label(
CuiResId(RID_CUISTR_ADDITIONS_INSTALLING));
744 m_xButtonInstall->set_sensitive(
false);
745 OUString aExtensionFile;
746 bool bResult = getExtensionFile(aExtensionFile);
750 m_xButtonInstall->set_label(
CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON));
751 m_xButtonInstall->set_sensitive(
true);
753 SAL_INFO(
"cui.dialogs",
"Couldn't get the extension file.");
761 m_pParentDialog->m_xExtensionManager->addExtension(
763 m_xButtonInstall->set_label(
CuiResId(RID_CUISTR_ADDITIONS_INSTALLEDBUTTON));
765 catch (
const ucb::CommandFailedException)
768 m_xButtonInstall->set_label(
CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON));
769 m_xButtonInstall->set_sensitive(
true);
771 catch (
const ucb::CommandAbortedException)
774 m_xButtonInstall->set_label(
CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON));
775 m_xButtonInstall->set_sensitive(
true);
777 catch (
const deployment::DeploymentException)
780 m_xButtonInstall->set_label(
CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON));
781 m_xButtonInstall->set_sensitive(
true);
783 catch (
const lang::IllegalArgumentException)
786 m_xButtonInstall->set_label(
CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON));
787 m_xButtonInstall->set_sensitive(
true);
789 catch (
const css::uno::Exception)
792 m_xButtonInstall->set_label(
CuiResId(RID_CUISTR_ADDITIONS_INSTALLBUTTON));
793 m_xButtonInstall->set_sensitive(
true);
814 OSL_ASSERT(xRequest->getRequest().getValueTypeClass() == uno::TypeClass_EXCEPTION);
820 Reference<task::XInteractionContinuation>
const* pConts = conts.getConstArray();
821 sal_Int32 len = conts.getLength();
822 for (sal_Int32 pos = 0;
pos < len; ++
pos)
828 if (xInteractionApprove.is())
830 xInteractionApprove->select();
847 if (rIdent ==
"gear_sort_voting")
849 std::sort(m_aAllExtensionsVector.begin(), m_aAllExtensionsVector.end(), sortByRating);
851 else if (rIdent ==
"gear_sort_comments")
853 std::sort(m_aAllExtensionsVector.begin(), m_aAllExtensionsVector.end(), sortByComment);
855 else if (rIdent ==
"gear_sort_downloads")
857 std::sort(m_aAllExtensionsVector.begin(), m_aAllExtensionsVector.end(), sortByDownload);
IMPL_LINK(AdditionsDialog, GearHdl, const OUString &, rIdent, void)
IMPL_LINK_NOARG(AdditionsDialog, ImplUpdateDataHdl, Timer *, void)
i18nutil::SearchOptions2 m_searchOptions
std::unique_ptr< weld::MenuButton > m_xGearBtn
std::vector< std::shared_ptr< AdditionsItem > > m_aAdditionsItems
std::vector< AdditionInfo > m_aAllExtensionsVector
void SetProgress(const OUString &rProgress)
static bool sortByComment(const AdditionInfo &a, const AdditionInfo &b)
std::unique_ptr< weld::Button > m_xButtonClose
AdditionsDialog(weld::Window *pParent, const OUString &sAdditionsTag)
std::unique_ptr< weld::Container > m_xContentGrid
std::unique_ptr< weld::Entry > m_xEntrySearch
css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager
static bool sortByRating(const AdditionInfo &a, const AdditionInfo &b)
~AdditionsDialog() override
static bool sortByDownload(const AdditionInfo &a, const AdditionInfo &b)
size_t m_nCurrentListItemCount
::rtl::Reference< SearchAndParseThread > m_pSearchThread
std::unique_ptr< weld::Label > m_xLabelProgress
css::uno::Sequence< css::uno::Sequence< css::uno::Reference< css::deployment::XPackage > > > getInstalledExtensions()
std::unique_ptr< weld::Image > m_xImageVoting5
std::unique_ptr< weld::Button > m_xButtonInstall
std::unique_ptr< weld::Label > m_xLabelName
std::unique_ptr< weld::Image > m_xImageVoting4
std::unique_ptr< weld::Image > m_xImageVoting3
std::unique_ptr< weld::Label > m_xLabelDownloadNumber
bool getExtensionFile(OUString &sExtensionFile)
std::unique_ptr< weld::Image > m_xImageVoting1
AdditionsDialog * m_pParentDialog
AdditionsItem(weld::Widget *pParent, AdditionsDialog *pParentDialog, const AdditionInfo &additionInfo)
std::unique_ptr< weld::LinkButton > m_xLinkButtonWebsite
std::unique_ptr< weld::Label > m_xLabelLicense
std::unique_ptr< weld::Label > m_xLabelAuthor
std::unique_ptr< weld::Widget > m_xContainer
std::unique_ptr< weld::LinkButton > m_xLinkButtonComments
std::unique_ptr< weld::Label > m_xLabelVersion
std::unique_ptr< weld::Label > m_xLabelDescription
std::unique_ptr< weld::Button > m_xButtonShowMore
std::unique_ptr< weld::Image > m_xImageVoting2
bool Scale(const Size &rNewSize, BmpScaleFlag nScaleFlag=BmpScaleFlag::Default)
const Size & GetSizePixel() const
ErrCode ImportGraphic(Graphic &rGraphic, const INetURLObject &rPath, sal_uInt16 nFormat=GRFILTER_FORMAT_DONTKNOW, sal_uInt16 *pDeterminedFormat=nullptr, GraphicFilterImportFlags nImportFlags=GraphicFilterImportFlags::NONE)
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
OUString getName(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
static OUString encode(std::u16string_view rText, Part ePart, EncodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
virtual void execute() override
virtual ~SearchAndParseThread() override
SearchAndParseThread(AdditionsDialog *pDialog, bool bIsFirstLoading)
AdditionsDialog * m_pAdditionsDialog
void Append(AdditionInfo &additionInfo)
std::atomic< bool > m_bExecute
void CheckInstalledExtensions()
constexpr tools::Long Height() const
constexpr tools::Long Width() const
void SetTimeout(sal_uInt64 nTimeoutMs)
void SetInvokeHandler(const Link< Timer *, void > &rLink)
virtual void SAL_CALL push(css::uno::Any const &Status) override
virtual void SAL_CALL handle(css::uno::Reference< css::task::XInteractionRequest > const &xRequest) override
virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL getInteractionHandler() override
TmpRepositoryCommandEnv()
virtual void SAL_CALL pop() override
virtual ~TmpRepositoryCommandEnv() override
virtual void SAL_CALL update(css::uno::Any const &Status) override
virtual css::uno::Reference< css::ucb::XProgressHandler > SAL_CALL getProgressHandler() override
reference_type * get() const
bool searchForward(const OUString &rStr)
static std::unique_ptr< SvStream > CreateStream(const OUString &rFileName, StreamMode eOpenMode, css::uno::Reference< css::awt::XWindow > xParentWin=nullptr)
void set_title(const OUString &rTitle)
std::shared_ptr< weld::Dialog > m_xDialog
constexpr ::Color COL_WHITE(0xFF, 0xFF, 0xFF)
#define SAL_CONFIGFILE(name)
OUString CuiResId(TranslateId aKey)
#define TOOLS_WARN_EXCEPTION(area, stream)
#define LINK(Instance, Class, Member)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
Reference< XComponentContext > getProcessComponentContext()
Any SAL_CALL getCaughtException()
Reference< XNameAccess > m_xContainer
OUString sCompatibleVersion
TransliterationFlags transliterateFlags
#define EDIT_UPDATEDATA_TIMEOUT