diff --git a/src/backtraceratingwidget.cpp b/src/backtraceratingwidget.cpp
index 5dc7a79d..13bcb33c 100644
--- a/src/backtraceratingwidget.cpp
+++ b/src/backtraceratingwidget.cpp
@@ -1,96 +1,95 @@
/*******************************************************************
* backtraceratingwidget.cpp
* Copyright 2009 Dario Andres Rodriguez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
******************************************************************/
#include "backtraceratingwidget.h"
#include
#include
-
BacktraceRatingWidget::BacktraceRatingWidget(QWidget * parent) :
QWidget(parent),
m_state(BacktraceGenerator::NotLoaded),
m_star1(false),
m_star2(false),
m_star3(false)
{
setMinimumSize(105, 24);
m_starPixmap = QIcon::fromTheme(QStringLiteral("rating")).pixmap(QSize(22, 22));
m_disabledStarPixmap = QIcon::fromTheme(QStringLiteral("rating")).pixmap(QSize(22, 22), QIcon::Disabled);
m_errorPixmap = QIcon::fromTheme(QStringLiteral("dialog-error")).pixmap(QSize(22, 22));
}
void BacktraceRatingWidget::setUsefulness(BacktraceParser::Usefulness usefulness)
{
switch (usefulness) {
case BacktraceParser::ReallyUseful: {
m_star1 = true;
m_star2 = true;
m_star3 = true;
break;
}
case BacktraceParser::MayBeUseful: {
m_star1 = true;
m_star2 = true;
m_star3 = false;
break;
}
case BacktraceParser::ProbablyUseless: {
m_star1 = true;
m_star2 = false;
m_star3 = false;
break;
}
case BacktraceParser::Useless:
case BacktraceParser::InvalidUsefulness: {
m_star1 = false;
m_star2 = false;
m_star3 = false;
break;
}
}
update();
}
void BacktraceRatingWidget::paintEvent(QPaintEvent * event)
{
Q_UNUSED(event);
QPainter p(this);
p.drawPixmap(QPoint(30, 1) , m_star1 ? m_starPixmap : m_disabledStarPixmap);
p.drawPixmap(QPoint(55, 1) , m_star2 ? m_starPixmap : m_disabledStarPixmap);
p.drawPixmap(QPoint(80, 1) , m_star3 ? m_starPixmap : m_disabledStarPixmap);
switch (m_state) {
case BacktraceGenerator::Failed:
case BacktraceGenerator::FailedToStart: {
p.drawPixmap(QPoint(0, 1) , m_errorPixmap);
break;
}
case BacktraceGenerator::Loading:
case BacktraceGenerator::Loaded:
default:
break;
}
p.end();
}
diff --git a/src/bugzillaintegration/reportassistantpages_base.cpp b/src/bugzillaintegration/reportassistantpages_base.cpp
index 47d0aa70..1d8d0e63 100644
--- a/src/bugzillaintegration/reportassistantpages_base.cpp
+++ b/src/bugzillaintegration/reportassistantpages_base.cpp
@@ -1,463 +1,462 @@
/*******************************************************************
* reportassistantpages_base.cpp
* Copyright 2009 Dario Andres Rodriguez
* Copyright 2009 A. L. Spehr
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
******************************************************************/
#include "reportassistantpages_base.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "drkonqi.h"
#include "debuggermanager.h"
#include "crashedapplication.h"
#include "reportinterface.h"
#include "parser/backtraceparser.h"
#include "backtracegenerator.h"
#include "backtracewidget.h"
#include "drkonqi_globals.h"
#include "applicationdetailsexamples.h"
//BEGIN IntroductionPage
IntroductionPage::IntroductionPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
{
ui.setupUi(this);
ui.m_warningIcon->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-warning")).pixmap(64,64));
}
//END IntroductionPage
//BEGIN CrashInformationPage
CrashInformationPage::CrashInformationPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
{
m_backtraceWidget = new BacktraceWidget(DrKonqi::debuggerManager()->backtraceGenerator(), this, true);
connect(m_backtraceWidget, &BacktraceWidget::stateChanged, this, &CrashInformationPage::emitCompleteChanged);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0,0,0,0);
layout->addWidget(m_backtraceWidget);
layout->addSpacing(10); //We need this for better usability until we get something better
//If the backtrace was already fetched on the main dialog, save it.
BacktraceGenerator *btGenerator = DrKonqi::debuggerManager()->backtraceGenerator();
if (btGenerator->state() == BacktraceGenerator::Loaded) {
BacktraceParser::Usefulness use = btGenerator->parser()->backtraceUsefulness();
if (use != BacktraceParser::Useless && use != BacktraceParser::InvalidUsefulness) {
reportInterface()->setBacktrace(btGenerator->backtrace());
}
}
}
void CrashInformationPage::aboutToShow()
{
m_backtraceWidget->generateBacktrace();
m_backtraceWidget->hilightExtraDetailsLabel(false);
emitCompleteChanged();
}
void CrashInformationPage::aboutToHide()
{
BacktraceGenerator *btGenerator = DrKonqi::debuggerManager()->backtraceGenerator();
BacktraceParser::Usefulness use = btGenerator->parser()->backtraceUsefulness();
if (use != BacktraceParser::Useless && use != BacktraceParser::InvalidUsefulness) {
reportInterface()->setBacktrace(btGenerator->backtrace());
}
reportInterface()->setFirstBacktraceFunctions(btGenerator->parser()->firstValidFunctions());
}
bool CrashInformationPage::isComplete()
{
BacktraceGenerator *generator = DrKonqi::debuggerManager()->backtraceGenerator();
return (generator->state() != BacktraceGenerator::NotLoaded &&
generator->state() != BacktraceGenerator::Loading);
}
bool CrashInformationPage::showNextPage()
{
BacktraceParser::Usefulness use =
DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness();
if (DrKonqi::ignoreQuality()) {
return true;
}
if ((use == BacktraceParser::InvalidUsefulness || use == BacktraceParser::ProbablyUseless
|| use == BacktraceParser::Useless) && m_backtraceWidget->canInstallDebugPackages()) {
if ( KMessageBox::Yes == KMessageBox::questionYesNo(this,
i18nc("@info","This crash information is not useful enough, "
"do you want to try to improve it? You will need "
"to install some debugging packages."),
i18nc("@title:window","Crash Information is not useful enough")) ) {
m_backtraceWidget->hilightExtraDetailsLabel(true);
m_backtraceWidget->focusImproveBacktraceButton();
return false; //Cancel show next, to allow the user to write more
} else {
return true; //Allow to continue
}
} else {
return true;
}
}
//END CrashInformationPage
//BEGIN BugAwarenessPage
BugAwarenessPage::BugAwarenessPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
{
ui.setupUi(this);
ui.m_actionsInsideApp->setText(i18nc("@option:check kind of information the user can provide "
"about the crash, %1 is the application name",
"What I was doing when the application \"%1\" crashed",
DrKonqi::crashedApplication()->name()));
connect(ui.m_rememberGroup, static_cast(&QButtonGroup::buttonClicked), this, &BugAwarenessPage::updateCheckBoxes);
// Also listen to toggle so radio buttons are covered.
connect(ui.m_rememberGroup, static_cast(&QButtonGroup::buttonToggled), this, &BugAwarenessPage::updateCheckBoxes);
ui.m_appSpecificDetailsExamples->setVisible(reportInterface()->appDetailsExamples()->hasExamples());
ui.m_appSpecificDetailsExamples->setText(
i18nc("@label examples about information the user can provide",
"Examples: %1", reportInterface()->appDetailsExamples()->examples()));
ui.m_appSpecificDetailsExamples->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
}
void BugAwarenessPage::aboutToShow()
{
updateCheckBoxes();
}
void BugAwarenessPage::aboutToHide()
{
//Save data
ReportInterface::Reproducible reproducible = ReportInterface::ReproducibleUnsure;
switch(ui.m_reproducibleBox->currentIndex()) {
case 0: {
reproducible = ReportInterface::ReproducibleUnsure;
break;
}
case 1: {
reproducible = ReportInterface::ReproducibleNever;
break;
}
case 2: {
reproducible = ReportInterface::ReproducibleSometimes;
break;
}
case 3: {
reproducible = ReportInterface::ReproducibleEverytime;
break;
}
}
reportInterface()->setBugAwarenessPageData(ui.m_rememberCrashSituationYes->isChecked(),
reproducible,
ui.m_actionsInsideApp->isChecked(),
ui.m_unusualSituation->isChecked(),
ui.m_appSpecificDetails->isChecked());
}
void BugAwarenessPage::updateCheckBoxes()
{
const bool rememberSituation = ui.m_rememberCrashSituationYes->isChecked();
ui.m_reproducibleLabel->setEnabled(rememberSituation);
ui.m_reproducibleBox->setEnabled(rememberSituation);
ui.m_informationLabel->setEnabled(rememberSituation);
ui.m_actionsInsideApp->setEnabled(rememberSituation);
ui.m_unusualSituation->setEnabled(rememberSituation);
ui.m_appSpecificDetails->setEnabled(rememberSituation);
ui.m_appSpecificDetailsExamples->setEnabled(rememberSituation);
}
//END BugAwarenessPage
//BEGIN ConclusionPage
ConclusionPage::ConclusionPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
, m_needToReport(false)
{
m_isBKO = DrKonqi::crashedApplication()->bugReportAddress().isKdeBugzilla();
ui.setupUi(this);
KGuiItem::assign(ui.m_showReportInformationButton,
KGuiItem2(i18nc("@action:button", "&Show Contents of the Report"),
QIcon::fromTheme(QStringLiteral("document-preview")),
i18nc("@info:tooltip", "Use this button to show the generated "
"report information about this crash.")));
connect(ui.m_showReportInformationButton, &QPushButton::clicked, this, &ConclusionPage::openReportInformation);
ui.m_restartAppOnFinish->setVisible(false);
}
void ConclusionPage::finishClicked()
{
//Manual report
if (m_needToReport && !m_isBKO) {
const CrashedApplication *crashedApp = DrKonqi::crashedApplication();
BugReportAddress reportAddress = crashedApp->bugReportAddress();
QString report = reportInterface()->generateReportFullText(false);
if (reportAddress.isEmail()) {
QString subject = QStringLiteral("[%1] [%2] Automatic crash report generated by DrKonqi");
subject= subject.arg(crashedApp->name());
subject= subject.arg(crashedApp->datetime().toString(QStringLiteral("yyyy-MM-dd")));
KToolInvocation::invokeMailer(reportAddress, QLatin1String(""), QLatin1String("") , subject, report);
} else {
QUrl url(reportAddress);
if (QUrl(reportAddress).isRelative()) { //Scheme is missing
url = QUrl(QString::fromLatin1("https://%1").arg(reportAddress));
}
QDesktopServices::openUrl(url);
}
//Show a copy of the bug reported
openReportInformation();
}
//Restart application
if (ui.m_restartAppOnFinish->isChecked()) {
DrKonqi::crashedApplication()->restart();
}
}
void ConclusionPage::aboutToShow()
{
connect(assistant()->finishButton(), &QPushButton::clicked, this, &ConclusionPage::finishClicked);
ui.m_restartAppOnFinish->setVisible(false);
ui.m_restartAppOnFinish->setChecked(false);
const bool isDuplicate = reportInterface()->duplicateId() && !reportInterface()->attachToBugNumber();
m_needToReport = reportInterface()->isWorthReporting() && !isDuplicate;
emitCompleteChanged();
BugReportAddress reportAddress = DrKonqi::crashedApplication()->bugReportAddress();
BacktraceParser::Usefulness use =
DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness();
QString explanationHTML = QLatin1String("
").arg(i18nc("@info","The automatically generated "
"crash information is useful."));
break;
}
case BacktraceParser::MayBeUseful: {
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The automatically generated "
"crash information lacks some "
"details "
"but may be still be useful."));
break;
}
case BacktraceParser::ProbablyUseless: {
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The automatically generated "
"crash information lacks important details "
"and it is probably not helpful."));
break;
}
case BacktraceParser::Useless:
case BacktraceParser::InvalidUsefulness: {
BacktraceGenerator::State state = DrKonqi::debuggerManager()->backtraceGenerator()->state();
if (state == BacktraceGenerator::NotLoaded) {
backtraceGenerated = false;
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The crash information was "
"not generated because it was not needed."));
} else {
explanationHTML += QStringLiteral("
%1 %2
").arg(
i18nc("@info","The automatically generated crash "
"information does not contain enough information to be "
"helpful."),
xi18nc("@info","You can improve it by "
"installing debugging packages and reloading the crash on "
"the Crash Information page. You can get help with the Bug "
"Reporting Guide by clicking on the "
"Help button."));
//but this guide doesn't mention bt packages? that's techbase
//->>and the help guide mention techbase page...
}
break;
}
}
//User can provide enough information
if (reportInterface()->isBugAwarenessPageDataUseful()) {
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The information you can "
"provide could be considered helpful."));
} else {
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The information you can "
"provide is not considered helpful enough in this case."));
}
if (isDuplicate) {
explanationHTML += QStringLiteral("
%1
").arg(xi18nc("@info","Your problem has already been "
"reported as bug %1.", QString::number(reportInterface()->duplicateId())));
}
explanationHTML += QLatin1String("
");
ui.m_explanationLabel->setText(explanationHTML);
//Hide the "Show contents of the report" button if the backtrace was not generated
ui.m_showReportInformationButton->setVisible(backtraceGenerated);
if (m_needToReport) {
ui.m_conclusionsLabel->setText(QStringLiteral("
%1").arg(i18nc("@info","This "
"report is considered helpful.")));
if (m_isBKO) {
emitCompleteChanged();
ui.m_howToProceedLabel->setText(xi18nc("@info","This application's bugs are reported "
"to the KDE bug tracking system: click Next"
" to start the reporting process. "
"You can manually report at %1",
reportAddress));
} else {
if (!DrKonqi::crashedApplication()->hasBeenRestarted()) {
ui.m_restartAppOnFinish->setVisible(true);
}
ui.m_howToProceedLabel->setText(xi18nc("@info","This application is not supported in the "
"KDE bug tracking system. Click "
"Finish to report this bug to "
"the application maintainer. Also, you can manually "
"report at %1.", reportAddress));
emit finished(false);
}
} else { // (m_needToReport)
if (!DrKonqi::crashedApplication()->hasBeenRestarted()) {
ui.m_restartAppOnFinish->setVisible(true);
}
ui.m_conclusionsLabel->setText(QStringLiteral("
%1 %2
").arg(
i18nc("@info","This report does not contain enough information for the "
"developers, so the automated bug reporting process is not "
"enabled for this crash."),
i18nc("@info","If you wish, you can go back and change your "
"answers. ")));
//Only mention "manual reporting" if the backtrace was generated.
//FIXME separate the texts "manual reporting" / "click finish to close"
//"manual reporting" should be ~"manual report using the contents of the report"....
//FIXME for 4.5 (workflow, see ToDo)
if (backtraceGenerated) {
if (m_isBKO) {
ui.m_howToProceedLabel->setText(xi18nc("@info","You can manually report this bug "
"at %1. "
"Click Finish to close the "
"assistant.",
reportAddress));
} else {
ui.m_howToProceedLabel->setText(xi18nc("@info","You can manually report this "
"bug to its maintainer at %1. "
"Click Finish to close the "
"assistant.", reportAddress));
}
}
emit finished(true);
}
}
void ConclusionPage::aboutToHide()
{
disconnect(assistant()->finishButton(), &QPushButton::clicked, this, &ConclusionPage::finishClicked);
}
void ConclusionPage::openReportInformation()
{
if (!m_infoDialog) {
QString info = reportInterface()->generateReportFullText(false) + QLatin1Char('\n') +
i18nc("@info report to url/mail address","Report to %1",
DrKonqi::crashedApplication()->bugReportAddress());
m_infoDialog = new ReportInformationDialog(info);
}
m_infoDialog->show();
m_infoDialog->raise();
m_infoDialog->activateWindow();
}
bool ConclusionPage::isComplete()
{
return (m_isBKO && m_needToReport);
}
//END ConclusionPage
//BEGIN ReportInformationDialog
ReportInformationDialog::ReportInformationDialog(const QString & reportText)
: QDialog()
{
setAttribute(Qt::WA_DeleteOnClose, true);
setWindowTitle(i18nc("@title:window","Contents of the Report"));
ui.setupUi(this);
ui.m_reportInformationBrowser->setPlainText(reportText);
QPushButton* saveButton = new QPushButton(ui.buttonBox);
KGuiItem::assign(saveButton, KGuiItem2(i18nc("@action:button", "&Save to File..."),
QIcon::fromTheme(QStringLiteral("document-save")),
i18nc("@info:tooltip", "Use this button to save the "
"generated crash report information to "
"a file. You can use this option to report the "
"bug later.")));
connect(saveButton, &QPushButton::clicked, this, &ReportInformationDialog::saveReport);
ui.buttonBox->addButton(saveButton, QDialogButtonBox::ActionRole);
- resize(QSize(800, 600));
KConfigGroup config(KSharedConfig::openConfig(), "ReportInformationDialog");
KWindowConfig::restoreWindowSize(windowHandle(), config);
}
ReportInformationDialog::~ReportInformationDialog()
{
KConfigGroup config(KSharedConfig::openConfig(), "ReportInformationDialog");
KWindowConfig::saveWindowSize(windowHandle(), config);
}
void ReportInformationDialog::saveReport()
{
DrKonqi::saveReport(ui.m_reportInformationBrowser->toPlainText(), this);
}
//END ReportInformationDialog
diff --git a/src/bugzillaintegration/reportassistantpages_bugzilla.cpp b/src/bugzillaintegration/reportassistantpages_bugzilla.cpp
index 6d883f60..de0ea5ed 100644
--- a/src/bugzillaintegration/reportassistantpages_bugzilla.cpp
+++ b/src/bugzillaintegration/reportassistantpages_bugzilla.cpp
@@ -1,886 +1,883 @@
/*******************************************************************
* reportassistantpages_bugzilla.cpp
* Copyright 2009, 2010, 2011 Dario Andres Rodriguez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
******************************************************************/
#include "reportassistantpages_bugzilla.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "drkonqi_debug.h"
#include
#include
#include
#include
#include
/* Unhandled error dialog includes */
#include
#include
#include
#include
#include "reportinterface.h"
#include "systeminformation.h"
#include "crashedapplication.h"
#include "bugzillalib.h"
#include "statuswidget.h"
#include "drkonqi.h"
#include "drkonqi_globals.h"
#include "applicationdetailsexamples.h"
static const char kWalletEntryName[] = "drkonqi_bugzilla";
static const char kWalletEntryUsername[] = "username";
static const char kWalletEntryPassword[] = "password";
static QString konquerorKWalletEntryName = KDE_BUGZILLA_URL + QStringLiteral("index.cgi#login");
static const char konquerorKWalletEntryUsername[] = "Bugzilla_login";
static const char konquerorKWalletEntryPassword[] = "Bugzilla_password";
//BEGIN BugzillaLoginPage
BugzillaLoginPage::BugzillaLoginPage(ReportAssistantDialog * parent) :
ReportAssistantPage(parent),
m_wallet(nullptr), m_walletWasOpenedBefore(false),
m_bugzillaVersionFound(false)
{
connect(bugzillaManager(), &BugzillaManager::bugzillaVersionFound, this, &BugzillaLoginPage::bugzillaVersionFound);
connect(bugzillaManager(), &BugzillaManager::loginFinished, this, &BugzillaLoginPage::loginFinished);
connect(bugzillaManager(), &BugzillaManager::loginError, this, &BugzillaLoginPage::loginError);
ui.setupUi(this);
ui.m_statusWidget->setIdle(i18nc("@info:status '1' is replaced with the short URL of the bugzilla ",
"You need to login with your %1 account in order to proceed.",
QLatin1String(KDE_BUGZILLA_SHORT_URL)));
KGuiItem::assign(ui.m_loginButton, KGuiItem2(i18nc("@action:button", "Login"),
QIcon::fromTheme(QStringLiteral("network-connect")),
i18nc("@info:tooltip", "Use this button to login "
"to the KDE bug tracking system using the provided "
"username and password.")));
ui.m_loginButton->setEnabled(false);
connect(ui.m_loginButton, &QPushButton::clicked, this, &BugzillaLoginPage::loginClicked);
connect(ui.m_userEdit, &KLineEdit::returnPressed, this, &BugzillaLoginPage::loginClicked);
connect(ui.m_passwordEdit->lineEdit(), &QLineEdit::returnPressed, this, &BugzillaLoginPage::loginClicked);
connect(ui.m_userEdit, &KLineEdit::textChanged, this, &BugzillaLoginPage::updateLoginButtonStatus);
connect(ui.m_passwordEdit, &KPasswordLineEdit::passwordChanged, this, &BugzillaLoginPage::updateLoginButtonStatus);
ui.m_noticeLabel->setText(
xi18nc("@info/rich","You need a user account on the "
"KDE bug tracking system in order to "
"file a bug report, because we may need to contact you later "
"for requesting further information. If you do not have "
"one, you can freely create one here. "
"Please do not use disposable email accounts.",
DrKonqi::crashedApplication()->bugReportAddress(),
KDE_BUGZILLA_CREATE_ACCOUNT_URL));
}
bool BugzillaLoginPage::isComplete()
{
return bugzillaManager()->getLogged();
}
void BugzillaLoginPage::bugzillaVersionFound()
{
// Login depends on first knowing the Bugzilla software version number.
m_bugzillaVersionFound = true;
updateLoginButtonStatus();
}
void BugzillaLoginPage::updateLoginButtonStatus()
{
ui.m_loginButton->setEnabled( !ui.m_userEdit->text().isEmpty() &&
!ui.m_passwordEdit->password().isEmpty() &&
m_bugzillaVersionFound );
}
void BugzillaLoginPage::loginError(const QString & err, const QString & extendedMessage)
{
loginFinished(false);
ui.m_statusWidget->setIdle(xi18nc("@info:status","Error when trying to login: "
"%1.", err));
if (!extendedMessage.isEmpty()) {
new UnhandledErrorDialog(this, err, extendedMessage);
}
}
void BugzillaLoginPage::aboutToShow()
{
if (bugzillaManager()->getLogged()) {
ui.m_loginButton->setEnabled(false);
ui.m_userEdit->setEnabled(false);
ui.m_userEdit->clear();
ui.m_passwordEdit->setEnabled(false);
ui.m_passwordEdit->clear();
ui.m_loginButton->setVisible(false);
ui.m_userEdit->setVisible(false);
ui.m_passwordEdit->setVisible(false);
ui.m_userLabel->setVisible(false);
ui.m_passwordLabel->setVisible(false);
ui.m_savePasswordCheckBox->setVisible(false);
ui.m_noticeLabel->setVisible(false);
ui.m_statusWidget->setIdle(i18nc("@info:status the user is logged at the bugtracker site "
"as USERNAME",
"Logged in at the KDE bug tracking system (%1) as: %2.",
QLatin1String(KDE_BUGZILLA_SHORT_URL),
bugzillaManager()->getUsername()));
} else {
//Try to show wallet dialog once this dialog is shown
QTimer::singleShot(100, this, &BugzillaLoginPage::walletLogin);
}
}
bool BugzillaLoginPage::kWalletEntryExists(const QString& entryName)
{
return !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
KWallet::Wallet::FormDataFolder(),
entryName);
}
void BugzillaLoginPage::openWallet()
{
//Store if the wallet was previously opened so we can know if we should close it later
m_walletWasOpenedBefore = KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet());
//Request open the wallet
m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(),
static_cast(this->parent())->winId());
}
void BugzillaLoginPage::walletLogin()
{
if (!m_wallet) {
if (kWalletEntryExists(QLatin1String(kWalletEntryName))) { //Key exists!
openWallet();
ui.m_savePasswordCheckBox->setCheckState(Qt::Checked);
//Was the wallet opened?
if (m_wallet) {
m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
//Use wallet data to try login
QMap values;
m_wallet->readMap(QLatin1String(kWalletEntryName), values);
QString username = values.value(QLatin1String(kWalletEntryUsername));
QString password = values.value(QLatin1String(kWalletEntryPassword));
if (!username.isEmpty() && !password.isEmpty()) {
ui.m_userEdit->setText(username);
ui.m_passwordEdit->setPassword(password);
}
}
} else if (kWalletEntryExists(konquerorKWalletEntryName)) {
//If the DrKonqi entry is empty, but a Konqueror entry exists, use and copy it.
openWallet();
if (m_wallet) {
m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
//Fetch Konqueror data
QMap values;
m_wallet->readMap(konquerorKWalletEntryName, values);
QString username = values.value(QLatin1String(konquerorKWalletEntryUsername));
QString password = values.value(QLatin1String(konquerorKWalletEntryPassword));
if (!username.isEmpty() && !password.isEmpty()) {
//Copy to DrKonqi own entries
values.clear();
values.insert(QLatin1String(kWalletEntryUsername), username);
values.insert(QLatin1String(kWalletEntryPassword), password);
m_wallet->writeMap(QLatin1String(kWalletEntryName), values);
ui.m_savePasswordCheckBox->setCheckState(Qt::Checked);
ui.m_userEdit->setText(username);
ui.m_passwordEdit->setPassword(password);
}
}
}
}
}
bool BugzillaLoginPage::canSetCookies()
{
if (bugzillaManager()->securityMethod() != BugzillaManager::UseCookies) {
qCDebug(DRKONQI_LOG) << "Bugzilla software no longer issues cookies.";
return false;
}
QDBusInterface kded(QStringLiteral("org.kde.kded5"),
QStringLiteral("/kded"),
QStringLiteral("org.kde.kded5"));
QDBusReply kcookiejarLoaded = kded.call(QStringLiteral("loadModule"),
QStringLiteral("kcookiejar"));
if (!kcookiejarLoaded.isValid()) {
KMessageBox::error(this, i18n("Failed to communicate with kded. Make sure it is running."));
return false;
} else if (!kcookiejarLoaded.value()) {
KMessageBox::error(this, i18n("Failed to load KCookieServer. Check your KDE installation."));
return false;
}
QDBusInterface kcookiejar(QStringLiteral("org.kde.kded5"),
QStringLiteral("/modules/kcookiejar"),
QStringLiteral("org.kde.KCookieServer"));
QDBusReply advice = kcookiejar.call(QStringLiteral("getDomainAdvice"),
KDE_BUGZILLA_URL);
if (!advice.isValid()) {
KMessageBox::error(this, i18n("Failed to communicate with KCookieServer."));
return false;
}
qCDebug(DRKONQI_LOG) << "Got reply from KCookieServer:" << advice.value();
if (advice.value() == QLatin1String("Reject")) {
QString msg = i18nc("@info 1 is the bugzilla website url",
"Cookies are not allowed in your KDE network settings. In order to "
"proceed, you need to allow %1 to set cookies.", KDE_BUGZILLA_URL);
KGuiItem yesItem = KStandardGuiItem::yes();
yesItem.setText(i18nc("@action:button 1 is the bugzilla website url",
"Allow %1 to set cookies", KDE_BUGZILLA_URL));
KGuiItem noItem = KStandardGuiItem::no();
noItem.setText(i18nc("@action:button do not allow the bugzilla website "
"to set cookies", "No, do not allow"));
if (KMessageBox::warningYesNo(this, msg, QString(), yesItem, noItem) == KMessageBox::Yes) {
QDBusReply success = kcookiejar.call(QStringLiteral("setDomainAdvice"),
KDE_BUGZILLA_URL,
QStringLiteral("Accept"));
if (!success.isValid() || !success.value()) {
qCWarning(DRKONQI_LOG) << "Failed to set domain advice in KCookieServer";
return false;
} else {
return true;
}
} else {
return false;
}
}
return true;
}
void BugzillaLoginPage::loginClicked()
{
if (!(ui.m_userEdit->text().isEmpty() || ui.m_passwordEdit->password().isEmpty())) {
if ((bugzillaManager()->securityMethod() == BugzillaManager::UseCookies)
&& (!canSetCookies())) {
return;
}
updateWidget(false);
if (ui.m_savePasswordCheckBox->checkState()==Qt::Checked) { //Wants to save data
if (!m_wallet) {
openWallet();
}
//Got wallet open ?
if (m_wallet) {
m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
QMap values;
values.insert(QLatin1String(kWalletEntryUsername), ui.m_userEdit->text());
values.insert(QLatin1String(kWalletEntryPassword), ui.m_passwordEdit->password());
m_wallet->writeMap(QLatin1String(kWalletEntryName), values);
}
} else { //User doesn't want to save or wants to remove.
if (kWalletEntryExists(QLatin1String(kWalletEntryName))) {
if (!m_wallet) {
openWallet();
}
//Got wallet open ?
if (m_wallet) {
m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
m_wallet->removeEntry(QLatin1String(kWalletEntryName));
}
}
}
ui.m_statusWidget->setBusy(i18nc("@info:status '1' is a url, '2' the username",
"Performing login at %1 as %2...",
QLatin1String(KDE_BUGZILLA_SHORT_URL), ui.m_userEdit->text()));
bugzillaManager()->tryLogin(ui.m_userEdit->text(), ui.m_passwordEdit->password());
} else {
loginFinished(false);
}
}
void BugzillaLoginPage::updateWidget(bool enabled)
{
ui.m_loginButton->setEnabled(enabled);
ui.m_userLabel->setEnabled(enabled);
ui.m_passwordLabel->setEnabled(enabled);
ui.m_userEdit->setEnabled(enabled);
ui.m_passwordEdit->setEnabled(enabled);
ui.m_savePasswordCheckBox->setEnabled(enabled);
}
void BugzillaLoginPage::loginFinished(bool logged)
{
if (logged) {
emitCompleteChanged();
aboutToShow();
if (m_wallet) {
if (m_wallet->isOpen() && !m_walletWasOpenedBefore) {
m_wallet->lockWallet();
}
}
emit loggedTurnToNextPage();
} else {
ui.m_statusWidget->setIdle(i18nc("@info:status","Error: Invalid username or "
"password"));
updateWidget(true);
ui.m_userEdit->setFocus(Qt::OtherFocusReason);
}
}
BugzillaLoginPage::~BugzillaLoginPage()
{
//Close wallet if we close the assistant in this step
if (m_wallet) {
if (m_wallet->isOpen() && !m_walletWasOpenedBefore) {
m_wallet->lockWallet();
}
delete m_wallet;
}
}
//END BugzillaLoginPage
//BEGIN BugzillaInformationPage
BugzillaInformationPage::BugzillaInformationPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent),
m_textsOK(false), m_distributionComboSetup(false), m_distroComboVisible(false),
m_requiredCharacters(1)
{
ui.setupUi(this);
m_textCompleteBar = new KCapacityBar(KCapacityBar::DrawTextInline, this);
ui.horizontalLayout_2->addWidget(m_textCompleteBar);
connect(ui.m_titleEdit, &KLineEdit::textChanged, this, &BugzillaInformationPage::checkTexts);
connect(ui.m_detailsEdit, &QTextEdit::textChanged, this, &BugzillaInformationPage::checkTexts);
connect(ui.m_titleLabel, &QLabel::linkActivated, this, &BugzillaInformationPage::showTitleExamples);
connect(ui.m_detailsLabel, &QLabel::linkActivated, this, &BugzillaInformationPage::showDescriptionHelpExamples);
ui.m_compiledSourcesCheckBox->setChecked(
DrKonqi::systemInformation()->compiledSources());
}
void BugzillaInformationPage::aboutToShow()
{
if (!m_distributionComboSetup) {
//Autodetecting distro failed ?
if (DrKonqi::systemInformation()->bugzillaPlatform() == QLatin1String("unspecified")) {
m_distroComboVisible = true;
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Unspecified"),QStringLiteral("unspecified"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Archlinux"), QStringLiteral("Archlinux Packages"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Chakra"), QStringLiteral("Chakra"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Debian stable"), QStringLiteral("Debian stable"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Debian testing"), QStringLiteral("Debian testing"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Debian unstable"), QStringLiteral("Debian unstable"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Exherbo"), QStringLiteral("Exherbo Packages"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Fedora"), QStringLiteral("Fedora RPMs"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Gentoo"), QStringLiteral("Gentoo Packages"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Mageia"), QStringLiteral("Mageia RPMs"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Mandriva"), QStringLiteral("Mandriva RPMs"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Neon"), QStringLiteral("Neon Packages"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"OpenSUSE"), QStringLiteral("openSUSE RPMs"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Pardus"), QStringLiteral("Pardus Packages"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"RedHat"), QStringLiteral("RedHat RPMs"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Slackware"), QStringLiteral("Slackware Packages"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Ubuntu (and derivatives)"),
QStringLiteral("Ubuntu Packages"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"FreeBSD (Ports)"), QStringLiteral("FreeBSD Ports"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"NetBSD (pkgsrc)"), QStringLiteral("NetBSD pkgsrc"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"OpenBSD"), QStringLiteral("OpenBSD Packages"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Mac OS X"), QStringLiteral("MacPorts Packages"));
ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
"Solaris"), QStringLiteral("Solaris Packages"));
//Restore previously selected bugzilla platform (distribution)
KConfigGroup config(KSharedConfig::openConfig(), "BugzillaInformationPage");
QString entry = config.readEntry("BugzillaPlatform","unspecified");
int index = ui.m_distroChooserCombo->findData(entry);
if ( index == -1 ) index = 0;
ui.m_distroChooserCombo->setCurrentIndex(index);
} else {
ui.m_distroChooserCombo->setVisible(false);
}
m_distributionComboSetup = true;
}
//Calculate the minimum number of characters required for a description
//If creating a new report: minimum 40, maximum 80
//If attaching to an existent report: minimum 30, maximum 50
int multiplier = (reportInterface()->attachToBugNumber() == 0) ? 10 : 5;
m_requiredCharacters = 20 + (reportInterface()->selectedOptionsRating() * multiplier);
//Fill the description textedit with some headings:
QString descriptionTemplate;
if (ui.m_detailsEdit->toPlainText().isEmpty()) {
if (reportInterface()->userCanProvideActionsAppDesktop()) {
descriptionTemplate += QLatin1String("- What I was doing when the application crashed:\n\n");
}
if (reportInterface()->userCanProvideUnusualBehavior()) {
descriptionTemplate += QLatin1String("- Unusual behavior I noticed:\n\n");
}
if (reportInterface()->userCanProvideApplicationConfigDetails()) {
descriptionTemplate += QLatin1String("- Custom settings of the application:\n\n");
}
ui.m_detailsEdit->setText(descriptionTemplate);
}
checkTexts(); //May be the options (canDetail) changed and we need to recheck
}
int BugzillaInformationPage::currentDescriptionCharactersCount()
{
QString description = ui.m_detailsEdit->toPlainText();
//Do not count template messages, and other misc chars
description.remove(QStringLiteral("What I was doing when the application crashed"));
description.remove(QStringLiteral("Unusual behavior I noticed"));
description.remove(QStringLiteral("Custom settings of the application"));
description.remove(QLatin1Char('\n'));
description.remove(QLatin1Char('-'));
description.remove(QLatin1Char(':'));
description.remove(QLatin1Char(' '));
return description.size();
}
void BugzillaInformationPage::checkTexts()
{
//If attaching this report to an existing one then the title is not needed
bool showTitle = (reportInterface()->attachToBugNumber() == 0);
ui.m_titleEdit->setVisible(showTitle);
ui.m_titleLabel->setVisible(showTitle);
bool ok = !((ui.m_titleEdit->isVisible() && ui.m_titleEdit->text().isEmpty())
|| ui.m_detailsEdit->toPlainText().isEmpty());
QString message;
int percent = currentDescriptionCharactersCount() * 100 / m_requiredCharacters;
if (percent >= 100) {
percent = 100;
message = i18nc("the minimum required length of a text was reached",
"Minimum length reached");
} else {
message = i18nc("the minimum required length of a text wasn't reached yet",
"Provide more information");
}
m_textCompleteBar->setValue(percent);
m_textCompleteBar->setText(message);
if (ok != m_textsOK) {
m_textsOK = ok;
emitCompleteChanged();
}
}
bool BugzillaInformationPage::showNextPage()
{
checkTexts();
if (m_textsOK) {
bool detailsShort = currentDescriptionCharactersCount() < m_requiredCharacters;
if (detailsShort) {
//The user input is less than we want.... encourage to write more
QString message = i18nc("@info","The description about the crash details does not provide "
"enough information yet.
");
message += QLatin1Char(' ') + i18nc("@info","The amount of required information is proportional to "
"the quality of the other information like the backtrace "
"or the reproducibility rate."
"
");
if (reportInterface()->userCanProvideActionsAppDesktop()
|| reportInterface()->userCanProvideUnusualBehavior()
|| reportInterface()->userCanProvideApplicationConfigDetails()) {
message += QLatin1Char(' ') + i18nc("@info","Previously, you told DrKonqi that you could provide some "
"contextual information. Try writing more details about your situation. "
"(even little ones could help us.)
");
}
message += QLatin1Char(' ') + i18nc("@info","If you cannot provide more information, your report "
"will probably waste developers' time. Can you tell us more?");
KGuiItem yesItem = KStandardGuiItem::yes();
yesItem.setText(i18n("Yes, let me add more information"));
KGuiItem noItem = KStandardGuiItem::no();
noItem.setText(i18n("No, I cannot add any other information"));
if (KMessageBox::warningYesNo(this, message,
i18nc("@title:window","We need more information"),
yesItem, noItem)
== KMessageBox::No) {
//Request the assistant to close itself (it will prompt for confirmation anyways)
assistant()->close();
return false;
}
} else {
return true;
}
}
return false;
}
bool BugzillaInformationPage::isComplete()
{
return m_textsOK;
}
void BugzillaInformationPage::aboutToHide()
{
//Save fields data
reportInterface()->setTitle(ui.m_titleEdit->text());
reportInterface()->setDetailText(ui.m_detailsEdit->toPlainText());
if (m_distroComboVisible) {
//Save bugzilla platform (distribution)
QString bugzillaPlatform = ui.m_distroChooserCombo->itemData(
ui.m_distroChooserCombo->currentIndex()).toString();
KConfigGroup config(KSharedConfig::openConfig(), "BugzillaInformationPage");
config.writeEntry("BugzillaPlatform", bugzillaPlatform);
DrKonqi::systemInformation()->setBugzillaPlatform(bugzillaPlatform);
}
bool compiledFromSources = ui.m_compiledSourcesCheckBox->checkState() == Qt::Checked;
DrKonqi::systemInformation()->setCompiledSources(compiledFromSources);
}
void BugzillaInformationPage::showTitleExamples()
{
QString titleExamples = xi18nc("@info:tooltip examples of good bug report titles",
"Examples of good titles:\"Plasma crashed after adding the Notes "
"widget and writing on it\"\"Konqueror crashed when accessing the Facebook "
"application 'X'\"\"Kopete suddenly closed after resuming the computer and "
"talking to a MSN buddy\"\"Kate closed while editing a log file and pressing the "
"Delete key a couple of times\"");
QToolTip::showText(QCursor::pos(), titleExamples);
}
void BugzillaInformationPage::showDescriptionHelpExamples()
{
QString descriptionHelp = i18nc("@info:tooltip help and examples of good bug descriptions",
"Describe in as much detail as possible the crash circumstances:");
if (reportInterface()->userCanProvideActionsAppDesktop()) {
descriptionHelp += QStringLiteral(" ") +
i18nc("@info:tooltip help and examples of good bug descriptions",
"- Detail which actions were you taking inside and outside the "
"application an instant before the crash.");
}
if (reportInterface()->userCanProvideUnusualBehavior()) {
descriptionHelp += QStringLiteral(" ") +
i18nc("@info:tooltip help and examples of good bug descriptions",
"- Note if you noticed any unusual behavior in the application "
"or in the whole environment.");
}
if (reportInterface()->userCanProvideApplicationConfigDetails()) {
descriptionHelp += QStringLiteral(" ") +
i18nc("@info:tooltip help and examples of good bug descriptions",
"- Note any non-default configuration in the application.");
if (reportInterface()->appDetailsExamples()->hasExamples()) {
descriptionHelp += QLatin1Char(' ') +
i18nc("@info:tooltip examples of configuration details. "
"the examples are already translated",
"Examples: %1",
reportInterface()->appDetailsExamples()->examples());
}
}
QToolTip::showText(QCursor::pos(), descriptionHelp);
}
//END BugzillaInformationPage
//BEGIN BugzillaPreviewPage
BugzillaPreviewPage::BugzillaPreviewPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
{
ui.setupUi(this);
}
void BugzillaPreviewPage::aboutToShow()
{
ui.m_previewEdit->setText(reportInterface()->generateReportFullText(true));
}
//END BugzillaPreviewPage
//BEGIN BugzillaSendPage
BugzillaSendPage::BugzillaSendPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent),
m_contentsDialog(nullptr)
{
connect(reportInterface(), &ReportInterface::reportSent, this, &BugzillaSendPage::sent);
connect(reportInterface(), &ReportInterface::sendReportError, this, &BugzillaSendPage::sendError);
ui.setupUi(this);
KGuiItem::assign(ui.m_retryButton, KGuiItem2(i18nc("@action:button", "Retry..."),
QIcon::fromTheme(QStringLiteral("view-refresh")),
i18nc("@info:tooltip", "Use this button to retry "
"sending the crash report if it failed before.")));
KGuiItem::assign(ui.m_showReportContentsButton,
KGuiItem2(i18nc("@action:button", "Sho&w Contents of the Report"),
QIcon::fromTheme(QStringLiteral("document-preview")),
i18nc("@info:tooltip", "Use this button to show the generated "
"report information about this crash.")));
connect(ui.m_showReportContentsButton, &QPushButton::clicked, this, &BugzillaSendPage::openReportContents);
ui.m_retryButton->setVisible(false);
connect(ui.m_retryButton, &QAbstractButton::clicked, this , &BugzillaSendPage::retryClicked);
ui.m_launchPageOnFinish->setVisible(false);
ui.m_restartAppOnFinish->setVisible(false);
connect(assistant()->finishButton(), &QPushButton::clicked, this, &BugzillaSendPage::finishClicked);
}
void BugzillaSendPage::retryClicked()
{
ui.m_retryButton->setEnabled(false);
aboutToShow();
}
void BugzillaSendPage::aboutToShow()
{
ui.m_statusWidget->setBusy(i18nc("@info:status","Sending crash report... (please wait)"));
reportInterface()->sendBugReport();
}
void BugzillaSendPage::sent(int bug_id)
{
ui.m_statusWidget->setVisible(false);
ui.m_retryButton->setEnabled(false);
ui.m_retryButton->setVisible(false);
ui.m_showReportContentsButton->setVisible(false);
ui.m_launchPageOnFinish->setVisible(true);
ui.m_restartAppOnFinish->setVisible(!DrKonqi::crashedApplication()->hasBeenRestarted());
ui.m_restartAppOnFinish->setChecked(false);
reportUrl = bugzillaManager()->urlForBug(bug_id);
ui.m_finishedLabel->setText(xi18nc("@info/rich","Crash report sent."
"URL: %1"
"Thank you for being part of KDE. "
"You can now close this window.", reportUrl));
emit finished(false);
}
void BugzillaSendPage::sendError(const QString & errorString, const QString & extendedMessage)
{
ui.m_statusWidget->setIdle(xi18nc("@info:status","Error sending the crash report: "
"%1.", errorString));
ui.m_retryButton->setEnabled(true);
ui.m_retryButton->setVisible(true);
if (!extendedMessage.isEmpty()) {
new UnhandledErrorDialog(this,errorString, extendedMessage);
}
}
void BugzillaSendPage::finishClicked()
{
if (ui.m_launchPageOnFinish->isChecked() && !reportUrl.isEmpty()) {
QDesktopServices::openUrl(QUrl(reportUrl));
}
if (ui.m_restartAppOnFinish->isChecked()) {
DrKonqi::crashedApplication()->restart();
}
}
void BugzillaSendPage::openReportContents()
{
if (!m_contentsDialog)
{
QString report = reportInterface()->generateReportFullText(false) + QLatin1Char('\n') +
i18nc("@info report to KDE bugtracker address","Report to %1",
DrKonqi::crashedApplication()->bugReportAddress());
m_contentsDialog = new ReportInformationDialog(report);
}
m_contentsDialog->show();
m_contentsDialog->raise();
m_contentsDialog->activateWindow();
}
//END BugzillaSendPage
/* Dialog for Unhandled Bugzilla Errors */
/* The user can save the bugzilla html output to check the error and/or to report this as a DrKonqi bug */
//BEGIN UnhandledErrorDialog
UnhandledErrorDialog::UnhandledErrorDialog(QWidget * parent, const QString & error, const QString & extendedMessage)
: QDialog(parent)
{
setWindowTitle(i18nc("@title:window", "Unhandled Bugzilla Error"));
setWindowModality(Qt::ApplicationModal);
QPushButton* saveButton = new QPushButton(this);
saveButton->setText(i18nc("@action:button save html to a file","Save to a file"));
saveButton->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
connect(saveButton, &QPushButton::clicked, this, &UnhandledErrorDialog::saveErrorMessage);
setAttribute(Qt::WA_DeleteOnClose);
QTextBrowser * htmlView = new QTextBrowser(this);
QLabel * iconLabel = new QLabel(this);
iconLabel->setFixedSize(32, 32);
iconLabel->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-warning")).pixmap(32, 32));
QLabel * mainLabel = new QLabel(this);
mainLabel->setWordWrap(true);
mainLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
QHBoxLayout * titleLayout = new QHBoxLayout();
titleLayout->setContentsMargins(5,2,5,2);
titleLayout->setSpacing(5);
titleLayout->addWidget(iconLabel);
titleLayout->addWidget(mainLabel);
QDialogButtonBox* buttonBox = new QDialogButtonBox(this);
buttonBox->setStandardButtons(QDialogButtonBox::Close);
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
QVBoxLayout * layout = new QVBoxLayout(this);
layout->addLayout(titleLayout);
layout->addWidget(htmlView);
layout->addWidget(buttonBox);
m_extendedHTMLError = extendedMessage;
mainLabel->setText(i18nc("@label", "There was an unhandled Bugzilla error: %1. "
"Below is the HTML that DrKonqi received. "
"Try to perform the action again or save this error page "
"to submit a bug against DrKonqi.").arg(error));
htmlView->setHtml(extendedMessage);
- setMinimumSize(QSize(550, 350));
- resize(minimumSize());
-
show();
}
void UnhandledErrorDialog::saveErrorMessage()
{
QString defaultName = QLatin1String("drkonqi-unhandled-bugzilla-error.html");
QPointer dlg(new QFileDialog(this));
dlg->selectFile(defaultName);
dlg->setWindowTitle(i18nc("@title:window","Select Filename"));
dlg->setAcceptMode(QFileDialog::AcceptSave);
dlg->setFileMode(QFileDialog::AnyFile);
dlg->setConfirmOverwrite(true);
if ( dlg->exec() == QDialog::Accepted )
{
if (!dlg) {
//Dialog closed externally (ex. via DBus)
return;
}
QUrl fileUrl;
if(!dlg->selectedUrls().isEmpty())
fileUrl = dlg->selectedUrls().first();
if (fileUrl.isValid()) {
QTemporaryFile tf;
if (tf.open()) {
QTextStream ts(&tf);
ts << m_extendedHTMLError;
ts.flush();
} else {
KMessageBox::sorry(this, xi18nc("@info","Cannot open file %1 "
"for writing.", tf.fileName()));
delete dlg;
return;
}
KIO::FileCopyJob* job = KIO::file_copy(QUrl::fromLocalFile(tf.fileName()), fileUrl);
KJobWidgets::setWindow(job, this);
if (!job->exec()) {
KMessageBox::sorry(this, job->errorString());
}
}
}
delete dlg;
}
//END UnhandledErrorDialog
diff --git a/src/bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp b/src/bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp
index a3961f72..98ea9147 100644
--- a/src/bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp
+++ b/src/bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp
@@ -1,996 +1,992 @@
/*******************************************************************
* reportassistantpages_bugzilla_duplicates.cpp
* Copyright 2009 Dario Andres Rodriguez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
******************************************************************/
#include "reportassistantpages_bugzilla_duplicates.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "drkonqi_globals.h"
#include "reportinterface.h"
#include "statuswidget.h"
//BEGIN BugzillaDuplicatesPage
BugzillaDuplicatesPage::BugzillaDuplicatesPage(ReportAssistantDialog * parent):
ReportAssistantPage(parent),
m_searching(false),
m_foundDuplicate(false)
{
resetDates();
connect(bugzillaManager(), &BugzillaManager::searchFinished,
this, &BugzillaDuplicatesPage::searchFinished);
connect(bugzillaManager(), SIGNAL(searchError(QString)),
this, SLOT(searchError(QString)));
ui.setupUi(this);
ui.information->hide();
connect(ui.m_bugListWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
this, SLOT(itemClicked(QTreeWidgetItem*,int)));
connect(ui.m_bugListWidget, &QTreeWidget::itemSelectionChanged, this, &BugzillaDuplicatesPage::itemSelectionChanged);
QHeaderView * header = ui.m_bugListWidget->header();
header->setSectionResizeMode(0, QHeaderView::ResizeToContents);
header->setSectionResizeMode(1, QHeaderView::Interactive);
//Create manual bug report entry (first one)
QTreeWidgetItem * customBugItem = new QTreeWidgetItem(
QStringList() << i18nc("@item:intable custom/manaul bug report number", "Manual")
<< i18nc("@item:intable custom bug report number description",
"Manually enter a bug report ID"));
customBugItem->setData(0, Qt::UserRole, QLatin1String("custom"));
customBugItem->setIcon(1, QIcon::fromTheme(QStringLiteral("edit-rename")));
QString helpMessage = i18nc("@info:tooltip / whatsthis",
"Select this option to manually load a specific bug report");
customBugItem->setToolTip(0, helpMessage);
customBugItem->setToolTip(1, helpMessage);
customBugItem->setWhatsThis(0, helpMessage);
customBugItem->setWhatsThis(1, helpMessage);
ui.m_bugListWidget->addTopLevelItem(customBugItem);
m_searchMoreGuiItem = KGuiItem2(i18nc("@action:button", "Search for more reports"),
QIcon::fromTheme(QStringLiteral("edit-find")),
i18nc("@info:tooltip", "Use this button to "
"search for more similar bug reports on an "
"earlier date."));
KGuiItem::assign(ui.m_searchMoreButton, m_searchMoreGuiItem);
connect(ui.m_searchMoreButton, &QAbstractButton::clicked, this, &BugzillaDuplicatesPage::searchMore);
m_retrySearchGuiItem = KGuiItem2(i18nc("@action:button", "Retry search"),
QIcon::fromTheme(QStringLiteral("edit-find")),
i18nc("@info:tooltip", "Use this button to "
"retry the search that previously "
"failed."));
KGuiItem::assign(ui.m_openReportButton, KGuiItem2(i18nc("@action:button", "Open selected report"),
QIcon::fromTheme(QStringLiteral("document-preview")),
i18nc("@info:tooltip", "Use this button to view "
"the information of the selected bug report.")));
connect(ui.m_openReportButton, &QAbstractButton::clicked, this, &BugzillaDuplicatesPage::openSelectedReport);
KGuiItem::assign(ui.m_stopSearchButton, KGuiItem2(i18nc("@action:button", "Stop searching"),
QIcon::fromTheme(QStringLiteral("process-stop")),
i18nc("@info:tooltip", "Use this button to stop "
"the current search.")));
ui.m_stopSearchButton->setText(QString()); //FIXME
connect(ui.m_stopSearchButton, &QAbstractButton::clicked, this, &BugzillaDuplicatesPage::stopCurrentSearch);
//Possible duplicates list and buttons
connect(ui.m_selectedDuplicatesList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
this, SLOT(itemClicked(QListWidgetItem*)));
connect(ui.m_selectedDuplicatesList, &QListWidget::itemSelectionChanged,
this, &BugzillaDuplicatesPage::possibleDuplicateSelectionChanged);
KGuiItem::assign(ui.m_removeSelectedDuplicateButton,
KGuiItem2(i18nc("@action:button remove the selected item from a list", "Remove"),
QIcon::fromTheme(QStringLiteral("list-remove")),
i18nc("@info:tooltip", "Use this button to remove a selected possible duplicate")));
ui.m_removeSelectedDuplicateButton->setEnabled(false);
connect(ui.m_removeSelectedDuplicateButton, &QAbstractButton::clicked, this,
&BugzillaDuplicatesPage::removeSelectedDuplicate);
ui.m_attachToReportIcon->setPixmap(QIcon::fromTheme(QStringLiteral("mail-attachment")).pixmap(16,16));
ui.m_attachToReportIcon->setFixedSize(16,16);
ui.m_attachToReportIcon->setVisible(false);
ui.m_attachToReportLabel->setVisible(false);
ui.m_attachToReportLabel->setContextMenuPolicy(Qt::NoContextMenu);
connect(ui.m_attachToReportLabel, &QLabel::linkActivated, this,
&BugzillaDuplicatesPage::cancelAttachToBugReport);
ui.information->setContextMenuPolicy(Qt::NoContextMenu);
connect(ui.information, &QLabel::linkActivated, this, &BugzillaDuplicatesPage::informationClicked);
showDuplicatesPanel(false);
}
BugzillaDuplicatesPage::~BugzillaDuplicatesPage()
{
}
void BugzillaDuplicatesPage::aboutToShow()
{
//Perform initial search if we are not currently searching and if there are no results yet
if (!m_searching && ui.m_bugListWidget->topLevelItemCount() == 1 && canSearchMore()) {
searchMore();
}
}
void BugzillaDuplicatesPage::aboutToHide()
{
stopCurrentSearch();
//Save selected possible duplicates by user
QStringList possibleDuplicates;
int count = ui.m_selectedDuplicatesList->count();
for(int i = 0; iitem(i)->text();
}
reportInterface()->setPossibleDuplicates(possibleDuplicates);
//Save possible duplicates by query
QStringList duplicatesByQuery;
count = ui.m_bugListWidget->topLevelItemCount();
for(int i = 1; itopLevelItem(i)->text(0);
}
reportInterface()->setPossibleDuplicatesByQuery(duplicatesByQuery);
}
bool BugzillaDuplicatesPage::isComplete()
{
return !m_searching;
}
bool BugzillaDuplicatesPage::showNextPage()
{
//Ask the user to check all the possible duplicates...
if (ui.m_bugListWidget->topLevelItemCount() != 1 && ui.m_selectedDuplicatesList->count() == 0
&& reportInterface()->attachToBugNumber() == 0 && !m_foundDuplicate) {
//The user didn't selected any possible duplicate nor a report to attach the new info.
//Double check this, we need to reduce the duplicate count.
KGuiItem noDuplicatesButton;
noDuplicatesButton.setText(i18n("There are no real duplicates"));
noDuplicatesButton.setWhatsThis(i18n("Press this button to declare that, in your opinion "
"and according to your experience, the reports found "
"as similar do not match the crash you have "
"experienced, and you believe it is unlikely that a "
"better match would be found after further review."));
noDuplicatesButton.setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")));
KGuiItem letMeCheckMoreReportsButton;
letMeCheckMoreReportsButton.setText(i18n("Let me check more reports"));
letMeCheckMoreReportsButton.setWhatsThis(i18n("Press this button if you would rather "
"review more reports in order to find a "
"match for the crash you have experienced."));
letMeCheckMoreReportsButton.setIcon(QIcon::fromTheme(QStringLiteral("document-preview")));
if (KMessageBox::questionYesNo(this,
i18nc("@info","You have not selected any possible duplicates, or a report to which to attach your "
"crash information. Have you read all the reports, and can you confirm that there are no "
"real duplicates?"),
i18nc("@title:window","No selected possible duplicates"), letMeCheckMoreReportsButton,
noDuplicatesButton)
== KMessageBox::Yes) {
return false;
}
}
return true;
}
//BEGIN Search related methods
void BugzillaDuplicatesPage::searchMore()
{
//1 year back
m_searchingEndDate = m_startDate;
m_searchingStartDate = m_searchingEndDate.addYears(-1);
performSearch();
}
void BugzillaDuplicatesPage::performSearch()
{
markAsSearching(true);
QString startDateStr = m_searchingStartDate.toString(QStringLiteral("yyyy-MM-dd"));
QString endDateStr = m_searchingEndDate.toString(QStringLiteral("yyyy-MM-dd"));
ui.m_statusWidget->setBusy(i18nc("@info:status","Searching for duplicates (from %1 to %2)...",
startDateStr, endDateStr));
//Bugzilla will not search on Today bugs if we send the date.
//we need to send "Now"
if (m_searchingEndDate == QDate::currentDate()) {
endDateStr = QLatin1String("Now");
}
#if 1
BugReport report = reportInterface()->newBugReportTemplate();
bugzillaManager()->searchBugs(reportInterface()->relatedBugzillaProducts(),
report.bugSeverity(), startDateStr, endDateStr,
reportInterface()->firstBacktraceFunctions().join(QStringLiteral(" ")));
#else //Test search
bugzillaManager()->searchBugs(QStringList() << "plasma", "crash", startDateStr, endDateStr,
"QGraphicsScenePrivate::processDirtyItemsRecursive");
#endif
}
void BugzillaDuplicatesPage::stopCurrentSearch()
{
if (m_searching) {
bugzillaManager()->stopCurrentSearch();
markAsSearching(false);
if (m_startDate==m_endDate) { //Never searched
ui.m_statusWidget->setIdle(i18nc("@info:status","Search stopped."));
} else {
ui.m_statusWidget->setIdle(i18nc("@info:status","Search stopped. Showing results from "
"%1 to %2", m_startDate.toString(QStringLiteral("yyyy-MM-dd")),
m_endDate.toString(QStringLiteral("yyyy-MM-dd"))));
}
}
}
void BugzillaDuplicatesPage::markAsSearching(bool searching)
{
m_searching = searching;
emitCompleteChanged();
ui.m_bugListWidget->setEnabled(!searching);
ui.m_searchMoreButton->setEnabled(!searching);
ui.m_searchMoreButton->setVisible(!searching);
ui.m_stopSearchButton->setEnabled(searching);
ui.m_stopSearchButton->setVisible(searching);
ui.m_selectedDuplicatesList->setEnabled(!searching);
ui.m_selectedPossibleDuplicatesLabel->setEnabled(!searching);
ui.m_removeSelectedDuplicateButton->setEnabled(!searching &&
!ui.m_selectedDuplicatesList->selectedItems().isEmpty());
ui.m_attachToReportLabel->setEnabled(!searching);
if (!searching) {
itemSelectionChanged();
} else {
ui.m_openReportButton->setEnabled(false);
}
}
bool BugzillaDuplicatesPage::canSearchMore()
{
return (m_startDate.year() >= 2009);
}
void BugzillaDuplicatesPage::searchFinished(const BugMapList & list)
{
KGuiItem::assign(ui.m_searchMoreButton, m_searchMoreGuiItem);
m_startDate = m_searchingStartDate;
int results = list.count();
if (results > 0) {
markAsSearching(false);
ui.m_statusWidget->setIdle(i18nc("@info:status","Showing results from %1 to %2",
m_startDate.toString(QStringLiteral("yyyy-MM-dd")),
m_endDate.toString(QStringLiteral("yyyy-MM-dd"))));
QList bugIds;
for (int i = 0; i < results; i++) {
BugMap bug = list.at(i);
bool ok;
int bugId = bug.value(QStringLiteral("bug_id")).toInt(&ok);
if (ok) {
bugIds << bugId;
}
QString title;
//Generate a non-geek readable status
QString customStatusString;
BugReport::Status status = BugReport::parseStatus(bug.value(QStringLiteral("bug_status")));
BugReport::Resolution resolution = BugReport::parseResolution(bug.value(QStringLiteral("resolution")));
if (BugReport::isOpen(status)) {
customStatusString = i18nc("@info bug status", "[Open]");
} else if (BugReport::isClosed(status) && status != BugReport::NeedsInfo) {
if (resolution == BugReport::Fixed) {
customStatusString = i18nc("@info bug resolution", "[Fixed]");
} else if (resolution == BugReport::WorksForMe) {
customStatusString = i18nc("@info bug resolution", "[Non-reproducible]");
} else if (resolution == BugReport::Duplicate) {
customStatusString = i18nc("@info bug resolution", "[Duplicate report]");
} else if (resolution == BugReport::Invalid) {
customStatusString = i18nc("@info bug resolution", "[Invalid]");
} else if (resolution == BugReport::Downstream
|| resolution == BugReport::Upstream) {
customStatusString = i18nc("@info bug resolution", "[External problem]");
}
} else if (status == BugReport::NeedsInfo) {
customStatusString = i18nc("@info bug status", "[Incomplete]");
}
title = customStatusString + QLatin1Char(' ') + bug[QStringLiteral("short_desc")];
QStringList fields = QStringList() << bug[QStringLiteral("bug_id")] << title;
QTreeWidgetItem * item = new QTreeWidgetItem(fields);
item->setToolTip(0, bug[QStringLiteral("short_desc")]);
item->setToolTip(1, bug[QStringLiteral("short_desc")]);
ui.m_bugListWidget->addTopLevelItem(item);
}
if (!m_foundDuplicate) {
markAsSearching(true);
DuplicateFinderJob *job = new DuplicateFinderJob(bugIds, bugzillaManager(), this);
connect(job, SIGNAL(result(KJob*)), this, SLOT(analyzedDuplicates(KJob*)));
job->start();
}
ui.m_bugListWidget->sortItems(0 , Qt::DescendingOrder);
ui.m_bugListWidget->resizeColumnToContents(1);
if (!canSearchMore()) {
ui.m_searchMoreButton->setEnabled(false);
}
} else {
if (canSearchMore()) {
//We don't call markAsSearching(false) to avoid flicker
//Delayed call to searchMore to avoid unexpected behaviour (signal/slot)
//because we are in a slot, and searchMore() will be ending calling this slot again
QTimer::singleShot(0, this, &BugzillaDuplicatesPage::searchMore);
} else {
markAsSearching(false);
ui.m_statusWidget->setIdle(i18nc("@info:status","Search Finished. "
"No reports found."));
ui.m_searchMoreButton->setEnabled(false);
if (ui.m_bugListWidget->topLevelItemCount() == 0) {
//No reports to mark as possible duplicate
ui.m_selectedDuplicatesList->setEnabled(false);
}
}
}
}
void BugzillaDuplicatesPage::analyzedDuplicates(KJob *j)
{
markAsSearching(false);
DuplicateFinderJob *job = static_cast(j);
m_result = job->result();
m_foundDuplicate = m_result.parentDuplicate;
reportInterface()->setDuplicateId(m_result.parentDuplicate);
ui.m_searchMoreButton->setEnabled(!m_foundDuplicate);
ui.information->setVisible(m_foundDuplicate);
BugReport::Status status = m_result.status;
const int duplicate = m_result.duplicate;
const int parentDuplicate = m_result.parentDuplicate;
if (m_foundDuplicate) {
const QList items = ui.m_bugListWidget->findItems(QString::number(parentDuplicate), Qt::MatchExactly, 0);
const QBrush brush = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NeutralBackground);
Q_FOREACH (QTreeWidgetItem* item, items) {
for (int i = 0; i < item->columnCount(); ++i) {
item->setBackground(i, brush);
}
}
QString text;
if (BugReport::isOpen(status) || (BugReport::isClosed(status) && status == BugReport::NeedsInfo)) {
text = (parentDuplicate == duplicate ? i18nc("@label", "Your crash is a duplicate and has already been reported as Bug %1.", QString::number(duplicate)) :
i18nc("@label", "Your crash has already been reported as Bug %1, which is a duplicate of Bug %2", QString::number(duplicate), QString::number(parentDuplicate))) +
QLatin1Char('\n') + i18nc("@label", "Only attach if you can add needed information to the bug report.", QStringLiteral("attach"));
} else if (BugReport::isClosed(status)) {
text = (parentDuplicate == duplicate ? i18nc("@label", "Your crash has already been reported as Bug %1 which has been closed.", QString::number(duplicate)) :
i18nc("@label", "Your crash has already been reported as Bug %1, which is a duplicate of the closedBug %2.", QString::number(duplicate), QString::number(parentDuplicate)));
}
ui.information->setText(text);
}
}
void BugzillaDuplicatesPage::informationClicked(const QString &activatedLink)
{
if (activatedLink == QLatin1String("attach")) {
attachToBugReport(m_result.parentDuplicate);
} else {
int number = activatedLink.toInt();
if (number) {
showReportInformationDialog(number, false);
}
}
}
void BugzillaDuplicatesPage::searchError(QString err)
{
KGuiItem::assign(ui.m_searchMoreButton, m_retrySearchGuiItem);
markAsSearching(false);
ui.m_statusWidget->setIdle(i18nc("@info:status","Error fetching the bug report list"));
KMessageBox::error(this , xi18nc("@info/rich","Error fetching the bug report list"
"%1."
"Please wait some time and try again.", err));
}
void BugzillaDuplicatesPage::resetDates()
{
m_endDate = QDate::currentDate();
m_startDate = m_endDate;
}
//END Search related methods
//BEGIN Duplicates list related methods
void BugzillaDuplicatesPage::openSelectedReport()
{
QList selected = ui.m_bugListWidget->selectedItems();
if (selected.count() == 1) {
itemClicked(selected.at(0), 0);
}
}
void BugzillaDuplicatesPage::itemClicked(QTreeWidgetItem * item, int col)
{
Q_UNUSED(col);
int bugNumber = 0;
if (item->data(0, Qt::UserRole) == QLatin1String("custom")) {
bool ok = false;
bugNumber = QInputDialog::getInt(this,
i18nc("@title:window", "Enter a custom bug report number"),
i18nc("@label", "Enter the number of the bug report you want to check"),
0, 0, 1000000, 1, &ok);
} else {
bugNumber = item->text(0).toInt();
}
showReportInformationDialog(bugNumber);
}
void BugzillaDuplicatesPage::itemClicked(QListWidgetItem * item)
{
showReportInformationDialog(item->text().toInt());
}
void BugzillaDuplicatesPage::showReportInformationDialog(int bugNumber, bool relatedButtonEnabled)
{
if (bugNumber <= 0) {
return;
}
BugzillaReportInformationDialog * infoDialog = new BugzillaReportInformationDialog(this);
connect(infoDialog, &BugzillaReportInformationDialog::possibleDuplicateSelected, this,
&BugzillaDuplicatesPage::addPossibleDuplicateNumber);
connect(infoDialog, &BugzillaReportInformationDialog::attachToBugReportSelected, this, &BugzillaDuplicatesPage::attachToBugReport);
infoDialog->showBugReport(bugNumber, relatedButtonEnabled);
}
void BugzillaDuplicatesPage::itemSelectionChanged()
{
ui.m_openReportButton->setEnabled(ui.m_bugListWidget->selectedItems().count() == 1);
}
//END Duplicates list related methods
//BEGIN Selected duplicates list related methods
void BugzillaDuplicatesPage::addPossibleDuplicateNumber(int bugNumber)
{
QString stringNumber = QString::number(bugNumber);
if (ui.m_selectedDuplicatesList->findItems(stringNumber, Qt::MatchExactly).isEmpty()) {
ui.m_selectedDuplicatesList->addItem(stringNumber);
}
showDuplicatesPanel(true);
}
void BugzillaDuplicatesPage::removeSelectedDuplicate()
{
QList items = ui.m_selectedDuplicatesList->selectedItems();
if (items.length() > 0) {
delete ui.m_selectedDuplicatesList->takeItem(ui.m_selectedDuplicatesList->row(items.at(0)));
}
if (ui.m_selectedDuplicatesList->count() == 0) {
showDuplicatesPanel(false);
}
}
void BugzillaDuplicatesPage::showDuplicatesPanel(bool show)
{
ui.m_removeSelectedDuplicateButton->setVisible(show);
ui.m_selectedDuplicatesList->setVisible(show);
ui.m_selectedPossibleDuplicatesLabel->setVisible(show);
}
void BugzillaDuplicatesPage::possibleDuplicateSelectionChanged()
{
ui.m_removeSelectedDuplicateButton->setEnabled(
!ui.m_selectedDuplicatesList->selectedItems().isEmpty());
}
//END Selected duplicates list related methods
//BEGIN Attach to bug related methods
void BugzillaDuplicatesPage::attachToBugReport(int bugNumber)
{
ui.m_attachToReportLabel->setText(xi18nc("@label", "The report is going to be "
"attached to bug %1. "
"Cancel", QString::number(bugNumber)));
ui.m_attachToReportLabel->setVisible(true);
ui.m_attachToReportIcon->setVisible(true);
reportInterface()->setAttachToBugNumber(bugNumber);
}
void BugzillaDuplicatesPage::cancelAttachToBugReport()
{
ui.m_attachToReportLabel->setVisible(false);
ui.m_attachToReportIcon->setVisible(false);
reportInterface()->setAttachToBugNumber(0);
}
//END Attach to bug related methods
//END BugzillaDuplicatesPage
//BEGIN BugzillaReportInformationDialog
BugzillaReportInformationDialog::BugzillaReportInformationDialog(BugzillaDuplicatesPage * parent) :
QDialog(parent),
m_relatedButtonEnabled(true),
m_parent(parent),
m_bugNumber(0),
m_duplicatesCount(0)
{
setWindowTitle(i18nc("@title:window","Bug Description"));
ui.setupUi(this);
KGuiItem::assign(ui.m_retryButton, KGuiItem2(i18nc("@action:button", "Retry..."),
QIcon::fromTheme(QStringLiteral("view-refresh")),
i18nc("@info:tooltip", "Use this button to retry "
"loading the bug report.")));
connect(ui.m_retryButton, &QPushButton::clicked, this, &BugzillaReportInformationDialog::reloadReport);
m_suggestButton = new QPushButton(this);
ui.buttonBox->addButton(m_suggestButton, QDialogButtonBox::ActionRole);
KGuiItem::assign(m_suggestButton,
KGuiItem2(i18nc("@action:button", "Suggest this crash is related"),
QIcon::fromTheme(QStringLiteral("list-add")), i18nc("@info:tooltip", "Use this button to suggest that "
"the crash you experienced is related to this bug "
"report")));
connect(m_suggestButton, &QPushButton::clicked, this, &BugzillaReportInformationDialog::relatedReportClicked);
connect(ui.m_showOwnBacktraceCheckBox, &QAbstractButton::toggled, this, &BugzillaReportInformationDialog::toggleShowOwnBacktrace);
//Connect bugzillalib signals
connect(m_parent->bugzillaManager(), &BugzillaManager::bugReportFetched,
this, &BugzillaReportInformationDialog::bugFetchFinished);
connect(m_parent->bugzillaManager(), SIGNAL(bugReportError(QString,QObject*)),
this, SLOT(bugFetchError(QString,QObject*)));
- resize(QSize(800, 600));
KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog");
KWindowConfig::restoreWindowSize(windowHandle(), config);
}
BugzillaReportInformationDialog::~BugzillaReportInformationDialog()
{
KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog");
KWindowConfig::saveWindowSize(windowHandle(), config);
}
void BugzillaReportInformationDialog::reloadReport()
{
showBugReport(m_bugNumber);
}
void BugzillaReportInformationDialog::showBugReport(int bugNumber, bool relatedButtonEnabled)
{
m_relatedButtonEnabled = relatedButtonEnabled;
ui.m_retryButton->setVisible(false);
m_closedStateString.clear();
m_bugNumber = bugNumber;
m_parent->bugzillaManager()->fetchBugReport(m_bugNumber, this);
m_suggestButton->setEnabled(false);
m_suggestButton->setVisible(m_relatedButtonEnabled);
ui.m_infoBrowser->setText(i18nc("@info:status","Loading..."));
ui.m_infoBrowser->setEnabled(false);
ui.m_linkLabel->setText(xi18nc("@info","Report's webpage",
m_parent->bugzillaManager()->urlForBug(m_bugNumber)));
ui.m_statusWidget->setBusy(xi18nc("@info:status","Loading information about bug "
"%1 from %2....",
QString::number(m_bugNumber),
QLatin1String(KDE_BUGZILLA_SHORT_URL)));
ui.m_backtraceBrowser->setPlainText(
i18nc("@info","Backtrace of the crash I experienced:\n\n") +
m_parent->reportInterface()->backtrace());
KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog");
bool showOwnBacktrace = config.readEntry("ShowOwnBacktrace", false);
ui.m_showOwnBacktraceCheckBox->setChecked(showOwnBacktrace);
if (!showOwnBacktrace) { //setChecked(false) will not emit toggled(false)
toggleShowOwnBacktrace(false);
}
show();
}
void BugzillaReportInformationDialog::bugFetchFinished(BugReport report, QObject * jobOwner)
{
if (jobOwner == this && isVisible()) {
if (report.isValid()) {
//Handle duplicate state
QString duplicate = report.markedAsDuplicateOf();
if (!duplicate.isEmpty()) {
bool ok = false;
int dupId = duplicate.toInt(&ok);
if (ok && dupId > 0) {
ui.m_statusWidget->setIdle(QString());
KGuiItem yesItem = KStandardGuiItem::yes();
yesItem.setText(i18nc("@action:button let the user to choose to read the "
"main report", "Yes, read the main report"));
KGuiItem noItem = KStandardGuiItem::no();
noItem.setText(i18nc("@action:button let the user choose to read the original "
"report", "No, let me read the report I selected"));
if (KMessageBox::questionYesNo(this,
xi18nc("@info","The report you selected (bug %1) is already "
"marked as duplicate of bug %2. "
"Do you want to read that report instead? (recommended)",
report.bugNumber(), QString::number(dupId)),
i18nc("@title:window","Nested duplicate detected"), yesItem, noItem)
== KMessageBox::Yes) {
showBugReport(dupId);
return;
}
}
}
//Generate html for comments (with proper numbering)
QLatin1String duplicatesMark = QLatin1String("has been marked as a duplicate of this bug.");
QString comments;
QStringList commentList = report.comments();
for (int i = 0; i < commentList.count(); i++) {
QString comment = commentList.at(i);
//Don't add duplicates mark comments
if (!comment.contains(duplicatesMark)) {
comment.replace(QLatin1Char('\n'), QLatin1String(" "));
comments += i18nc("comment $number to use as subtitle", "
Comment %1:
", (i+1))
+ QStringLiteral("
") + comment + QStringLiteral("
");
//Count the inline attached crashes (DrKonqi feature)
QLatin1String attachedCrashMark =
QLatin1String("New crash information added by DrKonqi");
if (comment.contains(attachedCrashMark)) {
m_duplicatesCount++;
}
} else {
//Count duplicate
m_duplicatesCount++;
}
}
//Generate a non-geek readable status
QString customStatusString;
BugReport::Status status = report.statusValue();
BugReport::Resolution resolution = report.resolutionValue();
if (status == BugReport::Unconfirmed) {
customStatusString = i18nc("@info bug status", "Opened (Unconfirmed)");
} else if (report.isOpen()) {
customStatusString = i18nc("@info bug status", "Opened (Unfixed)");
} else if (report.isClosed() && status != BugReport::NeedsInfo) {
QString customResolutionString;
if (resolution == BugReport::Fixed) {
if (!report.versionFixedIn().isEmpty()) {
customResolutionString = i18nc("@info bug resolution, fixed in version",
"Fixed in version \"%1\"",
report.versionFixedIn());
m_closedStateString = i18nc("@info bug resolution, fixed by kde devs in version",
"the bug was fixed by KDE developers in version \"%1\"",
report.versionFixedIn());
} else {
customResolutionString = i18nc("@info bug resolution", "Fixed");
m_closedStateString = i18nc("@info bug resolution", "the bug was fixed by KDE developers");
}
} else if (resolution == BugReport::WorksForMe) {
customResolutionString = i18nc("@info bug resolution", "Non-reproducible");
} else if (resolution == BugReport::Duplicate) {
customResolutionString = i18nc("@info bug resolution", "Duplicate report "
"(Already reported before)");
} else if (resolution == BugReport::Invalid) {
customResolutionString = i18nc("@info bug resolution", "Not a valid report/crash");
} else if (resolution == BugReport::Downstream || resolution == BugReport::Upstream) {
customResolutionString = i18nc("@info bug resolution", "Not caused by a problem "
"in the KDE's Applications or libraries");
m_closedStateString = i18nc("@info bug resolution", "the bug is caused by a "
"problem in an external application or library, or "
"by a distribution or packaging issue");
} else {
customResolutionString = report.resolution();
}
customStatusString = i18nc("@info bug status, %1 is the resolution", "Closed (%1)",
customResolutionString);
} else if (status == BugReport::NeedsInfo) {
customStatusString = i18nc("@info bug status", "Temporarily closed, because of a lack "
"of information");
} else { //Fallback to other raw values
customStatusString = QStringLiteral("%1 (%2)").arg(report.bugStatus(), report.resolution());
}
//Generate notes
QString notes = xi18n("
The bug report's title is often written by its reporter "
"and may not reflect the bug's nature, root cause or other visible "
"symptoms you could use to compare to your crash. Please read the "
"complete report and all the comments below.
");
if (m_duplicatesCount >= 10) { //Consider a possible mass duplicate crash
notes += xi18np("
This bug report has %1 duplicate report. That means this "
"is probably a common crash. Please consider only "
"adding a comment or a note if you can provide new valuable "
"information which was not already mentioned.
",
"
This bug report has %1 duplicate reports. That means this "
"is probably a common crash. Please consider only "
"adding a comment or a note if you can provide new valuable "
"information which was not already mentioned.
",
m_duplicatesCount);
}
//A manually entered bug ID could represent a normal bug
if (report.bugSeverity() != QLatin1String("crash")
&& report.bugSeverity() != QLatin1String("major")
&& report.bugSeverity() != QLatin1String("grave")
&& report.bugSeverity() != QLatin1String("critical"))
{
notes += xi18n("
This bug report is not about a crash or about any other "
"critical bug.
");
}
//Generate HTML text
QString text =
i18nc("@info bug report title (quoted)",
"