diff --git a/src/bugzillaintegration/reportassistantdialog.cpp b/src/bugzillaintegration/reportassistantdialog.cpp
index 83f4f8b1..998a1dba 100644
--- a/src/bugzillaintegration/reportassistantdialog.cpp
+++ b/src/bugzillaintegration/reportassistantdialog.cpp
@@ -1,413 +1,414 @@
/*******************************************************************
* reportassistantdialog.cpp
* Copyright 2009,2010 Dario Andres Rodriguez
* Copyright 2019 Harald Sitter
*
* 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 "reportassistantdialog.h"
#include
#include
#include
#include
#include "drkonqi.h"
#include "parser/backtraceparser.h"
#include "debuggermanager.h"
#include "backtracegenerator.h"
#include "assistantpage_bugzilla_version.h"
#include "crashedapplication.h"
#include "aboutbugreportingdialog.h"
#include "reportassistantpages_base.h"
#include "reportassistantpages_bugzilla.h"
#include "reportassistantpages_bugzilla_duplicates.h"
#include "reportinterface.h"
static const char KDE_BUGZILLA_DESCRIPTION[] = I18N_NOOP("the KDE Bug Tracking System");
ReportAssistantDialog::ReportAssistantDialog(QWidget * parent) :
KAssistantDialog(parent),
m_aboutBugReportingDialog(nullptr),
m_reportInterface(new ReportInterface(this)),
m_canClose(false)
{
setAttribute(Qt::WA_DeleteOnClose, true);
//Set window properties
setWindowTitle(i18nc("@title:window","Crash Reporting Assistant"));
setWindowIcon(QIcon::fromTheme(QStringLiteral("tools-report-bug")));
connect(this, &ReportAssistantDialog::currentPageChanged, this, &ReportAssistantDialog::currentPageChanged_slot);
connect(button(QDialogButtonBox::Help), &QPushButton::clicked, this, &ReportAssistantDialog::showHelp);
//Create the assistant pages
//-Introduction Page
KConfigGroup group(KSharedConfig::openConfig(), "ReportAssistant");
const bool skipIntroduction = group.readEntry("SkipIntroduction", false);
if (!skipIntroduction) {
IntroductionPage * m_introduction = new IntroductionPage(this);
KPageWidgetItem * m_introductionPage = new KPageWidgetItem(m_introduction,
QLatin1String(PAGE_INTRODUCTION_ID));
m_pageWidgetMap.insert(QLatin1String(PAGE_INTRODUCTION_ID),m_introductionPage);
m_introductionPage->setHeader(i18nc("@title","Welcome to the Reporting Assistant"));
m_introductionPage->setIcon(QIcon::fromTheme(QStringLiteral("tools-report-bug")));
addPage(m_introductionPage);
}
//-Bug Awareness Page
BugAwarenessPage * m_awareness = new BugAwarenessPage(this);
connectSignals(m_awareness);
KPageWidgetItem * m_awarenessPage = new KPageWidgetItem(m_awareness,
QLatin1String(PAGE_AWARENESS_ID));
m_pageWidgetMap.insert(QLatin1String(PAGE_AWARENESS_ID),m_awarenessPage);
m_awarenessPage->setHeader(i18nc("@title","What do you know about the crash?"));
m_awarenessPage->setIcon(QIcon::fromTheme(QStringLiteral("checkbox")));
//-Crash Information Page
CrashInformationPage * m_backtrace = new CrashInformationPage(this);
connectSignals(m_backtrace);
KPageWidgetItem * m_backtracePage = new KPageWidgetItem(m_backtrace,
QLatin1String(PAGE_CRASHINFORMATION_ID));
m_pageWidgetMap.insert(QLatin1String(PAGE_CRASHINFORMATION_ID),m_backtracePage);
m_backtracePage->setHeader(i18nc("@title","Fetching the Backtrace (Automatic Crash Information)"));
m_backtracePage->setIcon(QIcon::fromTheme(QStringLiteral("run-build")));
//-Results Page
ConclusionPage * m_conclusions = new ConclusionPage(this);
connectSignals(m_conclusions);
KPageWidgetItem * m_conclusionsPage = new KPageWidgetItem(m_conclusions,
QLatin1String(PAGE_CONCLUSIONS_ID));
m_pageWidgetMap.insert(QLatin1String(PAGE_CONCLUSIONS_ID),m_conclusionsPage);
m_conclusionsPage->setHeader(i18nc("@title","Results of the Analyzed Crash Details"));
m_conclusionsPage->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
connect(m_conclusions, &ConclusionPage::finished, this, &ReportAssistantDialog::assistantFinished);
// Version check page
BugzillaVersionPage *versionPage = new BugzillaVersionPage(this);
m_pageWidgetMap.insert(QLatin1String(PAGE_BZVERSION_ID), versionPage->item());
//-Bugzilla Login
BugzillaLoginPage * m_bugzillaLogin = new BugzillaLoginPage(this);
connectSignals(m_bugzillaLogin);
KPageWidgetItem * m_bugzillaLoginPage = new KPageWidgetItem(m_bugzillaLogin,
QLatin1String(PAGE_BZLOGIN_ID));
m_pageWidgetMap.insert(QLatin1String(PAGE_BZLOGIN_ID),m_bugzillaLoginPage);
m_bugzillaLoginPage->setHeader(i18nc("@title", "Login into %1", i18n(KDE_BUGZILLA_DESCRIPTION)));
m_bugzillaLoginPage->setIcon(QIcon::fromTheme(QStringLiteral("user-identity")));
connect(m_bugzillaLogin, &BugzillaLoginPage::loggedTurnToNextPage, this, &ReportAssistantDialog::loginFinished);
//-Bugzilla duplicates
BugzillaDuplicatesPage * m_bugzillaDuplicates = new BugzillaDuplicatesPage(this);
connectSignals(m_bugzillaDuplicates);
KPageWidgetItem * m_bugzillaDuplicatesPage = new KPageWidgetItem(m_bugzillaDuplicates,
QLatin1String(PAGE_BZDUPLICATES_ID));
m_pageWidgetMap.insert(QLatin1String(PAGE_BZDUPLICATES_ID),m_bugzillaDuplicatesPage);
m_bugzillaDuplicatesPage->setHeader(i18nc("@title","Look for Possible Duplicate Reports"));
m_bugzillaDuplicatesPage->setIcon(QIcon::fromTheme(QStringLiteral("repository")));
//-Bugzilla information
BugzillaInformationPage * m_bugzillaInformation = new BugzillaInformationPage(this);
connectSignals(m_bugzillaInformation);
KPageWidgetItem * m_bugzillaInformationPage = new KPageWidgetItem(m_bugzillaInformation,
QLatin1String(PAGE_BZDETAILS_ID));
m_pageWidgetMap.insert(QLatin1String(PAGE_BZDETAILS_ID),m_bugzillaInformationPage);
m_bugzillaInformationPage->setHeader(i18nc("@title","Enter the Details about the Crash"));
m_bugzillaInformationPage->setIcon(QIcon::fromTheme(QStringLiteral("document-edit")));
//-Bugzilla Report Preview
BugzillaPreviewPage * m_bugzillaPreview = new BugzillaPreviewPage(this);
KPageWidgetItem * m_bugzillaPreviewPage = new KPageWidgetItem(m_bugzillaPreview,
QLatin1String(PAGE_BZPREVIEW_ID));
m_pageWidgetMap.insert(QLatin1String(PAGE_BZPREVIEW_ID),m_bugzillaPreviewPage);
m_bugzillaPreviewPage->setHeader(i18nc("@title","Preview the Report"));
m_bugzillaPreviewPage->setIcon(QIcon::fromTheme(QStringLiteral("document-preview")));
//-Bugzilla commit
BugzillaSendPage * m_bugzillaSend = new BugzillaSendPage(this);
KPageWidgetItem * m_bugzillaSendPage = new KPageWidgetItem(m_bugzillaSend,
QLatin1String(PAGE_BZSEND_ID));
m_pageWidgetMap.insert(QLatin1String(PAGE_BZSEND_ID),m_bugzillaSendPage);
m_bugzillaSendPage->setHeader(i18nc("@title","Sending the Crash Report"));
m_bugzillaSendPage->setIcon(QIcon::fromTheme(QStringLiteral("applications-internet")));
connect(m_bugzillaSend, &BugzillaSendPage::finished, this, &ReportAssistantDialog::assistantFinished);
//TODO Remember to keep the pages ordered
addPage(m_awarenessPage);
addPage(m_backtracePage);
addPage(m_conclusionsPage);
addPage(versionPage->item());
addPage(m_bugzillaLoginPage);
addPage(m_bugzillaDuplicatesPage);
addPage(m_bugzillaInformationPage);
addPage(m_bugzillaPreviewPage);
addPage(m_bugzillaSendPage);
// Force a 16:9 ratio for nice appearance by default.
QSize aspect(16, 9);
aspect.scale(sizeHint(), Qt::KeepAspectRatioByExpanding);
resize(aspect);
}
ReportAssistantDialog::~ReportAssistantDialog()
{
}
void ReportAssistantDialog::setAboutToSend(bool aboutTo)
{
if (aboutTo) {
m_nextButtonIconCache = nextButton()->icon();
m_nextButtonTextCache = nextButton()->text();
nextButton()->setIcon(QIcon::fromTheme(QStringLiteral("document-send")));
nextButton()->setText(i18nc("@action button to submit report", "Submit"));
return;
}
nextButton()->setIcon(m_nextButtonIconCache);
nextButton()->setText(m_nextButtonTextCache);
m_nextButtonIconCache = QIcon();
m_nextButtonTextCache = QString();
}
void ReportAssistantDialog::connectSignals(ReportAssistantPage * page)
{
//React to the changes in the assistant pages
connect(page, &ReportAssistantPage::completeChanged,
this, &ReportAssistantDialog::completeChanged);
}
void ReportAssistantDialog::currentPageChanged_slot(KPageWidgetItem * current , KPageWidgetItem * before)
{
//Page changed
buttonBox()->button(QDialogButtonBox::Cancel)->setEnabled(true);
m_canClose = false;
//Save data of the previous page
if (before) {
ReportAssistantPage *beforePage = qobject_cast(before->widget());
beforePage->aboutToHide();
}
//Load data of the current(new) page
if (current) {
ReportAssistantPage *currentPage = qobject_cast(current->widget());
nextButton()->setEnabled(currentPage->isComplete());
currentPage->aboutToShow();
// If the current page is the last one, disable all the buttons until the bug is sent
if (current->name() == QLatin1String(PAGE_BZSEND_ID)) {
nextButton()->setEnabled(false);
backButton()->setEnabled(false);
finishButton()->setEnabled(false);
}
}
}
void ReportAssistantDialog::completeChanged(ReportAssistantPage* page, bool isComplete)
{
if (page == qobject_cast(currentPage()->widget())) {
nextButton()->setEnabled(isComplete);
}
}
void ReportAssistantDialog::assistantFinished(bool showBack)
{
//The assistant finished: allow the user to close the dialog normally
nextButton()->setEnabled(false);
backButton()->setEnabled(showBack);
finishButton()->setEnabled(true);
buttonBox()->button(QDialogButtonBox::Cancel)->setEnabled(false);
m_canClose = true;
}
void ReportAssistantDialog::loginFinished()
{
//Bugzilla login finished, go to the next page
if (currentPage()->name() == QLatin1String(PAGE_BZLOGIN_ID)) {
next();
}
}
void ReportAssistantDialog::showHelp()
{
//Show the bug reporting guide dialog
if (!m_aboutBugReportingDialog) {
m_aboutBugReportingDialog = new AboutBugReportingDialog();
}
m_aboutBugReportingDialog->show();
m_aboutBugReportingDialog->raise();
m_aboutBugReportingDialog->activateWindow();
m_aboutBugReportingDialog->showSection(QLatin1String(PAGE_HELP_BEGIN_ID));
m_aboutBugReportingDialog->showSection(currentPage()->name());
}
//Override KAssistantDialog "next" page implementation
void ReportAssistantDialog::next()
{
// FIXME: this entire function is a bit weird. It'd likely make more sense to
// use the page appropriateness more globally. i.e. mark pages inappropriate
// when they are not applicable based on earlier settings done (e.g. put
// a conclusion page under/after the awareness page but only mark it
// appropriate if the data is not useful. that way kassistantdialog would
// just skip over the page).
//Allow the widget to Ask a question to the user before changing the page
ReportAssistantPage *page = qobject_cast(currentPage()->widget());
if (page && !page->showNextPage()) {
return;
}
const QString name = currentPage()->name();
//If the information the user can provide is not useful, skip the backtrace page
if (name == QLatin1String(PAGE_AWARENESS_ID))
{
//Force save settings in the current page
page->aboutToHide();
if (!(m_reportInterface->isBugAwarenessPageDataUseful()))
{
setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_CONCLUSIONS_ID)));
return;
}
} else if (name == QLatin1String(PAGE_CRASHINFORMATION_ID)){
//Force save settings in current page
page->aboutToHide();
//If the crash is worth reporting and it is BKO, skip the Conclusions page
if (m_reportInterface->isWorthReporting() &&
DrKonqi::crashedApplication()->bugReportAddress().isKdeBugzilla())
{
// Depending on whether the page is appropriate either go to version
// check page or login page.
const auto versionPage = m_pageWidgetMap.value(QLatin1String(PAGE_BZVERSION_ID));
const auto loginPage = m_pageWidgetMap.value(QLatin1String(PAGE_BZLOGIN_ID));
setCurrentPage(isAppropriate(versionPage) ? versionPage : loginPage);
return;
}
} else if (name == QLatin1String(PAGE_BZDUPLICATES_ID)) {
//a duplicate has been found, yet the report is not being attached
if (m_reportInterface->duplicateId() && !m_reportInterface->attachToBugNumber()) {
setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_CONCLUSIONS_ID)));
return;
}
}
KAssistantDialog::next();
}
//Override KAssistantDialog "back"(previous) page implementation
//It has to mirror the custom next() implementation
void ReportAssistantDialog::back()
{
if (currentPage()->name() == QLatin1String(PAGE_CONCLUSIONS_ID))
{
if (m_reportInterface->duplicateId() && !m_reportInterface->attachToBugNumber()) {
setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_BZDUPLICATES_ID)));
return;
}
if (!(m_reportInterface->isBugAwarenessPageDataUseful()))
{
setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_AWARENESS_ID)));
return;
}
}
if (currentPage()->name() == QLatin1String(PAGE_BZLOGIN_ID))
{
if (m_reportInterface->isWorthReporting() &&
DrKonqi::crashedApplication()->bugReportAddress().isKdeBugzilla())
{
setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_CRASHINFORMATION_ID)));
return;
}
}
KAssistantDialog::back();
}
void ReportAssistantDialog::reject()
{
close();
}
void ReportAssistantDialog::closeEvent(QCloseEvent * event)
{
//Handle the close event
if (!m_canClose) {
//If the assistant didn't finished yet, offer the user the possibilities to
//Close, Cancel, or Save the bug report and Close"
KGuiItem closeItem = KStandardGuiItem::close();
closeItem.setText(i18nc("@action:button", "Close the assistant"));
KGuiItem keepOpenItem = KStandardGuiItem::cancel();
keepOpenItem.setText(i18nc("@action:button", "Cancel"));
BacktraceParser::Usefulness use =
DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness();
if (use == BacktraceParser::ReallyUseful || use == BacktraceParser::MayBeUseful) {
//Backtrace is still useful, let the user save it.
KGuiItem saveBacktraceItem = KStandardGuiItem::save();
saveBacktraceItem.setText(i18nc("@action:button", "Save information and close"));
int ret = KMessageBox::questionYesNoCancel(this,
xi18nc("@info","Do you really want to close the bug reporting assistant? "
"The crash information is still valid, so "
"you can save the report before closing if you want."),
i18nc("@title:window","Close the Assistant"),
closeItem, saveBacktraceItem, keepOpenItem, QString(), KMessageBox::Dangerous);
if(ret == KMessageBox::Yes)
{
event->accept();
} else if (ret == KMessageBox::No) {
//Save backtrace and accept event (dialog will be closed)
- DrKonqi::saveReport(reportInterface()->generateReportFullText(false));
+ DrKonqi::saveReport(reportInterface()->generateReportFullText(ReportInterface::DrKonqiStamp::Exclude,
+ ReportInterface::Backtrace::Complete));
event->accept();
} else {
event->ignore();
}
} else {
if (KMessageBox::questionYesNo(this, i18nc("@info","Do you really want to close the bug "
"reporting assistant?"),
i18nc("@title:window","Close the Assistant"),
closeItem, keepOpenItem, QString(), KMessageBox::Dangerous)
== KMessageBox::Yes) {
event->accept();
} else {
event->ignore();
}
}
} else {
event->accept();
}
}
diff --git a/src/bugzillaintegration/reportassistantpages_base.cpp b/src/bugzillaintegration/reportassistantpages_base.cpp
index 53f8c0ef..9a44aca6 100644
--- a/src/bugzillaintegration/reportassistantpages_base.cpp
+++ b/src/bugzillaintegration/reportassistantpages_base.cpp
@@ -1,456 +1,459 @@
/*******************************************************************
* 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 "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
static QHash s_reproducibleIndex {
{ 0, ReportInterface::ReproducibleUnsure },
{ 1, ReportInterface::ReproducibleNever },
{ 2, ReportInterface::ReproducibleSometimes },
{ 3, ReportInterface::ReproducibleEverytime }
};
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));
if (qEnvironmentVariableIsSet("DRKONQI_TEST_MODE")) {
ui.m_rememberCrashSituationYes->setChecked(true);
ui.m_reproducibleBox->setCurrentIndex(
s_reproducibleIndex.key(ReportInterface::ReproducibleEverytime));
}
}
void BugAwarenessPage::aboutToShow()
{
updateCheckBoxes();
}
void BugAwarenessPage::aboutToHide()
{
//Save data
ReportInterface::Reproducible reproducible = ReportInterface::ReproducibleUnsure;
reproducible = s_reproducibleIndex.value(ui.m_reproducibleBox->currentIndex());
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);
+ QString report = reportInterface()->generateReportFullText(ReportInterface::DrKonqiStamp::Exclude,
+ ReportInterface::Backtrace::Complete);
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') +
+ QString info = reportInterface()->generateReportFullText(ReportInterface::DrKonqiStamp::Exclude,
+ ReportInterface::Backtrace::Complete)
+ + 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);
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 91f7e7b8..48bb5e7f 100644
--- a/src/bugzillaintegration/reportassistantpages_bugzilla.cpp
+++ b/src/bugzillaintegration/reportassistantpages_bugzilla.cpp
@@ -1,746 +1,748 @@
/*******************************************************************
* reportassistantpages_bugzilla.cpp
* Copyright 2009, 2010, 2011 Dario Andres Rodriguez
* Copyright 2019 Harald Sitter
*
* 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 "drkonqi_debug.h"
#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)
{
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 "
"e-mail address 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));
// Don't advertise saving credentials when we can't save them.
// https://bugs.kde.org/show_bug.cgi?id=363570
if (!KWallet::Wallet::isEnabled()) {
ui.m_savePasswordCheckBox->setVisible(false);
ui.m_savePasswordCheckBox->setCheckState(Qt::Unchecked);
}
}
bool BugzillaLoginPage::isComplete()
{
return bugzillaManager()->getLogged();
}
void BugzillaLoginPage::updateLoginButtonStatus()
{
ui.m_loginButton->setEnabled(canLogin());
}
void BugzillaLoginPage::loginError(const QString &error)
{
loginFinished(false);
ui.m_statusWidget->setIdle(xi18nc("@info:status","Error when trying to login: "
"%1", error));
}
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);
}
}
}
if (canLogin()) {
loginClicked();
}
}
}
void BugzillaLoginPage::loginClicked()
{
if (!canLogin()) {
loginFinished(false);
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));
}
}
}
login();
}
bool BugzillaLoginPage::canLogin() const
{
return (!(ui.m_userEdit->text().isEmpty() ||
ui.m_passwordEdit->password().isEmpty()));
}
void BugzillaLoginPage::login()
{
Q_ASSERT(canLogin());
ui.m_statusWidget->setBusy(i18nc("@info:status '1' is a url, '2' the e-mail address",
"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());
}
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 e-mail address 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));
+ ui.m_previewEdit->setText(reportInterface()->generateReportFullText(ReportInterface::DrKonqiStamp::Include,
+ ReportInterface::Backtrace::Complete));
assistant()->setAboutToSend(true);
}
void BugzillaPreviewPage::aboutToHide()
{
assistant()->setAboutToSend(false);
}
//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)"));
// Trigger relogin. If the user took a long time to prepare the login our
// token might have gone invalid in the meantime. As a cheap way to prevent
// this we'll simply refresh the token regardless. It's plenty cheap and
// should reliably ensure that the token is current.
// Disconnect everything first though, this function may get called a bunch
// of times, so we don't want duplicated submissions.
disconnect(bugzillaManager(), &BugzillaManager::loginFinished,
reportInterface(), &ReportInterface::sendBugReport);
disconnect(bugzillaManager(), &BugzillaManager::loginError,
this, nullptr);
connect(bugzillaManager(), &BugzillaManager::loginFinished,
reportInterface(), &ReportInterface::sendBugReport);
connect(bugzillaManager(), &BugzillaManager::loginError,
this, &BugzillaSendPage::sendError);
bugzillaManager()->refreshToken();
}
void BugzillaSendPage::sent(int bug_id)
{
// Disconnect login->submit chain again.
disconnect(bugzillaManager(), &BugzillaManager::loginFinished,
reportInterface(), &ReportInterface::sendBugReport);
disconnect(bugzillaManager(), &BugzillaManager::loginError,
this, nullptr);
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)
{
ui.m_statusWidget->setIdle(xi18nc("@info:status","Error sending the crash report: "
"%1.", errorString));
ui.m_retryButton->setEnabled(true);
ui.m_retryButton->setVisible(true);
}
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') +
+ QString report = reportInterface()->generateReportFullText(ReportInterface::DrKonqiStamp::Exclude,
+ ReportInterface::Backtrace::Complete) + 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
diff --git a/src/bugzillaintegration/reportinterface.cpp b/src/bugzillaintegration/reportinterface.cpp
index 44a2bfdf..3e87a173 100644
--- a/src/bugzillaintegration/reportinterface.cpp
+++ b/src/bugzillaintegration/reportinterface.cpp
@@ -1,429 +1,490 @@
/*******************************************************************
* reportinterface.cpp
* Copyright 2009,2010, 2011 Dario Andres Rodriguez
* Copyright 2009 George Kiagiadakis
*
* 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 "reportinterface.h"
#include
#include "drkonqi.h"
#include "bugzillalib.h"
#include "productmapping.h"
#include "systeminformation.h"
#include "crashedapplication.h"
#include "debuggermanager.h"
#include "parser/backtraceparser.h"
#include "backtracegenerator.h"
#include "applicationdetailsexamples.h"
+// Max size a report may have. This is enforced in bugzilla, hardcoded, and
+// cannot be queried through the API, so handle this client-side in a hardcoded
+// fashion as well.
+static int s_maxReportSize = 65535;
+
ReportInterface::ReportInterface(QObject *parent)
: QObject(parent),
m_duplicate(0)
{
m_bugzillaManager = new BugzillaManager(KDE_BUGZILLA_URL, this);
m_productMapping = new ProductMapping(DrKonqi::crashedApplication(), m_bugzillaManager, this);
m_appDetailsExamples = new ApplicationDetailsExamples(this);
//Information the user can provide about the crash
m_userRememberCrashSituation = false;
m_reproducible = ReproducibleUnsure;
m_provideActionsApplicationDesktop = false;
m_provideUnusualBehavior = false;
m_provideApplicationConfigurationDetails = false;
//Do not attach the bug report to any other existent report (create a new one)
m_attachToBugNumber = 0;
}
void ReportInterface::setBugAwarenessPageData(bool rememberSituation,
Reproducible reproducible, bool actions,
bool unusual, bool configuration)
{
//Save the information the user can provide about the crash from the assistant page
m_userRememberCrashSituation = rememberSituation;
m_reproducible = reproducible;
m_provideActionsApplicationDesktop = actions;
m_provideUnusualBehavior = unusual;
m_provideApplicationConfigurationDetails = configuration;
}
bool ReportInterface::isBugAwarenessPageDataUseful() const
{
//Determine if the assistant should proceed, considering the amount of information
//the user can provide
int rating = selectedOptionsRating();
//Minimum information required even for a good backtrace.
bool useful = m_userRememberCrashSituation &&
(rating >= 2 || (m_reproducible==ReproducibleSometimes ||
m_reproducible==ReproducibleEverytime));
return useful;
}
int ReportInterface::selectedOptionsRating() const
{
//Check how many information the user can provide and generate a rating
int rating = 0;
if (m_provideActionsApplicationDesktop) {
rating += 3;
}
if (m_provideApplicationConfigurationDetails) {
rating += 2;
}
if (m_provideUnusualBehavior) {
rating += 1;
}
return rating;
}
QString ReportInterface::backtrace() const
{
return m_backtrace;
}
void ReportInterface::setBacktrace(const QString & backtrace)
{
m_backtrace = backtrace;
}
QStringList ReportInterface::firstBacktraceFunctions() const
{
return m_firstBacktraceFunctions;
}
void ReportInterface::setFirstBacktraceFunctions(const QStringList & functions)
{
m_firstBacktraceFunctions = functions;
}
QString ReportInterface::title() const
{
return m_reportTitle;
}
void ReportInterface::setTitle(const QString & text)
{
m_reportTitle = text;
}
void ReportInterface::setDetailText(const QString & text)
{
m_reportDetailText = text;
}
void ReportInterface::setPossibleDuplicates(const QStringList & list)
{
m_possibleDuplicates = list;
}
-QString ReportInterface::generateReportFullText(bool drKonqiStamp) const
+QString ReportInterface::generateReportFullText(DrKonqiStamp stamp, Backtrace inlineBacktrace) const
{
//Note: no translations should be done in this function's strings
const CrashedApplication * crashedApp = DrKonqi::crashedApplication();
const SystemInformation * sysInfo = DrKonqi::systemInformation();
QString report;
//Program name and versions
report.append(QStringLiteral("Application: %1 (%2)\n").arg(crashedApp->fakeExecutableBaseName(),
crashedApp->version()));
if ( sysInfo->compiledSources() ) {
report.append(QStringLiteral(" (Compiled from sources)\n"));
} else {
report.append(QStringLiteral("\n"));
}
report.append(QStringLiteral("Qt Version: %1\n").arg(sysInfo->qtVersion()));
report.append(QStringLiteral("Frameworks Version: %1\n").arg(sysInfo->frameworksVersion()));
report.append(QStringLiteral("Operating System: %1\n").arg(sysInfo->operatingSystem()));
//LSB output or manually selected distro
if ( !sysInfo->distributionPrettyName().isEmpty() ) {
report.append(QStringLiteral("Distribution: %1\n").arg(sysInfo->distributionPrettyName()));
} else if ( !sysInfo->bugzillaPlatform().isEmpty() &&
sysInfo->bugzillaPlatform() != QLatin1String("unspecified")) {
report.append(QStringLiteral("Distribution (Platform): %1\n").arg(
sysInfo->bugzillaPlatform()));
}
report.append(QLatin1Char('\n'));
//Details of the crash situation
if (isBugAwarenessPageDataUseful()) {
report.append(QStringLiteral("-- Information about the crash:\n"));
if (!m_reportDetailText.isEmpty()) {
report.append(m_reportDetailText.trimmed());
} else {
//If the user manual reports this crash, he/she should know what to put in here.
//This message is the only one translated in this function
report.append(xi18nc("@info/plain","In detail, tell us what you were doing "
" when the application crashed."));
}
report.append(QLatin1String("\n\n"));
}
//Crash reproducibility (only if useful)
if (m_reproducible != ReproducibleUnsure) {
if (m_reproducible == ReproducibleEverytime) {
report.append(QStringLiteral("The crash can be reproduced every time.\n\n"));
} else if (m_reproducible == ReproducibleSometimes) {
report.append(QStringLiteral("The crash can be reproduced sometimes.\n\n"));
} else if (m_reproducible == ReproducibleNever) {
report.append(QStringLiteral("The crash does not seem to be reproducible.\n\n"));
}
}
//Backtrace
- report.append(QStringLiteral("-- Backtrace:\n"));
+ switch (inlineBacktrace) {
+ case Backtrace::Complete:
+ report.append(QStringLiteral("-- Backtrace:\n"));
+ break;
+ case Backtrace::Reduced:
+ report.append(QStringLiteral("-- Backtrace (Reduced):\n"));
+ break;
+ case Backtrace::Exclude:
+ report.append(QStringLiteral("The backtrace was excluded and likely attached as a file.\n"));
+ break;
+ }
if (!m_backtrace.isEmpty()) {
- QString formattedBacktrace = m_backtrace.trimmed();
- report.append(formattedBacktrace + QLatin1Char('\n'));
+ switch (inlineBacktrace) {
+ case Backtrace::Complete:
+ report.append(m_backtrace.trimmed() + QLatin1Char('\n'));
+ break;
+ case Backtrace::Reduced:
+ report.append(DrKonqi::debuggerManager()->backtraceGenerator()->parser()->simplifiedBacktrace() + QLatin1Char('\n'));
+ break;
+ case Backtrace::Exclude:
+ report.append(QStringLiteral("The backtrace is attached as a comment due to length constraints\n"));
+ break;
+ }
} else {
report.append(QStringLiteral("A useful backtrace could not be generated\n"));
}
//Possible duplicates (selected by the user)
if (!m_possibleDuplicates.isEmpty()) {
report.append(QLatin1Char('\n'));
QString duplicatesString;
Q_FOREACH(const QString & dupe, m_possibleDuplicates) {
duplicatesString += QLatin1String("bug ") + dupe + QLatin1String(", ");
}
duplicatesString = duplicatesString.left(duplicatesString.length()-2) + QLatin1Char('.');
report.append(QStringLiteral("The reporter indicates this bug may be a duplicate of or related to %1\n")
.arg(duplicatesString));
}
//Several possible duplicates (by bugzilla query)
if (!m_allPossibleDuplicatesByQuery.isEmpty()) {
report.append(QLatin1Char('\n'));
QString duplicatesString;
int count = m_allPossibleDuplicatesByQuery.count();
for(int i=0; i < count && i < 5; i++) {
duplicatesString += QLatin1String("bug ") + m_allPossibleDuplicatesByQuery.at(i) +
QLatin1String(", ");
}
duplicatesString = duplicatesString.left(duplicatesString.length()-2) + QLatin1Char('.');
report.append(QStringLiteral("Possible duplicates by query: %1\n").arg(duplicatesString));
}
- if (drKonqiStamp) {
+ switch (stamp) {
+ case DrKonqiStamp::Include:
report.append(QLatin1String("\nReported using DrKonqi"));
+ break;
+ case DrKonqiStamp::Exclude:
+ break;
}
return report;
}
QString ReportInterface::generateAttachmentComment() const
{
//Note: no translations should be done in this function's strings
const CrashedApplication * crashedApp = DrKonqi::crashedApplication();
const SystemInformation * sysInfo = DrKonqi::systemInformation();
QString comment;
//Program name and versions
comment.append(QStringLiteral("%1 (%2) using Qt %4\n\n")
.arg(crashedApp->fakeExecutableBaseName())
.arg(crashedApp->version())
.arg(sysInfo->qtVersion()));
//Details of the crash situation
if (isBugAwarenessPageDataUseful()) {
comment.append(QStringLiteral("%1\n\n").arg(m_reportDetailText.trimmed()));
}
//Backtrace (only 6 lines)
comment.append(QStringLiteral("-- Backtrace (Reduced):\n"));
QString reducedBacktrace =
DrKonqi::debuggerManager()->backtraceGenerator()->parser()->simplifiedBacktrace();
comment.append(reducedBacktrace.trimmed());
return comment;
}
Bugzilla::NewBug ReportInterface::newBugReportTemplate() const
{
const SystemInformation *sysInfo = DrKonqi::systemInformation();
Bugzilla::NewBug bug;
bug.product = m_productMapping->bugzillaProduct();
bug.component = m_productMapping->bugzillaComponent();
bug.version = m_productMapping->bugzillaVersion();
bug.op_sys = sysInfo->bugzillaOperatingSystem();
if (sysInfo->compiledSources()) {
bug.platform = QLatin1String("Compiled Sources");
} else {
bug.platform = sysInfo->bugzillaPlatform();
}
bug.keywords = QStringList { QStringLiteral("drkonqi") };
bug.priority = QLatin1String("NOR");
bug.severity = QLatin1String("crash");
bug.summary = m_reportTitle;
return bug;
}
-void ReportInterface::sendBugReport() const
+void ReportInterface::sendBugReport()
{
if (m_attachToBugNumber > 0)
{
//We are going to attach the report to an existent one
- connect(m_bugzillaManager, &BugzillaManager::addMeToCCFinished, this, &ReportInterface::addedToCC);
+ connect(m_bugzillaManager, &BugzillaManager::addMeToCCFinished, this, &ReportInterface::attachBacktraceWithReport);
connect(m_bugzillaManager, &BugzillaManager::addMeToCCError, this, &ReportInterface::sendReportError);
//First add the user to the CC list, then attach
m_bugzillaManager->addMeToCC(m_attachToBugNumber);
} else {
//Creating a new bug report
+ bool attach = false;
Bugzilla::NewBug report = newBugReportTemplate();
- report.description = generateReportFullText(true);
+ report.description = generateReportFullText(ReportInterface::DrKonqiStamp::Include,
+ ReportInterface::Backtrace::Complete);
+
+ // If the report is too long try to reduce it, try to not include the
+ // backtrace and eventually give up.
+ // Bugzilla has a hard-limit on the server side, if we cannot strip the
+ // report down enough the submission will simply not work.
+ // Exhausting the cap with just user input is nigh impossible, so we'll
+ // forego handling of the report being too long even without without
+ // backtrace.
+ // https://bugs.kde.org/show_bug.cgi?id=248807
+ if (report.description.size() >= s_maxReportSize) {
+ report.description = generateReportFullText(ReportInterface::DrKonqiStamp::Include,
+ ReportInterface::Backtrace::Reduced);
+ attach = true;
+ }
+ if (report.description.size() >= s_maxReportSize) {
+ report.description = generateReportFullText(ReportInterface::DrKonqiStamp::Include,
+ ReportInterface::Backtrace::Exclude);
+ attach = true;
+ }
Q_ASSERT(!report.description.isEmpty());
connect(m_bugzillaManager, &BugzillaManager::sendReportErrorInvalidValues, this, &ReportInterface::sendUsingDefaultProduct);
- connect(m_bugzillaManager, &BugzillaManager::reportSent, this, &ReportInterface::reportSent);
+ connect(m_bugzillaManager, &BugzillaManager::reportSent,
+ this, [=](int bugId) {
+ if (attach) {
+ m_attachToBugNumber = bugId;
+ attachBacktrace(QStringLiteral("DrKonqi auto-attaching complete backtrace."));
+ } else {
+ emit reportSent(bugId);
+ }
+ });
connect(m_bugzillaManager, &BugzillaManager::sendReportError, this, &ReportInterface::sendReportError);
m_bugzillaManager->sendReport(report);
}
}
void ReportInterface::sendUsingDefaultProduct() const
{
//Fallback function: if some of the custom values fail, we need to reset all the fields to the default
//(and valid) bugzilla values; and try to resend
Bugzilla::NewBug bug = newBugReportTemplate();
bug.product = QLatin1String("kde");
bug.component = QLatin1String("general");
bug.platform = QLatin1String("unspecified");
- bug.description = generateReportFullText(true);
+ bug.description = generateReportFullText(ReportInterface::DrKonqiStamp::Include,
+ ReportInterface::Backtrace::Complete);
m_bugzillaManager->sendReport(bug);
}
-void ReportInterface::addedToCC()
+void ReportInterface::attachBacktraceWithReport()
+{
+ attachBacktrace(generateAttachmentComment());
+}
+
+void ReportInterface::attachBacktrace(const QString &comment)
{
//The user was added to the CC list, proceed with the attachment
connect(m_bugzillaManager, &BugzillaManager::attachToReportSent, this, &ReportInterface::attachSent);
connect(m_bugzillaManager, &BugzillaManager::attachToReportError, this, &ReportInterface::sendReportError);
- QString reportText = generateReportFullText(true);
- QString comment = generateAttachmentComment();
+ QString reportText = generateReportFullText(ReportInterface::DrKonqiStamp::Include,
+ ReportInterface::Backtrace::Complete);
QString filename = getSuggestedKCrashFilename(DrKonqi::crashedApplication());
QLatin1String summary("New crash information added by DrKonqi");
//Attach the report. The comment of the attachment also includes the bug description
m_bugzillaManager->attachTextToReport(reportText, filename, summary,
m_attachToBugNumber, comment);
}
void ReportInterface::attachSent(int attachId)
{
Q_UNUSED(attachId);
//The bug was attached, consider it "sent"
emit reportSent(m_attachToBugNumber);
}
QStringList ReportInterface::relatedBugzillaProducts() const
{
return m_productMapping->relatedBugzillaProducts();
}
bool ReportInterface::isWorthReporting() const
{
if (DrKonqi::ignoreQuality()) {
return true;
}
//Evaluate if the provided information is useful enough to enable the automatic report
bool needToReport = false;
if (!m_userRememberCrashSituation) {
//This should never happen... but...
return false;
}
int rating = selectedOptionsRating();
BacktraceParser::Usefulness use =
DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness();
switch (use) {
case BacktraceParser::ReallyUseful: {
//Perfect backtrace: require at least one option or a 100%-50% reproducible crash
needToReport = (rating >=2) ||
(m_reproducible == ReproducibleEverytime || m_reproducible == ReproducibleSometimes);
break;
}
case BacktraceParser::MayBeUseful: {
//Not perfect backtrace: require at least two options or a 100% reproducible crash
needToReport = (rating >=3) || (m_reproducible == ReproducibleEverytime);
break;
}
case BacktraceParser::ProbablyUseless:
//Bad backtrace: require at least two options and always reproducible (strict)
needToReport = (rating >=5) && (m_reproducible == ReproducibleEverytime);
break;
case BacktraceParser::Useless:
case BacktraceParser::InvalidUsefulness: {
needToReport = false;
}
}
return needToReport;
}
void ReportInterface::setAttachToBugNumber(uint bugNumber)
{
//If bugNumber>0, the report is going to be attached to bugNumber
m_attachToBugNumber = bugNumber;
}
uint ReportInterface::attachToBugNumber() const
{
return m_attachToBugNumber;
}
void ReportInterface::setDuplicateId(uint duplicate)
{
m_duplicate = duplicate;
}
uint ReportInterface::duplicateId() const
{
return m_duplicate;
}
void ReportInterface::setPossibleDuplicatesByQuery(const QStringList & list)
{
m_allPossibleDuplicatesByQuery = list;
}
BugzillaManager * ReportInterface::bugzillaManager() const
{
return m_bugzillaManager;
}
ApplicationDetailsExamples * ReportInterface::appDetailsExamples() const
{
return m_appDetailsExamples;
}
-
-
diff --git a/src/bugzillaintegration/reportinterface.h b/src/bugzillaintegration/reportinterface.h
index 96b2bc82..5bb4db24 100644
--- a/src/bugzillaintegration/reportinterface.h
+++ b/src/bugzillaintegration/reportinterface.h
@@ -1,134 +1,151 @@
/*******************************************************************
* reportinterface.h
* Copyright 2009, 2011 Dario Andres Rodriguez
* Copyright 2009 George Kiagiadakis
*
* 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 .
*
******************************************************************/
#ifndef REPORTINTERFACE__H
#define REPORTINTERFACE__H
#include
#include
namespace Bugzilla {
class NewBug;
}
class BugzillaManager;
class ProductMapping;
class ApplicationDetailsExamples;
class ReportInterface : public QObject
{
Q_OBJECT
public:
enum Reproducible { ReproducibleUnsure, ReproducibleNever,
ReproducibleSometimes, ReproducibleEverytime };
+ enum class Backtrace {
+ Complete,
+ Reduced,
+ Exclude
+ };
+
+ enum class DrKonqiStamp {
+ Include,
+ Exclude
+ };
+
explicit ReportInterface(QObject *parent = nullptr);
void setBugAwarenessPageData(bool, Reproducible, bool, bool, bool);
bool isBugAwarenessPageDataUseful() const;
int selectedOptionsRating() const;
QStringList firstBacktraceFunctions() const;
void setFirstBacktraceFunctions(const QStringList & functions);
QString backtrace() const;
void setBacktrace(const QString & backtrace);
QString title() const;
void setTitle(const QString & text);
void setDetailText(const QString & text);
void setPossibleDuplicates(const QStringList & duplicatesList);
- QString generateReportFullText(bool drKonqiStamp) const;
+ QString generateReportFullText(DrKonqiStamp stamp,
+ Backtrace inlineBacktrace) const;
Bugzilla::NewBug newBugReportTemplate() const;
QStringList relatedBugzillaProducts() const;
bool isWorthReporting() const;
//Zero means creating a new bug report
void setAttachToBugNumber(uint);
uint attachToBugNumber() const;
//Zero means there is no duplicate
void setDuplicateId(uint);
uint duplicateId() const;
void setPossibleDuplicatesByQuery(const QStringList &);
BugzillaManager * bugzillaManager() const;
ApplicationDetailsExamples * appDetailsExamples() const;
bool userCanProvideActionsAppDesktop() const {
return m_provideActionsApplicationDesktop;
}
bool userCanProvideUnusualBehavior() const {
return m_provideUnusualBehavior;
}
bool userCanProvideApplicationConfigDetails() const {
return m_provideApplicationConfigurationDetails;
}
public Q_SLOTS:
- void sendBugReport() const;
+ void sendBugReport();
private Q_SLOTS:
void sendUsingDefaultProduct() const;
- void addedToCC();
+ // Attach backtrace to bug and use collected report as comment.
+ void attachBacktraceWithReport();
void attachSent(int);
Q_SIGNALS:
void reportSent(int);
void sendReportError(const QString &);
private:
+ // Attach backtrace to bug. Only used internally when the comment isn't
+ // meant to be the full report.
+ void attachBacktrace(const QString &comment);
+
QString generateAttachmentComment() const;
//Information the user can provide
bool m_userRememberCrashSituation;
Reproducible m_reproducible;
bool m_provideActionsApplicationDesktop;
bool m_provideUnusualBehavior;
bool m_provideApplicationConfigurationDetails;
QString m_backtrace;
QStringList m_firstBacktraceFunctions;
QString m_reportTitle;
QString m_reportDetailText;
QStringList m_possibleDuplicates;
QStringList m_allPossibleDuplicatesByQuery;
uint m_attachToBugNumber;
uint m_duplicate;
ProductMapping * m_productMapping = nullptr;
BugzillaManager * m_bugzillaManager = nullptr;
ApplicationDetailsExamples * m_appDetailsExamples = nullptr;
};
#endif