File indexing completed on 2024-04-28 17:02:41

0001 /***************************************************************************
0002  *   Copyright (C) 2009-2018 by Daniel Nicoletti                           *
0003  *   dantti12@gmail.com                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; see the file COPYING. If not, write to       *
0017  *   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,  *
0018  *   Boston, MA 02110-1301, USA.                                           *
0019  ***************************************************************************/
0020 
0021 #include "UpdateDetails.h"
0022 
0023 #include <Daemon>
0024 #include <PkStrings.h>
0025 
0026 #include <KMessageBox>
0027 #include <KPixmapSequence>
0028 #include <KLocalizedString>
0029 #include <KFormat>
0030 
0031 #include <QAbstractAnimation>
0032 #include <QGraphicsOpacityEffect>
0033 #include <QStringBuilder>
0034 #include <KIconLoader>
0035 
0036 #include <Transaction>
0037 
0038 #include <QLoggingCategory>
0039 
0040 #define FINAL_HEIGHT 160
0041 
0042 Q_DECLARE_LOGGING_CATEGORY(APPER)
0043 
0044 UpdateDetails::UpdateDetails(QWidget *parent)
0045     : QWidget(parent)
0046 {
0047     setupUi(this);
0048     hideTB->setIcon(QIcon::fromTheme(QLatin1String("window-close")));
0049     connect(hideTB, &QToolButton::clicked, this, &UpdateDetails::hide);
0050 
0051     m_busySeq = new KPixmapSequenceOverlayPainter(this);
0052     m_busySeq->setSequence(KIconLoader::global()->loadPixmapSequence(QLatin1String("process-working"), KIconLoader::SizeSmallMedium));
0053     m_busySeq->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
0054     m_busySeq->setWidget(this);
0055 
0056     QWidget *actionsViewport = descriptionKTB->viewport();
0057     QPalette palette = actionsViewport->palette();
0058     palette.setColor(actionsViewport->backgroundRole(), Qt::transparent);
0059     palette.setColor(actionsViewport->foregroundRole(), palette.color(QPalette::WindowText));
0060     actionsViewport->setPalette(palette);
0061 
0062     auto effect = new QGraphicsOpacityEffect(descriptionKTB);
0063     effect->setOpacity(0);
0064     descriptionKTB->setGraphicsEffect(effect);
0065     m_fadeDetails = new QPropertyAnimation(effect, "opacity", this);
0066     m_fadeDetails->setDuration(500);
0067     m_fadeDetails->setStartValue(qreal(0));
0068     m_fadeDetails->setEndValue(qreal(1));
0069     connect(m_fadeDetails, &QPropertyAnimation::finished, this, &UpdateDetails::display);
0070 
0071 
0072     auto anim1 = new QPropertyAnimation(this, "maximumSize", this);
0073     anim1->setDuration(500);
0074     anim1->setEasingCurve(QEasingCurve::OutQuart);
0075     anim1->setStartValue(QSize(QWIDGETSIZE_MAX, 0));
0076     anim1->setEndValue(QSize(QWIDGETSIZE_MAX, FINAL_HEIGHT));
0077     auto anim2 = new QPropertyAnimation(this, "minimumSize", this);
0078     anim2->setDuration(500);
0079     anim2->setEasingCurve(QEasingCurve::OutQuart);
0080     anim2->setStartValue(QSize(QWIDGETSIZE_MAX, 0));
0081     anim2->setEndValue(QSize(QWIDGETSIZE_MAX, FINAL_HEIGHT));
0082 
0083     m_expandPanel = new QParallelAnimationGroup(this);
0084     m_expandPanel->addAnimation(anim1);
0085     m_expandPanel->addAnimation(anim2);
0086     connect(m_expandPanel, &QParallelAnimationGroup::finished, this, &UpdateDetails::display);
0087 
0088 }
0089 
0090 UpdateDetails::~UpdateDetails()
0091 {
0092 }
0093 
0094 void UpdateDetails::setPackage(const QString &packageId, Transaction::Info updateInfo)
0095 {
0096     if (m_packageId == packageId) {
0097         return;
0098     }
0099     m_show       = true;
0100     m_packageId  = packageId;
0101     m_updateInfo = updateInfo;
0102     m_currentDescription.clear();
0103     if (m_transaction) {
0104         disconnect(m_transaction, &Transaction::updateDetail, this, &UpdateDetails::updateDetail);
0105         disconnect(m_transaction, &Transaction::finished, this, &UpdateDetails::display);
0106     }
0107 
0108     m_transaction = Daemon::getUpdateDetail(m_packageId);
0109     connect(m_transaction, &Transaction::updateDetail, this, &UpdateDetails::updateDetail);
0110     connect(m_transaction, &Transaction::finished, this, &UpdateDetails::display);
0111 
0112     if (maximumSize().height() == 0) {
0113         // Expand the panel
0114         m_expandPanel->setDirection(QAbstractAnimation::Forward);
0115         m_expandPanel->start();
0116     } else if (m_fadeDetails->currentValue().toReal() != 0) {
0117         // Hide the old description
0118         m_fadeDetails->setDirection(QAbstractAnimation::Backward);
0119         m_fadeDetails->start();
0120     }
0121     m_busySeq->start();
0122 }
0123 
0124 void UpdateDetails::hide()
0125 {
0126     m_show = false;
0127     m_packageId.clear();
0128     if (maximumSize().height() == FINAL_HEIGHT &&
0129         m_fadeDetails->currentValue().toReal() == 1) {
0130         m_fadeDetails->setDirection(QAbstractAnimation::Backward);
0131         m_fadeDetails->start();
0132     } else if (maximumSize().height() == FINAL_HEIGHT &&
0133                m_fadeDetails->currentValue().toReal() == 0) {
0134         m_expandPanel->setDirection(QAbstractAnimation::Backward);
0135         m_expandPanel->start();
0136     }
0137 }
0138 
0139 void UpdateDetails::display()
0140 {
0141     qCDebug(APPER) << sender();
0142 
0143     // set transaction to 0 as if PK crashes
0144     // UpdateDetails won't be emmited
0145     m_transaction = nullptr;
0146 
0147     if (!m_show) {
0148         hide();
0149         return;
0150     }
0151 
0152     if (maximumSize().height() == FINAL_HEIGHT &&
0153         !m_currentDescription.isEmpty() &&
0154         m_fadeDetails->currentValue().toReal() == 0) {
0155         descriptionKTB->setHtml(m_currentDescription);
0156 
0157         m_fadeDetails->setDirection(QAbstractAnimation::Forward);
0158         m_fadeDetails->start();
0159     } else if (m_currentDescription.isEmpty()) {
0160         updateDetailFinished();
0161     }
0162 }
0163 
0164 void UpdateDetails::updateDetail(const QString &packageID,
0165                                  const QStringList &updates,
0166                                  const QStringList &obsoletes,
0167                                  const QStringList &vendorUrls,
0168                                  const QStringList &bugzillaUrls,
0169                                  const QStringList &cveUrls,
0170                                  PackageKit::Transaction::Restart restart,
0171                                  const QString &updateText,
0172                                  const QString &changelog,
0173                                  PackageKit::Transaction::UpdateState state,
0174                                  const QDateTime &issued,
0175                                  const QDateTime &updated)
0176 {
0177     //format and show description
0178     QString description;
0179 
0180     // update type (ie Security Update)
0181     if (m_updateInfo == Transaction::InfoEnhancement) {
0182         description += QLatin1String("<p>") +
0183                        i18n("This update will add new features and expand functionality.") +
0184                        QLatin1String("</p>");
0185     } else if (m_updateInfo == Transaction::InfoBugfix) {
0186         description += QLatin1String("<p>") +
0187                        i18n("This update will fix bugs and other non-critical problems.") +
0188                        QLatin1String("</p>");
0189     } else if (m_updateInfo == Transaction::InfoImportant) {
0190         description += QLatin1String("<p>") +
0191                        i18n("This update is important as it may solve critical problems.") +
0192                        QLatin1String("</p>");
0193     } else if (m_updateInfo == Transaction::InfoSecurity) {
0194         description += QLatin1String("<p>") +
0195                        i18n("This update is needed to fix a security vulnerability with this package.") +
0196                        QLatin1String("</p>");
0197     } else if (m_updateInfo == Transaction::InfoBlocked) {
0198         description += QLatin1String("<p>") +
0199                        i18n("This update is blocked.") +
0200                        QLatin1String("</p>");
0201     }
0202 
0203     // Issued and Updated
0204     if (!issued.isNull() && !updated.isNull()) {
0205         description += QLatin1String("<p>") +
0206                        i18n("This notification was issued on %1 and last updated on %2.",
0207                             QLocale::system().toString(issued, QLocale::ShortFormat),
0208                             QLocale::system().toString(updated, QLocale::ShortFormat)) +
0209                        QLatin1String("</p>");
0210     } else if (!issued.isNull()) {
0211         description += QLatin1String("<p>") +
0212                        i18n("This notification was issued on %1.",
0213                             QLocale::system().toString(issued, QLocale::ShortFormat)) +
0214                        QLatin1String("</p>");
0215     }
0216 
0217     // Description
0218     if (!updateText.isEmpty()) {
0219         QString _updateText = updateText;
0220         _updateText.replace(QLatin1Char('\n'), QLatin1String("<br/>"));
0221         _updateText.replace(QLatin1Char(' '), QLatin1String("&nbsp;"));
0222         description += QLatin1String("<p>") + _updateText + QLatin1String("</p>");
0223     }
0224 
0225     // links
0226     //  Vendor
0227     if (!vendorUrls.isEmpty()) {
0228         description += QLatin1String("<p>") +
0229                        i18np("For more information about this update please visit this website:",
0230                              "For more information about this update please visit these websites:",
0231                              vendorUrls.size()) + QLatin1String("<br/>") +
0232                        getLinkList(vendorUrls) +
0233                        QLatin1String("</p>");
0234     }
0235 
0236     //  Bugzilla
0237     if (!bugzillaUrls.isEmpty()) {
0238         description += QLatin1String("<p>") +
0239                        i18np("For more information about bugs fixed by this update please visit this website:",
0240                              "For more information about bugs fixed by this update please visit these websites:",
0241                              bugzillaUrls.size()) + QLatin1String("<br>") +
0242                        getLinkList(bugzillaUrls) +
0243                        QLatin1String("</p>");
0244     }
0245 
0246     //  CVE
0247     if (!cveUrls.isEmpty()) {
0248         description += QLatin1String("<p>") +
0249                        i18np("For more information about this security update please visit this website:",
0250                              "For more information about this security update please visit these websites:",
0251                              cveUrls.size()) + QLatin1String("<br>") +
0252                        getLinkList(cveUrls) +
0253                        QLatin1String("</p>");
0254     }
0255 
0256     // Notice (about the need for a reboot)
0257     if (restart == Transaction::RestartSystem) {
0258         description += QLatin1String("<p>") +
0259                        i18n("The computer will have to be restarted after the update for the changes to take effect.") +
0260                        QLatin1String("</p>");
0261     } else if (restart == Transaction::RestartSession) {
0262         description += QLatin1String("<p>") +
0263                        i18n("You will need to log out and back in after the update for the changes to take effect.") +
0264                        QLatin1String("</p>");
0265     }
0266 
0267     // State
0268     if (state == Transaction::UpdateStateUnstable) {
0269         description += QLatin1String("<p>") +
0270                        i18n("The classification of this update is unstable which means it is not designed for production use.") +
0271                        QLatin1String("</p>");
0272     } else if (state == Transaction::UpdateStateTesting) {
0273         description += QLatin1String("<p>") +
0274                        i18n("This is a test update, and is not designed for normal use. Please report any problems or regressions you encounter.") +
0275                        QLatin1String("</p>");
0276     }
0277 
0278     // only show changelog if we didn't have any update text
0279     if (updateText.isEmpty() && !changelog.isEmpty()) {
0280         QString _changelog = changelog;
0281         _changelog.replace(QLatin1Char('\n'), QLatin1String("<br/>"));
0282         _changelog.replace(QLatin1Char(' '), QLatin1String("&nbsp;"));
0283         description += QLatin1String("<p>") +
0284                        i18n("The developer logs will be shown as no description is available for this update:") +
0285                        QLatin1String("<br>") +
0286                        _changelog +
0287                        QLatin1String("</p>");
0288     }
0289 
0290     // Updates (lists of packages that are updated)
0291     if (!updates.isEmpty()) {
0292         description += QLatin1String("<p>") + i18n("Updates:") + QLatin1String("<br>");
0293         QStringList _updates;
0294         for (const QString &pid : updates) {
0295              _updates += QString::fromUtf8("\xE2\x80\xA2 ") + Transaction::packageName(pid) + QLatin1String(" - ") + Transaction::packageVersion(pid);
0296         }
0297         description += _updates.join(QLatin1String("<br>")) + QLatin1String("</p>");
0298     }
0299 
0300     // Obsoletes (lists of packages that are obsoleted)
0301     if (obsoletes.size()) {
0302         description += QLatin1String("<p></b>") + i18n("Obsoletes:") + QLatin1String("</b><br/>");
0303         QStringList _obsoletes;
0304         for (const QString &pid : obsoletes) {
0305              _obsoletes += QString::fromUtf8("\xE2\x80\xA2 ") + Transaction::packageName(pid) + QLatin1String(" - ") + Transaction::packageVersion(pid);
0306         }
0307         description += _obsoletes.join(QLatin1String("<br>/")) + QLatin1String("</p>");
0308     }
0309 
0310     // Repository (this is the repository the package comes from)
0311     if (!Transaction::packageData(packageID).isEmpty()) {
0312          description += QLatin1String("<p>") + i18n("Repository: %1", Transaction::packageData(packageID)) + QLatin1String("</p>");
0313     }
0314 
0315     m_currentDescription = description;
0316     m_busySeq->stop();
0317 }
0318 
0319 QString UpdateDetails::getLinkList(const QStringList &urls) const
0320 {
0321     QString ret;
0322     for (const QString &url : urls) {
0323         if (!ret.isEmpty()) {
0324             ret += QLatin1String("<br>");
0325         }
0326         ret += QString::fromUtf8(" \xE2\x80\xA2 <a href=\"") % url % QLatin1String("\">") % url % QLatin1String("</a>");
0327     }
0328     return ret;
0329 }
0330 
0331 void UpdateDetails::updateDetailFinished()
0332 {
0333     if (descriptionKTB->document()->toPlainText().isEmpty()) {
0334         descriptionKTB->setPlainText(i18n("No update description was found."));
0335     }
0336 }
0337 
0338 #include "moc_UpdateDetails.cpp"