Comparar commits

..

11 Commits

Autor SHA1 Mensagem Data
Klaas Freitag e4f4e96567 ActivityProxyModel: Set a proper filter sort role 2016-09-10 17:32:12 +02:00
Klaas Freitag abe136f0b7 Activityview: Add an as-you-type filter entry field. 2016-09-10 17:32:12 +02:00
Klaas Freitag e1804c3393 ActivityModel: Fetch 1000 activities at once.
Pagination does not properly work with current server.
2016-09-10 17:32:12 +02:00
Klaas Freitag 8fcc014a51 Activity: Handle files of an activity in a extra class. Plus list. 2016-09-10 17:32:12 +02:00
Klaas Freitag 60a0671a45 ActivityList: Handle by AccountState pointer not by name. 2016-09-10 17:32:12 +02:00
Klaas Freitag 600cae7423 Activity: Add fetcher class in a separate file. 2016-09-10 17:32:12 +02:00
Klaas Freitag feb8d342f5 Activity: Add a sortproxy class.
Also change the way the class holds the activity data from various
accounts.
2016-09-10 17:32:12 +02:00
Klaas Freitag e900632c5e ActivityData: Add a proper ActivityList class. 2016-09-10 17:32:12 +02:00
Markus Goetz 524220d090 Selective Sync: Fix request loop and show error in view (#5154)
I got into a situation where the model would endlessly request the directory
contents from the server because we did not notice yet that the server
is actually in maintenance mode while we were expanding the tree view when
changing the tab to the account or when just expanding it by clicking.
2016-09-06 11:11:03 +02:00
Christian Kamm af9c4d0e2f Recall: Copy instead of move recalled file #5150
That was an unintentional change in
2662203fb7

Also expand the test case to cover this.
2016-09-06 10:42:59 +02:00
Klaas Freitag c97d8aa8fd Show the rename target in the client log file. (#5149) 2016-09-05 13:26:49 +02:00
21 arquivos alterados com 704 adições e 205 exclusões
+4
Ver Arquivo
@@ -64,6 +64,10 @@ csync();
assert( -e glob(localDir().'dir/file2_.sys.admin#recall#-*.dat' ) );
assert( -e glob(localDir().'dir/file3_.sys.admin#recall#-*.dat' ) );
# verify that the original files still exist
assert( -e glob(localDir().'dir/file2.dat' ) );
assert( -e glob(localDir().'dir/file3.dat' ) );
#Remove the recall file
unlink(localDir() . ".sys.admin#recall#");
+1
Ver Arquivo
@@ -68,6 +68,7 @@ set(client_SRCS
activitylistmodel.cpp
activitywidget.cpp
activityitemdelegate.cpp
activityfetcher.cpp
selectivesyncdialog.cpp
settingsdialog.cpp
share.cpp
+70 -1
Ver Arquivo
@@ -15,10 +15,45 @@
#include "activitydata.h"
namespace OCC
{
ActivityFile::ActivityFile()
:_type(Unknown)
{
}
ActivityFile::ActivityFile( const QString& file )
:_relFileName(file),
_type(File)
{
}
void ActivityFile::setType( FileType type )
{
_type = type;
}
QString ActivityFile::relativePath() const
{
return _relFileName;
}
QString ActivityFile::fullPath( const QString _accountName ) const
{
QString fullPath(_relFileName);
// FIXME: get the account and prepend the base path.
if( _type == Directory && !fullPath.endsWith('/')) {
fullPath.append('/');
}
return fullPath;
}
/* ==================================================================== */
bool operator<( const Activity& rhs, const Activity& lhs ) {
return rhs._dateTime.toMSecsSinceEpoch() > lhs._dateTime.toMSecsSinceEpoch();
}
@@ -31,5 +66,39 @@ Activity::Identifier Activity::ident() const {
return Identifier( _id, _accName );
}
void Activity::addFile( const QString& file )
{
ActivityFile f(file);
_files.append(f);
}
void Activity::addDirectory( const QString& dir )
{
ActivityFile f(dir);
f.setType(ActivityFile::Directory);
_files.append(f);
}
QVector<ActivityFile> Activity::files()
{
return _files;
}
/* ==================================================================== */
ActivityList::ActivityList()
{
}
void ActivityList::setAccountState(AccountState *ast)
{
_ast = ast;
}
AccountState* ActivityList::accountState()
{
return _ast;
}
}
+52 -1
Ver Arquivo
@@ -16,6 +16,8 @@
#include <QtCore>
#include "accountstate.h"
namespace OCC {
/**
* @brief The ActivityLink class describes actions of an activity
@@ -33,6 +35,32 @@ public:
};
/* ==================================================================== */
/**
* @brief ActivityFile Structure
* @ingroup gui
*
* contains information about a file of an activity.
* Can handle the thumbnail and stuff later.
*/
class ActivityFile
{
public:
enum FileType {Unknown, File, Directory};
explicit ActivityFile();
explicit ActivityFile( const QString& file );
void setType( FileType type );
QString relativePath() const;
QString fullPath( const QString _accountName ) const;
private:
QString _relFileName;
FileType _type;
};
/* ==================================================================== */
/**
* @brief Activity Structure
* @ingroup gui
@@ -50,6 +78,11 @@ public:
NotificationType
};
void addFile( const QString& file );
void addDirectory( const QString& dir );
QVector<ActivityFile> files();
Type _type;
qlonglong _id;
QString _subject;
@@ -68,6 +101,10 @@ public:
Identifier ident() const;
private:
QVector<ActivityFile> _files;
};
bool operator==( const Activity& rhs, const Activity& lhs );
@@ -81,8 +118,22 @@ bool operator<( const Activity& rhs, const Activity& lhs );
* A QList based list of Activities
*/
typedef QList<Activity> ActivityList;
/**
* @brief The ActivityList
* @ingroup gui
*
* A QList based list of Activities
*/
class ActivityList:public QList<Activity>
{
public:
ActivityList();
void setAccountState(AccountState *ast);
AccountState* accountState();
private:
AccountState *_ast;
};
}
+245
Ver Arquivo
@@ -0,0 +1,245 @@
/*
* 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 "activityfetcher.h"
#include "activitydata.h"
#include "account.h"
#include "accountstate.h"
#include "json.h"
#include "networkjobs.h"
namespace OCC {
ActivityFetcher::ActivityFetcher(QObject *parent) : QObject(parent)
{
}
void ActivityFetcher::slotFetch(AccountState* s)
{
if( !(s && 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);
qDebug() << "Start fetching activities for " << s->account()->displayName();
job->start();
}
void ActivityFetcher::slotActivitiesReceived(const QVariantMap& json, int statusCode)
{
auto activities = json.value("ocs").toMap().value("data").toList();
qDebug() << "*** activities" << activities;
ActivityList list;
AccountState* ai = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
list.setAccountState( ai );
foreach( auto activ, activities ) {
auto json = activ.toMap();
Activity a;
a._accName = ai->account()->displayName();
a._id = json.value("id").toLongLong();
a._subject = json.value("subject").toString();
a._message = json.value("message").toString();
const QString f = json.value("file").toString();
a.addFile(f);
a._link = json.value("link").toUrl();
a._dateTime = json.value("date").toDateTime();
list.append(a);
}
// activity app is not enabled, signalling.
if( statusCode == 999 ) {
emit accountWithoutActivityApp(ai);
}
emit newActivityList(list);
}
/* ==================================================================== */
ActivityFetcherV2::ActivityFetcherV2()
: ActivityFetcher()
{
}
ActivityList ActivityFetcherV2::fetchFromDb( const QString& accountId )
{
// TODO fetch from database
ActivityList dbActivities;
return dbActivities;
}
int ActivityFetcherV2::lastSeenId()
{
int lastId = 0;
return lastId;
}
void ActivityFetcherV2::slotFetch(AccountState* s)
{
if( !(s && s->isConnected() )) {
return;
}
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v2.php/apps/activity/api/v2/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;
int lastId = lastSeenId();
if( lastId > 0 ) {
params.append(qMakePair(QString::fromLatin1("since"), QString::number(lastId)));
job->addQueryParams(params);
}
qDebug() << "Start fetching V2 activities for " << s->account()->displayName();
job->start();
}
#define QL1(X) QLatin1String(X)
bool ActivityFetcherV2::parseActionString( Activity *activity, const QString& subject, const QVariantList& params)
{
// the action contains a string describing what happened
bool re = true;
if( subject == QL1("shared_user_self") ) {
} else if( subject == QL1("reshared_user_by") ) {
} else if( subject == QL1("shared_group_self") ) {
} else if( subject == QL1("reshared_group_by") ) {
} else if( subject == QL1("reshared_link_by") ) {
} else if( subject == QL1("shared_user_self") ) {
} else if( subject == QL1("created_self") ) {
} else if( subject == QL1("created_by") ) {
} else if( subject == QL1("created_public") ) {
} else if( subject == QL1("changed_self") ) {
} else if( subject == QL1("changed_by") ) {
} else if( subject == QL1("deleted_self") ) {
} else if( subject == QL1("deleted_by") ) {
} else if( subject == QL1("restored_self") ) {
} else if( subject == QL1("restored_by") ) {
} else {
// unknown action.
re = false;
}
// parse the params
foreach( QVariant v, params ) {
QVariantMap vm = v.toMap();
if( vm.contains("type") ) {
const QString type = vm.value("type").toString();
const QString val = vm.value("value").toString();
if( type == QL1("collection") ) {
QVariantList items = vm.value("value").toList();
foreach( QVariant vFile, items ) {
QVariantMap vMap = vFile.toMap();
const QString fileType = vMap.value("type").toString();
const QString relFileName = vMap.value("value").toString();
if( fileType != QL1("file")) {
activity->addDirectory(relFileName);
} else {
activity->addFile(relFileName);
}
}
} else if( type == QL1("file")) {
const QString relFileName = val;
activity->addFile(relFileName);
} else if( type == QL1("dir")) {
const QString relFileName = val;
activity->addDirectory(relFileName);
// needs verification!
} else if( type == QL1("username")) {
const QString user = val;
} else if( type == QL1("typeicon")) {
const QString icon = val;
} else {
}
}
}
return re;
}
void ActivityFetcherV2::slotActivitiesReceived(const QVariantMap& json, int statusCode)
{
auto activities = json.value("ocs").toMap().value("data").toList();
qDebug() << "*** activities" << activities;
AccountState* ai = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
ActivityList list;
if( ai ) {
list = fetchFromDb(ai->account()->id());
list.setAccountState( ai );
foreach( auto activ, activities ) {
auto json = activ.toMap();
Activity a;
a._accName = ai->account()->displayName();
a._id = json.value("activity_id").toLongLong();
QString subject = json.value("subject").toString();
QVariantList subjectParams = json.value("subjectparams").toList();
bool knownAction = parseActionString( &a, subject, subjectParams );
a._subject = json.value("subject").toString();
a._message = json.value("message_prepared").toString();
// a._file = json.value("file").toString();
// a._link = json.value("link").toUrl();
a._dateTime = json.value("datetime").toDateTime();
list.append(a);
}
// activity app is not enabled, signalling.
if( statusCode == 999 ) {
emit accountWithoutActivityApp(ai);
}
}
emit newActivityList(list);
}
}
+79
Ver Arquivo
@@ -0,0 +1,79 @@
/*
* 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 ACTIVITYFETCHER_H
#define ACTIVITYFETCHER_H
#include <QtCore>
#include "activitydata.h"
#include "accountstate.h"
/**
* @brief The ActivityFetcher class
*
* Used to fetch the list of server acitivities from the server. Accesses
* the old ocs based API.
*/
namespace OCC {
class ActivityFetcher : public QObject
{
Q_OBJECT
public:
explicit ActivityFetcher(QObject *parent = 0);
public slots:
virtual void slotFetch(AccountState* s);
private slots:
virtual void slotActivitiesReceived(const QVariantMap& json, int statusCode);
signals:
void newActivityList( ActivityList list );
void accountWithoutActivityApp(AccountState*);
};
/* ==================================================================== */
/**
* @brief The ActivityFetcherV2 class
*
* To be used with the next version of the activity API. By now, it is
* completely unused.
*/
class ActivityFetcherV2 : public ActivityFetcher
{
Q_OBJECT
public:
explicit ActivityFetcherV2();
public slots:
virtual void slotFetch(AccountState* s);
private slots:
virtual void slotActivitiesReceived(const QVariantMap& json, int statusCode);
private:
bool parseActionString( Activity *activity, const QString& subject, const QVariantList& params);
ActivityList fetchFromDb(const QString &accountId );
int lastSeenId();
};
}
#endif // ACTIVITYFETCHER_H
+132 -75
Ver Arquivo
@@ -26,10 +26,35 @@
#include "activitydata.h"
#include "activitylistmodel.h"
#define FETCH_ACTIVITIES_AMOUNT 1000
namespace OCC {
/* ==================================================================== */
ActivitySortProxyModel::ActivitySortProxyModel(QObject *parent)
:QSortFilterProxyModel(parent)
{
setFilterRole(ActivityItemDelegate::ActionTextRole);
}
bool ActivitySortProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
QVariant leftData = sourceModel()->data(left);
QVariant rightData = sourceModel()->data(right);
if (leftData.type() == QVariant::DateTime) {
return leftData.toDateTime() < rightData.toDateTime();
} else {
qDebug() << "OOOOO " << endl;
}
return true;
}
/* ==================================================================== */
ActivityListModel::ActivityListModel(QWidget *parent)
:QAbstractListModel(parent)
:QAbstractListModel(parent),
_fetchEntriesAmount(FETCH_ACTIVITIES_AMOUNT)
{
}
@@ -40,7 +65,8 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
if (!index.isValid())
return QVariant();
a = _finalList.at(index.row());
a = findItem(index.row());
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
QStringList list;
@@ -89,47 +115,48 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
int ActivityListModel::rowCount(const QModelIndex&) const
{
return _finalList.count();
}
int cnt = 0;
// 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;
}
}
foreach( ActivityList al, _activityLists) {
cnt += al.count();
}
return false;
return cnt;
}
void ActivityListModel::startFetchJob(AccountState* s)
void ActivityListModel::startFetchJob(AccountState* ast)
{
if( !s->isConnected() ) {
if( !ast->isConnected() || _currentlyFetching.contains(ast)) {
return;
}
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
int activityListIndx = activityListIndxForAccountState(ast);
ActivityList activityList = _activityLists.at(activityListIndx);
// remove entries that might exist in this list.
int startItem = 0;
for( int i = 0; i < activityListIndx; i++ ) {
ActivityList al = _activityLists.at(i);
startItem += al.count();
}
beginRemoveRows(QModelIndex(), startItem, activityList.count() );
activityList.clear();
endRemoveRows();
_activityLists[activityListIndx] = activityList;
// start a new fetch job.
JsonApiJob *job = new JsonApiJob(ast->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));
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(ast));
QList< QPair<QString,QString> > params;
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0")));
params.append(qMakePair(QString::fromLatin1("pagesize"), QString::fromLatin1("100")));
params.append(qMakePair(QString::fromLatin1("start"), QLatin1String("0")));
params.append(qMakePair(QString::fromLatin1("count"), QString::number(_fetchEntriesAmount)));
job->addQueryParams(params);
_currentlyFetching.insert(s);
qDebug() << Q_FUNC_INFO << "Start fetching activities for " << s->account()->displayName();
_currentlyFetching.insert(ast);
qDebug() << Q_FUNC_INFO << "Start fetching activities for " << ast->account()->displayName();
job->start();
}
@@ -137,12 +164,14 @@ void ActivityListModel::slotActivitiesReceived(const QVariantMap& json, int stat
{
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();
// Read the new entries into a temporary list
ActivityList list;
foreach( auto activity, activities ) {
auto json = activity.toMap();
Activity a;
a._type = Activity::ActivityType;
@@ -156,73 +185,101 @@ void ActivityListModel::slotActivitiesReceived(const QVariantMap& json, int stat
list.append(a);
}
_activityLists[ast] = list;
emit activityJobStatusCode(ast, statusCode);
combineActivityLists();
addNewActivities(ast, list);
}
void ActivityListModel::combineActivityLists()
void ActivityListModel::addNewActivities(AccountState* ast, const ActivityList& newItemsList)
{
ActivityList resultList;
int startItem = 0; // the start number of items to delete in the virtual overall list
int activityListIndx = activityListIndxForAccountState(ast);
Q_ASSERT(activityListIndx != -1);
foreach( ActivityList list, _activityLists.values() ) {
resultList.append(list);
ActivityList accountList = _activityLists.at(activityListIndx);
for( int i = 0; i < activityListIndx; i++ ) {
ActivityList li = _activityLists.at(i);
startItem += li.count();
}
std::sort( resultList.begin(), resultList.end() );
beginResetModel();
_finalList.clear();
endResetModel();
beginInsertRows(QModelIndex(), 0, resultList.count());
_finalList = resultList;
// insert the new list
beginInsertRows(QModelIndex(), startItem, newItemsList.count() );
accountList.append(newItemsList);
endInsertRows();
_activityLists[activityListIndx] = accountList;
}
void ActivityListModel::fetchMore(const QModelIndex &)
int ActivityListModel::activityListIndxForAccountState(AccountState *ast)
{
QList<AccountStatePtr> accounts = AccountManager::instance()->accounts();
int i;
foreach (const AccountStatePtr& asp, accounts) {
if( !_activityLists.contains(asp.data()) && asp->isConnected() ) {
_activityLists[asp.data()] = ActivityList();
startFetchJob(asp.data());
}
for( i = 0; i < _activityLists.count(); i++ ) {
ActivityList li = _activityLists.at(i);
if( li.accountState() == ast )
return i;
}
// if the AccountState was not found yet, add it to the list
if( i == _activityLists.count() ) {
ActivityList li;
li.setAccountState(ast);
_activityLists.append(li);
}
return i;
}
void ActivityListModel::slotRefreshActivity(AccountState *ast)
{
if(ast && _activityLists.contains(ast)) {
_activityLists.remove(ast);
if(ast ) {
qDebug() << "**** Refreshing Activity list for" << ast->account()->displayName();
startFetchJob(ast);
}
startFetchJob(ast);
}
void ActivityListModel::slotRemoveAccount(AccountState *ast )
{
if( _activityLists.contains(ast) ) {
int i = 0;
const QString accountToRemove = ast->account()->displayName();
int removeIndx = activityListIndxForAccountState(ast);
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);
int startRow = 0;
for( int i = 0; i < removeIndx; i++) {
ActivityList al = _activityLists.at(i);
startRow += al.count();
}
beginRemoveRows(QModelIndex(), startRow, startRow+_activityLists.at(removeIndx).count());
_activityLists.removeAt(removeIndx);
endRemoveRows();
_currentlyFetching.remove(ast);
}
// combine all activities into one big result list
ActivityList ActivityListModel::activityList()
{
ActivityList all;
int i;
for( i = 0; i < _activityLists.count(); i++) {
ActivityList al = _activityLists.at(i);
all.append(al);
}
return all;
}
Activity ActivityListModel::findItem(int indx) const
{
Activity a;
foreach( ActivityList al, _activityLists ) {
if( indx < al.count() ) {
a = al.at(indx);
break;
}
indx -= al.count();
}
return a;
}
}
+19 -8
Ver Arquivo
@@ -17,10 +17,21 @@
#include <QtCore>
#include "activitydata.h"
#include "accountstate.h"
namespace OCC {
class AccountState;
class ActivitySortProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
ActivitySortProxyModel(QObject *parent = 0);
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const Q_DECL_OVERRIDE;
};
/**
* @brief The ActivityListModel
@@ -38,10 +49,8 @@ public:
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; }
ActivityList activityList();
Activity findItem(int indx) const;
public slots:
void slotRefreshActivity(AccountState* ast);
@@ -54,14 +63,16 @@ signals:
void activityJobStatusCode(AccountState* ast, int statusCode);
private:
void startFetchJob(AccountState* s);
void addNewActivities(AccountState* ast, const ActivityList& newItemsList);
void startFetchJob(AccountState *ast);
void combineActivityLists();
int activityListIndxForAccountState(AccountState *ast );
QMap<AccountState*, ActivityList> _activityLists;
QList<ActivityList> _activityLists;
ActivityList _finalList;
QSet<AccountState*> _currentlyFetching;
int _fetchEntriesAmount;
};
}
#endif // ACTIVITYLISTMODEL_H
+22 -30
Ver Arquivo
@@ -63,15 +63,19 @@ ActivityWidget::ActivityWidget(QWidget *parent) :
_ui->_activityList->setMinimumWidth(400);
#endif
_model = new ActivityListModel(this);
_model = new ActivitySortProxyModel(this);
ActivityListModel *rawModel = new ActivityListModel;
_model->setSourceModel(rawModel);
ActivityItemDelegate *delegate = new ActivityItemDelegate;
delegate->setParent(this);
_ui->_activityList->setItemDelegate(delegate);
_ui->_activityList->setAlternatingRowColors(true);
_ui->_activityList->setModel(_model);
_ui->_notifyLabel->hide();
_ui->_notifyScroll->hide();
_ui->_filterEdit->setClearButtonEnabled(true);
connect(_ui->_filterEdit, SIGNAL(textChanged(QString)),
SLOT(slotFilterTextChanged(QString)));
// Create a widget container for the notifications. The ui file defines
// a scroll area that get a widget with a layout as children
@@ -79,12 +83,10 @@ ActivityWidget::ActivityWidget(QWidget *parent) :
_notificationsLayout = new QVBoxLayout;
w->setLayout(_notificationsLayout);
_notificationsLayout->setAlignment(Qt::AlignTop);
_ui->_notifyScroll->setAlignment(Qt::AlignTop);
_ui->_notifyScroll->setWidget(w);
showLabels();
connect(_model, SIGNAL(activityJobStatusCode(AccountState*,int)),
connect(rawModel, SIGNAL(activityJobStatusCode(AccountState*,int)),
this, SLOT(slotAccountActivityStatus(AccountState*,int)));
_copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
@@ -105,11 +107,17 @@ ActivityWidget::~ActivityWidget()
delete _ui;
}
void ActivityWidget::slotFilterTextChanged(const QString& exp)
{
_model->setFilterRegExp(QRegExp(exp, Qt::CaseInsensitive, QRegExp::RegExp));
}
void ActivityWidget::slotRefreshActivities(AccountState *ptr)
{
_model->slotRefreshActivity(ptr);
qobject_cast<ActivityListModel*>(_model->sourceModel())->slotRefreshActivity(ptr);
}
void ActivityWidget::slotRefreshNotifications(AccountState *ptr)
{
// start a server notification handler if no notification requests
@@ -119,7 +127,7 @@ void ActivityWidget::slotRefreshNotifications(AccountState *ptr)
connect(snh, SIGNAL(newNotificationList(ActivityList)), this,
SLOT(slotBuildNotificationDisplay(ActivityList)));
snh->slotFetchNotifications(ptr);
snh->fetchNotifications(ptr);
} else {
qDebug() << Q_FUNC_INFO << "========> notification request counter not zero.";
}
@@ -127,24 +135,20 @@ void ActivityWidget::slotRefreshNotifications(AccountState *ptr)
void ActivityWidget::slotRemoveAccount( AccountState *ptr )
{
_model->slotRemoveAccount(ptr);
qobject_cast<ActivityListModel*>(_model->sourceModel())->slotRemoveAccount(ptr);
}
void ActivityWidget::showLabels()
{
QString t = tr("Server Activities");
_ui->_headerLabel->setTextFormat(Qt::RichText);
_ui->_headerLabel->setText(t);
QString t;
_ui->_notifyLabel->setText(tr("Action Required: Notifications"));
t.clear();
QSetIterator<QString> i(_accountsWithoutActivities);
while (i.hasNext() ) {
t.append( tr("<br/>Account %1 does not have activities enabled.").arg(i.next()));
}
_ui->_bottomLabel->setTextFormat(Qt::RichText);
_ui->_bottomLabel->setText(t);
_ui->_bottomLabel->setVisible(!t.isEmpty());
}
void ActivityWidget::slotAccountActivityStatus(AccountState *ast, int statusCode)
@@ -174,7 +178,7 @@ QString ActivityWidget::timeString(QDateTime dt, QLocale::FormatType format) con
void ActivityWidget::storeActivityList( QTextStream& ts )
{
ActivityList activities = _model->activityList();
ActivityList activities = qobject_cast<ActivityListModel*>(_model->sourceModel())->activityList();
foreach( Activity activity, activities ) {
ts << right
@@ -218,11 +222,9 @@ void ActivityWidget::checkActivityTabVisibility()
_accountsWithoutActivities.count() != accountCount;
bool hasNotifications = !_widgetForNotifId.isEmpty();
_ui->_headerLabel->setVisible( hasAccountsWithActivity );
_ui->_filterLabel->setVisible( hasAccountsWithActivity );
_ui->_activityList->setVisible( hasAccountsWithActivity );
_ui->_notifyLabel->setVisible( hasNotifications );
_ui->_notifyScroll->setVisible( hasNotifications );
_ui->_filterEdit->setVisible(hasAccountsWithActivity);
emit hideActivityTab(!hasAccountsWithActivity && !hasNotifications);
}
@@ -269,10 +271,6 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList& list)
this, SLOT(slotRequestCleanupAndBlacklist(Activity)));
_notificationsLayout->addWidget(widget);
// _ui->_notifyScroll->setMinimumHeight( widget->height());
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
_ui->_notifyScroll->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
#endif
_widgetForNotifId[activity.ident()] = widget;
newNotificationShown = true;
}
@@ -500,12 +498,6 @@ void ActivityWidget::slotCheckToCleanWidgets()
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);
}
}
+3 -1
Ver Arquivo
@@ -42,6 +42,7 @@ namespace Ui {
class ActivityWidget;
}
class Application;
class ActivitySortProxyModel;
/**
* @brief The ActivityWidget class
@@ -84,6 +85,7 @@ signals:
void newNotification();
private slots:
void slotFilterTextChanged(const QString& exp);
void slotBuildNotificationDisplay(const ActivityList& list);
void slotSendNotificationRequest(const QString &accountName, const QString& link, const QByteArray &verb);
void slotNotifyNetworkError( QNetworkReply* );
@@ -111,7 +113,7 @@ private:
// no query for notifications is started.
int _notificationRequestsRunning;
ActivityListModel *_model;
ActivitySortProxyModel *_model;
QVBoxLayout *_notificationsLayout;
};
+39 -67
Ver Arquivo
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>693</width>
<height>556</height>
<width>690</width>
<height>513</height>
</rect>
</property>
<property name="windowTitle">
@@ -15,81 +15,53 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="_notifyLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="_filterLabel">
<property name="text">
<string>&amp;Filter</string>
</property>
<property name="buddy">
<cstring>_filterEdit</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="_filterEdit">
<property name="maximumSize">
<size>
<width>180</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<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>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</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>
<widget class="QListView" name="_activityList"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="_headerLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class="QLabel" name="_bottomLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<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="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QDialogButtonBox" name="_dialogButtonBox"/>
</item>
</layout>
+5
Ver Arquivo
@@ -621,6 +621,11 @@ void Folder::removeFromSettings() const
settings->remove(FolderMan::escapeAlias(_definition.alias));
}
bool Folder::isFileExcludedAbsolute(const QString& fullPath) const
{
return _engine->excludedFiles().isExcluded(fullPath, path(), _definition.ignoreHiddenFiles);
}
bool Folder::isFileExcludedRelative(const QString& relativePath) const
{
return _engine->excludedFiles().isExcluded(path() + relativePath, path(), _definition.ignoreHiddenFiles);
+5
Ver Arquivo
@@ -184,6 +184,11 @@ public:
/// Removes the folder from the account's settings.
void removeFromSettings() const;
/**
* Returns whether a file inside this folder should be excluded.
*/
bool isFileExcludedAbsolute(const QString& fullPath) const;
/**
* Returns whether a file inside this folder should be excluded.
*/
+10 -1
Ver Arquivo
@@ -164,7 +164,8 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
switch(role) {
case Qt::DisplayRole:
if (x->_hasError) {
return tr("Error while loading the list of folders from the server.");
return QVariant(tr("Error while loading the list of folders from the server.")
+ QString("\n") + x->_lastErrorString);
} else {
return tr("Fetching folder list from server...");
}
@@ -498,6 +499,10 @@ bool FolderStatusModel::canFetchMore(const QModelIndex& parent) const
auto info = infoForIndex(parent);
if (!info || info->_fetched || info->_fetching)
return false;
if (info->_hasError) {
// Keep showing the error to the user, it will be hidden when the account reconnects
return false;
}
return true;
}
@@ -548,6 +553,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
if (parentInfo->hasLabel()) {
beginRemoveRows(idx, 0 ,0);
parentInfo->_lastErrorString.clear();
parentInfo->_hasError = false;
parentInfo->_fetchingLabel = false;
endRemoveRows();
@@ -675,6 +681,9 @@ void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply* r)
}
auto parentInfo = infoForIndex(idx);
if (parentInfo) {
qDebug() << r->errorString();
parentInfo->_lastErrorString = r->errorString();
if (r->error() == QNetworkReply::ContentNotFoundError) {
parentInfo->_fetched = true;
} else {
+1
Ver Arquivo
@@ -63,6 +63,7 @@ public:
bool _fetched; // If we did the LSCOL for this folder already
bool _fetching; // Whether a LSCOL job is currently running
bool _hasError; // If the last fetching job ended in an error
QString _lastErrorString;
bool _fetchingLabel; // Whether a 'fetching in progress' label is shown.
bool _isUndecided; // undecided folders are the big folders that the user has not accepted yet
+4 -13
Ver Arquivo
@@ -41,9 +41,7 @@ FolderWatcher::FolderWatcher(const QString &root, Folder* folder)
: QObject(folder),
_folder(folder)
{
_canonicalFolderPath = QFileInfo(root).canonicalFilePath();
_d.reset(new FolderWatcherPrivate(this, _canonicalFolderPath));
_d.reset(new FolderWatcherPrivate(this, root));
_timer.start();
}
@@ -57,17 +55,10 @@ bool FolderWatcher::pathIsIgnored( const QString& path )
if( !_folder ) return false;
#ifndef OWNCLOUD_TEST
QString relPath = path;
if (relPath.startsWith(_canonicalFolderPath)) {
relPath = relPath.remove(0, _canonicalFolderPath.length()+1);
if (_folder->isFileExcludedRelative(relPath)) {
qDebug() << "* Ignoring file" << relPath << "in" << _canonicalFolderPath;
return true;
}
if (_folder->isFileExcludedAbsolute(path)) {
qDebug() << "* Ignoring file" << path;
return true;
}
// there could be an odd watch event not being inside the _canonicalFolderPath
// We will just not ignore it then, who knows.
#endif
return false;
}
-1
Ver Arquivo
@@ -89,7 +89,6 @@ private:
QTime _timer;
QSet<QString> _lastPaths;
Folder* _folder;
QString _canonicalFolderPath;
friend class FolderWatcherPrivate;
};
+4 -3
Ver Arquivo
@@ -26,14 +26,14 @@ ServerNotificationHandler::ServerNotificationHandler(QObject *parent)
}
void ServerNotificationHandler::slotFetchNotifications(AccountState *ptr)
bool ServerNotificationHandler::fetchNotifications(AccountState *ptr)
{
// check connectivity and credentials
if( !( ptr && ptr->isConnected() && ptr->account() &&
ptr->account()->credentials() &&
ptr->account()->credentials()->ready() ) ) {
deleteLater();
return;
return false;
}
// check if the account has notifications enabled. If the capabilities are
// not yet valid, its assumed that notifications are available.
@@ -41,7 +41,7 @@ void ServerNotificationHandler::slotFetchNotifications(AccountState *ptr)
if( ! ptr->account()->capabilities().notificationsAvailable() ) {
qDebug() << Q_FUNC_INFO << "Account" << ptr->account()->displayName() << "does not have notifications enabled.";
deleteLater();
return;
return false;
}
}
@@ -52,6 +52,7 @@ void ServerNotificationHandler::slotFetchNotifications(AccountState *ptr)
_notificationJob->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(ptr));
_notificationJob->start();
return true;
}
void ServerNotificationHandler::slotNotificationsReceived(const QVariantMap& json, int statusCode)
+1 -1
Ver Arquivo
@@ -26,12 +26,12 @@ class ServerNotificationHandler : public QObject
Q_OBJECT
public:
explicit ServerNotificationHandler(QObject *parent = 0);
bool fetchNotifications(AccountState *ptr);
signals:
void newNotificationList(ActivityList);
public slots:
void slotFetchNotifications(AccountState *ptr);
private slots:
void slotNotificationsReceived(const QVariantMap& json, int statusCode);
+5 -1
Ver Arquivo
@@ -145,7 +145,11 @@ void SyncRunFileLog::logItem( const SyncFileItem& item )
const QChar L = QLatin1Char('|');
_out << ts << L;
_out << QString::number(item._requestDuration) << L;
_out << item._file << L;
if( item.log._instruction != CSYNC_INSTRUCTION_RENAME ) {
_out << item._file << L;
} else {
_out << item._file << QLatin1String(" -> ") << item._renameTarget << L;
}
_out << instructionToStr( item.log._instruction ) << L;
_out << directionToStr( item._direction ) << L;
_out << QString::number(item.log._modtime) << L;
+3 -2
Ver Arquivo
@@ -637,8 +637,9 @@ static void handleRecallFile(const QString &fn)
QString rpath = makeRecallFileName(fpath);
qDebug() << "Copy recall file: " << fpath << " -> " << rpath;
QString error;
FileSystem::uncheckedRenameReplace(fpath, rpath, &error);
// Remove the target first, QFile::copy will not overwrite it.
FileSystem::remove(rpath);
QFile::copy(fpath, rpath);
}
}