sharelinkwidget.cpp
sharemanager.cpp
shareusergroupwidget.cpp
+ profilepagewidget.cpp
sharee.cpp
sslbutton.cpp
sslerrordialog.cpp
guiutility.cpp
elidedlabel.cpp
headerbanner.cpp
- iconjob.cpp
iconutils.cpp
remotewipe.cpp
userstatusselectormodel.cpp
+++ /dev/null
-/*
- * Copyright (C) by Camila Ayres <hello@camila.codes>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "iconjob.h"
-
-namespace OCC {
-
-IconJob::IconJob(const QUrl &url, QObject *parent) :
- QObject(parent)
-{
- connect(&_accessManager, &QNetworkAccessManager::finished,
- this, &IconJob::finished);
-
- QNetworkRequest request(url);
-#if (QT_VERSION >= 0x050600)
- request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
-#endif
- _accessManager.get(request);
-}
-
-void IconJob::finished(QNetworkReply *reply)
-{
- if (reply->error() != QNetworkReply::NoError)
- return;
-
- reply->deleteLater();
- deleteLater();
- emit jobFinished(reply->readAll());
-}
-}
+++ /dev/null
-/*
- * Copyright (C) by Camila Ayres <hello@camila.codes>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#ifndef ICONJOB_H
-#define ICONJOB_H
-
-#include <QObject>
-#include <QByteArray>
-#include <QNetworkAccessManager>
-#include <QNetworkRequest>
-#include <QNetworkReply>
-
-namespace OCC {
-
-/**
- * @brief Job to fetch a icon
- * @ingroup gui
- */
-class IconJob : public QObject
-{
- Q_OBJECT
-public:
- explicit IconJob(const QUrl &url, QObject *parent = nullptr);
-
-signals:
- void jobFinished(QByteArray iconData);
-
-private slots:
- void finished(QNetworkReply *reply);
-
-private:
- QNetworkAccessManager _accessManager;
-};
-}
-
-#endif // ICONJOB_H
--- /dev/null
+#include "profilepagewidget.h"
+#include "guiutility.h"
+#include "ocsprofileconnector.h"
+
+namespace OCC {
+
+ProfilePageMenu::ProfilePageMenu(AccountPtr account, const QString &shareWithUserId, QWidget *parent)
+ : QWidget(parent)
+ , _profileConnector(account)
+{
+ connect(&_profileConnector, &OcsProfileConnector::hovercardFetched, this, &ProfilePageMenu::onHovercardFetched);
+ connect(&_profileConnector, &OcsProfileConnector::iconLoaded, this, &ProfilePageMenu::onIconLoaded);
+ _profileConnector.fetchHovercard(shareWithUserId);
+}
+
+ProfilePageMenu::~ProfilePageMenu() = default;
+
+void ProfilePageMenu::exec(const QPoint &globalPosition)
+{
+ _menu.exec(globalPosition);
+}
+
+void ProfilePageMenu::onHovercardFetched()
+{
+ _menu.clear();
+
+ const auto hovercardActions = _profileConnector.hovercard()._actions;
+ for (const auto &hovercardAction : hovercardActions) {
+ const auto action = _menu.addAction(hovercardAction._icon, hovercardAction._title);
+ const auto link = hovercardAction._link;
+ connect(action, &QAction::triggered, action, [link](bool) { Utility::openBrowser(link); });
+ }
+}
+
+void ProfilePageMenu::onIconLoaded(const std::size_t &hovercardActionIndex)
+{
+ const auto hovercardActions = _profileConnector.hovercard()._actions;
+ const auto menuActions = _menu.actions();
+ if (hovercardActionIndex >= hovercardActions.size()
+ || hovercardActionIndex >= static_cast<std::size_t>(menuActions.size())) {
+ return;
+ }
+ const auto menuAction = menuActions[static_cast<int>(hovercardActionIndex)];
+ menuAction->setIcon(hovercardActions[hovercardActionIndex]._icon);
+}
+}
--- /dev/null
+#pragma once
+
+#include "ocsprofileconnector.h"
+
+#include <QBoxLayout>
+#include <QLabel>
+#include <account.h>
+#include <QMenu>
+
+#include <cstddef>
+
+namespace OCC {
+
+class ProfilePageMenu : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit ProfilePageMenu(AccountPtr account, const QString &shareWithUserId, QWidget *parent = nullptr);
+ ~ProfilePageMenu() override;
+
+ void exec(const QPoint &globalPosition);
+
+private:
+ void onHovercardFetched();
+ void onIconLoaded(const std::size_t &hovercardActionIndex);
+
+ OcsProfileConnector _profileConnector;
+ QMenu _menu;
+};
+}
* for more details.
*/
+#include "ocsprofileconnector.h"
#include "sharee.h"
+#include "tray/usermodel.h"
#include "ui_shareusergroupwidget.h"
#include "ui_shareuserline.h"
#include "shareusergroupwidget.h"
#include <QFileInfo>
#include <QAbstractProxyModel>
#include <QCompleter>
-#include <qlayout.h>
+#include <QBoxLayout>
+#include <QIcon>
+#include <QLayout>
#include <QPropertyAnimation>
#include <QMenu>
#include <QAction>
#include <QPainter>
#include <QListWidget>
#include <QSvgRenderer>
+#include <QPushButton>
+#include <QContextMenuEvent>
#include <cstring>
namespace {
- const char *passwordIsSetPlaceholder = "●●●●●●●●";
+const char *passwordIsSetPlaceholder = "●●●●●●●●";
+
}
namespace OCC {
+AvatarEventFilter::AvatarEventFilter(QObject *parent)
+ : QObject(parent)
+{
+}
+
+
+bool AvatarEventFilter::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::ContextMenu) {
+ const auto contextMenuEvent = dynamic_cast<QContextMenuEvent *>(event);
+ if (!contextMenuEvent) {
+ return false;
+ }
+ emit contextMenu(contextMenuEvent->globalPos());
+ return true;
+ }
+ return QObject::eventFilter(obj, event);
+}
+
ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
const QString &sharePath,
const QString &localPath,
_ui->shareeLineEdit->setFocus();
}
-ShareUserLine::ShareUserLine(AccountPtr account,
- QSharedPointer<UserGroupShare> share,
- SharePermissions maxSharingPermissions,
- bool isFile,
- QWidget *parent)
+ShareUserLine::ShareUserLine(AccountPtr account, QSharedPointer<UserGroupShare> share,
+ SharePermissions maxSharingPermissions, bool isFile, QWidget *parent)
: QWidget(parent)
, _ui(new Ui::ShareUserLine)
, _account(account)
, _share(share)
, _isFile(isFile)
+ , _profilePageMenu(account, share->getShareWith()->shareWith())
{
Q_ASSERT(_share);
_ui->setupUi(this);
_permissionReshare->setVisible(false);
}
+ const auto avatarEventFilter = new AvatarEventFilter(_ui->avatar);
+ connect(avatarEventFilter, &AvatarEventFilter::contextMenu, this, &ShareUserLine::onAvatarContextMenu);
+ _ui->avatar->installEventFilter(avatarEventFilter);
+
loadAvatar();
customizeStyle();
}
+void ShareUserLine::onAvatarContextMenu(const QPoint &globalPosition)
+{
+ if (_share->getShareType() == Share::TypeUser) {
+ _profilePageMenu.exec(globalPosition);
+ }
+}
+
void ShareUserLine::loadAvatar()
{
const int avatarSize = 36;
#include "sharemanager.h"
#include "sharepermissions.h"
#include "sharee.h"
+#include "profilepagewidget.h"
#include "QProgressIndicator.h"
#include <QDialog>
#include <QWidget>
#include <QList>
#include <QVector>
#include <QTimer>
+#include <qpushbutton.h>
#include <qscrollarea.h>
class QAction;
class Share;
class ShareManager;
+class AvatarEventFilter : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit AvatarEventFilter(QObject *parent = nullptr);
+
+signals:
+ void clicked();
+ void contextMenu(const QPoint &globalPosition);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
+};
+
/**
* @brief The ShareDialog (user/group) class
* @ingroup gui
void slotConfirmPasswordClicked();
+ void onAvatarContextMenu(const QPoint &globalPosition);
+
private:
void displayPermissions();
void loadAvatar();
QSharedPointer<UserGroupShare> _share;
bool _isFile;
+ ProfilePageMenu _profilePageMenu;
+
// _permissionEdit is a checkbox
QAction *_permissionReshare;
QAction *_deleteShareButton;
<rect>
<x>0</x>
<y>0</y>
- <width>980</width>
- <height>239</height>
+ <width>899</width>
+ <height>310</height>
</rect>
</property>
<property name="sizePolicy">
<palette>
<active>
<colorrole role="WindowText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="200">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Button">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>65</red>
+ <green>70</green>
+ <blue>84</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Light">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>95</red>
+ <green>103</green>
+ <blue>127</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Midlight">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>49</red>
+ <green>49</green>
+ <blue>49</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Dark">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
+ <red>25</red>
+ <green>25</green>
+ <blue>25</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Text">
+ <brush brushstyle="SolidPattern">
+ <color alpha="200">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="ButtonText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="200">
<red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Base">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>64</red>
+ <green>69</green>
+ <blue>82</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Window">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>56</red>
+ <green>60</green>
+ <blue>74</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Shadow">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
+ <colorrole role="Highlight">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>82</red>
+ <green>148</green>
+ <blue>226</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="HighlightedText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Link">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>157</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="LinkVisited">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>158</red>
+ <green>79</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="AlternateBase">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>60</red>
+ <green>67</green>
+ <blue>79</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="ToolTipBase">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="ToolTipText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>238</red>
+ <green>252</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="PlaceholderText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="128">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
</active>
<inactive>
<colorrole role="WindowText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="200">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
+ <red>65</red>
+ <green>70</green>
+ <blue>84</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Light">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>95</red>
+ <green>103</green>
+ <blue>127</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Midlight">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>49</red>
+ <green>49</green>
+ <blue>49</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Dark">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Mid">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>25</red>
+ <green>25</green>
+ <blue>25</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Text">
+ <brush brushstyle="SolidPattern">
+ <color alpha="200">
<red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="ButtonText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="200">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Base">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>64</red>
+ <green>69</green>
+ <blue>82</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Window">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>56</red>
+ <green>60</green>
+ <blue>74</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Shadow">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Highlight">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>82</red>
+ <green>149</green>
+ <blue>225</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="HighlightedText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Link">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>157</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="LinkVisited">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>158</red>
+ <green>79</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="AlternateBase">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>60</red>
+ <green>67</green>
+ <blue>79</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="ToolTipBase">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
+ <colorrole role="ToolTipText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>238</red>
+ <green>252</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="PlaceholderText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="128">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="115">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Button">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>65</red>
+ <green>70</green>
+ <blue>84</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Light">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>95</red>
+ <green>103</green>
+ <blue>127</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Midlight">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>49</red>
+ <green>49</green>
+ <blue>49</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Dark">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Mid">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>25</red>
+ <green>25</green>
+ <blue>25</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Text">
+ <brush brushstyle="SolidPattern">
+ <color alpha="115">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="ButtonText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="115">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Base">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>64</red>
+ <green>69</green>
+ <blue>82</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Window">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>56</red>
+ <green>60</green>
+ <blue>74</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Shadow">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Highlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
- <red>123</red>
- <green>121</green>
- <blue>134</blue>
+ <red>82</red>
+ <green>148</green>
+ <blue>226</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="HighlightedText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Link">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>157</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="LinkVisited">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>158</red>
+ <green>79</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="AlternateBase">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>60</red>
+ <green>67</green>
+ <blue>79</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="ToolTipBase">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="ToolTipText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>238</red>
+ <green>252</green>
+ <blue>255</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="PlaceholderText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="128">
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
</color>
</brush>
</colorrole>
configfile.cpp
abstractnetworkjob.cpp
networkjobs.cpp
+ iconjob.cpp
owncloudpropagator.cpp
nextcloudtheme.cpp
abstractpropagateremotedeleteencrypted.cpp
datetimeprovider.cpp
ocsuserstatusconnector.cpp
userstatusconnector.cpp
+ ocsprofileconnector.cpp
creds/dummycredentials.cpp
creds/abstractcredentials.cpp
creds/credentialscommon.cpp
--- /dev/null
+/*
+ * Copyright (C) by Camila Ayres <hello@camila.codes>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "iconjob.h"
+
+namespace OCC {
+
+IconJob::IconJob(const QUrl &url, QObject *parent) :
+ QObject(parent)
+{
+ connect(&_accessManager, &QNetworkAccessManager::finished,
+ this, &IconJob::finished);
+
+ QNetworkRequest request(url);
+#if (QT_VERSION >= 0x050600)
+ request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
+#endif
+ _accessManager.get(request);
+}
+
+void IconJob::finished(QNetworkReply *reply)
+{
+ reply->deleteLater();
+ deleteLater();
+
+ const auto networkError = reply->error();
+ if (networkError != QNetworkReply::NoError) {
+ emit error(networkError);
+ return;
+ }
+
+ emit jobFinished(reply->readAll());
+}
+}
--- /dev/null
+/*
+ * Copyright (C) by Camila Ayres <hello@camila.codes>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef ICONJOB_H
+#define ICONJOB_H
+
+#include "owncloudlib.h"
+
+#include <QObject>
+#include <QByteArray>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+
+namespace OCC {
+
+/**
+ * @brief Job to fetch a icon
+ * @ingroup gui
+ */
+class OWNCLOUDSYNC_EXPORT IconJob : public QObject
+{
+ Q_OBJECT
+public:
+ explicit IconJob(const QUrl &url, QObject *parent = nullptr);
+
+signals:
+ void jobFinished(QByteArray iconData);
+ void error(QNetworkReply::NetworkError errorType);
+
+private slots:
+ void finished(QNetworkReply *reply);
+
+private:
+ QNetworkAccessManager _accessManager;
+};
+}
+
+#endif // ICONJOB_H
--- /dev/null
+#include "ocsprofileconnector.h"
+#include "accountfwd.h"
+#include "common/result.h"
+#include "networkjobs.h"
+#include "iconjob.h"
+#include "theme.h"
+#include "account.h"
+
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QLoggingCategory>
+#include <QIcon>
+#include <QPainter>
+#include <QImage>
+#include <QSvgRenderer>
+#include <QNetworkReply>
+#include <QPixmap>
+#include <QPixmapCache>
+
+namespace {
+Q_LOGGING_CATEGORY(lcOcsProfileConnector, "nextcloud.gui.ocsprofileconnector", QtInfoMsg)
+
+OCC::HovercardAction jsonToAction(const QJsonObject &jsonActionObject)
+{
+ const auto iconUrl = jsonActionObject.value(QStringLiteral("icon")).toString(QStringLiteral("no-icon"));
+ QPixmap iconPixmap;
+ OCC::HovercardAction hovercardAction{
+ jsonActionObject.value(QStringLiteral("title")).toString(QStringLiteral("No title")), iconUrl,
+ jsonActionObject.value(QStringLiteral("hyperlink")).toString(QStringLiteral("no-link"))};
+ if (QPixmapCache::find(iconUrl, &iconPixmap)) {
+ hovercardAction._icon = iconPixmap;
+ }
+ return hovercardAction;
+}
+
+OCC::Hovercard jsonToHovercard(const QJsonArray &jsonDataArray)
+{
+ OCC::Hovercard hovercard;
+ hovercard._actions.reserve(jsonDataArray.size());
+ for (const auto &jsonEntry : jsonDataArray) {
+ Q_ASSERT(jsonEntry.isObject());
+ if (!jsonEntry.isObject()) {
+ continue;
+ }
+ hovercard._actions.push_back(jsonToAction(jsonEntry.toObject()));
+ }
+ return hovercard;
+}
+
+OCC::Optional<QPixmap> createPixmapFromSvgData(const QByteArray &iconData)
+{
+ QSvgRenderer svgRenderer;
+ if (!svgRenderer.load(iconData)) {
+ return {};
+ }
+ QSize imageSize{16, 16};
+ if (OCC::Theme::isHidpi()) {
+ imageSize = QSize{32, 32};
+ }
+ QImage scaledSvg(imageSize, QImage::Format_ARGB32);
+ scaledSvg.fill("transparent");
+ QPainter svgPainter{&scaledSvg};
+ svgRenderer.render(&svgPainter);
+ return QPixmap::fromImage(scaledSvg);
+}
+
+OCC::Optional<QPixmap> iconDataToPixmap(const QByteArray iconData)
+{
+ if (!iconData.startsWith("<svg")) {
+ return {};
+ }
+ return createPixmapFromSvgData(iconData);
+}
+}
+
+namespace OCC {
+
+HovercardAction::HovercardAction() = default;
+
+HovercardAction::HovercardAction(QString title, QUrl iconUrl, QUrl link)
+ : _title(std::move(title))
+ , _iconUrl(std::move(iconUrl))
+ , _link(std::move(link))
+{
+}
+
+OcsProfileConnector::OcsProfileConnector(AccountPtr account, QObject *parent)
+ : QObject(parent)
+ , _account(account)
+{
+}
+
+void OcsProfileConnector::fetchHovercard(const QString &userId)
+{
+ if (_account->serverVersionInt() < Account::makeServerVersion(23, 0, 0)) {
+ qInfo(lcOcsProfileConnector) << "Server version" << _account->serverVersion()
+ << "does not support profile page";
+ emit error();
+ return;
+ }
+ const QString url = QStringLiteral("/ocs/v2.php/hovercard/v1/%1").arg(userId);
+ const auto job = new JsonApiJob(_account, url, this);
+ connect(job, &JsonApiJob::jsonReceived, this, &OcsProfileConnector::onHovercardFetched);
+ job->start();
+}
+
+void OcsProfileConnector::onHovercardFetched(const QJsonDocument &json, int statusCode)
+{
+ qCDebug(lcOcsProfileConnector) << "Hovercard fetched:" << json;
+
+ if (statusCode != 200) {
+ qCInfo(lcOcsProfileConnector) << "Fetching of hovercard finished with status code" << statusCode;
+ return;
+ }
+ const auto jsonData = json.object().value("ocs").toObject().value("data").toObject().value("actions");
+ Q_ASSERT(jsonData.isArray());
+ _currentHovercard = jsonToHovercard(jsonData.toArray());
+ fetchIcons();
+ emit hovercardFetched();
+}
+
+void OcsProfileConnector::setHovercardActionIcon(const std::size_t index, const QPixmap &pixmap)
+{
+ auto &hovercardAction = _currentHovercard._actions[index];
+ QPixmapCache::insert(hovercardAction._iconUrl.toString(), pixmap);
+ hovercardAction._icon = pixmap;
+ emit iconLoaded(index);
+}
+
+void OcsProfileConnector::loadHovercardActionIcon(const std::size_t hovercardActionIndex, const QByteArray &iconData)
+{
+ if (hovercardActionIndex >= _currentHovercard._actions.size()) {
+ // Note: Probably could do more checking, like checking if the url is still the same.
+ return;
+ }
+ const auto icon = iconDataToPixmap(iconData);
+ if (icon.isValid()) {
+ setHovercardActionIcon(hovercardActionIndex, icon.get());
+ return;
+ }
+ qCWarning(lcOcsProfileConnector) << "Could not load Svg icon from data" << iconData;
+}
+
+void OcsProfileConnector::startFetchIconJob(const std::size_t hovercardActionIndex)
+{
+ const auto hovercardAction = _currentHovercard._actions[hovercardActionIndex];
+ const auto iconJob = new IconJob{hovercardAction._iconUrl, this};
+ connect(iconJob, &IconJob::jobFinished,
+ [this, hovercardActionIndex](QByteArray iconData) { loadHovercardActionIcon(hovercardActionIndex, iconData); });
+ connect(iconJob, &IconJob::error, this, [](QNetworkReply::NetworkError errorType) {
+ qCWarning(lcOcsProfileConnector) << "Could not fetch icon:" << errorType;
+ });
+}
+
+void OcsProfileConnector::fetchIcons()
+{
+ for (auto hovercardActionIndex = 0u; hovercardActionIndex < _currentHovercard._actions.size();
+ ++hovercardActionIndex) {
+ startFetchIconJob(hovercardActionIndex);
+ }
+}
+
+const Hovercard &OcsProfileConnector::hovercard() const
+{
+ return _currentHovercard;
+}
+}
--- /dev/null
+#pragma once
+
+#include "accountfwd.h"
+#include "owncloudlib.h"
+
+#include <QObject>
+#include <QPixmap>
+#include <QUrl>
+#include <QString>
+
+namespace OCC {
+
+struct OWNCLOUDSYNC_EXPORT HovercardAction
+{
+public:
+ HovercardAction();
+ HovercardAction(QString title, QUrl iconUrl, QUrl link);
+
+ QString _title;
+ QUrl _iconUrl;
+ QPixmap _icon;
+ QUrl _link;
+};
+
+struct OWNCLOUDSYNC_EXPORT Hovercard
+{
+ std::vector<HovercardAction> _actions;
+};
+
+class OWNCLOUDSYNC_EXPORT OcsProfileConnector : public QObject
+{
+ Q_OBJECT
+public:
+ explicit OcsProfileConnector(AccountPtr account, QObject *parent = nullptr);
+
+ void fetchHovercard(const QString &userId);
+ const Hovercard &hovercard() const;
+
+signals:
+ void error();
+ void hovercardFetched();
+ void iconLoaded(const std::size_t hovercardActionIndex);
+
+private:
+ void onHovercardFetched(const QJsonDocument &json, int statusCode);
+
+ void fetchIcons();
+ void startFetchIconJob(const std::size_t hovercardActionIndex);
+ void setHovercardActionIcon(const std::size_t index, const QPixmap &pixmap);
+ void loadHovercardActionIcon(const std::size_t hovercardActionIndex, const QByteArray &iconData);
+
+ AccountPtr _account;
+ Hovercard _currentHovercard;
+};
+}
+
+Q_DECLARE_METATYPE(OCC::HovercardAction)
+Q_DECLARE_METATYPE(OCC::Hovercard)