From: Felix Ernst Date: Mon, 22 Jul 2024 10:51:33 +0000 (+0000) Subject: Notify users if authorization is required to proceed X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/6e6fcf8da80b5b6821837054ae51eaa19edc24b8 Notify users if authorization is required to proceed Previous to this commit entering a folder without read access would show the non-descriptive error message "Could not enter folder". If the user actually is not allowed to view the contents of the folder, this is indeed true and this commit will preserve this message as is. However, if an admin protocol is installed, users can actually view the contents of most folders after authorizing themselves as administrators. So this commit changes the error message in those specific circumstances to instead read "Authorization required to enter this folder." and provide a button to authorize themselves. This button is the "Act as Administrator" action. If no admin protocol is installed, this commit has no effect. The idea for this change came from Harald Sitter. I receive funding for changes like this by the Next Generation Internet Initiative, which (as I understand it) will no longer provide funds for future projects like this if the current EU draft budget goes through as it is. --- diff --git a/src/admin/bar.cpp b/src/admin/bar.cpp index 52bbc3772..e01ef81a5 100644 --- a/src/admin/bar.cpp +++ b/src/admin/bar.cpp @@ -58,7 +58,7 @@ Bar::Bar(DolphinViewContainer *parentViewContainer) m_closeButton->setFlat(true); 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(); + QAction *actAsAdminAction = WorkerIntegration::FriendAccess::actAsAdminAction(); if (actAsAdminAction->isChecked()) { actAsAdminAction->trigger(); } @@ -134,8 +134,8 @@ void Bar::hideTheNextTimeAuthorizationExpires() m_parentViewContainer->setUrl(viewContainerUrl); // Explain to users that their admin authorization expired. - if (!m_reenableActAsAdminAction) { - auto actAsAdminAction = WorkerIntegration::actAsAdminAction(); + if (!m_reenableActAsAdminAction) { // This code is similar to parts of DolphinViewContainer::slotViewErrorMessage(). + auto actAsAdminAction = WorkerIntegration::FriendAccess::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()); diff --git a/src/admin/workerintegration.cpp b/src/admin/workerintegration.cpp index 0b95ec782..80d8fda1b 100644 --- a/src/admin/workerintegration.cpp +++ b/src/admin/workerintegration.cpp @@ -134,6 +134,11 @@ void WorkerIntegration::createActAsAdminAction(KActionCollection *actionCollecti } } +QAction *WorkerIntegration::FriendAccess::actAsAdminAction() +{ + return instance->m_actAsAdminAction; +} + void WorkerIntegration::toggleActAsAdmin() { auto dolphinMainWindow = static_cast(parent()); @@ -187,8 +192,3 @@ void WorkerIntegration::updateActAsAdminAction() } } } - -QAction *WorkerIntegration::actAsAdminAction() -{ - return instance->m_actAsAdminAction; -} diff --git a/src/admin/workerintegration.h b/src/admin/workerintegration.h index 19cc5c172..1e32c33d3 100644 --- a/src/admin/workerintegration.h +++ b/src/admin/workerintegration.h @@ -11,6 +11,7 @@ #include class DolphinMainWindow; +class DolphinViewContainer; class KActionCollection; class QAction; class QUrl; @@ -57,6 +58,20 @@ public: */ static void createActAsAdminAction(KActionCollection *actionCollection, DolphinMainWindow *dolphinMainWindow); + /** + * An interface that only allows friend classes to show the WorkerIntegration::m_actAsAdminAction to users. + * Aside from these friend classes the action is only accessible through the actionCollection of DolphinMainWindow. + */ + class FriendAccess + { + /** @returns WorkerIntegration::m_actAsAdminAction or crashes if WorkerIntegration::createActAsAdminAction() has not been called previously. */ + static QAction *actAsAdminAction(); + + friend class Bar; /// Allows the bar to access the actAsAdminAction, so users can use the bar to change who they are acting as. + friend DolphinViewContainer; // Allows the view container to access the actAsAdminAction, so the action can be shown to users when they are trying to + // view a folder for which they are lacking read permissions. + }; + private: WorkerIntegration(DolphinMainWindow *parent, QAction *actAsAdminAction); @@ -69,14 +84,9 @@ 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 27f845fa7..3fedef6fc 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -7,6 +7,7 @@ #include "dolphinviewcontainer.h" #include "admin/bar.h" +#include "admin/workerintegration.h" #include "dolphin_compactmodesettings.h" #include "dolphin_contentdisplaysettings.h" #include "dolphin_detailsmodesettings.h" @@ -62,6 +63,7 @@ DolphinViewContainer::DolphinViewContainer(const QUrl &url, QWidget *parent) , m_searchBox(nullptr) , m_searchModeEnabled(false) , m_adminBar{nullptr} + , m_authorizeToEnterFolderAction{nullptr} , m_messageWidget(nullptr) , m_selectionModeTopBar{nullptr} , m_view(nullptr) @@ -139,7 +141,7 @@ DolphinViewContainer::DolphinViewContainer(const QUrl &url, QWidget *parent) connect(m_view, &DolphinView::directoryLoadingCanceled, this, &DolphinViewContainer::slotDirectoryLoadingCanceled); connect(m_view, &DolphinView::itemCountChanged, this, &DolphinViewContainer::delayedStatusBarUpdate); connect(m_view, &DolphinView::selectionChanged, this, &DolphinViewContainer::delayedStatusBarUpdate); - connect(m_view, &DolphinView::errorMessage, this, &DolphinViewContainer::showErrorMessage); + connect(m_view, &DolphinView::errorMessage, this, &DolphinViewContainer::slotErrorMessageFromView); connect(m_view, &DolphinView::urlIsFileError, this, &DolphinViewContainer::slotUrlIsFileError); connect(m_view, &DolphinView::activated, this, &DolphinViewContainer::activate); connect(m_view, &DolphinView::hiddenFilesShownChanged, this, &DolphinViewContainer::slotHiddenFilesShownChanged); @@ -897,6 +899,28 @@ void DolphinViewContainer::slotStatusBarZoomLevelChanged(int zoomLevel) m_view->setZoomLevel(zoomLevel); } +void DolphinViewContainer::slotErrorMessageFromView(const QString &message, const int kioErrorCode) +{ + if (kioErrorCode == KIO::ERR_CANNOT_ENTER_DIRECTORY && m_view->url().scheme() == QStringLiteral("file") + && KProtocolInfo::isKnownProtocol(QStringLiteral("admin")) && !rootItem().isReadable()) { + // Explain to users that they need authentication to see the folder contents. + if (!m_authorizeToEnterFolderAction) { // This code is similar to parts of Admin::Bar::hideTheNextTimeAuthorizationExpires(). + // We should not simply use the actAsAdminAction() itself here because that one always refers to the active view instead of this->m_view. + auto actAsAdminAction = Admin::WorkerIntegration::FriendAccess::actAsAdminAction(); + m_authorizeToEnterFolderAction = new QAction{actAsAdminAction->icon(), actAsAdminAction->text(), this}; + m_authorizeToEnterFolderAction->setToolTip(actAsAdminAction->toolTip()); + m_authorizeToEnterFolderAction->setWhatsThis(actAsAdminAction->whatsThis()); + connect(m_authorizeToEnterFolderAction, &QAction::triggered, this, [this, actAsAdminAction]() { + setActive(true); + actAsAdminAction->trigger(); + }); + } + showMessage(i18nc("@info", "Authorization required to enter this folder."), KMessageWidget::Error, {m_authorizeToEnterFolderAction}); + return; + } + Q_EMIT showErrorMessage(message); +} + void DolphinViewContainer::showErrorMessage(const QString &message) { showMessage(message, KMessageWidget::Error); diff --git a/src/dolphinviewcontainer.h b/src/dolphinviewcontainer.h index c5da6b48b..be28ecdeb 100644 --- a/src/dolphinviewcontainer.h +++ b/src/dolphinviewcontainer.h @@ -387,6 +387,11 @@ private Q_SLOTS: void slotStatusBarZoomLevelChanged(int zoomLevel); + /** + * Creates and shows an error message based on \p message and \p kioErrorCode. + */ + void slotErrorMessageFromView(const QString &message, const int kioErrorCode); + /** * Slot that calls showMessage(message, KMessageWidget::Error). */ @@ -449,6 +454,8 @@ private: /// A bar shown at the top of the view to signify that the view is currently viewed and acted on with elevated privileges. Admin::Bar *m_adminBar; + /// An action to switch to the admin protocol. This variable will always be nullptr unless kio-admin was installed. @see Admin::WorkerIntegration. + QAction *m_authorizeToEnterFolderAction; KMessageWidget *m_messageWidget; diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index c694da9f2..d18754ec7 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -2856,13 +2856,14 @@ bool KFileItemModel::isConsistent() const void KFileItemModel::slotListerError(KIO::Job *job) { - if (job->error() == KIO::ERR_IS_FILE) { + const int jobError = job->error(); + if (jobError == KIO::ERR_IS_FILE) { if (auto *listJob = qobject_cast(job)) { Q_EMIT urlIsFileError(listJob->url()); } } else { const QString errorString = job->errorString(); - Q_EMIT errorMessage(!errorString.isEmpty() ? errorString : i18nc("@info:status", "Unknown error.")); + Q_EMIT errorMessage(!errorString.isEmpty() ? errorString : i18nc("@info:status", "Unknown error."), jobError); } } diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index ce58f89ac..5662d4fa8 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -258,7 +258,7 @@ Q_SIGNALS: * Is emitted if an error message (e.g. "Unknown location") * should be shown. */ - void errorMessage(const QString &message); + void errorMessage(const QString &message, const int kioErrorCode); /** * Is emitted if a redirection from the current URL \a oldUrl diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index d42d9cfcd..85e652ecf 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -231,7 +231,9 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent) m_versionControlObserver->setView(this); m_versionControlObserver->setModel(m_model); connect(m_versionControlObserver, &VersionControlObserver::infoMessage, this, &DolphinView::infoMessage); - connect(m_versionControlObserver, &VersionControlObserver::errorMessage, this, &DolphinView::errorMessage); + connect(m_versionControlObserver, &VersionControlObserver::errorMessage, this, [this](const QString &message) { + Q_EMIT errorMessage(message, KIO::ERR_UNKNOWN); + }); connect(m_versionControlObserver, &VersionControlObserver::operationCompletedMessage, this, &DolphinView::operationCompletedMessage); m_twoClicksRenamingTimer = new QTimer(this); @@ -1453,7 +1455,7 @@ void DolphinView::onDirectoryLoadingCompletedAfterJob() void DolphinView::slotJobResult(KJob *job) { if (job->error() && job->error() != KIO::ERR_USER_CANCELED) { - Q_EMIT errorMessage(job->errorString()); + Q_EMIT errorMessage(job->errorString(), job->error()); } if (!m_selectJobCreatedItems) { m_selectedUrls.clear(); @@ -1826,7 +1828,7 @@ void DolphinView::slotTrashFileFinished(KJob *job) selectNextItem(); // Fixes BUG: 419914 via selecting next item Q_EMIT operationCompletedMessage(i18nc("@info:status", "Trash operation completed.")); } else if (job->error() != KIO::ERR_USER_CANCELED) { - Q_EMIT errorMessage(job->errorString()); + Q_EMIT errorMessage(job->errorString(), job->error()); } } @@ -1836,7 +1838,7 @@ void DolphinView::slotDeleteFileFinished(KJob *job) selectNextItem(); // Fixes BUG: 419914 via selecting next item Q_EMIT operationCompletedMessage(i18nc("@info:status", "Delete operation completed.")); } else if (job->error() != KIO::ERR_USER_CANCELED) { - Q_EMIT errorMessage(job->errorString()); + Q_EMIT errorMessage(job->errorString(), job->error()); } } @@ -2048,9 +2050,9 @@ void DolphinView::loadDirectory(const QUrl &url, bool reload) if (!url.isValid()) { const QString location(url.toDisplayString(QUrl::PreferLocalFile)); if (location.isEmpty()) { - Q_EMIT errorMessage(i18nc("@info:status", "The location is empty.")); + Q_EMIT errorMessage(i18nc("@info:status", "The location is empty."), KIO::ERR_UNKNOWN); } else { - Q_EMIT errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location)); + Q_EMIT errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location), KIO::ERR_UNKNOWN); } return; } diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index b55e2ee9b..c985f4eb9 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -567,7 +567,7 @@ Q_SIGNALS: * Is emitted if an error message with the content \a msg * should be shown. */ - void errorMessage(const QString &msg); + void errorMessage(const QString &message, const int kioErrorCode); /** * Is emitted if an "operation completed" message with the content \a msg