#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>
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();
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();
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);
}
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();
#include "animatedheightwidget.h"
+class DolphinViewContainer;
class QLabel;
class QPushButton;
class QResizeEvent;
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();
/** @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;
};
}
}
}
-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 (url.scheme() == QStringLiteral("admin")) {
+ url.setScheme(QStringLiteral("file"));
+ dolphinMainWindow->changeUrl(url);
+ return;
+ } else if (url.scheme() != QStringLiteral("file")) {
+ return;
+ }
- if (!risksAccepted) {
- updateActAsAdminAction(); // Uncheck the action
- return;
- }
+ 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);
}
- 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);
}
}
}
}
+
+QAction *WorkerIntegration::actAsAdminAction()
+{
+ return instance->m_actAsAdminAction;
+}
*/
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);
/** 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.
};
}
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);
});
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);
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
}
}
-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;
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());
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()
}
}
-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();
#include <QPushButton>
#include <QWidget>
+#include <initializer_list>
+
namespace Admin
{
class Bar;
/**
* 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.
*/
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();
/**