diff options
Diffstat (limited to 'src/admin')
| -rw-r--r-- | src/admin/bar.cpp | 99 | ||||
| -rw-r--r-- | src/admin/bar.h | 75 | ||||
| -rw-r--r-- | src/admin/workerintegration.cpp | 120 | ||||
| -rw-r--r-- | src/admin/workerintegration.h | 75 |
4 files changed, 369 insertions, 0 deletions
diff --git a/src/admin/bar.cpp b/src/admin/bar.cpp new file mode 100644 index 000000000..2b65127f6 --- /dev/null +++ b/src/admin/bar.cpp @@ -0,0 +1,99 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2024 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "bar.h" + +#include "workerintegration.h" + +#include <KColorScheme> +#include <KContextualHelpButton> +#include <KLocalizedString> + +#include <QEvent> +#include <QGuiApplication> +#include <QHBoxLayout> +#include <QLabel> +#include <QPushButton> +#include <QStyle> +#include <QToolButton> +#include <QWidgetAction> + +using namespace Admin; + +Bar::Bar(QWidget *parent) + : AnimatedHeightWidget{parent} +{ + setAutoFillBackground(true); + updateColors(); + + QWidget *contenntsContainer = prepareContentsContainer(); + + m_fullLabelString = i18nc("@info label above the view explaining the state", "Acting as an Administrator – Be careful!"); + m_shortLabelString = i18nc("@info label above the view explaining the state, keep short", "Acting as Admin"); + m_label = new QLabel(contenntsContainer); + m_label->setMinimumWidth(0); + m_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard); // for keyboard accessibility + + m_warningButton = new KContextualHelpButton(warningMessage(), nullptr, contenntsContainer); + m_warningButton->setIcon(QIcon::fromTheme(QStringLiteral("emblem-warning"))); + + m_closeButton = new QPushButton(QIcon::fromTheme(QStringLiteral("window-close-symbolic")), "", contenntsContainer); + m_closeButton->setToolTip(i18nc("@action:button", "Stop Acting as an Administrator")); + m_closeButton->setFlat(true); + connect(m_closeButton, &QAbstractButton::clicked, this, &Bar::activated); // Make sure the view connected to this bar is active before exiting admin mode. + connect(m_closeButton, &QAbstractButton::clicked, this, &WorkerIntegration::exitAdminMode); + + QHBoxLayout *layout = new QHBoxLayout(contenntsContainer); + auto contentsMargins = layout->contentsMargins(); + m_preferredHeight = contentsMargins.top() + m_label->sizeHint().height() + contentsMargins.bottom(); + m_warningButton->setFixedHeight(m_preferredHeight); + m_closeButton->setFixedHeight(m_preferredHeight); + layout->setContentsMargins(0, 0, 0, 0); + + layout->addStretch(); + layout->addWidget(m_label); + layout->addWidget(m_warningButton); + layout->addStretch(); + layout->addWidget(m_closeButton); +} + +bool Bar::event(QEvent *event) +{ + if (event->type() == QEvent::PaletteChange) { + updateColors(); + } + return AnimatedHeightWidget::event(event); +} + +void Bar::resizeEvent(QResizeEvent *resizeEvent) +{ + updateLabelString(); + return QWidget::resizeEvent(resizeEvent); +} + +void Bar::updateColors() +{ + QPalette palette = parentWidget()->palette(); + KColorScheme colorScheme{QPalette::Normal, KColorScheme::ColorSet::Window}; + colorScheme.adjustBackground(palette, KColorScheme::NegativeBackground, QPalette::Window, KColorScheme::ColorSet::Window); + colorScheme.adjustForeground(palette, KColorScheme::NegativeText, QPalette::WindowText, KColorScheme::ColorSet::Window); + setPalette(palette); +} + +void Bar::updateLabelString() +{ + QFontMetrics fontMetrics = m_label->fontMetrics(); + if (fontMetrics.horizontalAdvance(m_fullLabelString) + m_warningButton->sizeHint().width() + m_closeButton->sizeHint().width() + + style()->pixelMetric(QStyle::PM_LayoutLeftMargin) * 2 + style()->pixelMetric(QStyle::PM_LayoutRightMargin) * 2 + < width()) { + m_label->setText(m_fullLabelString); + } else { + m_label->setText(m_shortLabelString); + } +} + +#include "moc_bar.cpp" diff --git a/src/admin/bar.h b/src/admin/bar.h new file mode 100644 index 000000000..5399fa669 --- /dev/null +++ b/src/admin/bar.h @@ -0,0 +1,75 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2024 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef ADMINBAR_H +#define ADMINBAR_H + +#include "animatedheightwidget.h" + +class QLabel; +class QPushButton; +class QResizeEvent; +class QToolButton; + +namespace Admin +{ + +/** + * @brief A bar appearing above the view while the user is acting with administrative privileges. + * + * It contains a warning and allows revoking this administrative mode by closing this bar. + */ +class Bar : public AnimatedHeightWidget +{ + Q_OBJECT + +public: + explicit Bar(QWidget *parent); + + /** Used to recolor this bar when this application's color scheme changes. */ + bool event(QEvent *event) override; + +Q_SIGNALS: + void activated(); + +protected: + /** Calls updateLabelString() */ + void resizeEvent(QResizeEvent *resizeEvent) override; + +private: + /** Recolors this bar based on the current color scheme. */ + void updateColors(); + + /** Decides whether the m_fullLabelString or m_shortLabelString should be used based on available width. */ + void updateLabelString(); + + /** @see AnimatedHeightWidget::preferredHeight() */ + inline int preferredHeight() const override + { + return m_preferredHeight; + }; + +private: + /** The text on this bar */ + QLabel *m_label = nullptr; + /** Shows a warning about the dangers of acting with administrative privileges. */ + QToolButton *m_warningButton = nullptr; + /** Closes this bar and exits the administrative mode. */ + QPushButton *m_closeButton = nullptr; + + /** @see updateLabelString() */ + QString m_fullLabelString; + /** @see updateLabelString() */ + QString m_shortLabelString; + + /** @see preferredHeight() */ + int m_preferredHeight; +}; + +} + +#endif // ADMINBAR_H diff --git a/src/admin/workerintegration.cpp b/src/admin/workerintegration.cpp new file mode 100644 index 000000000..f9b587391 --- /dev/null +++ b/src/admin/workerintegration.cpp @@ -0,0 +1,120 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "workerintegration.h" + +#include "dolphinmainwindow.h" +#include "dolphinviewcontainer.h" + +#include <KActionCollection> +#include <KLocalizedString> +#include <KMessageBox> +#include <KMessageDialog> +#include <KProtocolInfo> + +#include <QAction> + +using namespace Admin; + +QString Admin::warningMessage() +{ + return xi18nc( + "@info", + "<para>You are about to use administrator privileges. While acting as an administrator you can change or replace any file or folder on this system. " + "This includes items which are critical for this system to function.</para><para>You are able to <emphasis>delete every users' data</emphasis> on this " + "computer and to <emphasis>break this installation beyond repair.</emphasis> Adding just one letter in a folder or file name or its contents can " + "render a system <emphasis>unbootable.</emphasis></para><para>There is probably not going to be another warning even if you are about to break this " + "system.</para><para>You might want to <emphasis>backup files and folders</emphasis> before proceeding.</para>"); +} + +namespace +{ +/** The only WorkerIntegration object that is ever constructed. It is only ever accessed directly from within this file. */ +WorkerIntegration *instance = nullptr; +} + +WorkerIntegration::WorkerIntegration(DolphinMainWindow *parent, QAction *actAsAdminAction) + : QObject{parent} + , m_actAsAdminAction{actAsAdminAction} +{ + Q_CHECK_PTR(parent); + Q_CHECK_PTR(actAsAdminAction); + + connect(parent, &DolphinMainWindow::urlChanged, this, &WorkerIntegration::updateActAsAdminAction); + + connect(actAsAdminAction, &QAction::triggered, this, &WorkerIntegration::toggleActAsAdmin); +} + +void WorkerIntegration::createActAsAdminAction(KActionCollection *actionCollection, DolphinMainWindow *dolphinMainWindow) +{ + Q_ASSERT(!instance); + if (KProtocolInfo::isKnownProtocol(QStringLiteral("admin"))) { + QAction *actAsAdminAction = actionCollection->addAction(QStringLiteral("act_as_admin")); + actAsAdminAction->setText(i18nc("@action:inmenu", "Act as Administrator")); + actAsAdminAction->setIcon(QIcon::fromTheme(QStringLiteral("system-switch-user"))); + actAsAdminAction->setCheckable(true); + actionCollection->setDefaultShortcut(actAsAdminAction, Qt::CTRL | Qt::SHIFT | Qt::ALT | Qt::Key_A); + + instance = new WorkerIntegration(dolphinMainWindow, actAsAdminAction); + } +} + +void WorkerIntegration::exitAdminMode() +{ + if (instance->m_actAsAdminAction->isChecked()) { + instance->m_actAsAdminAction->trigger(); + } +} + +void WorkerIntegration::toggleActAsAdmin() +{ + auto dolphinMainWindow = static_cast<DolphinMainWindow *>(parent()); + QUrl url = dolphinMainWindow->activeViewContainer()->urlNavigator()->locationUrl(); + if (url.scheme() == QStringLiteral("file")) { + bool risksAccepted = !KMessageBox::shouldBeShownContinue(warningDontShowAgainName); + + if (!risksAccepted) { + KMessageDialog warningDialog{KMessageDialog::QuestionTwoActions, warningMessage(), dolphinMainWindow}; + warningDialog.setCaption(i18nc("@title:window", "Risks of Acting as an Administrator")); + warningDialog.setIcon(QIcon::fromTheme(QStringLiteral("security-low"))); + warningDialog.setButtons(KGuiItem{i18nc("@action:button", "I Understand and Accept These Risks"), QStringLiteral("data-warning")}, + KStandardGuiItem::cancel()); + warningDialog.setDontAskAgainText(i18nc("@option:check", "Do not warn me about these risks again")); + + risksAccepted = warningDialog.exec() != 4 /* Cancel */; + if (warningDialog.isDontAskAgainChecked()) { + KMessageBox::saveDontShowAgainContinue(warningDontShowAgainName); + } + + if (!risksAccepted) { + updateActAsAdminAction(); // Uncheck the action + return; + } + } + + url.setScheme(QStringLiteral("admin")); + } else if (url.scheme() == QStringLiteral("admin")) { + url.setScheme(QStringLiteral("file")); + } + dolphinMainWindow->changeUrl(url); +} + +void WorkerIntegration::updateActAsAdminAction() +{ + if (instance) { + const QString currentUrlScheme = static_cast<DolphinMainWindow *>(instance->parent())->activeViewContainer()->url().scheme(); + if (currentUrlScheme == QStringLiteral("file")) { + instance->m_actAsAdminAction->setEnabled(true); + instance->m_actAsAdminAction->setChecked(false); + } else if (currentUrlScheme == QStringLiteral("admin")) { + instance->m_actAsAdminAction->setEnabled(true); + instance->m_actAsAdminAction->setChecked(true); + } else { + instance->m_actAsAdminAction->setEnabled(false); + } + } +} diff --git a/src/admin/workerintegration.h b/src/admin/workerintegration.h new file mode 100644 index 000000000..512303744 --- /dev/null +++ b/src/admin/workerintegration.h @@ -0,0 +1,75 @@ +/* + This file is part of the KDE project + SPDX-FileCopyrightText: 2022 Felix Ernst <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef ADMINWORKERINTEGRATION_H +#define ADMINWORKERINTEGRATION_H + +#include <QObject> + +class DolphinMainWindow; +class KActionCollection; +class QAction; +class QUrl; + +/** + * @brief This namespace contains everything that is necessary to nicely integrate "KIO Admin Worker" into Dolphin. + * + * @see https://commits.kde.org/kio-admin + */ +namespace Admin +{ +/** + * Used with the KMessageBox API so users can disable the warning. + * @see KMessageBox::saveDontShowAgainContinue() + * @see KMessageBox::enableMessage() + */ +constexpr QLatin1String warningDontShowAgainName{"warnAboutRisksBeforeActingAsAdmin"}; + +/** @returns an elaborate warning about the dangers of acting with administrative privileges. */ +QString warningMessage(); + +/** + * @brief A class encapsulating the "Act as Admin"-toggle action. + * + * @see https://commits.kde.org/kio-admin + */ +class WorkerIntegration : public QObject +{ + Q_OBJECT + +public: + /** + * Adds a toggle action to the \a actionCollection. + * The action switches between acting as a normal user or acting as an administrator. + */ + static void createActAsAdminAction(KActionCollection *actionCollection, DolphinMainWindow *dolphinMainWindow); + + /** + * Triggers the m_actAsAdminAction only if it is currently checked. + */ + static void exitAdminMode(); + +private: + WorkerIntegration(DolphinMainWindow *parent, QAction *actAsAdminAction); + + /** + * Toggles between acting with admin rights or not. + * This enables editing more files than the normal user account would be allowed to but requires re-authorization. + */ + void toggleActAsAdmin(); + + /** Updates the toggled/checked state of the action depending on the state of the currently active view. */ + static void updateActAsAdminAction(); + +private: + /** @see createActAsAdminAction() */ + QAction *const m_actAsAdminAction = nullptr; +}; + +} + +#endif // ADMINWORKERINTEGRATION_H |
