Changeset View
Changeset View
Standalone View
Standalone View
src/bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp
1 | /******************************************************************* | 1 | /******************************************************************* | ||
---|---|---|---|---|---|
2 | * reportassistantpages_bugzilla_duplicates.cpp | 2 | * reportassistantpages_bugzilla_duplicates.cpp | ||
3 | * Copyright 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com> | 3 | * Copyright 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com> | ||
4 | * Copyright 2019 Harald Sitter <sitter@kde.org> | ||||
4 | * | 5 | * | ||
5 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | 7 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation; either version 2 of | 8 | * published by the Free Software Foundation; either version 2 of | ||
8 | * the License, or (at your option) any later version. | 9 | * the License, or (at your option) any later version. | ||
9 | * | 10 | * | ||
10 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. | ||
14 | * | 15 | * | ||
15 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | * | 18 | * | ||
18 | ******************************************************************/ | 19 | ******************************************************************/ | ||
19 | 20 | | |||
20 | #include "reportassistantpages_bugzilla_duplicates.h" | 21 | #include "reportassistantpages_bugzilla_duplicates.h" | ||
21 | 22 | | |||
22 | #include <QDate> | 23 | #include <QDebug> | ||
23 | #include <QTimer> | 24 | #include <QTimer> | ||
24 | #include <QTreeWidgetItem> | 25 | #include <QTreeWidgetItem> | ||
25 | #include <QHeaderView> | 26 | #include <QHeaderView> | ||
26 | 27 | | |||
27 | #include <KColorScheme> | 28 | #include <KColorScheme> | ||
28 | #include <KMessageBox> | 29 | #include <KMessageBox> | ||
29 | #include <QInputDialog> | 30 | #include <QInputDialog> | ||
30 | #include <KLocalizedString> | 31 | #include <KLocalizedString> | ||
31 | #include <KWindowConfig> | 32 | #include <KWindowConfig> | ||
32 | 33 | | |||
33 | #include "drkonqi_globals.h" | 34 | #include "drkonqi_globals.h" | ||
34 | #include "reportinterface.h" | 35 | #include "reportinterface.h" | ||
35 | #include "statuswidget.h" | 36 | #include "statuswidget.h" | ||
36 | 37 | | |||
37 | //BEGIN BugzillaDuplicatesPage | 38 | //BEGIN BugzillaDuplicatesPage | ||
38 | 39 | | |||
39 | BugzillaDuplicatesPage::BugzillaDuplicatesPage(ReportAssistantDialog * parent): | 40 | BugzillaDuplicatesPage::BugzillaDuplicatesPage(ReportAssistantDialog *parent) | ||
40 | ReportAssistantPage(parent), | 41 | : ReportAssistantPage(parent) | ||
41 | m_searching(false), | | |||
42 | m_foundDuplicate(false) | | |||
43 | { | 42 | { | ||
44 | resetDates(); | | |||
45 | | ||||
46 | connect(bugzillaManager(), &BugzillaManager::searchFinished, | 43 | connect(bugzillaManager(), &BugzillaManager::searchFinished, | ||
47 | this, &BugzillaDuplicatesPage::searchFinished); | 44 | this, &BugzillaDuplicatesPage::searchFinished); | ||
48 | connect(bugzillaManager(), SIGNAL(searchError(QString)), | 45 | connect(bugzillaManager(), &BugzillaManager::searchError, | ||
49 | this, SLOT(searchError(QString))); | 46 | this, &BugzillaDuplicatesPage::searchError); | ||
50 | 47 | | |||
51 | ui.setupUi(this); | 48 | ui.setupUi(this); | ||
52 | ui.information->hide(); | 49 | ui.information->hide(); | ||
53 | 50 | | |||
54 | connect(ui.m_bugListWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), | 51 | connect(ui.m_bugListWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), | ||
55 | this, SLOT(itemClicked(QTreeWidgetItem*,int))); | 52 | this, SLOT(itemClicked(QTreeWidgetItem*,int))); | ||
56 | connect(ui.m_bugListWidget, &QTreeWidget::itemSelectionChanged, this, &BugzillaDuplicatesPage::itemSelectionChanged); | 53 | connect(ui.m_bugListWidget, &QTreeWidget::itemSelectionChanged, | ||
54 | this, &BugzillaDuplicatesPage::itemSelectionChanged); | ||||
57 | 55 | | |||
58 | QHeaderView * header = ui.m_bugListWidget->header(); | 56 | QHeaderView * header = ui.m_bugListWidget->header(); | ||
59 | header->setSectionResizeMode(0, QHeaderView::ResizeToContents); | 57 | header->setSectionResizeMode(0, QHeaderView::ResizeToContents); | ||
60 | header->setSectionResizeMode(1, QHeaderView::Interactive); | 58 | header->setSectionResizeMode(1, QHeaderView::Interactive); | ||
61 | 59 | | |||
62 | //Create manual bug report entry (first one) | 60 | //Create manual bug report entry (first one) | ||
63 | QTreeWidgetItem * customBugItem = new QTreeWidgetItem( | 61 | QTreeWidgetItem * customBugItem = new QTreeWidgetItem( | ||
64 | QStringList() << i18nc("@item:intable custom/manaul bug report number", "Manual") | 62 | QStringList() << i18nc("@item:intable custom/manaul bug report number", "Manual") | ||
Show All 9 Lines | |||||
74 | customBugItem->setWhatsThis(0, helpMessage); | 72 | customBugItem->setWhatsThis(0, helpMessage); | ||
75 | customBugItem->setWhatsThis(1, helpMessage); | 73 | customBugItem->setWhatsThis(1, helpMessage); | ||
76 | 74 | | |||
77 | ui.m_bugListWidget->addTopLevelItem(customBugItem); | 75 | ui.m_bugListWidget->addTopLevelItem(customBugItem); | ||
78 | 76 | | |||
79 | m_searchMoreGuiItem = KGuiItem2(i18nc("@action:button", "Search for more reports"), | 77 | m_searchMoreGuiItem = KGuiItem2(i18nc("@action:button", "Search for more reports"), | ||
80 | QIcon::fromTheme(QStringLiteral("edit-find")), | 78 | QIcon::fromTheme(QStringLiteral("edit-find")), | ||
81 | i18nc("@info:tooltip", "Use this button to " | 79 | i18nc("@info:tooltip", "Use this button to " | ||
82 | "search for more similar bug reports on an " | 80 | "search for more similar bug reports")); | ||
83 | "earlier date.")); | | |||
84 | KGuiItem::assign(ui.m_searchMoreButton, m_searchMoreGuiItem); | 81 | KGuiItem::assign(ui.m_searchMoreButton, m_searchMoreGuiItem); | ||
85 | connect(ui.m_searchMoreButton, &QAbstractButton::clicked, this, &BugzillaDuplicatesPage::searchMore); | 82 | connect(ui.m_searchMoreButton, &QAbstractButton::clicked, this, &BugzillaDuplicatesPage::searchMore); | ||
86 | 83 | | |||
87 | m_retrySearchGuiItem = KGuiItem2(i18nc("@action:button", "Retry search"), | 84 | m_retrySearchGuiItem = KGuiItem2(i18nc("@action:button", "Retry search"), | ||
88 | QIcon::fromTheme(QStringLiteral("edit-find")), | 85 | QIcon::fromTheme(QStringLiteral("edit-find")), | ||
89 | i18nc("@info:tooltip", "Use this button to " | 86 | i18nc("@info:tooltip", "Use this button to " | ||
90 | "retry the search that previously " | 87 | "retry the search that previously " | ||
91 | "failed.")); | 88 | "failed.")); | ||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Line(s) | 170 | if (ui.m_bugListWidget->topLevelItemCount() != 1 && ui.m_selectedDuplicatesList->count() == 0 | |||
201 | } | 198 | } | ||
202 | } | 199 | } | ||
203 | return true; | 200 | return true; | ||
204 | } | 201 | } | ||
205 | 202 | | |||
206 | //BEGIN Search related methods | 203 | //BEGIN Search related methods | ||
207 | void BugzillaDuplicatesPage::searchMore() | 204 | void BugzillaDuplicatesPage::searchMore() | ||
208 | { | 205 | { | ||
209 | //1 year back | 206 | if (m_offset < 0) { | ||
210 | m_searchingEndDate = m_startDate; | 207 | m_offset = 0; // initialize, -1 means no search done yet | ||
211 | m_searchingStartDate = m_searchingEndDate.addYears(-1); | | |||
212 | | ||||
213 | performSearch(); | | |||
214 | } | 208 | } | ||
215 | 209 | | |||
216 | void BugzillaDuplicatesPage::performSearch() | 210 | // This is fairly inefficient, unfortunately the API's offset/limit system | ||
217 | { | 211 | // is not useful to us. The search is always sorting by lowest id, and | ||
218 | markAsSearching(true); | 212 | // negative offests are not a thing. So, offset=0&limit=1 gives the first | ||
219 | 213 | // ever reported bug in the product, while what we want is the latest. | |||
220 | QString startDateStr = m_searchingStartDate.toString(QStringLiteral("yyyy-MM-dd")); | 214 | // We also cannot query all perintent bug ids by default. While the API | ||
221 | QString endDateStr = m_searchingEndDate.toString(QStringLiteral("yyyy-MM-dd")); | 215 | // is reasonably fast, it'll still produce upwards of 2MiB just for the | ||
216 | // ids of a dolphin crash (as it includes all sorts of extra products). | ||||
217 | // So we are left with somewhat shoddy time-based queries. | ||||
222 | 218 | | |||
223 | ui.m_statusWidget->setBusy(i18nc("@info:status","Searching for duplicates (from %1 to %2)...", | 219 | markAsSearching(true); | ||
224 | startDateStr, endDateStr)); | 220 | ui.m_statusWidget->setBusy(i18nc("@info:status", "Searching for duplicates...")); | ||
225 | 221 | | |||
226 | //Bugzilla will not search on Today bugs if we send the date. | 222 | // Grab the default severity for newbugs | ||
227 | //we need to send "Now" | 223 | static QString severity = reportInterface()->newBugReportTemplate().severity; | ||
228 | if (m_searchingEndDate == QDate::currentDate()) { | | |||
229 | endDateStr = QLatin1String("Now"); | | |||
230 | } | | |||
231 | 224 | | |||
232 | #if 1 | | |||
233 | BugReport report = reportInterface()->newBugReportTemplate(); | | |||
234 | bugzillaManager()->searchBugs(reportInterface()->relatedBugzillaProducts(), | 225 | bugzillaManager()->searchBugs(reportInterface()->relatedBugzillaProducts(), | ||
235 | report.bugSeverity(), startDateStr, endDateStr, | 226 | severity, | ||
236 | reportInterface()->firstBacktraceFunctions().join(QStringLiteral(" "))); | 227 | reportInterface()->firstBacktraceFunctions().join(QStringLiteral(" ")), | ||
237 | #else //Test search | 228 | m_offset); | ||
238 | bugzillaManager()->searchBugs(QStringList() << "plasma", "crash", startDateStr, endDateStr, | | |||
239 | "QGraphicsScenePrivate::processDirtyItemsRecursive"); | | |||
240 | #endif | | |||
241 | } | 229 | } | ||
242 | 230 | | |||
243 | void BugzillaDuplicatesPage::stopCurrentSearch() | 231 | void BugzillaDuplicatesPage::stopCurrentSearch() | ||
244 | { | 232 | { | ||
245 | if (m_searching) { | 233 | if (m_searching) { | ||
246 | bugzillaManager()->stopCurrentSearch(); | 234 | bugzillaManager()->stopCurrentSearch(); | ||
247 | 235 | | |||
248 | markAsSearching(false); | 236 | markAsSearching(false); | ||
249 | 237 | | |||
250 | if (m_startDate==m_endDate) { //Never searched | 238 | if (m_offset < 0) { //Never searched | ||
251 | ui.m_statusWidget->setIdle(i18nc("@info:status","Search stopped.")); | 239 | ui.m_statusWidget->setIdle(i18nc("@info:status", "Search stopped.")); | ||
252 | } else { | 240 | } else { | ||
253 | ui.m_statusWidget->setIdle(i18nc("@info:status","Search stopped. Showing results from " | 241 | ui.m_statusWidget->setIdle(i18nc("@info:status", "Search stopped. Showing results.")); | ||
254 | "%1 to %2", m_startDate.toString(QStringLiteral("yyyy-MM-dd")), | | |||
255 | m_endDate.toString(QStringLiteral("yyyy-MM-dd")))); | | |||
256 | } | 242 | } | ||
257 | } | 243 | } | ||
258 | } | 244 | } | ||
259 | 245 | | |||
260 | void BugzillaDuplicatesPage::markAsSearching(bool searching) | 246 | void BugzillaDuplicatesPage::markAsSearching(bool searching) | ||
261 | { | 247 | { | ||
262 | m_searching = searching; | 248 | m_searching = searching; | ||
263 | emitCompleteChanged(); | 249 | emitCompleteChanged(); | ||
Show All 15 Lines | 264 | if (!searching) { | |||
279 | itemSelectionChanged(); | 265 | itemSelectionChanged(); | ||
280 | } else { | 266 | } else { | ||
281 | ui.m_openReportButton->setEnabled(false); | 267 | ui.m_openReportButton->setEnabled(false); | ||
282 | } | 268 | } | ||
283 | } | 269 | } | ||
284 | 270 | | |||
285 | bool BugzillaDuplicatesPage::canSearchMore() | 271 | bool BugzillaDuplicatesPage::canSearchMore() | ||
286 | { | 272 | { | ||
287 | return (m_startDate.year() >= 2009); | 273 | return !m_atEnd; | ||
288 | } | 274 | } | ||
289 | 275 | | |||
290 | void BugzillaDuplicatesPage::searchFinished(const BugMapList & list) | 276 | static QString statusString(const Bugzilla::Bug::Ptr &bug) | ||
277 | { | ||||
278 | // Generate a non-geek readable status | ||||
279 | switch(bug->status()) { | ||||
280 | case Bugzilla::Bug::Status::UNCONFIRMED: | ||||
281 | case Bugzilla::Bug::Status::CONFIRMED: | ||||
282 | case Bugzilla::Bug::Status::ASSIGNED: | ||||
283 | case Bugzilla::Bug::Status::REOPENED: | ||||
284 | return i18nc("@info bug status", "[Open]"); | ||||
285 | | ||||
286 | case Bugzilla::Bug::Status::RESOLVED: | ||||
287 | case Bugzilla::Bug::Status::VERIFIED: | ||||
288 | case Bugzilla::Bug::Status::CLOSED: | ||||
289 | switch(bug->resolution()) { | ||||
290 | case Bugzilla::Bug::Resolution::FIXED: | ||||
291 | return i18nc("@info bug resolution", "[Fixed]"); | ||||
292 | case Bugzilla::Bug::Resolution::WORKSFORME: | ||||
293 | return i18nc("@info bug resolution", "[Non-reproducible]"); | ||||
294 | case Bugzilla::Bug::Resolution::DUPLICATE: | ||||
295 | return i18nc("@info bug resolution", "[Duplicate report]"); | ||||
296 | case Bugzilla::Bug::Resolution::INVALID: | ||||
297 | return i18nc("@info bug resolution", "[Invalid]"); | ||||
298 | case Bugzilla::Bug::Resolution::UPSTREAM: | ||||
299 | case Bugzilla::Bug::Resolution::DOWNSTREAM: | ||||
300 | return i18nc("@info bug resolution", "[External problem]"); | ||||
301 | case Bugzilla::Bug::Resolution::WONTFIX: | ||||
302 | case Bugzilla::Bug::Resolution::LATER: | ||||
303 | case Bugzilla::Bug::Resolution::REMIND: | ||||
304 | case Bugzilla::Bug::Resolution::MOVED: | ||||
305 | case Bugzilla::Bug::Resolution::WAITINGFORINFO: | ||||
306 | case Bugzilla::Bug::Resolution::BACKTRACE: | ||||
307 | case Bugzilla::Bug::Resolution::UNMAINTAINED: | ||||
308 | case Bugzilla::Bug::Resolution::NONE: | ||||
309 | return QStringLiteral("[%1]").arg(QVariant::fromValue(bug->resolution()).toString()); | ||||
310 | case Bugzilla::Bug::Resolution::Unknown: | ||||
311 | Q_FALLTHROUGH(); | ||||
312 | } | ||||
313 | break; | ||||
314 | case Bugzilla::Bug::Status::NEEDSINFO: | ||||
315 | return i18nc("@info bug status", "[Incomplete]"); | ||||
316 | | ||||
317 | case Bugzilla::Bug::Status::Unknown: | ||||
318 | Q_FALLTHROUGH(); | ||||
319 | } | ||||
320 | return QString(); | ||||
321 | } | ||||
322 | | ||||
323 | void BugzillaDuplicatesPage::searchFinished(const QList<Bugzilla::Bug::Ptr> &list) | ||||
291 | { | 324 | { | ||
292 | KGuiItem::assign(ui.m_searchMoreButton, m_searchMoreGuiItem); | 325 | KGuiItem::assign(ui.m_searchMoreButton, m_searchMoreGuiItem); | ||
293 | m_startDate = m_searchingStartDate; | | |||
294 | 326 | | |||
295 | int results = list.count(); | 327 | int results = list.count(); | ||
328 | m_offset += results; | ||||
296 | if (results > 0) { | 329 | if (results > 0) { | ||
330 | m_atEnd = false; | ||||
331 | | ||||
297 | markAsSearching(false); | 332 | markAsSearching(false); | ||
298 | 333 | | |||
299 | ui.m_statusWidget->setIdle(i18nc("@info:status","Showing results from %1 to %2", | 334 | ui.m_statusWidget->setIdle(i18nc("@info:status", "Showing results.")); | ||
300 | m_startDate.toString(QStringLiteral("yyyy-MM-dd")), | | |||
301 | m_endDate.toString(QStringLiteral("yyyy-MM-dd")))); | | |||
302 | 335 | | |||
303 | QList<int> bugIds; | | |||
304 | for (int i = 0; i < results; i++) { | 336 | for (int i = 0; i < results; i++) { | ||
305 | BugMap bug = list.at(i); | 337 | Bugzilla::Bug::Ptr bug = list.at(i); | ||
306 | 338 | | |||
307 | bool ok; | 339 | QString title = statusString(bug) + QLatin1Char(' ') + bug->summary(); | ||
308 | int bugId = bug.value(QStringLiteral("bug_id")).toInt(&ok); | | |||
309 | if (ok) { | | |||
310 | bugIds << bugId; | | |||
311 | } | | |||
312 | 340 | | |||
313 | QString title; | 341 | QStringList fields = QStringList() << QString::number(bug->id()) << title; | ||
314 | | ||||
315 | //Generate a non-geek readable status | | |||
316 | QString customStatusString; | | |||
317 | BugReport::Status status = BugReport::parseStatus(bug.value(QStringLiteral("bug_status"))); | | |||
318 | BugReport::Resolution resolution = BugReport::parseResolution(bug.value(QStringLiteral("resolution"))); | | |||
319 | if (BugReport::isOpen(status)) { | | |||
320 | customStatusString = i18nc("@info bug status", "[Open]"); | | |||
321 | } else if (BugReport::isClosed(status) && status != BugReport::NeedsInfo) { | | |||
322 | if (resolution == BugReport::Fixed) { | | |||
323 | customStatusString = i18nc("@info bug resolution", "[Fixed]"); | | |||
324 | } else if (resolution == BugReport::WorksForMe) { | | |||
325 | customStatusString = i18nc("@info bug resolution", "[Non-reproducible]"); | | |||
326 | } else if (resolution == BugReport::Duplicate) { | | |||
327 | customStatusString = i18nc("@info bug resolution", "[Duplicate report]"); | | |||
328 | } else if (resolution == BugReport::Invalid) { | | |||
329 | customStatusString = i18nc("@info bug resolution", "[Invalid]"); | | |||
330 | } else if (resolution == BugReport::Downstream | | |||
331 | || resolution == BugReport::Upstream) { | | |||
332 | customStatusString = i18nc("@info bug resolution", "[External problem]"); | | |||
333 | } | | |||
334 | } else if (status == BugReport::NeedsInfo) { | | |||
335 | customStatusString = i18nc("@info bug status", "[Incomplete]"); | | |||
336 | } | | |||
337 | | ||||
338 | title = customStatusString + QLatin1Char(' ') + bug[QStringLiteral("short_desc")]; | | |||
339 | | ||||
340 | QStringList fields = QStringList() << bug[QStringLiteral("bug_id")] << title; | | |||
341 | 342 | | |||
342 | QTreeWidgetItem * item = new QTreeWidgetItem(fields); | 343 | QTreeWidgetItem * item = new QTreeWidgetItem(fields); | ||
343 | item->setToolTip(0, bug[QStringLiteral("short_desc")]); | 344 | item->setToolTip(0, bug->summary()); | ||
344 | item->setToolTip(1, bug[QStringLiteral("short_desc")]); | 345 | item->setToolTip(1, bug->summary()); | ||
345 | 346 | | |||
346 | ui.m_bugListWidget->addTopLevelItem(item); | 347 | ui.m_bugListWidget->addTopLevelItem(item); | ||
347 | } | 348 | } | ||
348 | 349 | | |||
349 | if (!m_foundDuplicate) { | 350 | if (!m_foundDuplicate) { | ||
350 | markAsSearching(true); | 351 | markAsSearching(true); | ||
351 | DuplicateFinderJob *job = new DuplicateFinderJob(bugIds, bugzillaManager(), this); | 352 | DuplicateFinderJob *job = new DuplicateFinderJob(list, bugzillaManager(), this); | ||
352 | connect(job, SIGNAL(result(KJob*)), this, SLOT(analyzedDuplicates(KJob*))); | 353 | connect(job, &KJob::result, this, &BugzillaDuplicatesPage::analyzedDuplicates); | ||
353 | job->start(); | 354 | job->start(); | ||
354 | } | 355 | } | ||
355 | 356 | | |||
356 | ui.m_bugListWidget->sortItems(0 , Qt::DescendingOrder); | 357 | ui.m_bugListWidget->sortItems(0 , Qt::DescendingOrder); | ||
357 | ui.m_bugListWidget->resizeColumnToContents(1); | 358 | ui.m_bugListWidget->resizeColumnToContents(1); | ||
358 | 359 | | |||
359 | if (!canSearchMore()) { | 360 | if (!canSearchMore()) { | ||
360 | ui.m_searchMoreButton->setEnabled(false); | 361 | ui.m_searchMoreButton->setEnabled(false); | ||
361 | } | 362 | } | ||
362 | 363 | | |||
363 | } else { | 364 | } else { | ||
365 | m_atEnd = true; | ||||
364 | 366 | | |||
365 | if (canSearchMore()) { | 367 | if (canSearchMore()) { | ||
366 | //We don't call markAsSearching(false) to avoid flicker | 368 | //We don't call markAsSearching(false) to avoid flicker | ||
367 | //Delayed call to searchMore to avoid unexpected behaviour (signal/slot) | 369 | //Delayed call to searchMore to avoid unexpected behaviour (signal/slot) | ||
368 | //because we are in a slot, and searchMore() will be ending calling this slot again | 370 | //because we are in a slot, and searchMore() will be ending calling this slot again | ||
369 | QTimer::singleShot(0, this, &BugzillaDuplicatesPage::searchMore); | 371 | QTimer::singleShot(0, this, &BugzillaDuplicatesPage::searchMore); | ||
370 | } else { | 372 | } else { | ||
371 | markAsSearching(false); | 373 | markAsSearching(false); | ||
372 | ui.m_statusWidget->setIdle(i18nc("@info:status","Search Finished. " | 374 | ui.m_statusWidget->setIdle(i18nc("@info:status","Search Finished. " | ||
373 | "No reports found.")); | 375 | "No reports found.")); | ||
374 | ui.m_searchMoreButton->setEnabled(false); | 376 | ui.m_searchMoreButton->setEnabled(false); | ||
375 | if (ui.m_bugListWidget->topLevelItemCount() == 0) { | 377 | if (ui.m_bugListWidget->topLevelItemCount() == 0) { | ||
376 | //No reports to mark as possible duplicate | 378 | //No reports to mark as possible duplicate | ||
377 | ui.m_selectedDuplicatesList->setEnabled(false); | 379 | ui.m_selectedDuplicatesList->setEnabled(false); | ||
378 | } | 380 | } | ||
379 | } | 381 | } | ||
380 | } | 382 | } | ||
381 | } | 383 | } | ||
382 | 384 | | |||
385 | static bool isStatusOpen(Bugzilla::Bug::Status status) | ||||
386 | { | ||||
387 | switch(status) { | ||||
388 | case Bugzilla::Bug::Status::UNCONFIRMED: | ||||
389 | case Bugzilla::Bug::Status::CONFIRMED: | ||||
390 | case Bugzilla::Bug::Status::ASSIGNED: | ||||
391 | case Bugzilla::Bug::Status::REOPENED: | ||||
392 | return true; | ||||
393 | case Bugzilla::Bug::Status::RESOLVED: | ||||
394 | case Bugzilla::Bug::Status::NEEDSINFO: | ||||
395 | case Bugzilla::Bug::Status::VERIFIED: | ||||
396 | case Bugzilla::Bug::Status::CLOSED: | ||||
397 | return false; | ||||
398 | | ||||
399 | case Bugzilla::Bug::Status::Unknown: | ||||
400 | Q_FALLTHROUGH(); | ||||
401 | } | ||||
402 | return false; | ||||
403 | } | ||||
404 | | ||||
405 | static bool isStatusClosed(Bugzilla::Bug::Status status) | ||||
406 | { | ||||
407 | return !isStatusOpen(status); | ||||
408 | } | ||||
409 | | ||||
383 | void BugzillaDuplicatesPage::analyzedDuplicates(KJob *j) | 410 | void BugzillaDuplicatesPage::analyzedDuplicates(KJob *j) | ||
384 | { | 411 | { | ||
385 | markAsSearching(false); | 412 | markAsSearching(false); | ||
386 | 413 | | |||
387 | DuplicateFinderJob *job = static_cast<DuplicateFinderJob*>(j); | 414 | DuplicateFinderJob *job = static_cast<DuplicateFinderJob*>(j); | ||
388 | m_result = job->result(); | 415 | m_result = job->result(); | ||
389 | m_foundDuplicate = m_result.parentDuplicate; | 416 | m_foundDuplicate = m_result.parentDuplicate; | ||
390 | reportInterface()->setDuplicateId(m_result.parentDuplicate); | 417 | reportInterface()->setDuplicateId(m_result.parentDuplicate); | ||
391 | ui.m_searchMoreButton->setEnabled(!m_foundDuplicate); | 418 | ui.m_searchMoreButton->setEnabled(!m_foundDuplicate); | ||
392 | ui.information->setVisible(m_foundDuplicate); | 419 | ui.information->setVisible(m_foundDuplicate); | ||
393 | BugReport::Status status = m_result.status; | 420 | auto status = m_result.status; | ||
394 | const int duplicate = m_result.duplicate; | 421 | const int duplicate = m_result.duplicate; | ||
395 | const int parentDuplicate = m_result.parentDuplicate; | 422 | const int parentDuplicate = m_result.parentDuplicate; | ||
396 | 423 | | |||
397 | if (m_foundDuplicate) { | 424 | if (m_foundDuplicate) { | ||
398 | const QList<QTreeWidgetItem*> items = ui.m_bugListWidget->findItems(QString::number(parentDuplicate), Qt::MatchExactly, 0); | 425 | const QList<QTreeWidgetItem*> items = ui.m_bugListWidget->findItems(QString::number(parentDuplicate), Qt::MatchExactly, 0); | ||
399 | const QBrush brush = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NeutralBackground); | 426 | const QBrush brush = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NeutralBackground); | ||
400 | Q_FOREACH (QTreeWidgetItem* item, items) { | 427 | Q_FOREACH (QTreeWidgetItem* item, items) { | ||
401 | for (int i = 0; i < item->columnCount(); ++i) { | 428 | for (int i = 0; i < item->columnCount(); ++i) { | ||
402 | item->setBackground(i, brush); | 429 | item->setBackground(i, brush); | ||
403 | } | 430 | } | ||
404 | } | 431 | } | ||
405 | 432 | | |||
406 | QString text; | 433 | QString text; | ||
407 | if (BugReport::isOpen(status) || (BugReport::isClosed(status) && status == BugReport::NeedsInfo)) { | 434 | if (isStatusOpen(status) || status == Bugzilla::Bug::Status::NEEDSINFO) { | ||
408 | text = (parentDuplicate == duplicate ? i18nc("@label", "Your crash is a <strong>duplicate</strong> and has already been reported as <a href=\"%1\">Bug %1</a>.", QString::number(duplicate)) : | 435 | text = (parentDuplicate == duplicate ? i18nc("@label", "Your crash is a <strong>duplicate</strong> and has already been reported as <a href=\"%1\">Bug %1</a>.", QString::number(duplicate)) : | ||
409 | i18nc("@label", "Your crash has already been reported as <a href=\"%1\">Bug %1</a>, which is a <strong>duplicate</strong> of <a href=\"%2\">Bug %2</a>", QString::number(duplicate), QString::number(parentDuplicate))) + | 436 | i18nc("@label", "Your crash has already been reported as <a href=\"%1\">Bug %1</a>, which is a <strong>duplicate</strong> of <a href=\"%2\">Bug %2</a>", QString::number(duplicate), QString::number(parentDuplicate))) + | ||
410 | QLatin1Char('\n') + i18nc("@label", "Only <strong><a href=\"%1\">attach</a></strong> if you can add needed information to the bug report.", QStringLiteral("attach")); | 437 | QLatin1Char('\n') + i18nc("@label", "Only <strong><a href=\"%1\">attach</a></strong> if you can add needed information to the bug report.", QStringLiteral("attach")); | ||
411 | } else if (BugReport::isClosed(status)) { | 438 | } else if (isStatusClosed(status)) { | ||
412 | text = (parentDuplicate == duplicate ? i18nc("@label", "Your crash has already been reported as <a href=\"%1\">Bug %1</a> which has been <strong>closed</strong>.", QString::number(duplicate)) : | 439 | text = (parentDuplicate == duplicate ? i18nc("@label", "Your crash has already been reported as <a href=\"%1\">Bug %1</a> which has been <strong>closed</strong>.", QString::number(duplicate)) : | ||
413 | i18nc("@label", "Your crash has already been reported as <a href=\"%1\">Bug %1</a>, which is a duplicate of the <strong>closed</strong> <a href=\"%2\">Bug %2</a>.", QString::number(duplicate), QString::number(parentDuplicate))); | 440 | i18nc("@label", "Your crash has already been reported as <a href=\"%1\">Bug %1</a>, which is a duplicate of the <strong>closed</strong> <a href=\"%2\">Bug %2</a>.", QString::number(duplicate), QString::number(parentDuplicate))); | ||
414 | } | 441 | } | ||
415 | ui.information->setText(text); | 442 | ui.information->setText(text); | ||
416 | } | 443 | } | ||
417 | } | 444 | } | ||
418 | 445 | | |||
419 | void BugzillaDuplicatesPage::informationClicked(const QString &activatedLink) | 446 | void BugzillaDuplicatesPage::informationClicked(const QString &activatedLink) | ||
Show All 15 Lines | 459 | { | |||
435 | 462 | | |||
436 | ui.m_statusWidget->setIdle(i18nc("@info:status","Error fetching the bug report list")); | 463 | ui.m_statusWidget->setIdle(i18nc("@info:status","Error fetching the bug report list")); | ||
437 | 464 | | |||
438 | KMessageBox::error(this , xi18nc("@info/rich","Error fetching the bug report list<nl/>" | 465 | KMessageBox::error(this , xi18nc("@info/rich","Error fetching the bug report list<nl/>" | ||
439 | "<message>%1.</message><nl/>" | 466 | "<message>%1.</message><nl/>" | ||
440 | "Please wait some time and try again.", err)); | 467 | "Please wait some time and try again.", err)); | ||
441 | } | 468 | } | ||
442 | 469 | | |||
443 | void BugzillaDuplicatesPage::resetDates() | | |||
444 | { | | |||
445 | m_endDate = QDate::currentDate(); | | |||
446 | m_startDate = m_endDate; | | |||
447 | } | | |||
448 | //END Search related methods | 470 | //END Search related methods | ||
449 | 471 | | |||
450 | //BEGIN Duplicates list related methods | 472 | //BEGIN Duplicates list related methods | ||
451 | void BugzillaDuplicatesPage::openSelectedReport() | 473 | void BugzillaDuplicatesPage::openSelectedReport() | ||
452 | { | 474 | { | ||
453 | QList<QTreeWidgetItem*> selected = ui.m_bugListWidget->selectedItems(); | 475 | QList<QTreeWidgetItem*> selected = ui.m_bugListWidget->selectedItems(); | ||
454 | if (selected.count() == 1) { | 476 | if (selected.count() == 1) { | ||
455 | itemClicked(selected.at(0), 0); | 477 | itemClicked(selected.at(0), 0); | ||
▲ Show 20 Lines • Show All 128 Lines • ▼ Show 20 Line(s) | 604 | QIcon::fromTheme(QStringLiteral("list-add")), i18nc("@info:tooltip", "Use this button to suggest that " | |||
584 | "report"))); | 606 | "report"))); | ||
585 | connect(m_suggestButton, &QPushButton::clicked, this, &BugzillaReportInformationDialog::relatedReportClicked); | 607 | connect(m_suggestButton, &QPushButton::clicked, this, &BugzillaReportInformationDialog::relatedReportClicked); | ||
586 | 608 | | |||
587 | connect(ui.m_showOwnBacktraceCheckBox, &QAbstractButton::toggled, this, &BugzillaReportInformationDialog::toggleShowOwnBacktrace); | 609 | connect(ui.m_showOwnBacktraceCheckBox, &QAbstractButton::toggled, this, &BugzillaReportInformationDialog::toggleShowOwnBacktrace); | ||
588 | 610 | | |||
589 | //Connect bugzillalib signals | 611 | //Connect bugzillalib signals | ||
590 | connect(m_parent->bugzillaManager(), &BugzillaManager::bugReportFetched, | 612 | connect(m_parent->bugzillaManager(), &BugzillaManager::bugReportFetched, | ||
591 | this, &BugzillaReportInformationDialog::bugFetchFinished); | 613 | this, &BugzillaReportInformationDialog::bugFetchFinished); | ||
592 | connect(m_parent->bugzillaManager(), SIGNAL(bugReportError(QString,QObject*)), | 614 | connect(m_parent->bugzillaManager(), &BugzillaManager::bugReportError, | ||
593 | this, SLOT(bugFetchError(QString,QObject*))); | 615 | this, &BugzillaReportInformationDialog::bugFetchError); | ||
616 | connect(m_parent->bugzillaManager(), &BugzillaManager::commentsFetched, | ||||
617 | this, &BugzillaReportInformationDialog::onCommentsFetched); | ||||
618 | connect(m_parent->bugzillaManager(), &BugzillaManager::commentsError, | ||||
619 | this, &BugzillaReportInformationDialog::bugFetchError); | ||||
594 | 620 | | |||
595 | KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog"); | 621 | KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog"); | ||
596 | KWindowConfig::restoreWindowSize(windowHandle(), config); | 622 | KWindowConfig::restoreWindowSize(windowHandle(), config); | ||
597 | } | 623 | } | ||
598 | 624 | | |||
599 | BugzillaReportInformationDialog::~BugzillaReportInformationDialog() | 625 | BugzillaReportInformationDialog::~BugzillaReportInformationDialog() | ||
600 | { | 626 | { | ||
601 | KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog"); | 627 | KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog"); | ||
Show All 37 Lines | 637 | { | |||
639 | ui.m_showOwnBacktraceCheckBox->setChecked(showOwnBacktrace); | 665 | ui.m_showOwnBacktraceCheckBox->setChecked(showOwnBacktrace); | ||
640 | if (!showOwnBacktrace) { //setChecked(false) will not emit toggled(false) | 666 | if (!showOwnBacktrace) { //setChecked(false) will not emit toggled(false) | ||
641 | toggleShowOwnBacktrace(false); | 667 | toggleShowOwnBacktrace(false); | ||
642 | } | 668 | } | ||
643 | 669 | | |||
644 | show(); | 670 | show(); | ||
645 | } | 671 | } | ||
646 | 672 | | |||
647 | void BugzillaReportInformationDialog::bugFetchFinished(BugReport report, QObject * jobOwner) | 673 | struct Status2 { | ||
674 | QString statusString; | ||||
675 | QString closedStateString; | ||||
676 | }; | ||||
677 | | ||||
678 | static Status2 statusString2(const Bugzilla::Bug::Ptr &bug) | ||||
648 | { | 679 | { | ||
649 | if (jobOwner == this && isVisible()) { | 680 | // Generate a non-geek readable status | ||
650 | if (report.isValid()) { | 681 | switch(bug->status()) { | ||
682 | case Bugzilla::Bug::Status::UNCONFIRMED: | ||||
683 | return { i18nc("@info bug status", "Opened (Unconfirmed)"), QString() }; | ||||
684 | case Bugzilla::Bug::Status::CONFIRMED: | ||||
685 | case Bugzilla::Bug::Status::ASSIGNED: | ||||
686 | case Bugzilla::Bug::Status::REOPENED: | ||||
687 | return { i18nc("@info bug status", "Opened (Unfixed)"), QString() }; | ||||
688 | | ||||
689 | case Bugzilla::Bug::Status::RESOLVED: | ||||
690 | case Bugzilla::Bug::Status::VERIFIED: | ||||
691 | case Bugzilla::Bug::Status::CLOSED: | ||||
692 | switch(bug->resolution()) { | ||||
693 | case Bugzilla::Bug::Resolution::FIXED: { | ||||
694 | auto fixedIn = bug->customField("cf_versionfixedin").toString(); | ||||
695 | if (!fixedIn.isEmpty()) { | ||||
696 | return { i18nc("@info bug resolution, fixed in version", | ||||
697 | "Fixed in version \"%1\"", | ||||
698 | fixedIn), | ||||
699 | i18nc("@info bug resolution, fixed by kde devs in version", | ||||
700 | "the bug was fixed by KDE developers in version \"%1\"", | ||||
701 | fixedIn) | ||||
702 | }; | ||||
703 | } | ||||
704 | return { | ||||
705 | i18nc("@info bug resolution", "Fixed"), | ||||
706 | i18nc("@info bug resolution", "the bug was fixed by KDE developers") | ||||
707 | }; | ||||
708 | } | ||||
709 | | ||||
710 | case Bugzilla::Bug::Resolution::WORKSFORME: | ||||
711 | return { i18nc("@info bug resolution", "Non-reproducible"), QString() }; | ||||
712 | case Bugzilla::Bug::Resolution::DUPLICATE: | ||||
713 | return { i18nc("@info bug resolution", "Duplicate report (Already reported before)"), QString() }; | ||||
714 | case Bugzilla::Bug::Resolution::INVALID: | ||||
715 | return { i18nc("@info bug resolution", "Not a valid report/crash"), QString() }; | ||||
716 | case Bugzilla::Bug::Resolution::UPSTREAM: | ||||
717 | case Bugzilla::Bug::Resolution::DOWNSTREAM: | ||||
718 | return { i18nc("@info bug resolution", "Not caused by a problem in the KDE's Applications or libraries"), | ||||
719 | i18nc("@info bug resolution", "the bug is caused by a problem in an external application or library, or by a distribution or packaging issue") }; | ||||
720 | case Bugzilla::Bug::Resolution::WONTFIX: | ||||
721 | case Bugzilla::Bug::Resolution::LATER: | ||||
722 | case Bugzilla::Bug::Resolution::REMIND: | ||||
723 | case Bugzilla::Bug::Resolution::MOVED: | ||||
724 | case Bugzilla::Bug::Resolution::WAITINGFORINFO: | ||||
725 | case Bugzilla::Bug::Resolution::BACKTRACE: | ||||
726 | case Bugzilla::Bug::Resolution::UNMAINTAINED: | ||||
727 | case Bugzilla::Bug::Resolution::NONE: | ||||
728 | return { QVariant::fromValue(bug->resolution()).toString(), QString() }; | ||||
729 | case Bugzilla::Bug::Resolution::Unknown: | ||||
730 | Q_FALLTHROUGH(); | ||||
731 | } | ||||
732 | return {}; | ||||
733 | | ||||
734 | case Bugzilla::Bug::Status::NEEDSINFO: | ||||
735 | return { i18nc("@info bug status", "Temporarily closed, because of a lack of information"), QString() }; | ||||
736 | | ||||
737 | case Bugzilla::Bug::Status::Unknown: | ||||
738 | Q_FALLTHROUGH(); | ||||
739 | } | ||||
740 | return {}; | ||||
741 | } | ||||
742 | | ||||
743 | void BugzillaReportInformationDialog::bugFetchFinished(Bugzilla::Bug::Ptr bug, QObject *jobOwner) | ||||
744 | { | ||||
745 | if (jobOwner != this || !isVisible()) { | ||||
746 | return; | ||||
747 | } | ||||
748 | | ||||
749 | if (!bug) { | ||||
750 | bugFetchError(i18nc("@info", "Invalid report information (malformed data). This could " | ||||
751 | "mean that the bug report does not exist, or the bug tracking site " | ||||
752 | "is experiencing a problem."), this); | ||||
753 | return; | ||||
754 | } | ||||
755 | | ||||
756 | Q_ASSERT(!m_bug); // m_bug must only be set once we've selected one! | ||||
651 | 757 | | |||
652 | //Handle duplicate state | 758 | // Handle duplicate state | ||
653 | QString duplicate = report.markedAsDuplicateOf(); | 759 | if (bug->dupe_of() > 0) { | ||
654 | if (!duplicate.isEmpty()) { | | |||
655 | bool ok = false; | | |||
656 | int dupId = duplicate.toInt(&ok); | | |||
657 | if (ok && dupId > 0) { | | |||
658 | ui.m_statusWidget->setIdle(QString()); | 760 | ui.m_statusWidget->setIdle(QString()); | ||
659 | 761 | | |||
660 | KGuiItem yesItem = KStandardGuiItem::yes(); | 762 | KGuiItem yesItem = KStandardGuiItem::yes(); | ||
661 | yesItem.setText(i18nc("@action:button let the user to choose to read the " | 763 | yesItem.setText(i18nc("@action:button let the user to choose to read the " | ||
662 | "main report", "Yes, read the main report")); | 764 | "main report", "Yes, read the main report")); | ||
663 | 765 | | |||
664 | KGuiItem noItem = KStandardGuiItem::no(); | 766 | KGuiItem noItem = KStandardGuiItem::no(); | ||
665 | noItem.setText(i18nc("@action:button let the user choose to read the original " | 767 | noItem.setText(i18nc("@action:button let the user choose to read the original " | ||
666 | "report", "No, let me read the report I selected")); | 768 | "report", "No, let me read the report I selected")); | ||
667 | 769 | | |||
668 | if (KMessageBox::questionYesNo(this, | 770 | auto ret = KMessageBox::questionYesNo( | ||
771 | this, | ||||
669 | xi18nc("@info","The report you selected (bug %1) is already " | 772 | xi18nc("@info","The report you selected (bug %1) is already " | ||
670 | "marked as duplicate of bug %2. " | 773 | "marked as duplicate of bug %2. " | ||
671 | "Do you want to read that report instead? (recommended)", | 774 | "Do you want to read that report instead? (recommended)", | ||
672 | report.bugNumber(), QString::number(dupId)), | 775 | bug->id(), QString::number(bug->dupe_of())), | ||
673 | i18nc("@title:window","Nested duplicate detected"), yesItem, noItem) | 776 | i18nc("@title:window","Nested duplicate detected"), | ||
674 | == KMessageBox::Yes) { | 777 | yesItem, | ||
675 | showBugReport(dupId); | 778 | noItem); | ||
779 | if (ret == KMessageBox::Yes) { | ||||
780 | qDebug() << "REDIRECT"; | ||||
781 | showBugReport(bug->dupe_of()); | ||||
676 | return; | 782 | return; | ||
677 | } | 783 | } | ||
678 | } | 784 | } | ||
785 | | ||||
786 | // Process comments... | ||||
787 | m_bug = bug; | ||||
788 | m_parent->bugzillaManager()->fetchComments(m_bug, this); | ||||
789 | } | ||||
790 | | ||||
791 | void BugzillaReportInformationDialog::onCommentsFetched(QList<Bugzilla::Comment::Ptr> bugComments, | ||||
792 | QObject *jobOwner) | ||||
793 | { | ||||
794 | if (jobOwner != this || !isVisible()) { | ||||
795 | return; | ||||
679 | } | 796 | } | ||
680 | 797 | | |||
798 | Q_ASSERT(m_bug); | ||||
799 | | ||||
681 | //Generate html for comments (with proper numbering) | 800 | // Generate html for comments (with proper numbering) | ||
682 | QLatin1String duplicatesMark = QLatin1String("has been marked as a duplicate of this bug."); | 801 | QLatin1String duplicatesMark = QLatin1String("has been marked as a duplicate of this bug."); | ||
683 | 802 | | |||
803 | // TODO: the way comment objects are turned into comment strings is fairly | ||||
804 | // awkward and does not particularly object-centric. May benefit from a | ||||
805 | // slight redesign. | ||||
684 | QString comments; | 806 | QString comments; | ||
685 | QStringList commentList = report.comments(); | 807 | QString description; // aka first comment | ||
686 | for (int i = 0; i < commentList.count(); i++) { | 808 | if (bugComments.size() > 0) { | ||
687 | QString comment = commentList.at(i); | 809 | description = bugComments.takeFirst()->text(); | ||
810 | } | ||||
811 | for (auto it = bugComments.constBegin(); it != bugComments.constEnd(); ++it) { | ||||
812 | QString comment = (*it)->text(); | ||||
688 | //Don't add duplicates mark comments | 813 | // Don't add duplicates mark comments | ||
689 | if (!comment.contains(duplicatesMark)) { | 814 | if (!comment.contains(duplicatesMark)) { | ||
690 | comment.replace(QLatin1Char('\n'), QLatin1String("<br />")); | 815 | comment.replace(QLatin1Char('\n'), QLatin1String("<br />")); | ||
816 | const int i = it - bugComments.constBegin(); | ||||
691 | comments += i18nc("comment $number to use as subtitle", "<h4>Comment %1:</h4>", (i+1)) | 817 | comments += i18nc("comment $number to use as subtitle", "<h4>Comment %1:</h4>", (i+1)) | ||
692 | + QStringLiteral("<p>") + comment + QStringLiteral("</p><hr />"); | 818 | + QStringLiteral("<p>") + comment + QStringLiteral("</p><hr />"); | ||
693 | //Count the inline attached crashes (DrKonqi feature) | 819 | // Count the inline attached crashes (DrKonqi feature) | ||
694 | QLatin1String attachedCrashMark = | 820 | QLatin1String attachedCrashMark = | ||
695 | QLatin1String("New crash information added by DrKonqi"); | 821 | QLatin1String("New crash information added by DrKonqi"); | ||
696 | if (comment.contains(attachedCrashMark)) { | 822 | if (comment.contains(attachedCrashMark)) { | ||
697 | m_duplicatesCount++; | 823 | m_duplicatesCount++; | ||
698 | } | 824 | } | ||
699 | } else { | 825 | } else { | ||
700 | //Count duplicate | 826 | // Count duplicate | ||
701 | m_duplicatesCount++; | 827 | m_duplicatesCount++; | ||
702 | } | 828 | } | ||
703 | } | 829 | } | ||
704 | 830 | | |||
705 | //Generate a non-geek readable status | 831 | // Generate a non-geek readable status | ||
706 | QString customStatusString; | 832 | auto str = statusString2(m_bug); | ||
707 | BugReport::Status status = report.statusValue(); | 833 | QString customStatusString = str.statusString; | ||
708 | BugReport::Resolution resolution = report.resolutionValue(); | 834 | m_closedStateString = str.closedStateString; | ||
709 | if (status == BugReport::Unconfirmed) { | | |||
710 | customStatusString = i18nc("@info bug status", "Opened (Unconfirmed)"); | | |||
711 | } else if (report.isOpen()) { | | |||
712 | customStatusString = i18nc("@info bug status", "Opened (Unfixed)"); | | |||
713 | } else if (report.isClosed() && status != BugReport::NeedsInfo) { | | |||
714 | QString customResolutionString; | | |||
715 | if (resolution == BugReport::Fixed) { | | |||
716 | if (!report.versionFixedIn().isEmpty()) { | | |||
717 | customResolutionString = i18nc("@info bug resolution, fixed in version", | | |||
718 | "Fixed in version \"%1\"", | | |||
719 | report.versionFixedIn()); | | |||
720 | m_closedStateString = i18nc("@info bug resolution, fixed by kde devs in version", | | |||
721 | "the bug was fixed by KDE developers in version \"%1\"", | | |||
722 | report.versionFixedIn()); | | |||
723 | } else { | | |||
724 | customResolutionString = i18nc("@info bug resolution", "Fixed"); | | |||
725 | m_closedStateString = i18nc("@info bug resolution", "the bug was fixed by KDE developers"); | | |||
726 | } | | |||
727 | } else if (resolution == BugReport::WorksForMe) { | | |||
728 | customResolutionString = i18nc("@info bug resolution", "Non-reproducible"); | | |||
729 | } else if (resolution == BugReport::Duplicate) { | | |||
730 | customResolutionString = i18nc("@info bug resolution", "Duplicate report " | | |||
731 | "(Already reported before)"); | | |||
732 | } else if (resolution == BugReport::Invalid) { | | |||
733 | customResolutionString = i18nc("@info bug resolution", "Not a valid report/crash"); | | |||
734 | } else if (resolution == BugReport::Downstream || resolution == BugReport::Upstream) { | | |||
735 | customResolutionString = i18nc("@info bug resolution", "Not caused by a problem " | | |||
736 | "in the KDE's Applications or libraries"); | | |||
737 | m_closedStateString = i18nc("@info bug resolution", "the bug is caused by a " | | |||
738 | "problem in an external application or library, or " | | |||
739 | "by a distribution or packaging issue"); | | |||
740 | } else { | | |||
741 | customResolutionString = report.resolution(); | | |||
742 | } | | |||
743 | | ||||
744 | customStatusString = i18nc("@info bug status, %1 is the resolution", "Closed (%1)", | | |||
745 | customResolutionString); | | |||
746 | } else if (status == BugReport::NeedsInfo) { | | |||
747 | customStatusString = i18nc("@info bug status", "Temporarily closed, because of a lack " | | |||
748 | "of information"); | | |||
749 | } else { //Fallback to other raw values | | |||
750 | customStatusString = QStringLiteral("%1 (%2)").arg(report.bugStatus(), report.resolution()); | | |||
751 | } | | |||
752 | 835 | | |||
753 | //Generate notes | 836 | // Generate notes | ||
754 | QString notes = xi18n("<p><note>The bug report's title is often written by its reporter " | 837 | QString notes = xi18n("<p><note>The bug report's title is often written by its reporter " | ||
755 | "and may not reflect the bug's nature, root cause or other visible " | 838 | "and may not reflect the bug's nature, root cause or other visible " | ||
756 | "symptoms you could use to compare to your crash. Please read the " | 839 | "symptoms you could use to compare to your crash. Please read the " | ||
757 | "complete report and all the comments below.</note></p>"); | 840 | "complete report and all the comments below.</note></p>"); | ||
758 | 841 | | |||
759 | if (m_duplicatesCount >= 10) { //Consider a possible mass duplicate crash | 842 | if (m_duplicatesCount >= 10) { //Consider a possible mass duplicate crash | ||
760 | notes += xi18np("<p><note>This bug report has %1 duplicate report. That means this " | 843 | notes += xi18np("<p><note>This bug report has %1 duplicate report. That means this " | ||
761 | "is probably a <strong>common crash</strong>. <i>Please consider only " | 844 | "is probably a <strong>common crash</strong>. <i>Please consider only " | ||
762 | "adding a comment or a note if you can provide new valuable " | 845 | "adding a comment or a note if you can provide new valuable " | ||
763 | "information which was not already mentioned.</i></note></p>", | 846 | "information which was not already mentioned.</i></note></p>", | ||
764 | "<p><note>This bug report has %1 duplicate reports. That means this " | 847 | "<p><note>This bug report has %1 duplicate reports. That means this " | ||
765 | "is probably a <strong>common crash</strong>. <i>Please consider only " | 848 | "is probably a <strong>common crash</strong>. <i>Please consider only " | ||
766 | "adding a comment or a note if you can provide new valuable " | 849 | "adding a comment or a note if you can provide new valuable " | ||
767 | "information which was not already mentioned.</i></note></p>", | 850 | "information which was not already mentioned.</i></note></p>", | ||
768 | m_duplicatesCount); | 851 | m_duplicatesCount); | ||
769 | } | 852 | } | ||
770 | 853 | | |||
771 | //A manually entered bug ID could represent a normal bug | 854 | // A manually entered bug ID could represent a normal bug | ||
772 | if (report.bugSeverity() != QLatin1String("crash") | 855 | if (m_bug->severity() != QLatin1String("crash") | ||
773 | && report.bugSeverity() != QLatin1String("major") | 856 | && m_bug->severity() != QLatin1String("major") | ||
774 | && report.bugSeverity() != QLatin1String("grave") | 857 | && m_bug->severity() != QLatin1String("grave") | ||
775 | && report.bugSeverity() != QLatin1String("critical")) | 858 | && m_bug->severity() != QLatin1String("critical")) | ||
776 | { | 859 | { | ||
777 | notes += xi18n("<p><note>This bug report is not about a crash or about any other " | 860 | notes += xi18n("<p><note>This bug report is not about a crash or about any other " | ||
778 | "critical bug.</note></p>"); | 861 | "critical bug.</note></p>"); | ||
779 | } | 862 | } | ||
780 | 863 | | |||
781 | //Generate HTML text | 864 | // Generate HTML text | ||
782 | QString text = | 865 | QString text = | ||
783 | i18nc("@info bug report title (quoted)", | 866 | i18nc("@info bug report title (quoted)", | ||
784 | "<h3>\"%1\"</h3>", report.shortDescription()) + | 867 | "<h3>\"%1\"</h3>", m_bug->summary()) + | ||
785 | notes + | 868 | notes + | ||
786 | i18nc("@info bug report status", | 869 | i18nc("@info bug report status", | ||
787 | "<h4>Bug Report Status: %1</h4>", customStatusString) + | 870 | "<h4>Bug Report Status: %1</h4>", customStatusString) + | ||
788 | i18nc("@info bug report product and component", | 871 | i18nc("@info bug report product and component", | ||
789 | "<h4>Affected Component: %1 (%2)</h4>", | 872 | "<h4>Affected Component: %1 (%2)</h4>", | ||
790 | report.product(), report.component()) + | 873 | m_bug->product(), | ||
874 | m_bug->component()) + | ||||
791 | i18nc("@info bug report description", | 875 | i18nc("@info bug report description", | ||
792 | "<h3>Description of the bug</h3><p>%1</p>", | 876 | "<h3>Description of the bug</h3><p>%1</p>", | ||
793 | report.description().replace(QLatin1Char('\n'), QLatin1String("<br />"))); | 877 | description.replace(QLatin1Char('\n'), QLatin1String("<br />"))); | ||
794 | 878 | | |||
795 | if (!comments.isEmpty()) { | 879 | if (!comments.isEmpty()) { | ||
796 | text += i18nc("@label:textbox bug report comments (already formatted)", | 880 | text += i18nc("@label:textbox bug report comments (already formatted)", | ||
797 | "<h2>Additional Comments</h2>%1", comments); | 881 | "<h2>Additional Comments</h2>%1", comments); | ||
798 | } | 882 | } | ||
799 | 883 | | |||
800 | ui.m_infoBrowser->setText(text); | 884 | ui.m_infoBrowser->setText(text); | ||
801 | ui.m_infoBrowser->setEnabled(true); | 885 | ui.m_infoBrowser->setEnabled(true); | ||
802 | 886 | | |||
803 | m_suggestButton->setEnabled(m_relatedButtonEnabled); | 887 | m_suggestButton->setEnabled(m_relatedButtonEnabled); | ||
804 | m_suggestButton->setVisible(m_relatedButtonEnabled); | 888 | m_suggestButton->setVisible(m_relatedButtonEnabled); | ||
805 | 889 | | |||
806 | ui.m_statusWidget->setIdle(xi18nc("@info:status", "Showing bug %1", | 890 | ui.m_statusWidget->setIdle(xi18nc("@info:status", "Showing bug %1", | ||
807 | QString::number(report.bugNumberAsInt()))); | 891 | QString::number(m_bug->id()))); | ||
808 | } else { | | |||
809 | bugFetchError(i18nc("@info", "Invalid report information (malformed data). This could " | | |||
810 | "mean that the bug report does not exist, or the bug tracking site " | | |||
811 | "is experiencing a problem."), this); | | |||
812 | } | | |||
813 | } | | |||
814 | } | 892 | } | ||
815 | 893 | | |||
816 | void BugzillaReportInformationDialog::markAsDuplicate() | 894 | void BugzillaReportInformationDialog::markAsDuplicate() | ||
817 | { | 895 | { | ||
818 | emit possibleDuplicateSelected(m_bugNumber); | 896 | emit possibleDuplicateSelected(m_bugNumber); | ||
819 | hide(); | 897 | hide(); | ||
820 | } | 898 | } | ||
821 | 899 | | |||
▲ Show 20 Lines • Show All 171 Lines • Show Last 20 Lines |