]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Notify users if authorization is required to proceed
authorFelix Ernst <felixernst@kde.org>
Mon, 22 Jul 2024 10:51:33 +0000 (10:51 +0000)
committerFelix Ernst <felixernst@kde.org>
Mon, 22 Jul 2024 10:51:33 +0000 (10:51 +0000)
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.

src/admin/bar.cpp
src/admin/workerintegration.cpp
src/admin/workerintegration.h
src/dolphinviewcontainer.cpp
src/dolphinviewcontainer.h
src/kitemviews/kfileitemmodel.cpp
src/kitemviews/kfileitemmodel.h
src/views/dolphinview.cpp
src/views/dolphinview.h

index 52bbc37724206ab159f4f45c65efdc3162bdd680..e01ef81a5c2df356ea7f9867e8084e6f12f702e6 100644 (file)
@@ -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());
index 0b95ec782119a6c6fdca042b001bffc4c12f40cf..80d8fda1b15e72f5865b7df3ed352698398fd44e 100644 (file)
@@ -134,6 +134,11 @@ void WorkerIntegration::createActAsAdminAction(KActionCollection *actionCollecti
     }
 }
 
+QAction *WorkerIntegration::FriendAccess::actAsAdminAction()
+{
+    return instance->m_actAsAdminAction;
+}
+
 void WorkerIntegration::toggleActAsAdmin()
 {
     auto dolphinMainWindow = static_cast<DolphinMainWindow *>(parent());
@@ -187,8 +192,3 @@ void WorkerIntegration::updateActAsAdminAction()
         }
     }
 }
-
-QAction *WorkerIntegration::actAsAdminAction()
-{
-    return instance->m_actAsAdminAction;
-}
index 19cc5c17272a4a2d10cb7388504e3f7d3e04e205..1e32c33d37b2f1470d1cff787cfc12234a2dcc22 100644 (file)
@@ -11,6 +11,7 @@
 #include <QObject>
 
 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.
 };
 
 }
index 27f845fa798fa3245248241167ca22775b8bca24..3fedef6fc9f220db1e28ae3089d4d6fc4ec666eb 100644 (file)
@@ -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);
index c5da6b48b12e5974a9d4d6f4c6947ce6c7f724c5..be28ecdebd71098ae7462740ee861dc688cb6846 100644 (file)
@@ -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;
 
index c694da9f256f041f91d1ed0712032474db7266c8..d18754ec75352fa7c9e1b5fc916d361f48292aa8 100644 (file)
@@ -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<KIO::ListJob *>(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);
     }
 }
 
index ce58f89acc96ff2334dd0cb7f6c99f9601cb3571..5662d4fa8b00eda322e02caf000288bd42848b74 100644 (file)
@@ -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
index d42d9cfcd81d7fe9f909548f16766e38c26259ff..85e652ecf70baa14c8f412399b9f421d89d4c8a6 100644 (file)
@@ -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;
     }
index b55e2ee9becc5b2463f28c59acd7456f79509481..c985f4eb9616998c612504ac891e18e6560609a1 100644 (file)
@@ -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