1/*******************************************************************
2* reportassistantpages_bugzilla.cpp
3* Copyright 2009, 2010, 2011 Dario Andres Rodriguez <andresbajotierra@gmail.com>
4*
5* This program is free software; you can redistribute it and/or
6* modify it under the terms of the GNU General Public License as
7* published by the Free Software Foundation; either version 2 of
8* the License, or (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17*
18******************************************************************/
19
20#include "reportassistantpages_bugzilla.h"
21
22#include <QtCore/QTimer>
23
24#include <QLabel>
25#include <QCheckBox>
26#include <QToolTip>
27#include <QCursor>
28
29#include <QtDBus/QDBusInterface>
30#include <QtDBus/QDBusReply>
31
32#include <KDebug>
33#include <KIcon>
34#include <KMessageBox>
35#include <KToolInvocation>
36#include <KWallet/Wallet>
37#include <KCapacityBar>
38
39/* Unhandled error dialog includes */
40#include <KFileDialog>
41#include <KWebView>
42#include <KIO/Job>
43#include <KIO/NetAccess>
44#include <KTemporaryFile>
45
46#include "reportinterface.h"
47#include "systeminformation.h"
48#include "crashedapplication.h"
49#include "bugzillalib.h"
50#include "statuswidget.h"
51#include "drkonqi.h"
52#include "drkonqi_globals.h"
53#include "applicationdetailsexamples.h"
54
55static const char kWalletEntryName[] = "drkonqi_bugzilla";
56static const char kWalletEntryUsername[] = "username";
57static const char kWalletEntryPassword[] = "password";
58
59static const char konquerorKWalletEntryName[] = KDE_BUGZILLA_URL "index.cgi#login";
60static const char konquerorKWalletEntryUsername[] = "Bugzilla_login";
61static const char konquerorKWalletEntryPassword[] = "Bugzilla_password";
62
63//BEGIN BugzillaLoginPage
64
65BugzillaLoginPage::BugzillaLoginPage(ReportAssistantDialog * parent) :
66 ReportAssistantPage(parent),
67 m_wallet(0), m_walletWasOpenedBefore(false)
68{
69 connect(bugzillaManager(), SIGNAL(loginFinished(bool)), this, SLOT(loginFinished(bool)));
70 connect(bugzillaManager(), SIGNAL(loginError(QString,QString)), this, SLOT(loginError(QString,QString)));
71
72 ui.setupUi(this);
73 ui.m_statusWidget->setIdle(i18nc("@info:status '1' is replaced with the short URL of the bugzilla ",
74 "You need to login with your %1 account in order to proceed.",
75 QLatin1String(KDE_BUGZILLA_SHORT_URL)));
76
77 ui.m_loginButton->setGuiItem(KGuiItem2(i18nc("@action:button", "Login"),
78 KIcon("network-connect"),
79 i18nc("@info:tooltip", "Use this button to login "
80 "to the KDE bug tracking system using the provided "
81 "username and password.")));
82 ui.m_loginButton->setEnabled(false);
83
84 connect(ui.m_loginButton, SIGNAL(clicked()) , this, SLOT(loginClicked()));
85
86 connect(ui.m_userEdit, SIGNAL(returnPressed()) , this, SLOT(loginClicked()));
87 connect(ui.m_passwordEdit, SIGNAL(returnPressed()) , this, SLOT(loginClicked()));
88
89 connect(ui.m_userEdit, SIGNAL(textChanged(QString)) , this, SLOT(updateLoginButtonStatus()));
90 connect(ui.m_passwordEdit, SIGNAL(textChanged(QString)) , this, SLOT(updateLoginButtonStatus()));
91
92 ui.m_noticeLabel->setText(
93 i18nc("@info/rich","<note>You need a user account on the "
94 "<link url='%1'>KDE bug tracking system</link> in order to "
95 "file a bug report, because we may need to contact you later "
96 "for requesting further information. If you do not have "
97 "one, you can freely <link url='%2'>create one here</link>. "
98 "Please do not use disposable email accounts.</note>",
99 DrKonqi::crashedApplication()->bugReportAddress(),
100 QLatin1String(KDE_BUGZILLA_CREATE_ACCOUNT_URL)));
101}
102
103bool BugzillaLoginPage::isComplete()
104{
105 return bugzillaManager()->getLogged();
106}
107
108void BugzillaLoginPage::updateLoginButtonStatus()
109{
110 ui.m_loginButton->setEnabled( !ui.m_userEdit->text().isEmpty() &&
111 !ui.m_passwordEdit->text().isEmpty() );
112}
113
114void BugzillaLoginPage::loginError(const QString & err, const QString & extendedMessage)
115{
116 loginFinished(false);
117 ui.m_statusWidget->setIdle(i18nc("@info:status","Error when trying to login: "
118 "<message>%1.</message>", err));
119 if (!extendedMessage.isEmpty()) {
120 new UnhandledErrorDialog(this, err, extendedMessage);
121 }
122}
123
124void BugzillaLoginPage::aboutToShow()
125{
126 if (bugzillaManager()->getLogged()) {
127 ui.m_loginButton->setEnabled(false);
128
129 ui.m_userEdit->setEnabled(false);
130 ui.m_userEdit->clear();
131 ui.m_passwordEdit->setEnabled(false);
132 ui.m_passwordEdit->clear();
133
134 ui.m_loginButton->setVisible(false);
135 ui.m_userEdit->setVisible(false);
136 ui.m_passwordEdit->setVisible(false);
137 ui.m_userLabel->setVisible(false);
138 ui.m_passwordLabel->setVisible(false);
139
140 ui.m_savePasswordCheckBox->setVisible(false);
141
142 ui.m_noticeLabel->setVisible(false);
143
144 ui.m_statusWidget->setIdle(i18nc("@info:status the user is logged at the bugtracker site "
145 "as USERNAME",
146 "Logged in at the KDE bug tracking system (%1) as: %2.",
147 QLatin1String(KDE_BUGZILLA_SHORT_URL),
148 bugzillaManager()->getUsername()));
149 } else {
150 //Try to show wallet dialog once this dialog is shown
151 QTimer::singleShot(100, this, SLOT(walletLogin()));
152 }
153}
154
155bool BugzillaLoginPage::kWalletEntryExists(const QString& entryName)
156{
157 return !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
158 KWallet::Wallet::FormDataFolder(),
159 entryName);
160}
161
162void BugzillaLoginPage::openWallet()
163{
164 //Store if the wallet was previously opened so we can know if we should close it later
165 m_walletWasOpenedBefore = KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet());
166 //Request open the wallet
167 m_wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(),
168 static_cast<QWidget*>(this->parent())->winId());
169}
170
171void BugzillaLoginPage::walletLogin()
172{
173 if (!m_wallet) {
174 if (kWalletEntryExists(QLatin1String(kWalletEntryName))) { //Key exists!
175 openWallet();
176 ui.m_savePasswordCheckBox->setCheckState(Qt::Checked);
177 //Was the wallet opened?
178 if (m_wallet) {
179 m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
180
181 //Use wallet data to try login
182 QMap<QString, QString> values;
183 m_wallet->readMap(QLatin1String(kWalletEntryName), values);
184 QString username = values.value(QLatin1String(kWalletEntryUsername));
185 QString password = values.value(QLatin1String(kWalletEntryPassword));
186
187 if (!username.isEmpty() && !password.isEmpty()) {
188 ui.m_userEdit->setText(username);
189 ui.m_passwordEdit->setText(password);
190 }
191 }
192 } else if (kWalletEntryExists(QLatin1String(konquerorKWalletEntryName))) {
193 //If the DrKonqi entry is empty, but a Konqueror entry exists, use and copy it.
194 openWallet();
195 if (m_wallet) {
196 m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
197
198 //Fetch Konqueror data
199 QMap<QString, QString> values;
200 m_wallet->readMap(QLatin1String(konquerorKWalletEntryName), values);
201 QString username = values.value(QLatin1String(konquerorKWalletEntryUsername));
202 QString password = values.value(QLatin1String(konquerorKWalletEntryPassword));
203
204 if (!username.isEmpty() && !password.isEmpty()) {
205 //Copy to DrKonqi own entries
206 values.clear();
207 values.insert(QLatin1String(kWalletEntryUsername), username);
208 values.insert(QLatin1String(kWalletEntryPassword), password);
209 m_wallet->writeMap(QLatin1String(kWalletEntryName), values);
210
211 ui.m_savePasswordCheckBox->setCheckState(Qt::Checked);
212
213 ui.m_userEdit->setText(username);
214 ui.m_passwordEdit->setText(password);
215 }
216 }
217
218 }
219 }
220}
221
222bool BugzillaLoginPage::canSetCookies()
223{
224 QDBusInterface kded(QLatin1String("org.kde.kded"),
225 QLatin1String("/kded"),
226 QLatin1String("org.kde.kded"));
227 QDBusReply<bool> kcookiejarLoaded = kded.call(QLatin1String("loadModule"),
228 QLatin1String("kcookiejar"));
229 if (!kcookiejarLoaded.isValid()) {
230 KMessageBox::error(this, i18n("Failed to communicate with kded. Make sure it is running."));
231 return false;
232 } else if (!kcookiejarLoaded.value()) {
233 KMessageBox::error(this, i18n("Failed to load KCookieServer. Check your KDE installation."));
234 return false;
235 }
236
237
238 QDBusInterface kcookiejar(QLatin1String("org.kde.kded"),
239 QLatin1String("/modules/kcookiejar"),
240 QLatin1String("org.kde.KCookieServer"));
241 QDBusReply<QString> advice = kcookiejar.call(QLatin1String("getDomainAdvice"),
242 QLatin1String(KDE_BUGZILLA_URL));
243
244 if (!advice.isValid()) {
245 KMessageBox::error(this, i18n("Failed to communicate with KCookieServer."));
246 return false;
247 }
248
249 kDebug() << "Got reply from KCookieServer:" << advice.value();
250
251 if (advice.value() == QLatin1String("Reject")) {
252 QString msg = i18nc("@info 1 is the bugzilla website url",
253 "Cookies are not allowed in your KDE network settings. In order to "
254 "proceed, you need to allow %1 to set cookies.", KDE_BUGZILLA_URL);
255
256 KGuiItem yesItem = KStandardGuiItem::yes();
257 yesItem.setText(i18nc("@action:button 1 is the bugzilla website url",
258 "Allow %1 to set cookies", KDE_BUGZILLA_URL));
259
260 KGuiItem noItem = KStandardGuiItem::no();
261 noItem.setText(i18nc("@action:button do not allow the bugzilla website "
262 "to set cookies", "No, do not allow"));
263
264 if (KMessageBox::warningYesNo(this, msg, QString(), yesItem, noItem) == KMessageBox::Yes) {
265 QDBusReply<bool> success = kcookiejar.call(QLatin1String("setDomainAdvice"),
266 QLatin1String(KDE_BUGZILLA_URL),
267 QLatin1String("Accept"));
268 if (!success.isValid() || !success.value()) {
269 kWarning() << "Failed to set domain advice in KCookieServer";
270 return false;
271 } else {
272 return true;
273 }
274 } else {
275 return false;
276 }
277 }
278
279 return true;
280}
281
282void BugzillaLoginPage::loginClicked()
283{
284 if (!(ui.m_userEdit->text().isEmpty() || ui.m_passwordEdit->text().isEmpty())) {
285
286 if (!canSetCookies()) {
287 return;
288 }
289
290 ui.m_loginButton->setEnabled(false);
291
292 ui.m_userLabel->setEnabled(false);
293 ui.m_passwordLabel->setEnabled(false);
294
295 ui.m_userEdit->setEnabled(false);
296 ui.m_passwordEdit->setEnabled(false);
297 ui.m_savePasswordCheckBox->setEnabled(false);
298
299 if (ui.m_savePasswordCheckBox->checkState()==Qt::Checked) { //Wants to save data
300 if (!m_wallet) {
301 openWallet();
302 }
303 //Got wallet open ?
304 if (m_wallet) {
305 m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
306
307 QMap<QString, QString> values;
308 values.insert(QLatin1String(kWalletEntryUsername), ui.m_userEdit->text());
309 values.insert(QLatin1String(kWalletEntryPassword), ui.m_passwordEdit->text());
310 m_wallet->writeMap(QLatin1String(kWalletEntryName), values);
311 }
312
313 } else { //User doesn't want to save or wants to remove.
314 if (kWalletEntryExists(QLatin1String(kWalletEntryName))) {
315 if (!m_wallet) {
316 openWallet();
317 }
318 //Got wallet open ?
319 if (m_wallet) {
320 m_wallet->setFolder(KWallet::Wallet::FormDataFolder());
321 m_wallet->removeEntry(QLatin1String(kWalletEntryName));
322 }
323 }
324 }
325
326 ui.m_statusWidget->setBusy(i18nc("@info:status '1' is a url, '2' the username",
327 "Performing login at %1 as %2...",
328 QLatin1String(KDE_BUGZILLA_SHORT_URL), ui.m_userEdit->text()));
329
330 bugzillaManager()->tryLogin(ui.m_userEdit->text(), ui.m_passwordEdit->text());
331 } else {
332 loginFinished(false);
333 }
334}
335
336void BugzillaLoginPage::loginFinished(bool logged)
337{
338 if (logged) {
339 emitCompleteChanged();
340
341 aboutToShow();
342 if (m_wallet) {
343 if (m_wallet->isOpen() && !m_walletWasOpenedBefore) {
344 m_wallet->lockWallet();
345 }
346 }
347
348 emit loggedTurnToNextPage();
349 } else {
350 ui.m_statusWidget->setIdle(i18nc("@info:status/rich","<b>Error: Invalid username or "
351 "password</b>"));
352
353 ui.m_loginButton->setEnabled(true);
354
355 ui.m_userEdit->setEnabled(true);
356 ui.m_passwordEdit->setEnabled(true);
357 ui.m_savePasswordCheckBox->setEnabled(true);
358
359 ui.m_userEdit->setFocus(Qt::OtherFocusReason);
360 }
361}
362
363BugzillaLoginPage::~BugzillaLoginPage()
364{
365 //Close wallet if we close the assistant in this step
366 if (m_wallet) {
367 if (m_wallet->isOpen() && !m_walletWasOpenedBefore) {
368 m_wallet->lockWallet();
369 }
370 delete m_wallet;
371 }
372}
373
374//END BugzillaLoginPage
375
376//BEGIN BugzillaInformationPage
377
378BugzillaInformationPage::BugzillaInformationPage(ReportAssistantDialog * parent)
379 : ReportAssistantPage(parent),
380 m_textsOK(false), m_distributionComboSetup(false), m_distroComboVisible(false),
381 m_requiredCharacters(1)
382{
383 ui.setupUi(this);
384 m_textCompleteBar = new KCapacityBar(KCapacityBar::DrawTextInline, this);
385 ui.horizontalLayout_2->addWidget(m_textCompleteBar);
386
387 connect(ui.m_titleEdit, SIGNAL(textChanged(QString)), this, SLOT(checkTexts()));
388 connect(ui.m_detailsEdit, SIGNAL(textChanged()), this, SLOT(checkTexts()));
389
390 connect(ui.m_titleLabel, SIGNAL(linkActivated(QString)), this, SLOT(showTitleExamples()));
391 connect(ui.m_detailsLabel, SIGNAL(linkActivated(QString)), this,
392 SLOT(showDescriptionHelpExamples()));
393
394 ui.m_compiledSourcesCheckBox->setChecked(
395 DrKonqi::systemInformation()->compiledSources());
396
397}
398
399void BugzillaInformationPage::aboutToShow()
400{
401 if (!m_distributionComboSetup) {
402 //Autodetecting distro failed ?
403 if (DrKonqi::systemInformation()->bugzillaPlatform() == QLatin1String("unspecified")) {
404 m_distroComboVisible = true;
405 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
406 "Unspecified"),"unspecified");
407 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
408 "Archlinux"), "Archlinux Packages");
409 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
410 "Chakra"), "Chakra");
411 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
412 "Debian stable"), "Debian stable");
413 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
414 "Debian testing"), "Debian testing");
415 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
416 "Debian unstable"), "Debian unstable");
417 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
418 "Exherbo"), "Exherbo Packages");
419 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
420 "Fedora"), "Fedora RPMs");
421 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
422 "Gentoo"), "Gentoo Packages");
423 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
424 "Mageia"), "Mageia RPMs");
425 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
426 "Mandriva"), "Mandriva RPMs");
427 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
428 "OpenSUSE"), "openSUSE RPMs");
429 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
430 "Pardus"), "Pardus Packages");
431 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
432 "RedHat"), "RedHat RPMs");
433 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
434 "Slackware"), "Slackware Packages");
435 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
436 "Ubuntu (and derivatives)"),
437 "Ubuntu Packages");
438 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
439 "FreeBSD (Ports)"), "FreeBSD Ports");
440 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
441 "NetBSD (pkgsrc)"), "NetBSD pkgsrc");
442 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
443 "OpenBSD"), "OpenBSD Packages");
444 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
445 "Mac OS X"), "MacPorts Packages");
446 ui.m_distroChooserCombo->addItem(i18nc("@label:listbox KDE distribution method",
447 "Solaris"), "Solaris Packages");
448
449 //Restore previously selected bugzilla platform (distribution)
450 KConfigGroup config(KGlobal::config(), "BugzillaInformationPage");
451 QString entry = config.readEntry("BugzillaPlatform","unspecified");
452 int index = ui.m_distroChooserCombo->findData(entry);
453 if ( index == -1 ) index = 0;
454 ui.m_distroChooserCombo->setCurrentIndex(index);
455 } else {
456 ui.m_distroChooserCombo->setVisible(false);
457 }
458 m_distributionComboSetup = true;
459 }
460
461 //Calculate the minimum number of characters required for a description
462 //If creating a new report: minimum 40, maximum 80
463 //If attaching to an existent report: minimum 30, maximum 50
464 int multiplier = (reportInterface()->attachToBugNumber() == 0) ? 10 : 5;
465 m_requiredCharacters = 20 + (reportInterface()->selectedOptionsRating() * multiplier);
466
467 //Fill the description textedit with some headings:
468 QString descriptionTemplate;
469 if (ui.m_detailsEdit->toPlainText().isEmpty()) {
470 if (reportInterface()->userCanProvideActionsAppDesktop()) {
471 descriptionTemplate += "- What I was doing when the application crashed:\n\n";
472 }
473 if (reportInterface()->userCanProvideUnusualBehavior()) {
474 descriptionTemplate += "- Unusual behavior I noticed:\n\n";
475 }
476 if (reportInterface()->userCanProvideApplicationConfigDetails()) {
477 descriptionTemplate += "- Custom settings of the application:\n\n";
478 }
479 ui.m_detailsEdit->setText(descriptionTemplate);
480 }
481
482 checkTexts(); //May be the options (canDetail) changed and we need to recheck
483}
484
485int BugzillaInformationPage::currentDescriptionCharactersCount()
486{
487 QString description = ui.m_detailsEdit->toPlainText();
488
489 //Do not count template messages, and other misc chars
490 description.remove("What I was doing when the application crashed");
491 description.remove("Unusual behavior I noticed");
492 description.remove("Custom settings of the application");
493 description.remove('\n');
494 description.remove('-');
495 description.remove(':');
496 description.remove(' ');
497
498 return description.size();
499}
500
501void BugzillaInformationPage::checkTexts()
502{
503 //If attaching this report to an existing one then the title is not needed
504 bool showTitle = (reportInterface()->attachToBugNumber() == 0);
505 ui.m_titleEdit->setVisible(showTitle);
506 ui.m_titleLabel->setVisible(showTitle);
507
508 bool ok = !((ui.m_titleEdit->isVisible() && ui.m_titleEdit->text().isEmpty())
509 || ui.m_detailsEdit->toPlainText().isEmpty());
510
511 QString message;
512 int percent = currentDescriptionCharactersCount() * 100 / m_requiredCharacters;
513 if (percent >= 100) {
514 percent = 100;
515 message = i18nc("the minimum required length of a text was reached",
516 "Minimum length reached");
517 } else {
518 message = i18nc("the minimum required length of a text wasn't reached yet",
519 "Provide more information");
520 }
521 m_textCompleteBar->setValue(percent);
522 m_textCompleteBar->setText(message);
523
524 if (ok != m_textsOK) {
525 m_textsOK = ok;
526 emitCompleteChanged();
527 }
528}
529
530bool BugzillaInformationPage::showNextPage()
531{
532 checkTexts();
533
534 if (m_textsOK) {
535 bool detailsShort = currentDescriptionCharactersCount() < m_requiredCharacters;
536
537 if (detailsShort) {
538 //The user input is less than we want.... encourage to write more
539 QString message = i18nc("@info","The description about the crash details does not provide "
540 "enough information yet.<br /><br />");
541
542 message += ' ' + i18nc("@info","The amount of required information is proportional to "
543 "the quality of the other information like the backtrace "
544 "or the reproducibility rate."
545 "<br /><br />");
546
547 if (reportInterface()->userCanProvideActionsAppDesktop()
548 || reportInterface()->userCanProvideUnusualBehavior()
549 || reportInterface()->userCanProvideApplicationConfigDetails()) {
550 message += ' ' + i18nc("@info","Previously, you told DrKonqi that you could provide some "
551 "contextual information. Try writing more details about your situation. "
552 "(even little ones could help us.)<br /><br />");
553 }
554
555 message += ' ' + i18nc("@info","If you cannot provide more information, your report "
556 "will probably waste developers' time. Can you tell us more?");
557
558 KGuiItem yesItem = KStandardGuiItem::yes();
559 yesItem.setText(i18n("Yes, let me add more information"));
560
561 KGuiItem noItem = KStandardGuiItem::no();
562 noItem.setText(i18n("No, I cannot add any other information"));
563
564 if (KMessageBox::warningYesNo(this, message,
565 i18nc("@title:window","We need more information"),
566 yesItem, noItem)
567 == KMessageBox::No) {
568 //Request the assistant to close itself (it will prompt for confirmation anyways)
569 assistant()->close();
570 return false;
571 }
572 } else {
573 return true;
574 }
575 }
576
577 return false;
578}
579
580bool BugzillaInformationPage::isComplete()
581{
582 return m_textsOK;
583}
584
585void BugzillaInformationPage::aboutToHide()
586{
587 //Save fields data
588 reportInterface()->setTitle(ui.m_titleEdit->text());
589 reportInterface()->setDetailText(ui.m_detailsEdit->toPlainText());
590
591 if (m_distroComboVisible) {
592 //Save bugzilla platform (distribution)
593 QString bugzillaPlatform = ui.m_distroChooserCombo->itemData(
594 ui.m_distroChooserCombo->currentIndex()).toString();
595 KConfigGroup config(KGlobal::config(), "BugzillaInformationPage");
596 config.writeEntry("BugzillaPlatform", bugzillaPlatform);
597 DrKonqi::systemInformation()->setBugzillaPlatform(bugzillaPlatform);
598 }
599 bool compiledFromSources = ui.m_compiledSourcesCheckBox->checkState() == Qt::Checked;
600 DrKonqi::systemInformation()->setCompiledSources(compiledFromSources);
601
602}
603
604void BugzillaInformationPage::showTitleExamples()
605{
606 QString titleExamples = i18nc("@info:tooltip examples of good bug report titles",
607 "<strong>Examples of good titles:</strong><nl />\"Plasma crashed after adding the Notes "
608 "widget and writing on it\"<nl />\"Konqueror crashed when accessing the Facebook "
609 "application 'X'\"<nl />\"Kopete suddenly closed after resuming the computer and "
610 "talking to a MSN buddy\"<nl />\"Kate closed while editing a log file and pressing the "
611 "Delete key a couple of times\"");
612 QToolTip::showText(QCursor::pos(), titleExamples);
613}
614
615void BugzillaInformationPage::showDescriptionHelpExamples()
616{
617 QString descriptionHelp = i18nc("@info:tooltip help and examples of good bug descriptions",
618 "Describe in as much detail as possible the crash circumstances:");
619 if (reportInterface()->userCanProvideActionsAppDesktop()) {
620 descriptionHelp += "<br />" +
621 i18nc("@info:tooltip help and examples of good bug descriptions",
622 "- Detail which actions were you taking inside and outside the "
623 "application an instant before the crash.");
624 }
625 if (reportInterface()->userCanProvideUnusualBehavior()) {
626 descriptionHelp += "<br />" +
627 i18nc("@info:tooltip help and examples of good bug descriptions",
628 "- Note if you noticed any unusual behavior in the application "
629 "or in the whole environment.");
630 }
631 if (reportInterface()->userCanProvideApplicationConfigDetails()) {
632 descriptionHelp += "<br />" +
633 i18nc("@info:tooltip help and examples of good bug descriptions",
634 "- Note any non-default configuration in the application.");
635 if (reportInterface()->appDetailsExamples()->hasExamples()) {
636 descriptionHelp += ' ' +
637 i18nc("@info:tooltip examples of configuration details. "
638 "the examples are already translated",
639 "Examples: %1",
640 reportInterface()->appDetailsExamples()->examples());
641 }
642 }
643 QToolTip::showText(QCursor::pos(), descriptionHelp);
644}
645
646//END BugzillaInformationPage
647
648//BEGIN BugzillaPreviewPage
649
650BugzillaPreviewPage::BugzillaPreviewPage(ReportAssistantDialog * parent)
651 : ReportAssistantPage(parent)
652{
653 ui.setupUi(this);
654}
655
656void BugzillaPreviewPage::aboutToShow()
657{
658 ui.m_previewEdit->setText(reportInterface()->generateReportFullText(true));
659}
660
661//END BugzillaPreviewPage
662
663//BEGIN BugzillaSendPage
664
665BugzillaSendPage::BugzillaSendPage(ReportAssistantDialog * parent)
666 : ReportAssistantPage(parent),
667 m_contentsDialog(0)
668{
669 connect(reportInterface(), SIGNAL(reportSent(int)), this, SLOT(sent(int)));
670 connect(reportInterface(), SIGNAL(sendReportError(QString,QString)), this, SLOT(sendError(QString,QString)));
671
672 ui.setupUi(this);
673
674 ui.m_retryButton->setGuiItem(KGuiItem2(i18nc("@action:button", "Retry..."),
675 KIcon("view-refresh"),
676 i18nc("@info:tooltip", "Use this button to retry "
677 "sending the crash report if it failed before.")));
678
679 ui.m_showReportContentsButton->setGuiItem(
680 KGuiItem2(i18nc("@action:button", "Sho&w Contents of the Report"),
681 KIcon("document-preview"),
682 i18nc("@info:tooltip", "Use this button to show the generated "
683 "report information about this crash.")));
684 connect(ui.m_showReportContentsButton, SIGNAL(clicked()), this, SLOT(openReportContents()));
685
686 ui.m_retryButton->setVisible(false);
687 connect(ui.m_retryButton, SIGNAL(clicked()), this , SLOT(retryClicked()));
688
689 ui.m_launchPageOnFinish->setVisible(false);
690 ui.m_restartAppOnFinish->setVisible(false);
691
692 connect(assistant(), SIGNAL(user1Clicked()), this, SLOT(finishClicked()));
693}
694
695void BugzillaSendPage::retryClicked()
696{
697 ui.m_retryButton->setEnabled(false);
698 aboutToShow();
699}
700
701void BugzillaSendPage::aboutToShow()
702{
703 ui.m_statusWidget->setBusy(i18nc("@info:status","Sending crash report... (please wait)"));
704 reportInterface()->sendBugReport();
705}
706
707void BugzillaSendPage::sent(int bug_id)
708{
709 ui.m_statusWidget->setVisible(false);
710 ui.m_retryButton->setEnabled(false);
711 ui.m_retryButton->setVisible(false);
712
713 ui.m_showReportContentsButton->setVisible(false);
714
715 ui.m_launchPageOnFinish->setVisible(true);
716 ui.m_restartAppOnFinish->setVisible(!DrKonqi::crashedApplication()->hasBeenRestarted());
717 ui.m_restartAppOnFinish->setChecked(false);
718
719 reportUrl = bugzillaManager()->urlForBug(bug_id);
720 ui.m_finishedLabel->setText(i18nc("@info/rich","Crash report sent.<nl/>"
721 "URL: <link>%1</link><nl/>"
722 "Thank you for being part of KDE. "
723 "You can now close this window.", reportUrl));
724
725 emit finished(false);
726}
727
728void BugzillaSendPage::sendError(const QString & errorString, const QString & extendedMessage)
729{
730 ui.m_statusWidget->setIdle(i18nc("@info:status","Error sending the crash report: "
731 "<message>%1.</message>", errorString));
732
733 ui.m_retryButton->setEnabled(true);
734 ui.m_retryButton->setVisible(true);
735
736 if (!extendedMessage.isEmpty()) {
737 new UnhandledErrorDialog(this,errorString, extendedMessage);
738 }
739}
740
741void BugzillaSendPage::finishClicked()
742{
743 if (ui.m_launchPageOnFinish->isChecked() && !reportUrl.isEmpty()) {
744 KToolInvocation::invokeBrowser(reportUrl);
745 }
746 if (ui.m_restartAppOnFinish->isChecked()) {
747 DrKonqi::crashedApplication()->restart();
748 }
749}
750
751void BugzillaSendPage::openReportContents()
752{
753 if (!m_contentsDialog)
754 {
755 QString report = reportInterface()->generateReportFullText(false) + QLatin1Char('\n') +
756 i18nc("@info/plain report to KDE bugtracker address","Report to %1",
757 DrKonqi::crashedApplication()->bugReportAddress());
758 m_contentsDialog = new ReportInformationDialog(report);
759 }
760 m_contentsDialog->show();
761 m_contentsDialog->raise();
762 m_contentsDialog->activateWindow();
763}
764
765//END BugzillaSendPage
766
767/* Dialog for Unhandled Bugzilla Errors */
768/* The user can save the bugzilla html output to check the error and/or to report this as a DrKonqi bug */
769
770//BEGIN UnhandledErrorDialog
771
772UnhandledErrorDialog::UnhandledErrorDialog(QWidget * parent, const QString & error, const QString & extendedMessage)
773 : KDialog(parent)
774{
775 setWindowTitle(KDialog::makeStandardCaption(i18nc("@title:window", "Unhandled Bugzilla Error")));
776 setWindowModality(Qt::ApplicationModal);
777
778 setButtons(KDialog::Close | KDialog::User1);
779 setButtonText(KDialog::User1, i18nc("@action:button save html to a file","Save to a file"));
780 setButtonIcon(KDialog::User1, KIcon("document-save"));
781 connect(this, SIGNAL(user1Clicked()), this, SLOT(saveErrorMessage()));
782
783 setAttribute(Qt::WA_DeleteOnClose);
784
785 KWebView * htmlView = new KWebView(this);
786
787 QLabel * iconLabel = new QLabel(this);
788 iconLabel->setFixedSize(32, 32);
789 iconLabel->setPixmap(KIcon("dialog-warning").pixmap(32, 32));
790
791 QLabel * mainLabel = new QLabel(this);
792 mainLabel->setWordWrap(true);
793 mainLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
794
795 QHBoxLayout * titleLayout = new QHBoxLayout();
796 titleLayout->setContentsMargins(5,2,5,2);
797 titleLayout->setSpacing(5);
798 titleLayout->addWidget(iconLabel);
799 titleLayout->addWidget(mainLabel);
800
801 QVBoxLayout * layout = new QVBoxLayout();
802 layout->addLayout(titleLayout);
803 layout->addWidget(htmlView);
804
805 QWidget * mainWidget = new QWidget(this);
806 mainWidget->setLayout(layout);
807 setMainWidget(mainWidget);
808
809 m_extendedHTMLError = extendedMessage;
810 mainLabel->setText(i18nc("@label", "There was an unhandled Bugzilla error: %1.<br />"
811 "Below is the HTML that DrKonqi received. "
812 "Try to perform the action again or save this error page "
813 "to submit a bug against DrKonqi.").arg(error));
814 htmlView->setHtml(extendedMessage);
815
816 setMinimumSize(QSize(550, 350));
817 resize(minimumSize());
818
819 show();
820}
821
822void UnhandledErrorDialog::saveErrorMessage()
823{
824 QString defaultName = QLatin1String("drkonqi-unhandled-bugzilla-error.html");
825 QWeakPointer<KFileDialog> dlg = new KFileDialog(defaultName, QString(), this);
826 dlg.data()->setSelection(defaultName);
827 dlg.data()->setCaption(i18nc("@title:window","Select Filename"));
828 dlg.data()->setOperationMode(KFileDialog::Saving);
829 dlg.data()->setMode(KFile::File);
830 dlg.data()->setConfirmOverwrite(true);
831 if ( dlg.data()->exec() )
832 {
833 if (dlg.isNull()) {
834 //Dialog closed externally (ex. via DBus)
835 return;
836 }
837
838 KUrl fileUrl = dlg.data()->selectedUrl();
839 delete dlg.data();
840
841 if (fileUrl.isValid()) {
842 KTemporaryFile tf;
843 if (tf.open()) {
844 QTextStream ts(&tf);
845 ts << m_extendedHTMLError;
846 ts.flush();
847 } else {
848 KMessageBox::sorry(this, i18nc("@info","Cannot open file <filename>%1</filename> "
849 "for writing.", tf.fileName()));
850 return;
851 }
852
853 if (!KIO::NetAccess::upload(tf.fileName(), fileUrl, this)) {
854 KMessageBox::sorry(this, KIO::NetAccess::lastErrorString());
855 }
856 }
857 }
858 else
859 delete dlg.data();
860
861}
862
863//END UnhandledErrorDialog
864