13 #include <string_view>
15 #include <config_folders.h>
18 #include <dialmgr.hxx>
19 #include <strings.hrc>
23 #include <com/sun/star/graphic/GraphicProvider.hpp>
24 #include <com/sun/star/graphic/XGraphicProvider.hpp>
25 #include <com/sun/star/ucb/NameClash.hpp>
26 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
27 #include <osl/file.hxx>
28 #include <rtl/bootstrap.hxx>
38 #include <com/sun/star/util/SearchFlags.hpp>
39 #include <com/sun/star/util/SearchAlgorithms2.hpp>
44 #include <com/sun/star/deployment/DeploymentException.hpp>
45 #include <com/sun/star/deployment/ExtensionManager.hpp>
46 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
47 #include <com/sun/star/ucb/CommandAbortedException.hpp>
48 #include <com/sun/star/ucb/CommandFailedException.hpp>
50 #include <com/sun/star/task/XInteractionApprove.hpp>
52 #include <orcus/json_document_tree.hpp>
53 #include <orcus/config.hpp>
54 #include <orcus/pstring.hpp>
59 using ::com::sun::star::uno::Reference;
60 using ::com::sun::star::uno::XComponentContext;
61 using ::com::sun::star::uno::UNO_QUERY_THROW;
62 using ::com::sun::star::uno::Exception;
63 using ::com::sun::star::graphic::GraphicProvider;
64 using ::com::sun::star::graphic::XGraphicProvider;
65 using ::com::sun::star::uno::Sequence;
66 using ::com::sun::star::beans::PropertyValue;
67 using ::com::sun::star::graphic::XGraphic;
77 std::string ucbGet(
const OUString& rURL)
84 SAL_WARN(
"cui.dialogs",
"CreateStream <" << rURL <<
"> failed");
87 std::string response_body;
91 auto const n = s->ReadBytes(buf,
sizeof buf);
92 response_body.append(buf,
n);
96 SAL_WARN(
"cui.dialogs",
"Reading <" << rURL <<
"> failed with " << s->GetError());
101 catch (css::uno::Exception&)
109 void ucbDownload(
const OUString& rURL,
const OUString& sFolderURL,
const OUString& fileName)
116 css::ucb::NameClash::OVERWRITE);
118 catch (css::uno::Exception&)
124 void parseResponse(
const std::string& rResponse, std::vector<AdditionInfo>& aAdditions)
126 orcus::json::document_tree aJsonDoc;
127 orcus::json_config aConfig;
129 if (rResponse.empty())
132 aJsonDoc.load(rResponse, aConfig);
134 auto aDocumentRoot = aJsonDoc.get_document_root();
135 if (aDocumentRoot.type() != orcus::json::node_t::object)
137 SAL_WARN(
"cui.dialogs",
"invalid root entries: " << rResponse);
141 auto resultsArray = aDocumentRoot.child(
"extension");
143 for (
size_t i = 0;
i < resultsArray.child_count(); ++
i)
145 auto arrayElement = resultsArray.child(
i);
150 OStringToOUString(std::string_view(arrayElement.child(
"id").string_value().get()),
151 RTL_TEXTENCODING_UTF8),
152 OStringToOUString(std::string_view(arrayElement.child(
"name").string_value().get()),
153 RTL_TEXTENCODING_UTF8),
155 std::string_view(arrayElement.child(
"author").string_value().get()),
156 RTL_TEXTENCODING_UTF8),
157 OStringToOUString(std::string_view(arrayElement.child(
"url").string_value().get()),
158 RTL_TEXTENCODING_UTF8),
160 std::string_view(arrayElement.child(
"screenshotURL").string_value().get()),
161 RTL_TEXTENCODING_UTF8),
164 arrayElement.child(
"extensionIntroduction").string_value().get()),
165 RTL_TEXTENCODING_UTF8),
168 arrayElement.child(
"extensionDescription").string_value().get()),
169 RTL_TEXTENCODING_UTF8),
170 OStringToOUString(std::string_view(arrayElement.child(
"releases")
172 .child(
"compatibility")
175 RTL_TEXTENCODING_UTF8),
176 OStringToOUString(std::string_view(arrayElement.child(
"releases")
178 .child(
"releaseName")
181 RTL_TEXTENCODING_UTF8),
182 OStringToOUString(std::string_view(arrayElement.child(
"releases")
187 RTL_TEXTENCODING_UTF8),
189 std::string_view(arrayElement.child(
"commentNumber").string_value().get()),
190 RTL_TEXTENCODING_UTF8),
192 std::string_view(arrayElement.child(
"commentURL").string_value().get()),
193 RTL_TEXTENCODING_UTF8),
195 std::string_view(arrayElement.child(
"rating").string_value().get()),
196 RTL_TEXTENCODING_UTF8),
198 std::string_view(arrayElement.child(
"downloadNumber").string_value().get()),
199 RTL_TEXTENCODING_UTF8),
200 OStringToOUString(std::string_view(arrayElement.child(
"releases")
202 .child(
"downloadURL")
205 RTL_TEXTENCODING_UTF8)
208 aAdditions.push_back(aNewAddition);
210 catch (orcus::json::document_error& e)
214 SAL_WARN(
"cui.dialogs",
"Additions JSON parse error: " << e.what());
219 bool getPreviewFile(
const AdditionInfo& aAdditionInfo, OUString& sPreviewFile)
221 uno::Reference<ucb::XSimpleFileAccess3> xFileAccess
225 OUString userFolder =
"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
227 rtl::Bootstrap::expandMacros(userFolder);
228 userFolder +=
"/user/additions/" + aAdditionInfo.
sExtensionID +
"/";
235 osl::Directory::createPath(userFolder);
237 if (!xFileAccess->exists(userFolder + aPreviewFile))
238 ucbDownload(aPreviewURL, userFolder, aPreviewFile);
240 catch (
const uno::Exception&)
244 sPreviewFile = userFolder + aPreviewFile;
248 void LoadImage(
const OUString& rPreviewFile, std::shared_ptr<AdditionsItem> pCurrentItem)
263 Size aThumbSize(pCurrentItem->m_xImageScreenshot->get_size_request());
267 if (aBmpSize.
Width() > aThumbSize.Width() - 2 * Margin)
269 aScale =
static_cast<double>(aBmpSize.
Width()) / (aThumbSize.Width() - 2 * Margin);
272 else if (aBmpSize.
Height() > aThumbSize.Height() - 2 * Margin)
274 aScale =
static_cast<double>(aBmpSize.
Height()) / (aThumbSize.Height() - 2 * Margin);
281 xVirDev->SetOutputSizePixel(aThumbSize);
285 xVirDev->DrawBitmapEx(
Point(aThumbSize.Width() / 2 - aBmpSize.
Width() / 2, Margin), aBmp);
286 pCurrentItem->m_xImageScreenshot->set_image(xVirDev.
get());
293 : Thread(
"cuiAdditionsSearchThread")
294 , m_pAdditionsDialog(pDialog)
296 , m_bIsFirstLoading(isFirstLoading)
306 OUString aPreviewFile;
307 bool bResult = getPreviewFile(additionInfo, aPreviewFile);
311 SAL_INFO(
"cui.dialogs",
"Couldn't get the preview file. Skipping: " << aPreviewFile);
322 LoadImage(aPreviewFile, aCurrentItem);
329 aCurrentItem->m_xButtonShowMore->set_visible(
true);
339 size_t nIteration = 0;
345 OUString sExtensionName = rInfo.sName;
346 OUString sExtensionDescription = rInfo.sDescription;
366 uno::Sequence<uno::Sequence<uno::Reference<deployment::XPackage>>> xAllPackages
369 if (!xAllPackages.hasElements())
372 OUString currentExtensionName;
374 for (
auto&
package : xAllPackages)
376 for (
auto& extensionVersion :
package)
378 if (extensionVersion.is())
380 currentExtensionName = extensionVersion->getName();
381 if (currentExtensionName.isEmpty())
389 OUString sExtensionDownloadURL = rInfo->m_sDownloadURL;
398 rInfo->m_xButtonInstall->set_sensitive(
false);
399 rInfo->m_xButtonInstall->set_label(
400 CuiResId(RID_SVXSTR_ADDITIONS_INSTALLEDBUTTON));
412 sProgress =
CuiResId(RID_SVXSTR_ADDITIONS_LOADING);
414 sProgress =
CuiResId(RID_SVXSTR_ADDITIONS_SEARCHING);
442 : GenericDialogController(pParent,
"cui/ui/additionsdialog.ui",
"AdditionsDialog")
443 , m_aSearchDataTimer(
"SearchDataTimer")
444 , m_xEntrySearch(m_xBuilder->weld_entry(
"entrySearch"))
445 , m_xButtonClose(m_xBuilder->weld_button(
"buttonClose"))
446 , m_xMenuButtonSettings(m_xBuilder->weld_menu_button(
"buttonGear"))
447 , m_xContentWindow(m_xBuilder->weld_scrolled_window(
"contentWindow"))
448 , m_xContentGrid(m_xBuilder->weld_container(
"contentGrid"))
449 , m_xLabelProgress(m_xBuilder->weld_label(
"labelProgress"))
450 , m_xGearBtn(m_xBuilder->weld_menu_button(
"buttonGear"))
453 m_xGearBtn->set_item_active(
"gear_sort_voting",
true);
467 OUString titlePrefix =
CuiResId(RID_SVXSTR_ADDITIONS_DIALOG_TITLE_PREFIX);
470 this->
set_title(titlePrefix +
": " + sAdditionsTag);
478 OUString rURL =
"https://libreoffice.yusufketen.com/api/" +
m_sTag +
".json";
488 | css::util::SearchFlags::REG_NOT_ENDOFLINE);
506 uno::Sequence<uno::Sequence<uno::Reference<deployment::XPackage>>>
509 uno::Sequence<uno::Sequence<uno::Reference<deployment::XPackage>>> xAllPackages;
514 uno::Reference<task::XAbortChannel>(), uno::Reference<ucb::XCommandEnvironment>());
516 catch (
const deployment::DeploymentException&)
520 catch (
const ucb::CommandFailedException&)
524 catch (
const ucb::CommandAbortedException&)
528 catch (
const lang::IllegalArgumentException& e)
531 throw css::lang::WrappedTargetRuntimeException(e.Message, e.Context, anyEx);
538 if (rProgress.isEmpty())
559 item->m_xContainer->hide();
561 this->m_aAdditionsItems.clear();
592 : m_xBuilder(
Application::CreateBuilder(pParent,
"cui/ui/additionsfragment.ui"))
593 ,
m_xContainer(m_xBuilder->weld_widget(
"additionsEntry"))
594 , m_xImageScreenshot(m_xBuilder->weld_image(
"imageScreenshot"))
595 , m_xButtonInstall(m_xBuilder->weld_button(
"buttonInstall"))
596 , m_xLinkButtonWebsite(m_xBuilder->weld_link_button(
"btnWebsite"))
597 , m_xLabelName(m_xBuilder->weld_label(
"lbName"))
598 , m_xLabelAuthor(m_xBuilder->weld_label(
"labelAuthor"))
599 , m_xLabelDesc(m_xBuilder->weld_label(
"labelDesc"))
600 , m_xLabelDescription(m_xBuilder->weld_label(
"labelDescription"))
601 , m_xLabelLicense(m_xBuilder->weld_label(
"lbLicenseText"))
602 , m_xLabelVersion(m_xBuilder->weld_label(
"lbVersionText"))
603 , m_xLabelComments(m_xBuilder->weld_label(
"labelComments"))
604 , m_xLinkButtonComments(m_xBuilder->weld_link_button(
"linkButtonComments"))
605 , m_xImageVoting1(m_xBuilder->weld_image(
"imageVoting1"))
606 , m_xImageVoting2(m_xBuilder->weld_image(
"imageVoting2"))
607 , m_xImageVoting3(m_xBuilder->weld_image(
"imageVoting3"))
608 , m_xImageVoting4(m_xBuilder->weld_image(
"imageVoting4"))
609 , m_xImageVoting5(m_xBuilder->weld_image(
"imageVoting5"))
610 , m_xLabelNoVoting(m_xBuilder->weld_label(
"votingLabel"))
611 , m_xImageDownloadNumber(m_xBuilder->weld_image(
"imageDownloadNumber"))
612 , m_xLabelDownloadNumber(m_xBuilder->weld_label(
"labelDownloadNumber"))
613 , m_xButtonShowMore(m_xBuilder->weld_button(
"buttonShowMore"))
614 , m_pParentDialog(pParentDialog)
625 OUString sExtensionName;
626 const sal_Int32 maxExtensionNameLength = 30;
628 if (additionInfo.
sName.getLength() > maxExtensionNameLength)
630 OUString sShortName = additionInfo.
sName.copy(0, maxExtensionNameLength - 3);
631 sExtensionName = sShortName +
"...";
635 sExtensionName = additionInfo.
sName;
640 double aExtensionRating = additionInfo.
sRating.toDouble();
641 switch (
int(aExtensionRating))
663 if (!additionInfo.
sAuthorName.equalsIgnoreAsciiCase(
"null"))
682 uno::Reference<ucb::XSimpleFileAccess3> xFileAccess
686 OUString userFolder =
"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
688 rtl::Bootstrap::expandMacros(userFolder);
696 osl::Directory::createPath(userFolder);
698 if (!xFileAccess->exists(userFolder + aExtesionsFile))
699 ucbDownload(aExtesionsURL, userFolder, aExtesionsFile);
701 catch (
const uno::Exception&)
705 sExtensionFile = userFolder + aExtesionsFile;
713 m_aSearchDataTimer.Start();
718 if (m_aSearchDataTimer.IsActive())
720 m_aSearchDataTimer.Stop();
721 m_aSearchDataTimer.Invoke();
727 if (m_pSearchThread.is())
728 m_pSearchThread->StopExecution();
734 this->m_xButtonShowMore->set_visible(
false);
735 m_pParentDialog->m_nMaxItemCount +=
PAGE_SIZE;
736 if (m_pParentDialog->m_pSearchThread.is())
737 m_pParentDialog->m_pSearchThread->StopExecution();
739 m_pParentDialog->m_pSearchThread->launch();
744 m_xButtonInstall->set_label(
CuiResId(RID_SVXSTR_ADDITIONS_INSTALLING));
745 m_xButtonInstall->set_sensitive(
false);
746 OUString aExtensionFile;
747 bool bResult = getExtensionFile(aExtensionFile);
751 m_xButtonInstall->set_label(
CuiResId(RID_SVXSTR_ADDITIONS_INSTALLBUTTON));
752 m_xButtonInstall->set_sensitive(
true);
754 SAL_INFO(
"cui.dialogs",
"Couldn't get the extension file.");
759 uno::Reference<ucb::XCommandEnvironment> xCmdEnv(static_cast<cppu::OWeakObject*>(pCmdEnv),
761 uno::Reference<task::XAbortChannel> xAbortChannel;
764 m_pParentDialog->m_xExtensionManager->addExtension(
765 aExtensionFile, uno::Sequence<beans::NamedValue>(),
"user", xAbortChannel, xCmdEnv);
766 m_xButtonInstall->set_label(
CuiResId(RID_SVXSTR_ADDITIONS_INSTALLEDBUTTON));
768 catch (
const ucb::CommandFailedException)
771 m_xButtonInstall->set_label(
CuiResId(RID_SVXSTR_ADDITIONS_INSTALLBUTTON));
772 m_xButtonInstall->set_sensitive(
true);
774 catch (
const ucb::CommandAbortedException)
777 m_xButtonInstall->set_label(
CuiResId(RID_SVXSTR_ADDITIONS_INSTALLBUTTON));
778 m_xButtonInstall->set_sensitive(
true);
780 catch (
const deployment::DeploymentException)
783 m_xButtonInstall->set_label(
CuiResId(RID_SVXSTR_ADDITIONS_INSTALLBUTTON));
784 m_xButtonInstall->set_sensitive(
true);
786 catch (
const lang::IllegalArgumentException)
789 m_xButtonInstall->set_label(
CuiResId(RID_SVXSTR_ADDITIONS_INSTALLBUTTON));
790 m_xButtonInstall->set_sensitive(
true);
792 catch (
const css::uno::Exception)
795 m_xButtonInstall->set_label(
CuiResId(RID_SVXSTR_ADDITIONS_INSTALLBUTTON));
796 m_xButtonInstall->set_sensitive(
true);
817 OSL_ASSERT(xRequest->getRequest().getValueTypeClass() == uno::TypeClass_EXCEPTION);
822 uno::Sequence<Reference<task::XInteractionContinuation>> conts(xRequest->getContinuations());
823 Reference<task::XInteractionContinuation>
const* pConts = conts.getConstArray();
824 sal_Int32 len = conts.getLength();
825 for (sal_Int32 pos = 0;
pos < len; ++
pos)
829 uno::Reference<task::XInteractionApprove> xInteractionApprove(pConts[pos],
831 if (xInteractionApprove.is())
833 xInteractionApprove->select();
850 if (rIdent ==
"gear_sort_voting")
852 std::sort(m_aAllExtensionsVector.begin(), m_aAllExtensionsVector.end(), sortByRating);
854 else if (rIdent ==
"gear_sort_comments")
856 std::sort(m_aAllExtensionsVector.begin(), m_aAllExtensionsVector.end(), sortByComment);
858 else if (rIdent ==
"gear_sort_downloads")
860 std::sort(m_aAllExtensionsVector.begin(), m_aAllExtensionsVector.end(), sortByDownload);
#define LINK(Instance, Class, Member)
std::shared_ptr< weld::Dialog > m_xDialog
AdditionsDialog * m_pAdditionsDialog
virtual css::uno::Reference< css::ucb::XProgressHandler > SAL_CALL getProgressHandler() override
std::unique_ptr< weld::Label > m_xLabelProgress
void set_title(const OUString &rTitle)
std::unique_ptr< weld::Image > m_xImageVoting4
virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL getInteractionHandler() override
ErrCode ImportGraphic(Graphic &rGraphic, const INetURLObject &rPath, sal_uInt16 nFormat=GRFILTER_FORMAT_DONTKNOW, sal_uInt16 *pDeterminedFormat=nullptr, GraphicFilterImportFlags nImportFlags=GraphicFilterImportFlags::NONE)
std::unique_ptr< weld::MenuButton > m_xGearBtn
TmpRepositoryCommandEnv()
bool getExtensionFile(OUString &sExtensionFile)
bool Scale(const Size &rNewSize, BmpScaleFlag nScaleFlag=BmpScaleFlag::Default)
virtual void SAL_CALL push(css::uno::Any const &Status) override
size_t m_nCurrentListItemCount
static bool sortByComment(const AdditionInfo &a, const AdditionInfo &b)
::rtl::Reference< SearchAndParseThread > m_pSearchThread
std::unique_ptr< weld::Image > m_xImageVoting3
std::unique_ptr< weld::Button > m_xButtonClose
i18nutil::SearchOptions2 m_searchOptions
static bool sortByRating(const AdditionInfo &a, const AdditionInfo &b)
std::unique_ptr< weld::Image > m_xImageVoting5
std::unique_ptr< weld::LinkButton > m_xLinkButtonWebsite
Reference< XNameAccess > m_xContainer
static std::unique_ptr< SvStream > CreateStream(const OUString &rFileName, StreamMode eOpenMode, css::uno::Reference< css::awt::XWindow > xParentWin=nullptr)
std::unique_ptr< weld::Button > m_xButtonShowMore
Any SAL_CALL getCaughtException()
std::atomic< bool > m_bExecute
virtual void SAL_CALL pop() override
std::unique_ptr< weld::Button > m_xButtonInstall
SearchAndParseThread(AdditionsDialog *pDialog, const bool &bIsFirstLoading)
void SetDebugName(const char *pDebugName)
std::unique_ptr< weld::Label > m_xLabelAuthor
std::unique_ptr< weld::Entry > m_xEntrySearch
#define SAL_CONFIGFILE(name)
#define TOOLS_WARN_EXCEPTION(area, stream)
std::vector< std::shared_ptr< AdditionsItem > > m_aAdditionsItems
OUString getName(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
TransliterationFlags transliterateFlags
AdditionsItem(weld::Widget *pParent, AdditionsDialog *pParentDialog, const AdditionInfo &additionInfo)
tools::Long Width() const
~AdditionsDialog() override
std::unique_ptr< weld::Label > m_xLabelVersion
bool searchForward(const OUString &rStr)
std::unique_ptr< weld::Label > m_xLabelLicense
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
std::unique_ptr< weld::Label > m_xLabelDownloadNumber
void SetTimeout(sal_uInt64 nTimeoutMs)
OUString sCompatibleVersion
AdditionsDialog * m_pParentDialog
OUString CuiResId(const char *pKey)
virtual void SAL_CALL handle(css::uno::Reference< css::task::XInteractionRequest > const &xRequest) override
IMPL_LINK(AdditionsDialog, GearHdl, const OString &, rIdent, void)
std::unique_ptr< weld::Widget > m_xContainer
css::uno::Sequence< css::uno::Sequence< css::uno::Reference< css::deployment::XPackage > > > getInstalledExtensions()
virtual ~TmpRepositoryCommandEnv() override
virtual void SAL_CALL update(css::uno::Any const &Status) override
std::unique_ptr< weld::Image > m_xImageVoting2
void Append(AdditionInfo &additionInfo)
#define EDIT_UPDATEDATA_TIMEOUT
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
#define SAL_INFO(area, stream)
IMPL_LINK_NOARG(AdditionsDialog, ImplUpdateDataHdl, Timer *, void)
std::unique_ptr< weld::Container > m_xContentGrid
tools::Long Height() const
void CheckInstalledExtensions()
css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager
Reference< XComponentContext > getProcessComponentContext()
void SetInvokeHandler(const Link< Timer *, void > &rLink)
virtual ~SearchAndParseThread() override
std::unique_ptr< weld::LinkButton > m_xLinkButtonComments
std::unique_ptr< weld::Label > m_xLabelName
void SetProgress(const OUString &rProgress)
VirtualDevice * get() const
std::unique_ptr< weld::Image > m_xImageVoting1
#define SAL_WARN(area, stream)
std::unique_ptr< weld::Label > m_xLabelDescription
static bool sortByDownload(const AdditionInfo &a, const AdditionInfo &b)
AdditionsDialog(weld::Window *pParent, const OUString &sAdditionsTag)
const Size & GetSizePixel() const
virtual void execute() override
std::vector< AdditionInfo > m_aAllExtensionsVector