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("

    "); bool backtraceGenerated = true; switch (use) { case BacktraceParser::ReallyUseful: { explanationHTML += QStringLiteral("
  • %1
  • ").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