1 | /* |
2 | * Copyright (C) by Klaas Freitag <freitag@owncloud.com> |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation; either version 2 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, but |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
11 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
12 | * for more details. |
13 | */ |
14 | |
15 | #include <QtCore> |
16 | #include <QAbstractListModel> |
17 | #include <QWidget> |
18 | #include <QIcon> |
19 | #include <QJsonObject> |
20 | #include <QJsonDocument> |
21 | |
22 | #include "account.h" |
23 | #include "accountstate.h" |
24 | #include "accountmanager.h" |
25 | #include "folderman.h" |
26 | #include "accessmanager.h" |
27 | #include "activityitemdelegate.h" |
28 | |
29 | #include "activitydata.h" |
30 | #include "activitylistmodel.h" |
31 | |
32 | namespace OCC { |
33 | |
34 | Q_LOGGING_CATEGORY(lcActivity, "gui.activity" , QtInfoMsg) |
35 | |
36 | ActivityListModel::ActivityListModel(QWidget *parent) |
37 | : QAbstractListModel(parent) |
38 | { |
39 | } |
40 | |
41 | QVariant ActivityListModel::data(const QModelIndex &index, int role) const |
42 | { |
43 | Activity a; |
44 | |
45 | if (!index.isValid()) |
46 | return QVariant(); |
47 | |
48 | a = _finalList.at(index.row()); |
49 | AccountStatePtr ast = AccountManager::instance()->account(a._accName); |
50 | if (!ast) |
51 | return QVariant(); |
52 | QStringList list; |
53 | |
54 | switch (role) { |
55 | case ActivityItemDelegate::PathRole: |
56 | list = FolderMan::instance()->findFileInLocalFolders(a._file, ast->account()); |
57 | if (list.count() > 0) { |
58 | return QVariant(list.at(0)); |
59 | } |
60 | // File does not exist anymore? Let's try to open its path |
61 | list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(a._file).path(), ast->account()); |
62 | if (list.count() > 0) { |
63 | return QVariant(list.at(0)); |
64 | } |
65 | return QVariant(); |
66 | break; |
67 | case ActivityItemDelegate::ActionIconRole: |
68 | return QVariant(); // FIXME once the action can be quantified, display on Icon |
69 | break; |
70 | case ActivityItemDelegate::UserIconRole: |
71 | return QIcon(QLatin1String(":/client/resources/account.png" )); |
72 | break; |
73 | case Qt::ToolTipRole: |
74 | case ActivityItemDelegate::ActionTextRole: |
75 | return a._subject; |
76 | break; |
77 | case ActivityItemDelegate::LinkRole: |
78 | return a._link; |
79 | break; |
80 | case ActivityItemDelegate::AccountRole: |
81 | return a._accName; |
82 | break; |
83 | case ActivityItemDelegate::PointInTimeRole: |
84 | return Utility::timeAgoInWords(a._dateTime); |
85 | break; |
86 | case ActivityItemDelegate::AccountConnectedRole: |
87 | return (ast && ast->isConnected()); |
88 | break; |
89 | default: |
90 | return QVariant(); |
91 | } |
92 | return QVariant(); |
93 | } |
94 | |
95 | int ActivityListModel::rowCount(const QModelIndex &) const |
96 | { |
97 | return _finalList.count(); |
98 | } |
99 | |
100 | // current strategy: Fetch 100 items per Account |
101 | // ATTENTION: This method is const and thus it is not possible to modify |
102 | // the _activityLists hash or so. Doesn't make it easier... |
103 | bool ActivityListModel::canFetchMore(const QModelIndex &) const |
104 | { |
105 | if (_activityLists.count() == 0) |
106 | return true; |
107 | |
108 | for (auto i = _activityLists.begin(); i != _activityLists.end(); ++i) { |
109 | AccountState *ast = i.key(); |
110 | if (ast && ast->isConnected()) { |
111 | ActivityList activities = i.value(); |
112 | if (activities.count() == 0 && !_currentlyFetching.contains(ast)) { |
113 | return true; |
114 | } |
115 | } |
116 | } |
117 | |
118 | return false; |
119 | } |
120 | |
121 | void ActivityListModel::startFetchJob(AccountState *s) |
122 | { |
123 | if (!s->isConnected()) { |
124 | return; |
125 | } |
126 | JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity" ), this); |
127 | QObject::connect(job, &JsonApiJob::jsonReceived, |
128 | this, &ActivityListModel::slotActivitiesReceived); |
129 | job->setProperty("AccountStatePtr" , QVariant::fromValue<QPointer<AccountState>>(s)); |
130 | |
131 | QUrlQuery params; |
132 | params.addQueryItem(QLatin1String("page" ), QLatin1String("0" )); |
133 | params.addQueryItem(QLatin1String("pagesize" ), QLatin1String("100" )); |
134 | job->addQueryParams(params); |
135 | |
136 | _currentlyFetching.insert(s); |
137 | qCInfo(lcActivity) << "Start fetching activities for " << s->account()->displayName(); |
138 | job->start(); |
139 | } |
140 | |
141 | void ActivityListModel::slotActivitiesReceived(const QJsonDocument &json, int statusCode) |
142 | { |
143 | auto activities = json.object().value("ocs" ).toObject().value("data" ).toArray(); |
144 | |
145 | ActivityList list; |
146 | auto ast = qvariant_cast<QPointer<AccountState>>(sender()->property("AccountStatePtr" )); |
147 | if (!ast) |
148 | return; |
149 | |
150 | _currentlyFetching.remove(ast); |
151 | |
152 | foreach (auto activ, activities) { |
153 | auto json = activ.toObject(); |
154 | |
155 | Activity a; |
156 | a._type = Activity::ActivityType; |
157 | a._accName = ast->account()->displayName(); |
158 | a._id = json.value("id" ).toInt(); |
159 | a._subject = json.value("subject" ).toString(); |
160 | a._message = json.value("message" ).toString(); |
161 | a._file = json.value("file" ).toString(); |
162 | a._link = QUrl(json.value("link" ).toString()); |
163 | a._dateTime = QDateTime::fromString(json.value("date" ).toString(), Qt::ISODate); |
164 | list.append(a); |
165 | } |
166 | |
167 | _activityLists[ast] = list; |
168 | |
169 | emit activityJobStatusCode(ast, statusCode); |
170 | |
171 | combineActivityLists(); |
172 | } |
173 | |
174 | |
175 | void ActivityListModel::combineActivityLists() |
176 | { |
177 | ActivityList resultList; |
178 | |
179 | foreach (ActivityList list, _activityLists.values()) { |
180 | resultList.append(list); |
181 | } |
182 | |
183 | std::sort(resultList.begin(), resultList.end()); |
184 | |
185 | beginResetModel(); |
186 | _finalList.clear(); |
187 | endResetModel(); |
188 | |
189 | beginInsertRows(QModelIndex(), 0, resultList.count()); |
190 | _finalList = resultList; |
191 | endInsertRows(); |
192 | } |
193 | |
194 | void ActivityListModel::fetchMore(const QModelIndex &) |
195 | { |
196 | QList<AccountStatePtr> accounts = AccountManager::instance()->accounts(); |
197 | |
198 | foreach (const AccountStatePtr &asp, accounts) { |
199 | if (!_activityLists.contains(asp.data()) && asp->isConnected()) { |
200 | _activityLists[asp.data()] = ActivityList(); |
201 | startFetchJob(asp.data()); |
202 | } |
203 | } |
204 | } |
205 | |
206 | void ActivityListModel::slotRefreshActivity(AccountState *ast) |
207 | { |
208 | if (ast && _activityLists.contains(ast)) { |
209 | _activityLists.remove(ast); |
210 | } |
211 | startFetchJob(ast); |
212 | } |
213 | |
214 | void ActivityListModel::slotRemoveAccount(AccountState *ast) |
215 | { |
216 | if (_activityLists.contains(ast)) { |
217 | int i = 0; |
218 | const QString accountToRemove = ast->account()->displayName(); |
219 | |
220 | QMutableListIterator<Activity> it(_finalList); |
221 | |
222 | while (it.hasNext()) { |
223 | Activity activity = it.next(); |
224 | if (activity._accName == accountToRemove) { |
225 | beginRemoveRows(QModelIndex(), i, i + 1); |
226 | it.remove(); |
227 | endRemoveRows(); |
228 | } |
229 | } |
230 | _activityLists.remove(ast); |
231 | _currentlyFetching.remove(ast); |
232 | } |
233 | } |
234 | } |
235 | |