diff options
| -rw-r--r-- | src/admin/bar.cpp | 84 | ||||
| -rw-r--r-- | src/admin/bar.h | 27 | ||||
| -rw-r--r-- | src/admin/workerintegration.cpp | 57 | ||||
| -rw-r--r-- | src/admin/workerintegration.h | 10 | ||||
| -rw-r--r-- | src/dolphinviewcontainer.cpp | 41 | ||||
| -rw-r--r-- | src/dolphinviewcontainer.h | 10 |
6 files changed, 164 insertions, 65 deletions
diff --git a/src/admin/bar.cpp b/src/admin/bar.cpp index f2e7250ad..52bbc3772 100644 --- a/src/admin/bar.cpp +++ b/src/admin/bar.cpp @@ -7,16 +7,20 @@ #include "bar.h" +#include "dolphinviewcontainer.h" #include "workerintegration.h" #include <KColorScheme> #include <KContextualHelpButton> +#include <KIO/JobUiDelegateFactory> +#include <KIO/SimpleJob> #include <KLocalizedString> #include <QEvent> #include <QGuiApplication> #include <QHBoxLayout> #include <QLabel> +#include <QPointer> #include <QPushButton> #include <QStyle> #include <QToolButton> @@ -24,8 +28,14 @@ using namespace Admin; -Bar::Bar(QWidget *parent) - : AnimatedHeightWidget{parent} +namespace +{ +QPointer<KIO::SimpleJob> waitingForExpirationOfAuthorization; +} + +Bar::Bar(DolphinViewContainer *parentViewContainer) + : AnimatedHeightWidget{parentViewContainer} + , m_parentViewContainer{parentViewContainer} { setAutoFillBackground(true); updateColors(); @@ -46,8 +56,19 @@ Bar::Bar(QWidget *parent) contenntsContainer); m_closeButton->setToolTip(i18nc("@info:tooltip", "Finish 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); + connect(m_closeButton, &QAbstractButton::clicked, m_parentViewContainer, [this]() { + m_parentViewContainer->setActive(true); // Make sure the view connected to this bar is active before exiting admin mode. + QAction *actAsAdminAction = WorkerIntegration::actAsAdminAction(); + if (actAsAdminAction->isChecked()) { + actAsAdminAction->trigger(); + } + }); + connect(m_parentViewContainer->view(), &DolphinView::urlChanged, this, [this](const QUrl &url) { + // The bar is closely related to administrative rights, so we want to hide it instantly when we are no longer using the admin protocol. + if (url.scheme() != QStringLiteral("admin")) { + setVisible(false, WithAnimation); + } + }); QHBoxLayout *layout = new QHBoxLayout(contenntsContainer); auto contentsMargins = layout->contentsMargins(); @@ -65,8 +86,15 @@ Bar::Bar(QWidget *parent) bool Bar::event(QEvent *event) { - if (event->type() == QEvent::PaletteChange) { + switch (event->type()) { + case QEvent::PaletteChange: updateColors(); + break; + case QEvent::Show: + hideTheNextTimeAuthorizationExpires(); + break; + default: + break; } return AnimatedHeightWidget::event(event); } @@ -77,6 +105,52 @@ void Bar::resizeEvent(QResizeEvent *resizeEvent) return QWidget::resizeEvent(resizeEvent); } +void Bar::hideTheNextTimeAuthorizationExpires() +{ + if (waitingForExpirationOfAuthorization.isNull()) { + QByteArray packedArgs; + QDataStream stream(&packedArgs, QIODevice::WriteOnly); + stream << (int)1; + waitingForExpirationOfAuthorization = KIO::special(QUrl(QStringLiteral("admin:/")), packedArgs, KIO::HideProgressInfo); + waitingForExpirationOfAuthorization->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoWarningHandlingEnabled, m_parentViewContainer)); + + connect(waitingForExpirationOfAuthorization, &KJob::finished, this, [](KJob *job) { + if (job->error()) { + job->uiDelegate()->showErrorMessage(); + } + }); + } + + connect(waitingForExpirationOfAuthorization, &KJob::finished, this, [this](KJob *job) { + if (job->error()) { + return; + } + // We exit admin mode now to avoid random password prompts popping up. + QUrl viewContainerUrl = m_parentViewContainer->url(); + if (viewContainerUrl.scheme() != QStringLiteral("admin")) { + return; + } + viewContainerUrl.setScheme("file"); + m_parentViewContainer->setUrl(viewContainerUrl); + + // Explain to users that their admin authorization expired. + if (!m_reenableActAsAdminAction) { + auto actAsAdminAction = WorkerIntegration::actAsAdminAction(); + m_reenableActAsAdminAction = + new QAction{actAsAdminAction->icon(), i18nc("@action:button shown after acting as admin ended", "Act as Administrator Again"), this}; + m_reenableActAsAdminAction->setToolTip(actAsAdminAction->toolTip()); + m_reenableActAsAdminAction->setWhatsThis(actAsAdminAction->whatsThis()); + connect(m_reenableActAsAdminAction, &QAction::triggered, this, [this, actAsAdminAction]() { + m_parentViewContainer->setActive(true); + actAsAdminAction->trigger(); + }); + } + m_parentViewContainer->showMessage(i18nc("@info", "Administrator authorization has expired."), + KMessageWidget::Information, + {m_reenableActAsAdminAction}); + }); +} + void Bar::updateColors() { QPalette palette = parentWidget()->palette(); diff --git a/src/admin/bar.h b/src/admin/bar.h index 5399fa669..d1754f3bb 100644 --- a/src/admin/bar.h +++ b/src/admin/bar.h @@ -10,6 +10,7 @@ #include "animatedheightwidget.h" +class DolphinViewContainer; class QLabel; class QPushButton; class QResizeEvent; @@ -28,19 +29,24 @@ class Bar : public AnimatedHeightWidget Q_OBJECT public: - explicit Bar(QWidget *parent); + explicit Bar(DolphinViewContainer *parentViewContainer); /** 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: + /** + * Makes sure this admin bar hides itself when the elevated privileges expire so the user doesn't mistakenly assume that they are still acting with + * administrative rights. The view container is also changed to a non-admin url, so no password prompts will pop up unexpectedly. + * Then this method shows a message to the user to explain this. + * The mechanism of this method only fires once and will need to be called again the next time the user regains administrative rights for this view. + */ + void hideTheNextTimeAuthorizationExpires(); + /** Recolors this bar based on the current color scheme. */ void updateColors(); @@ -68,6 +74,19 @@ private: /** @see preferredHeight() */ int m_preferredHeight; + + /** + * A proxy action for the real actAsAdminAction. + * This proxy action can be used to reenable admin mode for the view belonging to this bar object specifically. + */ + QAction *m_reenableActAsAdminAction = nullptr; + + /** + * The parent of this bar. The bar acts on the DolphinViewContainer to exit the admin mode. This can happen in two ways: + * 1. The user closes the bar which implies exiting of the admin mode. + * 2. The admin authorization expires so all views can factually no longer be in admin mode. + */ + DolphinViewContainer *m_parentViewContainer; }; } diff --git a/src/admin/workerintegration.cpp b/src/admin/workerintegration.cpp index 038c52094..0b95ec782 100644 --- a/src/admin/workerintegration.cpp +++ b/src/admin/workerintegration.cpp @@ -134,43 +134,41 @@ void WorkerIntegration::createActAsAdminAction(KActionCollection *actionCollecti } } -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")); + if (url.scheme() == QStringLiteral("admin")) { + url.setScheme(QStringLiteral("file")); + dolphinMainWindow->changeUrl(url); + return; + } else if (url.scheme() != QStringLiteral("file")) { + return; + } - risksAccepted = warningDialog.exec() != 4 /* Cancel */; - if (warningDialog.isDontAskAgainChecked()) { - KMessageBox::saveDontShowAgainContinue(warningDontShowAgainName); - } + bool risksAccepted = !KMessageBox::shouldBeShownContinue(warningDontShowAgainName); - if (!risksAccepted) { - updateActAsAdminAction(); // Uncheck the action - return; - } + 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); } - url.setScheme(QStringLiteral("admin")); - } else if (url.scheme() == QStringLiteral("admin")) { - url.setScheme(QStringLiteral("file")); + if (!risksAccepted) { + updateActAsAdminAction(); // Uncheck the action + return; + } } + + url.setScheme(QStringLiteral("admin")); dolphinMainWindow->changeUrl(url); } @@ -189,3 +187,8 @@ void WorkerIntegration::updateActAsAdminAction() } } } + +QAction *WorkerIntegration::actAsAdminAction() +{ + return instance->m_actAsAdminAction; +} diff --git a/src/admin/workerintegration.h b/src/admin/workerintegration.h index 0c87c2ecf..19cc5c172 100644 --- a/src/admin/workerintegration.h +++ b/src/admin/workerintegration.h @@ -57,11 +57,6 @@ public: */ 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); @@ -74,9 +69,14 @@ private: /** Updates the toggled/checked state of the action depending on the state of the currently active view. */ static void updateActAsAdminAction(); + /** Used by the friend class Bar to show the m_actAsAdminAction to users. */ + static QAction *actAsAdminAction(); + private: /** @see createActAsAdminAction() */ QAction *const m_actAsAdminAction = nullptr; + + friend class Bar; // Allows the bar to access the actAsAdminAction, so users can use the bar to change who they are acting as. }; } diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 99ba4efa8..27f845fa7 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -145,7 +145,6 @@ DolphinViewContainer::DolphinViewContainer(const QUrl &url, QWidget *parent) connect(m_view, &DolphinView::hiddenFilesShownChanged, this, &DolphinViewContainer::slotHiddenFilesShownChanged); connect(m_view, &DolphinView::sortHiddenLastChanged, this, &DolphinViewContainer::slotSortHiddenLastChanged); connect(m_view, &DolphinView::currentDirectoryRemoved, this, &DolphinViewContainer::slotCurrentDirectoryRemoved); - connect(m_view, &DolphinView::urlChanged, this, &DolphinViewContainer::updateAdminBarVisibility); // Initialize status bar m_statusBar = new DolphinStatusBar(this); @@ -165,7 +164,9 @@ DolphinViewContainer::DolphinViewContainer(const QUrl &url, QWidget *parent) }); connect(m_statusBar, &DolphinStatusBar::stopPressed, this, &DolphinViewContainer::stopDirectoryLoading); connect(m_statusBar, &DolphinStatusBar::zoomLevelChanged, this, &DolphinViewContainer::slotStatusBarZoomLevelChanged); - connect(m_statusBar, &DolphinStatusBar::showMessage, this, &DolphinViewContainer::showMessage); + connect(m_statusBar, &DolphinStatusBar::showMessage, this, [this](const QString &message, KMessageWidget::MessageType messageType) { + showMessage(message, messageType); + }); m_statusBarTimer = new QTimer(this); m_statusBarTimer->setSingleShot(true); @@ -182,7 +183,6 @@ DolphinViewContainer::DolphinViewContainer(const QUrl &url, QWidget *parent) m_topLayout->addWidget(m_statusBar, positionFor.statusBar, 0); setSearchModeEnabled(isSearchUrl(url)); - updateAdminBarVisibility(url); // Update view as the ContentDisplaySettings change // this happens here and not in DolphinView as DolphinviewContainer and DolphinView are not in the same build target ATM @@ -404,7 +404,7 @@ void DolphinViewContainer::slotSplitTabDisabled() } } -void DolphinViewContainer::showMessage(const QString &message, KMessageWidget::MessageType messageType) +void DolphinViewContainer::showMessage(const QString &message, KMessageWidget::MessageType messageType, std::initializer_list<QAction *> buttonActions) { if (message.isEmpty()) { return; @@ -417,6 +417,14 @@ void DolphinViewContainer::showMessage(const QString &message, KMessageWidget::M m_messageWidget->setWordWrap(true); m_messageWidget->setMessageType(messageType); + const QList<QAction *> previousMessageWidgetActions = m_messageWidget->actions(); + for (auto action : previousMessageWidgetActions) { + m_messageWidget->removeAction(action); + } + for (QAction *action : buttonActions) { + m_messageWidget->addAction(action); + } + m_messageWidget->setWordWrap(false); const int unwrappedWidth = m_messageWidget->sizeHint().width(); m_messageWidget->setWordWrap(unwrappedWidth > size().width()); @@ -638,6 +646,17 @@ void DolphinViewContainer::slotDirectoryLoadingCompleted() if (m_urlNavigatorConnected) { m_urlNavigatorConnected->setReadOnlyBadgeVisible(rootItem().isLocalFile() && !rootItem().isWritable()); } + + // Update admin bar visibility + if (m_view->url().scheme() == QStringLiteral("admin")) { + if (!m_adminBar) { + m_adminBar = new Admin::Bar(this); + m_topLayout->addWidget(m_adminBar, positionFor.adminBar, 0); + } + m_adminBar->setVisible(true, WithAnimation); + } else if (m_adminBar) { + m_adminBar->setVisible(false, WithAnimation); + } } void DolphinViewContainer::slotDirectoryLoadingCanceled() @@ -740,20 +759,6 @@ void DolphinViewContainer::showItemInfo(const KFileItem &item) } } -void DolphinViewContainer::updateAdminBarVisibility(const QUrl &url) -{ - if (url.scheme() == QStringLiteral("admin")) { - if (!m_adminBar) { - m_adminBar = new Admin::Bar(this); - m_topLayout->addWidget(m_adminBar, positionFor.adminBar, 0); - connect(m_adminBar, &Admin::Bar::activated, this, &DolphinViewContainer::activate); - } - m_adminBar->setVisible(true, WithAnimation); - } else if (m_adminBar) { - m_adminBar->setVisible(false, WithAnimation); - } -} - void DolphinViewContainer::closeFilterBar() { m_filterBar->closeFilterBar(); diff --git a/src/dolphinviewcontainer.h b/src/dolphinviewcontainer.h index 55d00a3b8..c5da6b48b 100644 --- a/src/dolphinviewcontainer.h +++ b/src/dolphinviewcontainer.h @@ -21,6 +21,8 @@ #include <QPushButton> #include <QWidget> +#include <initializer_list> + namespace Admin { class Bar; @@ -150,8 +152,9 @@ public: /** * Shows the message \message with the given type \messageType non-modal above the view-content. + * \buttonActions defines actions which the user can trigger as a response to this message. They are presented as buttons below the \message. */ - void showMessage(const QString &message, KMessageWidget::MessageType messageType); + void showMessage(const QString &message, KMessageWidget::MessageType messageType, std::initializer_list<QAction *> buttonActions = {}); /** * Refreshes the view container to get synchronized with the (updated) Dolphin settings. @@ -328,11 +331,6 @@ private Q_SLOTS: */ void showItemInfo(const KFileItem &item); - /** - * Sets the Admin::Bar visible or invisible based on whether \a url is an admin url. - */ - void updateAdminBarVisibility(const QUrl &url); - void closeFilterBar(); /** |
