mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-26 06:55:59 +03:00
Merge branch 'notifications'
This commit is contained in:
commit
6b0d535120
30 changed files with 1559 additions and 318 deletions
|
@ -22,5 +22,6 @@
|
||||||
<file>resources/account.png</file>
|
<file>resources/account.png</file>
|
||||||
<file>resources/more.png</file>
|
<file>resources/more.png</file>
|
||||||
<file>resources/delete.png</file>
|
<file>resources/delete.png</file>
|
||||||
|
<file>resources/bell.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -25,3 +25,6 @@ You can change the following configuration settings (must be under the ``[ownClo
|
||||||
- ``chunkSize`` (default: ``5242880``) -- Specifies the chunk size of uploaded files in bytes.
|
- ``chunkSize`` (default: ``5242880``) -- Specifies the chunk size of uploaded files in bytes.
|
||||||
|
|
||||||
- ``promptDeleteAllFiles`` (default: ``true``) -- If a UI prompt should ask for confirmation if it was detected that all files and folders were deleted.
|
- ``promptDeleteAllFiles`` (default: ``true``) -- If a UI prompt should ask for confirmation if it was detected that all files and folders were deleted.
|
||||||
|
|
||||||
|
- ``notificationRefreshInterval`` (default``300,000``) -- Specifies the default interval of checking for new server notifications in milliseconds.
|
||||||
|
|
||||||
|
|
BIN
resources/bell.png
Normal file
BIN
resources/bell.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 900 B |
|
@ -32,6 +32,7 @@ set(client_UI
|
||||||
owncloudsetuppage.ui
|
owncloudsetuppage.ui
|
||||||
addcertificatedialog.ui
|
addcertificatedialog.ui
|
||||||
proxyauthdialog.ui
|
proxyauthdialog.ui
|
||||||
|
notificationwidget.ui
|
||||||
wizard/owncloudadvancedsetuppage.ui
|
wizard/owncloudadvancedsetuppage.ui
|
||||||
wizard/owncloudconnectionmethoddialog.ui
|
wizard/owncloudconnectionmethoddialog.ui
|
||||||
wizard/owncloudhttpcredspage.ui
|
wizard/owncloudhttpcredspage.ui
|
||||||
|
@ -62,6 +63,8 @@ set(client_SRCS
|
||||||
owncloudgui.cpp
|
owncloudgui.cpp
|
||||||
owncloudsetupwizard.cpp
|
owncloudsetupwizard.cpp
|
||||||
protocolwidget.cpp
|
protocolwidget.cpp
|
||||||
|
activitydata.cpp
|
||||||
|
activitylistmodel.cpp
|
||||||
activitywidget.cpp
|
activitywidget.cpp
|
||||||
activityitemdelegate.cpp
|
activityitemdelegate.cpp
|
||||||
selectivesyncdialog.cpp
|
selectivesyncdialog.cpp
|
||||||
|
@ -85,6 +88,9 @@ set(client_SRCS
|
||||||
proxyauthdialog.cpp
|
proxyauthdialog.cpp
|
||||||
synclogdialog.cpp
|
synclogdialog.cpp
|
||||||
tooltipupdater.cpp
|
tooltipupdater.cpp
|
||||||
|
notificationwidget.cpp
|
||||||
|
notificationconfirmjob.cpp
|
||||||
|
servernotificationhandler.cpp
|
||||||
creds/credentialsfactory.cpp
|
creds/credentialsfactory.cpp
|
||||||
creds/httpcredentialsgui.cpp
|
creds/httpcredentialsgui.cpp
|
||||||
creds/shibbolethcredentials.cpp
|
creds/shibbolethcredentials.cpp
|
||||||
|
|
35
src/gui/activitydata.cpp
Normal file
35
src/gui/activitydata.cpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 <QtCore>
|
||||||
|
|
||||||
|
#include "activitydata.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace OCC
|
||||||
|
{
|
||||||
|
|
||||||
|
bool operator<( const Activity& rhs, const Activity& lhs ) {
|
||||||
|
return rhs._dateTime.toMSecsSinceEpoch() > lhs._dateTime.toMSecsSinceEpoch();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==( const Activity& rhs, const Activity& lhs ) {
|
||||||
|
return (rhs._type == lhs._type && rhs._id== lhs._id && rhs._accName == lhs._accName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity::Identifier Activity::ident() const {
|
||||||
|
return Identifier( _id, _accName );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
89
src/gui/activitydata.h
Normal file
89
src/gui/activitydata.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 ACTIVITYDATA_H
|
||||||
|
#define ACTIVITYDATA_H
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
/**
|
||||||
|
* @brief The ActivityLink class describes actions of an activity
|
||||||
|
*
|
||||||
|
* These are part of notifications which are mapped into activities.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ActivityLink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString _label;
|
||||||
|
QString _link;
|
||||||
|
QByteArray _verb;
|
||||||
|
bool _isPrimary;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ==================================================================== */
|
||||||
|
/**
|
||||||
|
* @brief Activity Structure
|
||||||
|
* @ingroup gui
|
||||||
|
*
|
||||||
|
* contains all the information describing a single activity.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Activity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef QPair<qlonglong, QString> Identifier;
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
ActivityType,
|
||||||
|
NotificationType
|
||||||
|
};
|
||||||
|
|
||||||
|
Type _type;
|
||||||
|
qlonglong _id;
|
||||||
|
QString _subject;
|
||||||
|
QString _message;
|
||||||
|
QString _file;
|
||||||
|
QUrl _link;
|
||||||
|
QDateTime _dateTime;
|
||||||
|
QString _accName;
|
||||||
|
|
||||||
|
QVector <ActivityLink> _links;
|
||||||
|
/**
|
||||||
|
* @brief Sort operator to sort the list youngest first.
|
||||||
|
* @param val
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
Identifier ident() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==( const Activity& rhs, const Activity& lhs );
|
||||||
|
bool operator<( const Activity& rhs, const Activity& lhs );
|
||||||
|
|
||||||
|
/* ==================================================================== */
|
||||||
|
/**
|
||||||
|
* @brief The ActivityList
|
||||||
|
* @ingroup gui
|
||||||
|
*
|
||||||
|
* A QList based list of Activities
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef QList<Activity> ActivityList;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ACTIVITYDATA_H
|
228
src/gui/activitylistmodel.cpp
Normal file
228
src/gui/activitylistmodel.cpp
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 <QtCore>
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
#include "account.h"
|
||||||
|
#include "accountstate.h"
|
||||||
|
#include "accountmanager.h"
|
||||||
|
#include "folderman.h"
|
||||||
|
#include "accessmanager.h"
|
||||||
|
#include "activityitemdelegate.h"
|
||||||
|
|
||||||
|
#include "activitydata.h"
|
||||||
|
#include "activitylistmodel.h"
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
ActivityListModel::ActivityListModel(QWidget *parent)
|
||||||
|
:QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
Activity a;
|
||||||
|
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
a = _finalList.at(index.row());
|
||||||
|
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
|
||||||
|
QStringList list;
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case ActivityItemDelegate::PathRole:
|
||||||
|
list = FolderMan::instance()->findFileInLocalFolders(a._file, ast->account());
|
||||||
|
if( list.count() > 0 ) {
|
||||||
|
return QVariant(list.at(0));
|
||||||
|
}
|
||||||
|
// File does not exist anymore? Let's try to open its path
|
||||||
|
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(a._file).path(), ast->account());
|
||||||
|
if( list.count() > 0 ) {
|
||||||
|
return QVariant(list.at(0));
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
break;
|
||||||
|
case ActivityItemDelegate::ActionIconRole:
|
||||||
|
return QVariant(); // FIXME once the action can be quantified, display on Icon
|
||||||
|
break;
|
||||||
|
case ActivityItemDelegate::UserIconRole:
|
||||||
|
return QIcon(QLatin1String(":/client/resources/account.png"));
|
||||||
|
break;
|
||||||
|
case Qt::ToolTipRole:
|
||||||
|
case ActivityItemDelegate::ActionTextRole:
|
||||||
|
return a._subject;
|
||||||
|
break;
|
||||||
|
case ActivityItemDelegate::LinkRole:
|
||||||
|
return a._link;
|
||||||
|
break;
|
||||||
|
case ActivityItemDelegate::AccountRole:
|
||||||
|
return a._accName;
|
||||||
|
break;
|
||||||
|
case ActivityItemDelegate::PointInTimeRole:
|
||||||
|
return Utility::timeAgoInWords(a._dateTime);
|
||||||
|
break;
|
||||||
|
case ActivityItemDelegate::AccountConnectedRole:
|
||||||
|
return (ast && ast->isConnected());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int ActivityListModel::rowCount(const QModelIndex&) const
|
||||||
|
{
|
||||||
|
return _finalList.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
// current strategy: Fetch 100 items per Account
|
||||||
|
// ATTENTION: This method is const and thus it is not possible to modify
|
||||||
|
// the _activityLists hash or so. Doesn't make it easier...
|
||||||
|
bool ActivityListModel::canFetchMore(const QModelIndex& ) const
|
||||||
|
{
|
||||||
|
if( _activityLists.count() == 0 ) return true;
|
||||||
|
|
||||||
|
for(auto i = _activityLists.begin() ; i != _activityLists.end(); ++i) {
|
||||||
|
AccountState *ast = i.key();
|
||||||
|
if( ast && ast->isConnected() ) {
|
||||||
|
ActivityList activities = i.value();
|
||||||
|
if( activities.count() == 0 &&
|
||||||
|
! _currentlyFetching.contains(ast) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityListModel::startFetchJob(AccountState* s)
|
||||||
|
{
|
||||||
|
if( !s->isConnected() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
|
||||||
|
QObject::connect(job, SIGNAL(jsonReceived(QVariantMap, int)),
|
||||||
|
this, SLOT(slotActivitiesReceived(QVariantMap, int)));
|
||||||
|
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(s));
|
||||||
|
|
||||||
|
QList< QPair<QString,QString> > params;
|
||||||
|
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0")));
|
||||||
|
params.append(qMakePair(QString::fromLatin1("pagesize"), QString::fromLatin1("100")));
|
||||||
|
job->addQueryParams(params);
|
||||||
|
|
||||||
|
_currentlyFetching.insert(s);
|
||||||
|
qDebug() << Q_FUNC_INFO << "Start fetching activities for " << s->account()->displayName();
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityListModel::slotActivitiesReceived(const QVariantMap& json, int statusCode)
|
||||||
|
{
|
||||||
|
auto activities = json.value("ocs").toMap().value("data").toList();
|
||||||
|
|
||||||
|
ActivityList list;
|
||||||
|
AccountState* ast = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
|
||||||
|
_currentlyFetching.remove(ast);
|
||||||
|
|
||||||
|
foreach( auto activ, activities ) {
|
||||||
|
auto json = activ.toMap();
|
||||||
|
|
||||||
|
Activity a;
|
||||||
|
a._type = Activity::ActivityType;
|
||||||
|
a._accName = ast->account()->displayName();
|
||||||
|
a._id = json.value("id").toLongLong();
|
||||||
|
a._subject = json.value("subject").toString();
|
||||||
|
a._message = json.value("message").toString();
|
||||||
|
a._file = json.value("file").toString();
|
||||||
|
a._link = json.value("link").toUrl();
|
||||||
|
a._dateTime = json.value("date").toDateTime();
|
||||||
|
list.append(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
_activityLists[ast] = list;
|
||||||
|
|
||||||
|
emit activityJobStatusCode(ast, statusCode);
|
||||||
|
|
||||||
|
combineActivityLists();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ActivityListModel::combineActivityLists()
|
||||||
|
{
|
||||||
|
ActivityList resultList;
|
||||||
|
|
||||||
|
foreach( ActivityList list, _activityLists.values() ) {
|
||||||
|
resultList.append(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort( resultList.begin(), resultList.end() );
|
||||||
|
|
||||||
|
beginResetModel();
|
||||||
|
_finalList.clear();
|
||||||
|
endResetModel();
|
||||||
|
|
||||||
|
beginInsertRows(QModelIndex(), 0, resultList.count());
|
||||||
|
_finalList = resultList;
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityListModel::fetchMore(const QModelIndex &)
|
||||||
|
{
|
||||||
|
QList<AccountStatePtr> accounts = AccountManager::instance()->accounts();
|
||||||
|
|
||||||
|
foreach (const AccountStatePtr& asp, accounts) {
|
||||||
|
|
||||||
|
if( !_activityLists.contains(asp.data()) && asp->isConnected() ) {
|
||||||
|
_activityLists[asp.data()] = ActivityList();
|
||||||
|
startFetchJob(asp.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityListModel::slotRefreshActivity(AccountState *ast)
|
||||||
|
{
|
||||||
|
if(ast && _activityLists.contains(ast)) {
|
||||||
|
_activityLists.remove(ast);
|
||||||
|
}
|
||||||
|
startFetchJob(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityListModel::slotRemoveAccount(AccountState *ast )
|
||||||
|
{
|
||||||
|
if( _activityLists.contains(ast) ) {
|
||||||
|
int i = 0;
|
||||||
|
const QString accountToRemove = ast->account()->displayName();
|
||||||
|
|
||||||
|
QMutableListIterator<Activity> it(_finalList);
|
||||||
|
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Activity activity = it.next();
|
||||||
|
if( activity._accName == accountToRemove ) {
|
||||||
|
beginRemoveRows(QModelIndex(), i, i+1);
|
||||||
|
it.remove();
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_activityLists.remove(ast);
|
||||||
|
_currentlyFetching.remove(ast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
67
src/gui/activitylistmodel.h
Normal file
67
src/gui/activitylistmodel.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 ACTIVITYLISTMODEL_H
|
||||||
|
#define ACTIVITYLISTMODEL_H
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
#include "activitydata.h"
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
class AccountState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The ActivityListModel
|
||||||
|
* @ingroup gui
|
||||||
|
*
|
||||||
|
* Simple list model to provide the list view with data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ActivityListModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ActivityListModel(QWidget *parent=0);
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
|
||||||
|
int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
bool canFetchMore(const QModelIndex& ) const Q_DECL_OVERRIDE;
|
||||||
|
void fetchMore(const QModelIndex&) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
ActivityList activityList() { return _finalList; }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotRefreshActivity(AccountState* ast);
|
||||||
|
void slotRemoveAccount( AccountState *ast );
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotActivitiesReceived(const QVariantMap& json, int statusCode);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void activityJobStatusCode(AccountState* ast, int statusCode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void startFetchJob(AccountState* s);
|
||||||
|
void combineActivityLists();
|
||||||
|
|
||||||
|
QMap<AccountState*, ActivityList> _activityLists;
|
||||||
|
ActivityList _finalList;
|
||||||
|
QSet<AccountState*> _currentlyFetching;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // ACTIVITYLISTMODEL_H
|
|
@ -16,8 +16,8 @@
|
||||||
#include <QtWidgets>
|
#include <QtWidgets>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "activitylistmodel.h"
|
||||||
#include "activitywidget.h"
|
#include "activitywidget.h"
|
||||||
#include "configfile.h"
|
|
||||||
#include "syncresult.h"
|
#include "syncresult.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
@ -33,234 +33,28 @@
|
||||||
#include "activityitemdelegate.h"
|
#include "activityitemdelegate.h"
|
||||||
#include "protocolwidget.h"
|
#include "protocolwidget.h"
|
||||||
#include "QProgressIndicator.h"
|
#include "QProgressIndicator.h"
|
||||||
|
#include "notificationwidget.h"
|
||||||
|
#include "notificationconfirmjob.h"
|
||||||
|
#include "servernotificationhandler.h"
|
||||||
|
#include "theme.h"
|
||||||
|
#include "ocsjob.h"
|
||||||
|
|
||||||
#include "ui_activitywidget.h"
|
#include "ui_activitywidget.h"
|
||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
|
// time span in milliseconds which has to be between two
|
||||||
|
// refreshes of the notifications
|
||||||
|
#define NOTIFICATION_REQUEST_FREE_PERIOD 15000
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
void ActivityList::setAccountName( const QString& name )
|
|
||||||
{
|
|
||||||
_accountName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ActivityList::accountName() const
|
|
||||||
{
|
|
||||||
return _accountName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==================================================================== */
|
|
||||||
|
|
||||||
ActivityListModel::ActivityListModel(QWidget *parent)
|
|
||||||
:QAbstractListModel(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
Activity a;
|
|
||||||
|
|
||||||
if (!index.isValid())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
a = _finalList.at(index.row());
|
|
||||||
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
|
|
||||||
QStringList list;
|
|
||||||
|
|
||||||
if (role == Qt::EditRole)
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
switch (role) {
|
|
||||||
case ActivityItemDelegate::PathRole:
|
|
||||||
list = FolderMan::instance()->findFileInLocalFolders(a._file, ast->account());
|
|
||||||
if( list.count() > 0 ) {
|
|
||||||
return QVariant(list.at(0));
|
|
||||||
}
|
|
||||||
// File does not exist anymore? Let's try to open its path
|
|
||||||
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(a._file).path(), ast->account());
|
|
||||||
if( list.count() > 0 ) {
|
|
||||||
return QVariant(list.at(0));
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::ActionIconRole:
|
|
||||||
return QVariant(); // FIXME once the action can be quantified, display on Icon
|
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::UserIconRole:
|
|
||||||
return QIcon(QLatin1String(":/client/resources/account.png"));
|
|
||||||
break;
|
|
||||||
case Qt::ToolTipRole:
|
|
||||||
case ActivityItemDelegate::ActionTextRole:
|
|
||||||
return a._subject;
|
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::LinkRole:
|
|
||||||
return a._link;
|
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::AccountRole:
|
|
||||||
return a._accName;
|
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::PointInTimeRole:
|
|
||||||
return Utility::timeAgoInWords(a._dateTime);
|
|
||||||
break;
|
|
||||||
case ActivityItemDelegate::AccountConnectedRole:
|
|
||||||
return (ast && ast->isConnected());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int ActivityListModel::rowCount(const QModelIndex&) const
|
|
||||||
{
|
|
||||||
return _finalList.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
// current strategy: Fetch 100 items per Account
|
|
||||||
// ATTENTION: This method is const and thus it is not possible to modify
|
|
||||||
// the _activityLists hash or so. Doesn't make it easier...
|
|
||||||
bool ActivityListModel::canFetchMore(const QModelIndex& ) const
|
|
||||||
{
|
|
||||||
if( _activityLists.count() == 0 ) return true;
|
|
||||||
|
|
||||||
QMap<AccountState*, ActivityList>::const_iterator i = _activityLists.begin();
|
|
||||||
while (i != _activityLists.end()) {
|
|
||||||
AccountState *ast = i.key();
|
|
||||||
if( ast && ast->isConnected() ) {
|
|
||||||
ActivityList activities = i.value();
|
|
||||||
if( activities.count() == 0 &&
|
|
||||||
! _currentlyFetching.contains(ast) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActivityListModel::startFetchJob(AccountState* s)
|
|
||||||
{
|
|
||||||
if( !s->isConnected() ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
|
|
||||||
QObject::connect(job, SIGNAL(jsonReceived(QVariantMap, int)),
|
|
||||||
this, SLOT(slotActivitiesReceived(QVariantMap, int)));
|
|
||||||
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(s));
|
|
||||||
|
|
||||||
QList< QPair<QString,QString> > params;
|
|
||||||
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0")));
|
|
||||||
params.append(qMakePair(QString::fromLatin1("pagesize"), QString::fromLatin1("100")));
|
|
||||||
job->addQueryParams(params);
|
|
||||||
|
|
||||||
_currentlyFetching.insert(s);
|
|
||||||
qDebug() << "Start fetching activities for " << s->account()->displayName();
|
|
||||||
job->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActivityListModel::slotActivitiesReceived(const QVariantMap& json, int statusCode)
|
|
||||||
{
|
|
||||||
auto activities = json.value("ocs").toMap().value("data").toList();
|
|
||||||
qDebug() << "*** activities" << activities;
|
|
||||||
|
|
||||||
ActivityList list;
|
|
||||||
AccountState* ast = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
|
|
||||||
_currentlyFetching.remove(ast);
|
|
||||||
list.setAccountName( ast->account()->displayName());
|
|
||||||
|
|
||||||
foreach( auto activ, activities ) {
|
|
||||||
auto json = activ.toMap();
|
|
||||||
|
|
||||||
Activity a;
|
|
||||||
a._accName = ast->account()->displayName();
|
|
||||||
a._id = json.value("id").toLongLong();
|
|
||||||
a._subject = json.value("subject").toString();
|
|
||||||
a._message = json.value("message").toString();
|
|
||||||
a._file = json.value("file").toString();
|
|
||||||
a._link = json.value("link").toUrl();
|
|
||||||
a._dateTime = json.value("date").toDateTime();
|
|
||||||
list.append(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
_activityLists[ast] = list;
|
|
||||||
|
|
||||||
emit activityJobStatusCode(ast, statusCode);
|
|
||||||
|
|
||||||
combineActivityLists();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ActivityListModel::combineActivityLists()
|
|
||||||
{
|
|
||||||
ActivityList resultList;
|
|
||||||
|
|
||||||
foreach( ActivityList list, _activityLists.values() ) {
|
|
||||||
resultList.append(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort( resultList.begin(), resultList.end() );
|
|
||||||
|
|
||||||
beginInsertRows(QModelIndex(), 0, resultList.count()-1);
|
|
||||||
_finalList = resultList;
|
|
||||||
endInsertRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActivityListModel::fetchMore(const QModelIndex &)
|
|
||||||
{
|
|
||||||
QList<AccountStatePtr> accounts = AccountManager::instance()->accounts();
|
|
||||||
|
|
||||||
foreach (AccountStatePtr asp, accounts) {
|
|
||||||
bool newItem = false;
|
|
||||||
|
|
||||||
if( !_activityLists.contains(asp.data()) && asp->isConnected() ) {
|
|
||||||
_activityLists[asp.data()] = ActivityList();
|
|
||||||
newItem = true;
|
|
||||||
}
|
|
||||||
if( newItem ) {
|
|
||||||
startFetchJob(asp.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActivityListModel::slotRefreshActivity(AccountState *ast)
|
|
||||||
{
|
|
||||||
if(ast && _activityLists.contains(ast)) {
|
|
||||||
qDebug() << "**** Refreshing Activity list for" << ast->account()->displayName();
|
|
||||||
_activityLists.remove(ast);
|
|
||||||
}
|
|
||||||
startFetchJob(ast);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActivityListModel::slotRemoveAccount(AccountState *ast )
|
|
||||||
{
|
|
||||||
if( _activityLists.contains(ast) ) {
|
|
||||||
int i = 0;
|
|
||||||
const QString accountToRemove = ast->account()->displayName();
|
|
||||||
|
|
||||||
QMutableListIterator<Activity> it(_finalList);
|
|
||||||
|
|
||||||
while (it.hasNext()) {
|
|
||||||
Activity activity = it.next();
|
|
||||||
if( activity._accName == accountToRemove ) {
|
|
||||||
beginRemoveRows(QModelIndex(), i, i+1);
|
|
||||||
it.remove();
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_activityLists.remove(ast);
|
|
||||||
_currentlyFetching.remove(ast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==================================================================== */
|
/* ==================================================================== */
|
||||||
|
|
||||||
ActivityWidget::ActivityWidget(QWidget *parent) :
|
ActivityWidget::ActivityWidget(QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
_ui(new Ui::ActivityWidget)
|
_ui(new Ui::ActivityWidget),
|
||||||
|
_notificationRequestsRunning(0)
|
||||||
{
|
{
|
||||||
_ui->setupUi(this);
|
_ui->setupUi(this);
|
||||||
|
|
||||||
|
@ -276,6 +70,16 @@ ActivityWidget::ActivityWidget(QWidget *parent) :
|
||||||
_ui->_activityList->setAlternatingRowColors(true);
|
_ui->_activityList->setAlternatingRowColors(true);
|
||||||
_ui->_activityList->setModel(_model);
|
_ui->_activityList->setModel(_model);
|
||||||
|
|
||||||
|
_ui->_notifyLabel->hide();
|
||||||
|
_ui->_notifyScroll->hide();
|
||||||
|
|
||||||
|
// Create a widget container for the notifications. The ui file defines
|
||||||
|
// a scroll area that get a widget with a layout as children
|
||||||
|
QWidget *w = new QWidget(this);
|
||||||
|
_notificationsLayout = new QVBoxLayout(this);
|
||||||
|
w->setLayout(_notificationsLayout);
|
||||||
|
_ui->_notifyScroll->setWidget(w);
|
||||||
|
|
||||||
showLabels();
|
showLabels();
|
||||||
|
|
||||||
connect(_model, SIGNAL(activityJobStatusCode(AccountState*,int)),
|
connect(_model, SIGNAL(activityJobStatusCode(AccountState*,int)),
|
||||||
|
@ -289,6 +93,9 @@ ActivityWidget::ActivityWidget(QWidget *parent) :
|
||||||
|
|
||||||
connect( _ui->_activityList, SIGNAL(activated(QModelIndex)), this,
|
connect( _ui->_activityList, SIGNAL(activated(QModelIndex)), this,
|
||||||
SLOT(slotOpenFile(QModelIndex)));
|
SLOT(slotOpenFile(QModelIndex)));
|
||||||
|
|
||||||
|
connect( &_removeTimer, SIGNAL(timeout()), this, SLOT(slotCheckToCleanWidgets()) );
|
||||||
|
_removeTimer.setInterval(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActivityWidget::~ActivityWidget()
|
ActivityWidget::~ActivityWidget()
|
||||||
|
@ -296,11 +103,26 @@ ActivityWidget::~ActivityWidget()
|
||||||
delete _ui;
|
delete _ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActivityWidget::slotRefresh(AccountState *ptr)
|
void ActivityWidget::slotRefreshActivities(AccountState *ptr)
|
||||||
{
|
{
|
||||||
_model->slotRefreshActivity(ptr);
|
_model->slotRefreshActivity(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActivityWidget::slotRefreshNotifications(AccountState *ptr)
|
||||||
|
{
|
||||||
|
// start a server notification handler if no notification requests
|
||||||
|
// are running
|
||||||
|
if( _notificationRequestsRunning == 0 ) {
|
||||||
|
ServerNotificationHandler *snh = new ServerNotificationHandler;
|
||||||
|
connect(snh, SIGNAL(newNotificationList(ActivityList)), this,
|
||||||
|
SLOT(slotBuildNotificationDisplay(ActivityList)));
|
||||||
|
|
||||||
|
snh->slotFetchNotifications(ptr);
|
||||||
|
} else {
|
||||||
|
qDebug() << Q_FUNC_INFO << "========> notification request counter not zero.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ActivityWidget::slotRemoveAccount( AccountState *ptr )
|
void ActivityWidget::slotRemoveAccount( AccountState *ptr )
|
||||||
{
|
{
|
||||||
_model->slotRemoveAccount(ptr);
|
_model->slotRemoveAccount(ptr);
|
||||||
|
@ -312,6 +134,8 @@ void ActivityWidget::showLabels()
|
||||||
_ui->_headerLabel->setTextFormat(Qt::RichText);
|
_ui->_headerLabel->setTextFormat(Qt::RichText);
|
||||||
_ui->_headerLabel->setText(t);
|
_ui->_headerLabel->setText(t);
|
||||||
|
|
||||||
|
_ui->_notifyLabel->setText(tr("Action Required: Notifications"));
|
||||||
|
|
||||||
t.clear();
|
t.clear();
|
||||||
QSetIterator<QString> i(_accountsWithoutActivities);
|
QSetIterator<QString> i(_accountsWithoutActivities);
|
||||||
while (i.hasNext() ) {
|
while (i.hasNext() ) {
|
||||||
|
@ -389,7 +213,7 @@ void ActivityWidget::storeActivityList( QTextStream& ts )
|
||||||
|
|
||||||
void ActivityWidget::slotOpenFile(QModelIndex indx)
|
void ActivityWidget::slotOpenFile(QModelIndex indx)
|
||||||
{
|
{
|
||||||
qDebug() << indx.isValid() << indx.data(ActivityItemDelegate::PathRole).toString() << QFile::exists(indx.data(ActivityItemDelegate::PathRole).toString());
|
qDebug() << Q_FUNC_INFO << indx.isValid() << indx.data(ActivityItemDelegate::PathRole).toString() << QFile::exists(indx.data(ActivityItemDelegate::PathRole).toString());
|
||||||
if( indx.isValid() ) {
|
if( indx.isValid() ) {
|
||||||
QString fullPath = indx.data(ActivityItemDelegate::PathRole).toString();
|
QString fullPath = indx.data(ActivityItemDelegate::PathRole).toString();
|
||||||
|
|
||||||
|
@ -399,6 +223,264 @@ void ActivityWidget::slotOpenFile(QModelIndex indx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GUI: Display the notifications.
|
||||||
|
// All notifications in list are coming from the same account
|
||||||
|
// but in the _widgetForNotifId hash widgets for all accounts are
|
||||||
|
// collected.
|
||||||
|
void ActivityWidget::slotBuildNotificationDisplay(const ActivityList& list)
|
||||||
|
{
|
||||||
|
QHash<QString, int> accNotified;
|
||||||
|
QString listAccountName;
|
||||||
|
|
||||||
|
foreach( auto activity, list ) {
|
||||||
|
if( _blacklistedNotifications.contains(activity)) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Activity in blacklist, skip";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationWidget *widget = 0;
|
||||||
|
|
||||||
|
if( _widgetForNotifId.contains( activity.ident()) ) {
|
||||||
|
widget = _widgetForNotifId[activity.ident()];
|
||||||
|
} else {
|
||||||
|
widget = new NotificationWidget(this);
|
||||||
|
connect(widget, SIGNAL(sendNotificationRequest(QString, QString, QByteArray)),
|
||||||
|
this, SLOT(slotSendNotificationRequest(QString, QString, QByteArray)));
|
||||||
|
connect(widget, SIGNAL(requestCleanupAndBlacklist(Activity)),
|
||||||
|
this, SLOT(slotRequestCleanupAndBlacklist(Activity)));
|
||||||
|
|
||||||
|
_notificationsLayout->addWidget(widget);
|
||||||
|
// _ui->_notifyScroll->setMinimumHeight( widget->height());
|
||||||
|
_ui->_notifyScroll->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
|
||||||
|
_widgetForNotifId[activity.ident()] = widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget->setActivity( activity );
|
||||||
|
|
||||||
|
// remember the list account name for the strayCat handling below.
|
||||||
|
listAccountName = activity._accName;
|
||||||
|
|
||||||
|
// handle gui logs. In order to NOT annoy the user with every fetching of the
|
||||||
|
// notifications the notification id is stored in a Set. Only if an id
|
||||||
|
// is not in the set, it qualifies for guiLog.
|
||||||
|
// Important: The _guiLoggedNotifications set must be wiped regularly which
|
||||||
|
// will repeat the gui log.
|
||||||
|
|
||||||
|
// after one hour, clear the gui log notification store
|
||||||
|
if( _guiLogTimer.elapsed() > 60*60*1000 ) {
|
||||||
|
_guiLoggedNotifications.clear();
|
||||||
|
}
|
||||||
|
if( !_guiLoggedNotifications.contains(activity._id)) {
|
||||||
|
QString host = activity._accName;
|
||||||
|
// store the name of the account that sends the notification to be
|
||||||
|
// able to add it to the tray notification
|
||||||
|
// remove the user name from the account as that is not accurate here.
|
||||||
|
int indx = host.indexOf(QChar('@'));
|
||||||
|
if( indx>-1 ) {
|
||||||
|
host.remove(0, 1+indx);
|
||||||
|
}
|
||||||
|
if( !host.isEmpty() ) {
|
||||||
|
if( accNotified.contains(host)) {
|
||||||
|
accNotified[host] = accNotified[host]+1;
|
||||||
|
} else {
|
||||||
|
accNotified[host] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_guiLoggedNotifications.insert(activity._id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if there are widgets that have no corresponding activity from
|
||||||
|
// the server any more. Collect them in a list
|
||||||
|
QList< Activity::Identifier > strayCats;
|
||||||
|
foreach( auto id, _widgetForNotifId.keys() ) {
|
||||||
|
NotificationWidget *widget = _widgetForNotifId[id];
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
// do not mark widgets of other accounts to delete.
|
||||||
|
if( widget->activity()._accName != listAccountName ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach( auto activity, list ) {
|
||||||
|
if( activity.ident() == id ) {
|
||||||
|
// found an activity
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( ! found ) {
|
||||||
|
// the activity does not exist any more.
|
||||||
|
strayCats.append(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// .. and now delete all these stray cat widgets.
|
||||||
|
foreach( auto strayCatId, strayCats ) {
|
||||||
|
NotificationWidget *widgetToGo = _widgetForNotifId[strayCatId];
|
||||||
|
scheduleWidgetToRemove(widgetToGo, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ui->_notifyLabel->setHidden( _widgetForNotifId.isEmpty() );
|
||||||
|
_ui->_notifyScroll->setHidden( _widgetForNotifId.isEmpty() );
|
||||||
|
|
||||||
|
int newGuiLogCount = accNotified.count();
|
||||||
|
|
||||||
|
if( newGuiLogCount > 0 ) {
|
||||||
|
// restart the gui log timer now that we show a notification
|
||||||
|
_guiLogTimer.restart();
|
||||||
|
|
||||||
|
// Assemble a tray notification
|
||||||
|
QString msg = tr("You received %n new notification(s) from %2.", "", accNotified[accNotified.keys().at(0)]).
|
||||||
|
arg(accNotified.keys().at(0));
|
||||||
|
|
||||||
|
if( newGuiLogCount >= 2 ) {
|
||||||
|
QString acc1 = accNotified.keys().at(0);
|
||||||
|
QString acc2 = accNotified.keys().at(1);
|
||||||
|
if( newGuiLogCount == 2 ) {
|
||||||
|
int notiCount = accNotified[ acc1 ] + accNotified[ acc2 ];
|
||||||
|
msg = tr("You received %n new notification(s) from %1 and %2.", "", notiCount).arg(acc1, acc2);
|
||||||
|
} else {
|
||||||
|
msg = tr("You received new notifications from %1, %2 and other accounts.").arg(acc1, acc2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString log = tr("%1 Notifications - Action Required").arg(Theme::instance()->appNameGUI());
|
||||||
|
emit guiLog( log, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityWidget::slotSendNotificationRequest(const QString& accountName, const QString& link, const QByteArray& verb)
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO << "Server Notification Request " << verb << link << "on account" << accountName;
|
||||||
|
NotificationWidget *theSender = qobject_cast<NotificationWidget*>(sender());
|
||||||
|
|
||||||
|
const QStringList validVerbs = QStringList() << "GET" << "PUT" << "POST" << "DELETE";
|
||||||
|
|
||||||
|
if( validVerbs.contains(verb)) {
|
||||||
|
AccountStatePtr acc = AccountManager::instance()->account(accountName);
|
||||||
|
if( acc ) {
|
||||||
|
NotificationConfirmJob *job = new NotificationConfirmJob(acc->account());
|
||||||
|
QUrl l(link);
|
||||||
|
job->setLinkAndVerb(l, verb);
|
||||||
|
job->setWidget(theSender);
|
||||||
|
connect( job, SIGNAL( networkError(QNetworkReply*)),
|
||||||
|
this, SLOT(slotNotifyNetworkError(QNetworkReply*)));
|
||||||
|
connect( job, SIGNAL( jobFinished(QString, int)),
|
||||||
|
this, SLOT(slotNotifyServerFinished(QString, int)) );
|
||||||
|
job->start();
|
||||||
|
|
||||||
|
// count the number of running notification requests. If this member var
|
||||||
|
// is larger than zero, no new fetching of notifications is started
|
||||||
|
_notificationRequestsRunning++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Notification Links: Invalid verb:" << verb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityWidget::endNotificationRequest( NotificationWidget *widget, int replyCode )
|
||||||
|
{
|
||||||
|
_notificationRequestsRunning--;
|
||||||
|
if( widget ) {
|
||||||
|
widget->slotNotificationRequestFinished(replyCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityWidget::slotNotifyNetworkError( QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
NotificationConfirmJob *job = qobject_cast<NotificationConfirmJob*>(sender());
|
||||||
|
if( !job ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resultCode =0;
|
||||||
|
if( reply ) {
|
||||||
|
resultCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
endNotificationRequest(job->widget(), resultCode);
|
||||||
|
qDebug() << Q_FUNC_INFO << "Server notify job failed with code " << resultCode;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityWidget::slotNotifyServerFinished( const QString& reply, int replyCode )
|
||||||
|
{
|
||||||
|
NotificationConfirmJob *job = qobject_cast<NotificationConfirmJob*>(sender());
|
||||||
|
if( !job ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
endNotificationRequest(job->widget(), replyCode);
|
||||||
|
// FIXME: remove the widget after a couple of seconds
|
||||||
|
qDebug() << Q_FUNC_INFO << "Server Notification reply code"<< replyCode << reply;
|
||||||
|
|
||||||
|
// if the notification was successful start a timer that triggers
|
||||||
|
// removal of the done widgets in a few seconds
|
||||||
|
// Add 200 millisecs to the predefined value to make sure that the timer in
|
||||||
|
// widget's method readyToClose() has elapsed.
|
||||||
|
if( replyCode == OCS_SUCCESS_STATUS_CODE ) {
|
||||||
|
scheduleWidgetToRemove( job->widget() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// blacklist the activity coming in here.
|
||||||
|
void ActivityWidget::slotRequestCleanupAndBlacklist(const Activity& blacklistActivity)
|
||||||
|
{
|
||||||
|
if ( ! _blacklistedNotifications.contains(blacklistActivity) ) {
|
||||||
|
_blacklistedNotifications.append(blacklistActivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationWidget *widget = _widgetForNotifId[ blacklistActivity.ident() ];
|
||||||
|
scheduleWidgetToRemove(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivityWidget::scheduleWidgetToRemove(NotificationWidget *widget, int milliseconds)
|
||||||
|
{
|
||||||
|
if( !widget ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// in fife seconds from now, remove the widget.
|
||||||
|
QDateTime removeTime = QDateTime::currentDateTime().addMSecs(milliseconds);
|
||||||
|
|
||||||
|
QPair<QDateTime, NotificationWidget*> removeInfo = qMakePair(removeTime, widget);
|
||||||
|
if( !_widgetsToRemove.contains(removeInfo) ) {
|
||||||
|
_widgetsToRemove.insert( removeInfo );
|
||||||
|
if( !_removeTimer.isActive() ) {
|
||||||
|
_removeTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every second to see if widgets need to be removed.
|
||||||
|
void ActivityWidget::slotCheckToCleanWidgets()
|
||||||
|
{
|
||||||
|
// loop over all widgets in the to-remove queue
|
||||||
|
foreach( auto toRemove, _widgetsToRemove ) {
|
||||||
|
QDateTime t = toRemove.first;
|
||||||
|
NotificationWidget *widget = toRemove.second;
|
||||||
|
|
||||||
|
if( QDateTime::currentDateTime() > t ) {
|
||||||
|
// found one to remove!
|
||||||
|
Activity::Identifier id = widget->activity().ident();
|
||||||
|
_widgetForNotifId.remove(id);
|
||||||
|
widget->deleteLater();
|
||||||
|
_widgetsToRemove.remove(toRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _widgetsToRemove.isEmpty() ) {
|
||||||
|
_removeTimer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if the whole notification pane should be hidden
|
||||||
|
if( _widgetForNotifId.isEmpty() ) {
|
||||||
|
_ui->_notifyLabel->setHidden(true);
|
||||||
|
_ui->_notifyScroll->setHidden(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ==================================================================== */
|
/* ==================================================================== */
|
||||||
|
|
||||||
ActivitySettings::ActivitySettings(QWidget *parent)
|
ActivitySettings::ActivitySettings(QWidget *parent)
|
||||||
|
@ -414,6 +496,7 @@ ActivitySettings::ActivitySettings(QWidget *parent)
|
||||||
_activityTabId = _tab->insertTab(0, _activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity"));
|
_activityTabId = _tab->insertTab(0, _activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity"));
|
||||||
connect(_activityWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard()));
|
connect(_activityWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard()));
|
||||||
connect(_activityWidget, SIGNAL(hideAcitivityTab(bool)), this, SLOT(setActivityTabHidden(bool)));
|
connect(_activityWidget, SIGNAL(hideAcitivityTab(bool)), this, SLOT(setActivityTabHidden(bool)));
|
||||||
|
connect(_activityWidget, SIGNAL(guiLog(QString,QString)), this, SIGNAL(guiLog(QString,QString)));
|
||||||
|
|
||||||
_protocolWidget = new ProtocolWidget(this);
|
_protocolWidget = new ProtocolWidget(this);
|
||||||
_tab->insertTab(1, _protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol"));
|
_tab->insertTab(1, _protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol"));
|
||||||
|
@ -438,6 +521,9 @@ ActivitySettings::ActivitySettings(QWidget *parent)
|
||||||
_progressIndicator = new QProgressIndicator(this);
|
_progressIndicator = new QProgressIndicator(this);
|
||||||
_tab->setCornerWidget(_progressIndicator);
|
_tab->setCornerWidget(_progressIndicator);
|
||||||
|
|
||||||
|
connect(&_notificationCheckTimer, SIGNAL(timeout()),
|
||||||
|
this, SLOT(slotRegularNotificationCheck()));
|
||||||
|
|
||||||
// connect a model signal to stop the animation.
|
// connect a model signal to stop the animation.
|
||||||
connect(_activityWidget, SIGNAL(rowsInserted()), _progressIndicator, SLOT(stopAnimation()));
|
connect(_activityWidget, SIGNAL(rowsInserted()), _progressIndicator, SLOT(stopAnimation()));
|
||||||
|
|
||||||
|
@ -445,6 +531,12 @@ ActivitySettings::ActivitySettings(QWidget *parent)
|
||||||
_tab->setCurrentIndex(1);
|
_tab->setCurrentIndex(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActivitySettings::setNotificationRefreshInterval( quint64 interval )
|
||||||
|
{
|
||||||
|
qDebug() << "Starting Notification refresh timer with " << interval/1000 << " sec interval";
|
||||||
|
_notificationCheckTimer.start(interval);
|
||||||
|
}
|
||||||
|
|
||||||
void ActivitySettings::setActivityTabHidden(bool hidden)
|
void ActivitySettings::setActivityTabHidden(bool hidden)
|
||||||
{
|
{
|
||||||
if( hidden && _activityTabId > -1 ) {
|
if( hidden && _activityTabId > -1 ) {
|
||||||
|
@ -490,10 +582,32 @@ void ActivitySettings::slotRemoveAccount( AccountState *ptr )
|
||||||
|
|
||||||
void ActivitySettings::slotRefresh( AccountState* ptr )
|
void ActivitySettings::slotRefresh( AccountState* ptr )
|
||||||
{
|
{
|
||||||
if( ptr && ptr->isConnected() && isVisible()) {
|
QElapsedTimer timer = _timeSinceLastCheck[ptr];
|
||||||
qDebug() << "Refreshing Activity list for " << ptr->account()->displayName();
|
|
||||||
|
// Fetch Activities only if visible and if last check is longer than 15 secs ago
|
||||||
|
if( timer.isValid() && timer.elapsed() < NOTIFICATION_REQUEST_FREE_PERIOD ) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "do not check as last check is only secs ago: " << timer.elapsed() / 1000;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if( ptr && ptr->isConnected() ) {
|
||||||
|
if( isVisible() ) {
|
||||||
_progressIndicator->startAnimation();
|
_progressIndicator->startAnimation();
|
||||||
_activityWidget->slotRefresh(ptr);
|
_activityWidget->slotRefreshActivities( ptr);
|
||||||
|
}
|
||||||
|
_activityWidget->slotRefreshNotifications(ptr);
|
||||||
|
if( !( _timeSinceLastCheck[ptr].isValid() ) ) {
|
||||||
|
_timeSinceLastCheck[ptr].start();
|
||||||
|
} else {
|
||||||
|
_timeSinceLastCheck[ptr].restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivitySettings::slotRegularNotificationCheck()
|
||||||
|
{
|
||||||
|
AccountManager *am = AccountManager::instance();
|
||||||
|
foreach (AccountStatePtr a, am->accounts()) {
|
||||||
|
slotRefresh(a.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "progressdispatcher.h"
|
#include "progressdispatcher.h"
|
||||||
#include "owncloudgui.h"
|
#include "owncloudgui.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
|
#include "activitydata.h"
|
||||||
|
|
||||||
#include "ui_activitywidget.h"
|
#include "ui_activitywidget.h"
|
||||||
|
|
||||||
|
@ -33,97 +34,15 @@ namespace OCC {
|
||||||
class Account;
|
class Account;
|
||||||
class AccountStatusPtr;
|
class AccountStatusPtr;
|
||||||
class ProtocolWidget;
|
class ProtocolWidget;
|
||||||
|
class JsonApiJob;
|
||||||
|
class NotificationWidget;
|
||||||
|
class ActivityListModel;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ActivityWidget;
|
class ActivityWidget;
|
||||||
}
|
}
|
||||||
class Application;
|
class Application;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Activity Structure
|
|
||||||
* @ingroup gui
|
|
||||||
*
|
|
||||||
* contains all the information describing a single activity.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Activity
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
qlonglong _id;
|
|
||||||
QString _subject;
|
|
||||||
QString _message;
|
|
||||||
QString _file;
|
|
||||||
QUrl _link;
|
|
||||||
QDateTime _dateTime;
|
|
||||||
QString _accName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sort operator to sort the list youngest first.
|
|
||||||
* @param val
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
bool operator<( const Activity& val ) const {
|
|
||||||
return _dateTime.toMSecsSinceEpoch() > val._dateTime.toMSecsSinceEpoch();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The ActivityList
|
|
||||||
* @ingroup gui
|
|
||||||
*
|
|
||||||
* A QList based list of Activities
|
|
||||||
*/
|
|
||||||
class ActivityList:public QList<Activity>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void setAccountName( const QString& name );
|
|
||||||
QString accountName() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString _accountName;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The ActivityListModel
|
|
||||||
* @ingroup gui
|
|
||||||
*
|
|
||||||
* Simple list model to provide the list view with data.
|
|
||||||
*/
|
|
||||||
class ActivityListModel : public QAbstractListModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ActivityListModel(QWidget *parent=0);
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
|
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
|
||||||
|
|
||||||
bool canFetchMore(const QModelIndex& ) const Q_DECL_OVERRIDE;
|
|
||||||
void fetchMore(const QModelIndex&) Q_DECL_OVERRIDE;
|
|
||||||
|
|
||||||
ActivityList activityList() { return _finalList; }
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void slotRefreshActivity(AccountState* ast);
|
|
||||||
void slotRemoveAccount( AccountState *ast );
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void slotActivitiesReceived(const QVariantMap& json, int statusCode);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void activityJobStatusCode(AccountState* ast, int statusCode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void startFetchJob(AccountState* s);
|
|
||||||
void combineActivityLists();
|
|
||||||
|
|
||||||
QMap<AccountState*, ActivityList> _activityLists;
|
|
||||||
ActivityList _finalList;
|
|
||||||
QSet<AccountState*> _currentlyFetching;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The ActivityWidget class
|
* @brief The ActivityWidget class
|
||||||
* @ingroup gui
|
* @ingroup gui
|
||||||
|
@ -143,15 +62,27 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void slotOpenFile(QModelIndex indx);
|
void slotOpenFile(QModelIndex indx);
|
||||||
void slotRefresh(AccountState* ptr);
|
void slotRefreshActivities(AccountState* ptr);
|
||||||
|
void slotRefreshNotifications(AccountState *ptr);
|
||||||
void slotRemoveAccount( AccountState *ptr );
|
void slotRemoveAccount( AccountState *ptr );
|
||||||
void slotAccountActivityStatus(AccountState *ast, int statusCode);
|
void slotAccountActivityStatus(AccountState *ast, int statusCode);
|
||||||
|
void slotRequestCleanupAndBlacklist(const Activity& blacklistActivity);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void guiLog(const QString&, const QString&);
|
void guiLog(const QString&, const QString&);
|
||||||
void copyToClipboard();
|
void copyToClipboard();
|
||||||
void rowsInserted();
|
void rowsInserted();
|
||||||
void hideAcitivityTab(bool);
|
void hideAcitivityTab(bool);
|
||||||
|
void newNotificationList(const ActivityList& list);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotBuildNotificationDisplay(const ActivityList& list);
|
||||||
|
void slotSendNotificationRequest(const QString &accountName, const QString& link, const QByteArray &verb);
|
||||||
|
void slotNotifyNetworkError( QNetworkReply* );
|
||||||
|
void slotNotifyServerFinished( const QString& reply, int replyCode );
|
||||||
|
void endNotificationRequest(NotificationWidget *widget , int replyCode);
|
||||||
|
void scheduleWidgetToRemove(NotificationWidget *widget, int milliseconds = 4500);
|
||||||
|
void slotCheckToCleanWidgets();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void showLabels();
|
void showLabels();
|
||||||
|
@ -160,8 +91,21 @@ private:
|
||||||
QPushButton *_copyBtn;
|
QPushButton *_copyBtn;
|
||||||
|
|
||||||
QSet<QString> _accountsWithoutActivities;
|
QSet<QString> _accountsWithoutActivities;
|
||||||
|
QMap<Activity::Identifier, NotificationWidget*> _widgetForNotifId;
|
||||||
|
QElapsedTimer _guiLogTimer;
|
||||||
|
QSet<int> _guiLoggedNotifications;
|
||||||
|
ActivityList _blacklistedNotifications;
|
||||||
|
|
||||||
|
QSet< QPair<QDateTime, NotificationWidget*> > _widgetsToRemove;
|
||||||
|
QTimer _removeTimer;
|
||||||
|
|
||||||
|
// number of currently running notification requests. If non zero,
|
||||||
|
// no query for notifications is started.
|
||||||
|
int _notificationRequestsRunning;
|
||||||
|
|
||||||
ActivityListModel *_model;
|
ActivityListModel *_model;
|
||||||
|
QVBoxLayout *_notificationsLayout;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,9 +128,12 @@ public slots:
|
||||||
void slotRefresh( AccountState* ptr );
|
void slotRefresh( AccountState* ptr );
|
||||||
void slotRemoveAccount( AccountState *ptr );
|
void slotRemoveAccount( AccountState *ptr );
|
||||||
|
|
||||||
|
void setNotificationRefreshInterval( quint64 interval );
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotCopyToClipboard();
|
void slotCopyToClipboard();
|
||||||
void setActivityTabHidden(bool hidden);
|
void setActivityTabHidden(bool hidden);
|
||||||
|
void slotRegularNotificationCheck();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void guiLog(const QString&, const QString&);
|
void guiLog(const QString&, const QString&);
|
||||||
|
@ -200,7 +147,8 @@ private:
|
||||||
ActivityWidget *_activityWidget;
|
ActivityWidget *_activityWidget;
|
||||||
ProtocolWidget *_protocolWidget;
|
ProtocolWidget *_protocolWidget;
|
||||||
QProgressIndicator *_progressIndicator;
|
QProgressIndicator *_progressIndicator;
|
||||||
|
QTimer _notificationCheckTimer;
|
||||||
|
QHash<AccountState*, QElapsedTimer> _timeSinceLastCheck;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,23 +15,60 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="_headerLabel">
|
<widget class="QLabel" name="_notifyLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>TextLabel</string>
|
<string>TextLabel</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QListView" name="_activityList"/>
|
<widget class="QScrollArea" name="_notifyScroll">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="_scrollAreaWidgetContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>677</width>
|
||||||
|
<height>70</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="_bottomLabel">
|
<widget class="QLabel" name="_headerLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>TextLabel</string>
|
<string>TextLabel</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
|
<widget class="QListView" name="_activityList">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="_bottomLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
<widget class="QDialogButtonBox" name="_dialogButtonBox"/>
|
<widget class="QDialogButtonBox" name="_dialogButtonBox"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
81
src/gui/notificationconfirmjob.cpp
Normal file
81
src/gui/notificationconfirmjob.cpp
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 "notificationconfirmjob.h"
|
||||||
|
#include "networkjobs.h"
|
||||||
|
#include "account.h"
|
||||||
|
#include "json.h"
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
NotificationConfirmJob::NotificationConfirmJob(AccountPtr account)
|
||||||
|
: AbstractNetworkJob(account, ""),
|
||||||
|
_widget(0)
|
||||||
|
{
|
||||||
|
setIgnoreCredentialFailure(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationConfirmJob::setLinkAndVerb(const QUrl& link, const QByteArray &verb)
|
||||||
|
{
|
||||||
|
_link = link;
|
||||||
|
_verb = verb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationConfirmJob::setWidget( NotificationWidget *widget )
|
||||||
|
{
|
||||||
|
_widget = widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationWidget *NotificationConfirmJob::widget()
|
||||||
|
{
|
||||||
|
return _widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationConfirmJob::start()
|
||||||
|
{
|
||||||
|
if( !_link.isValid() ) {
|
||||||
|
qDebug() << "Attempt to trigger invalid URL: " << _link.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QNetworkRequest req;
|
||||||
|
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
|
QIODevice *iodevice = 0;
|
||||||
|
setReply(davRequest(_verb, _link, req, iodevice));
|
||||||
|
setupConnections(reply());
|
||||||
|
|
||||||
|
AbstractNetworkJob::start();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotificationConfirmJob::finished()
|
||||||
|
{
|
||||||
|
int replyCode = 0;
|
||||||
|
// FIXME: check for the reply code!
|
||||||
|
const QString replyStr = reply()->readAll();
|
||||||
|
|
||||||
|
if( replyStr.contains( "<?xml version=\"1.0\"?>") ) {
|
||||||
|
QRegExp rex("<statuscode>(\\d+)</statuscode>");
|
||||||
|
if( replyStr.contains(rex) ) {
|
||||||
|
// this is a error message coming back from ocs.
|
||||||
|
replyCode = rex.cap(1).toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit jobFinished(replyStr, replyCode);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
90
src/gui/notificationconfirmjob.h
Normal file
90
src/gui/notificationconfirmjob.h
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 NOTIFICATIONCONFIRMJOB_H
|
||||||
|
#define NOTIFICATIONCONFIRMJOB_H
|
||||||
|
|
||||||
|
#include "accountfwd.h"
|
||||||
|
#include "abstractnetworkjob.h"
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include <QList>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
class NotificationWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The NotificationConfirmJob class
|
||||||
|
* @ingroup gui
|
||||||
|
*
|
||||||
|
* Class to call an action-link of a notification coming from the server.
|
||||||
|
* All the communication logic is handled in this class.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class NotificationConfirmJob : public AbstractNetworkJob {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit NotificationConfirmJob(AccountPtr account);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the verb and link for the job
|
||||||
|
*
|
||||||
|
* @param verb currently supported GET PUT POST DELETE
|
||||||
|
*/
|
||||||
|
void setLinkAndVerb(const QUrl& link, const QByteArray &verb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start the OCS request
|
||||||
|
*/
|
||||||
|
void start() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief setWidget stores the associated widget to be able to use
|
||||||
|
* it when the job has finished
|
||||||
|
* @param widget pointer to the notification widget to store
|
||||||
|
*/
|
||||||
|
void setWidget( NotificationWidget *widget );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief widget - get the associated notification widget as stored
|
||||||
|
* with setWidget method.
|
||||||
|
* @return widget pointer to the notification widget
|
||||||
|
*/
|
||||||
|
NotificationWidget *widget();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of the OCS request
|
||||||
|
*
|
||||||
|
* @param reply the reply
|
||||||
|
*/
|
||||||
|
void jobFinished(QString reply, int replyCode);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
virtual bool finished() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QByteArray _verb;
|
||||||
|
QUrl _link;
|
||||||
|
NotificationWidget *_widget;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NotificationConfirmJob_H
|
147
src/gui/notificationwidget.cpp
Normal file
147
src/gui/notificationwidget.cpp
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 "notificationwidget.h"
|
||||||
|
#include "QProgressIndicator.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
#include "ocsjob.h"
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
NotificationWidget::NotificationWidget(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
_ui.setupUi(this);
|
||||||
|
_progressIndi = new QProgressIndicator(this);
|
||||||
|
_ui.horizontalLayout->addWidget(_progressIndi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationWidget::setActivity(const Activity& activity)
|
||||||
|
{
|
||||||
|
_myActivity = activity;
|
||||||
|
|
||||||
|
Q_ASSERT( !activity._accName.isEmpty() );
|
||||||
|
_accountName = activity._accName;
|
||||||
|
|
||||||
|
// _ui._headerLabel->setText( );
|
||||||
|
_ui._subjectLabel->setText(activity._subject);
|
||||||
|
if( activity._message.isEmpty()) {
|
||||||
|
_ui._messageLabel->hide();
|
||||||
|
} else {
|
||||||
|
_ui._messageLabel->setText(activity._message);
|
||||||
|
}
|
||||||
|
_ui._notifIcon->setPixmap(QPixmap(":/client/resources/bell.png"));
|
||||||
|
_ui._notifIcon->setMinimumWidth(64);
|
||||||
|
_ui._notifIcon->setMinimumHeight(64);
|
||||||
|
_ui._notifIcon->show();
|
||||||
|
|
||||||
|
QString tText = tr("Created at %1").arg(Utility::timeAgoInWords(activity._dateTime));
|
||||||
|
_ui._timeLabel->setText(tText);
|
||||||
|
|
||||||
|
// always remove the buttons
|
||||||
|
foreach( auto button, _ui._buttonBox->buttons() ) {
|
||||||
|
_ui._buttonBox->removeButton(button);
|
||||||
|
}
|
||||||
|
_buttons.clear();
|
||||||
|
|
||||||
|
// display buttons for the links
|
||||||
|
if( activity._links.isEmpty() ) {
|
||||||
|
// in case there is no action defined, do a close button.
|
||||||
|
QPushButton *b = _ui._buttonBox->addButton( QDialogButtonBox::Close );
|
||||||
|
b->setDefault(true);
|
||||||
|
connect(b, SIGNAL(clicked()), this, SLOT(slotButtonClicked()));
|
||||||
|
_buttons.append(b);
|
||||||
|
} else {
|
||||||
|
foreach( auto link, activity._links ) {
|
||||||
|
QPushButton *b = _ui._buttonBox->addButton(link._label, QDialogButtonBox::AcceptRole);
|
||||||
|
b->setDefault(link._isPrimary);
|
||||||
|
connect(b, SIGNAL(clicked()), this, SLOT(slotButtonClicked()));
|
||||||
|
_buttons.append(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity NotificationWidget::activity() const
|
||||||
|
{
|
||||||
|
return _myActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationWidget::slotButtonClicked()
|
||||||
|
{
|
||||||
|
QObject *buttonWidget = QObject::sender();
|
||||||
|
int index = -1;
|
||||||
|
if( buttonWidget ) {
|
||||||
|
// find the button that was clicked, it has to be in the list
|
||||||
|
// of buttons that were added to the button box before.
|
||||||
|
for( int i = 0; i < _buttons.count(); i++ ) {
|
||||||
|
if( _buttons.at(i) == buttonWidget ) {
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
_buttons.at(i)->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the button was found, the link must be called
|
||||||
|
if( index > -1 && _myActivity._links.count() == 0 ) {
|
||||||
|
// no links, that means it was the close button
|
||||||
|
// empty link. Just close and remove the widget.
|
||||||
|
QString doneText = tr("Closing in a few seconds...");
|
||||||
|
_ui._timeLabel->setText(doneText);
|
||||||
|
emit requestCleanupAndBlacklist(_myActivity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( index > -1 && index < _myActivity._links.count() ) {
|
||||||
|
ActivityLink triggeredLink = _myActivity._links.at(index);
|
||||||
|
_actionLabel = triggeredLink._label;
|
||||||
|
|
||||||
|
if( ! triggeredLink._link.isEmpty() ) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Notification Link: "<< triggeredLink._verb << triggeredLink._link;
|
||||||
|
_progressIndi->startAnimation();
|
||||||
|
emit sendNotificationRequest( _accountName, triggeredLink._link, triggeredLink._verb );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationWidget::slotNotificationRequestFinished(int statusCode)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
QString doneText;
|
||||||
|
QLocale locale;
|
||||||
|
|
||||||
|
QString timeStr = locale.toString(QTime::currentTime());
|
||||||
|
|
||||||
|
// the ocs API returns stat code 100 if it succeeded.
|
||||||
|
if( statusCode != OCS_SUCCESS_STATUS_CODE ) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Notification Request to Server failed, leave button visible.";
|
||||||
|
for( i = 0; i < _buttons.count(); i++ ) {
|
||||||
|
_buttons.at(i)->setEnabled(true);
|
||||||
|
}
|
||||||
|
//: The second parameter is a time, such as 'failed at 09:58pm'
|
||||||
|
doneText = tr("%1 request failed at %2").arg(_actionLabel, timeStr);
|
||||||
|
} else {
|
||||||
|
// the call to the ocs API succeeded.
|
||||||
|
_ui._buttonBox->hide();
|
||||||
|
|
||||||
|
//: The second parameter is a time, such as 'selected at 09:58pm'
|
||||||
|
doneText = tr("'%1' selected at %2").arg(_actionLabel, timeStr);
|
||||||
|
}
|
||||||
|
_ui._timeLabel->setText( doneText );
|
||||||
|
|
||||||
|
_progressIndi->stopAnimation();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
60
src/gui/notificationwidget.h
Normal file
60
src/gui/notificationwidget.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 NOTIFICATIONWIDGET_H
|
||||||
|
#define NOTIFICATIONWIDGET_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "activitydata.h"
|
||||||
|
|
||||||
|
#include "ui_notificationwidget.h"
|
||||||
|
|
||||||
|
#define NOTIFICATION_WIDGET_CLOSE_AFTER_MILLISECS 4800
|
||||||
|
|
||||||
|
class QProgressIndicator;
|
||||||
|
|
||||||
|
namespace OCC {
|
||||||
|
|
||||||
|
class NotificationWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit NotificationWidget(QWidget *parent = 0);
|
||||||
|
|
||||||
|
bool readyToClose();
|
||||||
|
Activity activity() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sendNotificationRequest( const QString&, const QString& link, const QByteArray& verb);
|
||||||
|
void requestCleanupAndBlacklist( const Activity& activity );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setActivity(const Activity& activity);
|
||||||
|
void slotNotificationRequestFinished(int statusCode);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotButtonClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui_NotificationWidget _ui;
|
||||||
|
Activity _myActivity;
|
||||||
|
QList<QPushButton*> _buttons;
|
||||||
|
QString _accountName;
|
||||||
|
QProgressIndicator *_progressIndi;
|
||||||
|
QString _actionLabel;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NOTIFICATIONWIDGET_H
|
129
src/gui/notificationwidget.ui
Normal file
129
src/gui/notificationwidget.ui
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>NotificationWidget</class>
|
||||||
|
<widget class="QWidget" name="NotificationWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>725</width>
|
||||||
|
<height>139</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="_notifIcon">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap">
|
||||||
|
<pixmap>../../../../resources/bell.png</pixmap>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="_subjectLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Lorem ipsum dolor sit amet</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="_messageLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam </string>
|
||||||
|
</property>
|
||||||
|
<property name="scaledContents">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="_timeLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>8</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="_buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::HLine</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>4</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>1</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -23,7 +23,7 @@ namespace OCC {
|
||||||
OcsJob::OcsJob(AccountPtr account)
|
OcsJob::OcsJob(AccountPtr account)
|
||||||
: AbstractNetworkJob(account, "")
|
: AbstractNetworkJob(account, "")
|
||||||
{
|
{
|
||||||
_passStatusCodes.append(100);
|
_passStatusCodes.append(OCS_SUCCESS_STATUS_CODE);
|
||||||
setIgnoreCredentialFailure(true);
|
setIgnoreCredentialFailure(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#define OCS_SUCCESS_STATUS_CODE 100
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
103
src/gui/servernotificationhandler.cpp
Normal file
103
src/gui/servernotificationhandler.cpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 "servernotificationhandler.h"
|
||||||
|
#include "accountstate.h"
|
||||||
|
#include "capabilities.h"
|
||||||
|
#include "json.h"
|
||||||
|
#include "networkjobs.h"
|
||||||
|
|
||||||
|
namespace OCC
|
||||||
|
{
|
||||||
|
|
||||||
|
ServerNotificationHandler::ServerNotificationHandler(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerNotificationHandler::slotFetchNotifications(AccountState *ptr)
|
||||||
|
{
|
||||||
|
// check connectivity and credentials
|
||||||
|
if( !( ptr && ptr->isConnected() && ptr->account() &&
|
||||||
|
ptr->account()->credentials() &&
|
||||||
|
ptr->account()->credentials()->ready() ) ) {
|
||||||
|
deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// check if the account has notifications enabled. If the capabilities are
|
||||||
|
// not yet valid, its assumed that notifications are available.
|
||||||
|
if( ptr->account()->capabilities().isValid() ) {
|
||||||
|
if( ! ptr->account()->capabilities().notificationsAvailable() ) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Account" << ptr->account()->displayName() << "does not have notifications enabled.";
|
||||||
|
deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the previous notification job has finished, start next.
|
||||||
|
_notificationJob = new JsonApiJob( ptr->account(), QLatin1String("ocs/v2.php/apps/notifications/api/v1/notifications"), this );
|
||||||
|
QObject::connect(_notificationJob.data(), SIGNAL(jsonReceived(QVariantMap, int)),
|
||||||
|
this, SLOT(slotNotificationsReceived(QVariantMap, int)));
|
||||||
|
_notificationJob->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(ptr));
|
||||||
|
|
||||||
|
_notificationJob->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerNotificationHandler::slotNotificationsReceived(const QVariantMap& json, int statusCode)
|
||||||
|
{
|
||||||
|
if( statusCode != 200 ) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Notifications failed with status code " << statusCode;
|
||||||
|
deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto notifies = json.value("ocs").toMap().value("data").toList();
|
||||||
|
|
||||||
|
AccountState* ai = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
|
||||||
|
|
||||||
|
ActivityList list;
|
||||||
|
|
||||||
|
foreach( auto element, notifies ) {
|
||||||
|
Activity a;
|
||||||
|
auto json = element.toMap();
|
||||||
|
a._type = Activity::NotificationType;
|
||||||
|
a._accName = ai->account()->displayName();
|
||||||
|
a._id = json.value("notification_id").toLongLong();
|
||||||
|
a._subject = json.value("subject").toString();
|
||||||
|
a._message = json.value("message").toString();
|
||||||
|
QString s = json.value("link").toString();
|
||||||
|
if( !s.isEmpty() ) {
|
||||||
|
a._link = QUrl(s);
|
||||||
|
}
|
||||||
|
a._dateTime = json.value("datetime").toDateTime();
|
||||||
|
|
||||||
|
auto actions = json.value("actions").toList();
|
||||||
|
foreach( auto action, actions) {
|
||||||
|
auto actionJson = action.toMap();
|
||||||
|
ActivityLink al;
|
||||||
|
al._label = QUrl::fromPercentEncoding(actionJson.value("label").toByteArray());
|
||||||
|
al._link = actionJson.value("link").toString();
|
||||||
|
al._verb = actionJson.value("type").toByteArray();
|
||||||
|
al._isPrimary = actionJson.value("primary").toBool();
|
||||||
|
|
||||||
|
a._links.append(al);
|
||||||
|
}
|
||||||
|
list.append(a);
|
||||||
|
}
|
||||||
|
emit newNotificationList( list );
|
||||||
|
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
src/gui/servernotificationhandler.h
Normal file
47
src/gui/servernotificationhandler.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||||
|
*
|
||||||
|
* 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; version 2 of the License.
|
||||||
|
*
|
||||||
|
* 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 SERVERNOTIFICATIONHANDLER_H
|
||||||
|
#define SERVERNOTIFICATIONHANDLER_H
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
#include "activitywidget.h"
|
||||||
|
|
||||||
|
namespace OCC
|
||||||
|
{
|
||||||
|
|
||||||
|
class ServerNotificationHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ServerNotificationHandler(QObject *parent = 0);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void newNotificationList(ActivityList);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void slotFetchNotifications(AccountState *ptr);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotNotificationsReceived(const QVariantMap& json, int statusCode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<JsonApiJob> _notificationJob;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SERVERNOTIFICATIONHANDLER_H
|
|
@ -61,6 +61,8 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent) :
|
||||||
QDialog(parent)
|
QDialog(parent)
|
||||||
, _ui(new Ui::SettingsDialog), _gui(gui)
|
, _ui(new Ui::SettingsDialog), _gui(gui)
|
||||||
{
|
{
|
||||||
|
ConfigFile cfg;
|
||||||
|
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
_ui->setupUi(this);
|
_ui->setupUi(this);
|
||||||
_toolBar = new QToolBar;
|
_toolBar = new QToolBar;
|
||||||
|
@ -89,6 +91,7 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent) :
|
||||||
_ui->stack->addWidget(_activitySettings);
|
_ui->stack->addWidget(_activitySettings);
|
||||||
connect( _activitySettings, SIGNAL(guiLog(QString,QString)), _gui,
|
connect( _activitySettings, SIGNAL(guiLog(QString,QString)), _gui,
|
||||||
SLOT(slotShowOptionalTrayMessage(QString,QString)) );
|
SLOT(slotShowOptionalTrayMessage(QString,QString)) );
|
||||||
|
_activitySettings->setNotificationRefreshInterval( cfg.notificationRefreshInterval());
|
||||||
|
|
||||||
QAction *generalAction = createColorAwareAction(QLatin1String(":/client/resources/settings.png"), tr("General"));
|
QAction *generalAction = createColorAwareAction(QLatin1String(":/client/resources/settings.png"), tr("General"));
|
||||||
_actionGroup->addAction(generalAction);
|
_actionGroup->addAction(generalAction);
|
||||||
|
@ -128,7 +131,6 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent) :
|
||||||
|
|
||||||
customizeStyle();
|
customizeStyle();
|
||||||
|
|
||||||
ConfigFile cfg;
|
|
||||||
cfg.restoreGeometry(this);
|
cfg.restoreGeometry(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,11 @@ QNetworkReply *AbstractNetworkJob::headRequest(const QUrl &url)
|
||||||
return addTimer(_account->headRequest(url));
|
return addTimer(_account->headRequest(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QNetworkReply *AbstractNetworkJob::deleteRequest(const QUrl &url)
|
||||||
|
{
|
||||||
|
return addTimer(_account->deleteRequest(url));
|
||||||
|
}
|
||||||
|
|
||||||
void AbstractNetworkJob::slotFinished()
|
void AbstractNetworkJob::slotFinished()
|
||||||
{
|
{
|
||||||
_timer.stop();
|
_timer.stop();
|
||||||
|
|
|
@ -77,6 +77,7 @@ protected:
|
||||||
QNetworkReply* getRequest(const QUrl &url);
|
QNetworkReply* getRequest(const QUrl &url);
|
||||||
QNetworkReply* headRequest(const QString &relPath);
|
QNetworkReply* headRequest(const QString &relPath);
|
||||||
QNetworkReply* headRequest(const QUrl &url);
|
QNetworkReply* headRequest(const QUrl &url);
|
||||||
|
QNetworkReply* deleteRequest(const QUrl &url);
|
||||||
|
|
||||||
int maxRedirects() const { return 10; }
|
int maxRedirects() const { return 10; }
|
||||||
virtual bool finished() = 0;
|
virtual bool finished() = 0;
|
||||||
|
|
|
@ -239,6 +239,15 @@ QNetworkReply *Account::getRequest(const QUrl &url)
|
||||||
return _am->get(request);
|
return _am->get(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QNetworkReply *Account::deleteRequest( const QUrl &url)
|
||||||
|
{
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
|
||||||
|
request.setSslConfiguration(this->getOrCreateSslConfig());
|
||||||
|
#endif
|
||||||
|
return _am->deleteResource(request);
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkReply *Account::davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data)
|
QNetworkReply *Account::davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data)
|
||||||
{
|
{
|
||||||
return davRequest(verb, concatUrlPath(davUrl(), relPath), req, data);
|
return davRequest(verb, concatUrlPath(davUrl(), relPath), req, data);
|
||||||
|
|
|
@ -111,6 +111,7 @@ public:
|
||||||
QNetworkReply* headRequest(const QUrl &url);
|
QNetworkReply* headRequest(const QUrl &url);
|
||||||
QNetworkReply* getRequest(const QString &relPath);
|
QNetworkReply* getRequest(const QString &relPath);
|
||||||
QNetworkReply* getRequest(const QUrl &url);
|
QNetworkReply* getRequest(const QUrl &url);
|
||||||
|
QNetworkReply* deleteRequest( const QUrl &url);
|
||||||
QNetworkReply* davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data = 0);
|
QNetworkReply* davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data = 0);
|
||||||
QNetworkReply* davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data = 0);
|
QNetworkReply* davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data = 0);
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,16 @@ bool Capabilities::shareResharing() const
|
||||||
return _capabilities["files_sharing"].toMap()["resharing"].toBool();
|
return _capabilities["files_sharing"].toMap()["resharing"].toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Capabilities::notificationsAvailable() const
|
||||||
|
{
|
||||||
|
return _capabilities.contains("notifications");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Capabilities::isValid() const
|
||||||
|
{
|
||||||
|
return !_capabilities.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
QList<QByteArray> Capabilities::supportedChecksumTypesAdvertised() const
|
QList<QByteArray> Capabilities::supportedChecksumTypesAdvertised() const
|
||||||
{
|
{
|
||||||
return QList<QByteArray>();
|
return QList<QByteArray>();
|
||||||
|
|
|
@ -40,6 +40,12 @@ public:
|
||||||
int sharePublicLinkExpireDateDays() const;
|
int sharePublicLinkExpireDateDays() const;
|
||||||
bool shareResharing() const;
|
bool shareResharing() const;
|
||||||
|
|
||||||
|
/// returns true if the capabilities report notifications
|
||||||
|
bool notificationsAvailable() const;
|
||||||
|
|
||||||
|
/// returns true if the capabilities are loaded already.
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
/// Returns the checksum types the server explicitly advertises
|
/// Returns the checksum types the server explicitly advertises
|
||||||
QList<QByteArray> supportedChecksumTypesAdvertised() const;
|
QList<QByteArray> supportedChecksumTypesAdvertised() const;
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace OCC {
|
||||||
//static const char caCertsKeyC[] = "CaCertificates"; only used from account.cpp
|
//static const char caCertsKeyC[] = "CaCertificates"; only used from account.cpp
|
||||||
static const char remotePollIntervalC[] = "remotePollInterval";
|
static const char remotePollIntervalC[] = "remotePollInterval";
|
||||||
static const char forceSyncIntervalC[] = "forceSyncInterval";
|
static const char forceSyncIntervalC[] = "forceSyncInterval";
|
||||||
|
static const char notificationRefreshIntervalC[] = "notificationRefreshInterval";
|
||||||
static const char monoIconsC[] = "monoIcons";
|
static const char monoIconsC[] = "monoIcons";
|
||||||
static const char promptDeleteC[] = "promptDeleteAllFiles";
|
static const char promptDeleteC[] = "promptDeleteAllFiles";
|
||||||
static const char crashReporterC[] = "crashReporter";
|
static const char crashReporterC[] = "crashReporter";
|
||||||
|
@ -390,6 +391,22 @@ quint64 ConfigFile::forceSyncInterval(const QString& connection) const
|
||||||
return interval;
|
return interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quint64 ConfigFile::notificationRefreshInterval(const QString& connection) const
|
||||||
|
{
|
||||||
|
QString con( connection );
|
||||||
|
if( connection.isEmpty() ) con = defaultConnection();
|
||||||
|
QSettings settings(configFile(), QSettings::IniFormat);
|
||||||
|
settings.beginGroup( con );
|
||||||
|
|
||||||
|
quint64 defaultInterval = 5 * 60 * 1000ull; // 5 minutes
|
||||||
|
quint64 interval = settings.value( QLatin1String(notificationRefreshIntervalC), defaultInterval ).toULongLong();
|
||||||
|
if( interval < 60*1000ull) {
|
||||||
|
qDebug() << "notification refresh interval smaller than one minute, setting to one minute";
|
||||||
|
interval = 60*1000ull;
|
||||||
|
}
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
int ConfigFile::updateCheckInterval( const QString& connection ) const
|
int ConfigFile::updateCheckInterval( const QString& connection ) const
|
||||||
{
|
{
|
||||||
QString con( connection );
|
QString con( connection );
|
||||||
|
|
|
@ -63,6 +63,9 @@ public:
|
||||||
/* Set poll interval. Value in milliseconds has to be larger than 5000 */
|
/* Set poll interval. Value in milliseconds has to be larger than 5000 */
|
||||||
void setRemotePollInterval(int interval, const QString& connection = QString() );
|
void setRemotePollInterval(int interval, const QString& connection = QString() );
|
||||||
|
|
||||||
|
/* Interval to check for new notifications */
|
||||||
|
quint64 notificationRefreshInterval(const QString& connection = QString()) const;
|
||||||
|
|
||||||
/* Force sync interval, in milliseconds */
|
/* Force sync interval, in milliseconds */
|
||||||
quint64 forceSyncInterval(const QString &connection = QString()) const;
|
quint64 forceSyncInterval(const QString &connection = QString()) const;
|
||||||
|
|
||||||
|
|
|
@ -453,7 +453,7 @@ QByteArray Utility::versionOfInstalledBinary( const QString& command )
|
||||||
|
|
||||||
QString Utility::timeAgoInWords(const QDateTime& dt, const QDateTime& from)
|
QString Utility::timeAgoInWords(const QDateTime& dt, const QDateTime& from)
|
||||||
{
|
{
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTimeUtc();
|
||||||
|
|
||||||
if( from.isValid() ) {
|
if( from.isValid() ) {
|
||||||
now = from;
|
now = from;
|
||||||
|
|
Loading…
Reference in a new issue