1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "kbugreport.h" |
9 | |
10 | #include <QDesktopServices> |
11 | #include <QDialogButtonBox> |
12 | #include <QHBoxLayout> |
13 | #include <QLabel> |
14 | #include <QPushButton> |
15 | #include <QTimer> |
16 | #include <QUrl> |
17 | #include <QUrlQuery> |
18 | |
19 | #include <KAboutData> |
20 | #include <KConfig> |
21 | #include <KLocalizedString> |
22 | #include <KMessageBox> |
23 | #include <KMessageDialog> |
24 | #include <KTitleWidget> |
25 | |
26 | #include "config-xmlgui.h" |
27 | #include "systeminformation_p.h" |
28 | #include <kxmlgui_version.h> |
29 | |
30 | #include <chrono> |
31 | |
32 | using namespace std::chrono_literals; |
33 | |
34 | class KBugReportPrivate |
35 | { |
36 | public: |
37 | KBugReportPrivate(KBugReport *qq) |
38 | : q(qq) |
39 | { |
40 | } |
41 | |
42 | enum BugDestination { |
43 | BugsKdeOrg, |
44 | CustomUrl, |
45 | }; |
46 | |
47 | // Update the url to match the current OS, selected app, etc |
48 | void updateUrl(); |
49 | |
50 | KBugReport *const q; |
51 | |
52 | QLabel *m_version = nullptr; |
53 | QString m_strVersion; |
54 | |
55 | QString lastError; |
56 | QString kde_version; |
57 | QString appname; |
58 | QString os; |
59 | QUrl url; |
60 | BugDestination bugDestination = KBugReportPrivate::CustomUrl; |
61 | }; |
62 | |
63 | KBugReport::KBugReport(const KAboutData &aboutData, QWidget *_parent) |
64 | : QDialog(_parent) |
65 | , d(new KBugReportPrivate(this)) |
66 | { |
67 | setWindowTitle(i18nc("@title:window" , "Submit Bug Report" )); |
68 | |
69 | QDialogButtonBox *buttonBox = new QDialogButtonBox(this); |
70 | buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); |
71 | connect(sender: buttonBox, signal: &QDialogButtonBox::accepted, context: this, slot: &QDialog::accept); |
72 | connect(sender: buttonBox, signal: &QDialogButtonBox::rejected, context: this, slot: &QDialog::reject); |
73 | |
74 | const QString bugAddress = aboutData.bugAddress(); |
75 | if (bugAddress == QLatin1String("submit@bugs.kde.org" )) { |
76 | // This is a core KDE application -> redirect to the web form |
77 | d->bugDestination = KBugReportPrivate::BugsKdeOrg; |
78 | } else if (!QUrl(bugAddress).scheme().isEmpty()) { |
79 | // The bug reporting address is a URL -> redirect to that |
80 | d->bugDestination = KBugReportPrivate::CustomUrl; |
81 | } |
82 | |
83 | KGuiItem::assign(button: buttonBox->button(which: QDialogButtonBox::Cancel), item: KStandardGuiItem::close()); |
84 | |
85 | QLabel *tmpLabel; |
86 | QVBoxLayout *lay = new QVBoxLayout(this); |
87 | |
88 | KTitleWidget *title = new KTitleWidget(this); |
89 | title->setText(i18n("Submit Bug Report" )); |
90 | title->setIconSize(QSize(32, 32)); |
91 | title->setIcon(icon: QIcon::fromTheme(QStringLiteral("tools-report-bug" ))); |
92 | lay->addWidget(title); |
93 | |
94 | QGridLayout *glay = new QGridLayout(); |
95 | lay->addLayout(layout: glay); |
96 | |
97 | int row = 0; |
98 | |
99 | // Program name |
100 | QString qwtstr = |
101 | i18n("The application for which you wish to submit a bug report - if incorrect, please use the Report Bug menu item of the correct application" ); |
102 | tmpLabel = new QLabel(i18n("Application: " ), this); |
103 | glay->addWidget(tmpLabel, row, column: 0); |
104 | tmpLabel->setWhatsThis(qwtstr); |
105 | QLabel *appLabel = new QLabel(this); |
106 | d->appname = aboutData.productName(); |
107 | appLabel->setText(d->appname); |
108 | glay->addWidget(appLabel, row, column: 1); |
109 | tmpLabel->setWhatsThis(qwtstr); |
110 | |
111 | // Version |
112 | qwtstr = i18n("The version of this application - please make sure that no newer version is available before sending a bug report" ); |
113 | tmpLabel = new QLabel(i18n("Version:" ), this); |
114 | glay->addWidget(tmpLabel, row: ++row, column: 0); |
115 | tmpLabel->setWhatsThis(qwtstr); |
116 | d->m_strVersion = aboutData.version(); |
117 | if (d->m_strVersion.isEmpty()) { |
118 | d->m_strVersion = i18n("no version set (programmer error)" ); |
119 | } |
120 | d->kde_version = QStringLiteral(KXMLGUI_VERSION_STRING); |
121 | if (d->bugDestination != KBugReportPrivate::BugsKdeOrg) { |
122 | d->m_strVersion = i18nc("%1 is the program version, %2 is the kde framework version" , "%1 with KDE Frameworks %2" , d->m_strVersion, d->kde_version); |
123 | } |
124 | d->m_version = new QLabel(d->m_strVersion, this); |
125 | d->m_version->setTextInteractionFlags(Qt::TextBrowserInteraction); |
126 | // glay->addWidget( d->m_version, row, 1 ); |
127 | glay->addWidget(d->m_version, row, column: 1, rowSpan: 1, columnSpan: 2); |
128 | d->m_version->setWhatsThis(qwtstr); |
129 | |
130 | tmpLabel = new QLabel(i18n("OS:" ), this); |
131 | glay->addWidget(tmpLabel, row: ++row, column: 0); |
132 | |
133 | #ifdef Q_OS_WINDOWS |
134 | d->os = i18nc("%1 is the operating system name, e.g. 'Windows 10', %2 is the CPU architecture, e.g. 'x86_64'" , |
135 | "%1 (%2)" , |
136 | QSysInfo::prettyProductName(), |
137 | QSysInfo::currentCpuArchitecture()); |
138 | #else |
139 | if (QSysInfo::productVersion() != QLatin1String("unknown" )) { |
140 | d->os = i18nc( |
141 | "%1 is the operating system name, e.g. 'Fedora Linux', %2 is the operating system version, e.g. '35', %3 is the CPU architecture, e.g. 'x86_64'" , |
142 | "%1 %2 (%3)" , |
143 | QSysInfo::prettyProductName(), |
144 | QSysInfo::productVersion(), |
145 | QSysInfo::currentCpuArchitecture()); |
146 | } else { |
147 | d->os = i18nc("%1 is the operating system name, e.g. 'Fedora Linux', %2 is the CPU architecture, e.g. 'x86_64'" , |
148 | "%1 (%2)" , |
149 | QSysInfo::prettyProductName(), |
150 | QSysInfo::currentCpuArchitecture()); |
151 | } |
152 | #endif |
153 | |
154 | tmpLabel = new QLabel(d->os, this); |
155 | tmpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); |
156 | glay->addWidget(tmpLabel, row, column: 1, rowSpan: 1, columnSpan: 2); |
157 | // Point to the web form |
158 | |
159 | QString text; |
160 | if (d->bugDestination == KBugReportPrivate::BugsKdeOrg) { |
161 | text = i18n( |
162 | "<qt>To submit a bug report, click on the button below. This will open a web browser " |
163 | "window on <a href=\"https://bugs.kde.org\">https://bugs.kde.org</a> where you will find " |
164 | "a form to fill in. The information displayed above will be transferred to that server.</qt>" ); |
165 | d->updateUrl(); |
166 | } else { |
167 | text = i18n( |
168 | "<qt>To submit a bug report, click on the button below. This will open a web browser " |
169 | "window on <a href=\"%1\">%2</a>.</qt>" , |
170 | bugAddress, |
171 | bugAddress); |
172 | d->url = QUrl(bugAddress); |
173 | } |
174 | |
175 | lay->addSpacing(size: 10); |
176 | QLabel *label = new QLabel(text, this); |
177 | label->setOpenExternalLinks(true); |
178 | label->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); |
179 | label->setWordWrap(true); |
180 | lay->addWidget(label); |
181 | lay->addSpacing(size: 10); |
182 | |
183 | QPushButton *okButton = buttonBox->button(which: QDialogButtonBox::Ok); |
184 | if (d->bugDestination == KBugReportPrivate::BugsKdeOrg) { |
185 | okButton->setText(i18nc("@action:button" , "&Launch Bug Report Wizard" )); |
186 | } else { |
187 | okButton->setText(i18nc("@action:button" , "&Submit Bug Report" )); |
188 | } |
189 | okButton->setIcon(QIcon::fromTheme(QStringLiteral("tools-report-bug" ))); |
190 | |
191 | lay->addWidget(buttonBox); |
192 | setMinimumHeight(sizeHint().height() + 20); // WORKAROUND: prevent "cropped" qcombobox |
193 | } |
194 | |
195 | KBugReport::~KBugReport() = default; |
196 | |
197 | void KBugReportPrivate::updateUrl() |
198 | { |
199 | url = QUrl(QStringLiteral("https://bugs.kde.org/enter_bug.cgi" )); |
200 | QUrlQuery query; |
201 | query.addQueryItem(QStringLiteral("format" ), QStringLiteral("guided" )); // use the guided form |
202 | |
203 | // the string format is product/component, where component is optional |
204 | QStringList list = appname.split(sep: QLatin1Char('/')); |
205 | query.addQueryItem(QStringLiteral("product" ), value: list[0]); |
206 | if (list.size() == 2) { |
207 | query.addQueryItem(QStringLiteral("component" ), value: list[1]); |
208 | } |
209 | |
210 | query.addQueryItem(QStringLiteral("version" ), value: m_strVersion); |
211 | url.setQuery(query); |
212 | |
213 | // TODO: guess and fill OS(sys_os) and Platform(rep_platform) fields |
214 | } |
215 | |
216 | void KBugReport::accept() |
217 | { |
218 | QDesktopServices::openUrl(url: d->url); |
219 | |
220 | // HACK: accept will close the window, which breaks the xdg-activation handling in QDesktopServices::openUrl() |
221 | // Slightly delay the closing to give openUrl time to finish |
222 | QTimer::singleShot(interval: 500ms, slot: [this] { |
223 | QDialog::accept(); |
224 | }); |
225 | } |
226 | |
227 | #include "moc_kbugreport.cpp" |
228 | |