...
 
Commits (13)
......@@ -40,6 +40,7 @@
#include <QTemporaryFile>
#include <QTest>
#include <untrustedprogramhandlerinterface.h>
#include <openorexecutefileinterface.h>
QTEST_GUILESS_MAIN(OpenUrlJobTest)
......@@ -47,6 +48,7 @@ extern KSERVICE_EXPORT int ksycoca_ms_between_checks;
namespace KIO {
KIOGUI_EXPORT void setDefaultUntrustedProgramHandler(KIO::UntrustedProgramHandlerInterface *iface);
KIOGUI_EXPORT void setDefaultOpenOrExecuteFileHandler(KIO::OpenOrExecuteFileInterface *iface);
}
class TestUntrustedProgramHandler : public KIO::UntrustedProgramHandlerInterface
{
......@@ -62,9 +64,33 @@ public:
QStringList m_calls;
bool m_retVal = false;
};
static TestUntrustedProgramHandler s_handler;
class TestOpenOrExecuteHandler : public KIO::OpenOrExecuteFileInterface
{
public:
void promptUserOpenOrExecute(KJob *job, const QString &mimeType) override
{
Q_UNUSED(job)
Q_UNUSED(mimeType);
if (m_cancelIt) {
Q_EMIT canceled();
m_cancelIt = false;
return;
}
Q_EMIT executeFile(m_executeFile);
}
void setExecuteFile(bool b) { m_executeFile = b; }
void setCanceled() { m_cancelIt = true; }
private:
bool m_executeFile = false;
bool m_cancelIt = false;
};
static TestOpenOrExecuteHandler s_openOrExecuteFileHandler;
static const char s_tempServiceName[] = "openurljobtest_service.desktop";
void OpenUrlJobTest::initTestCase()
......@@ -80,7 +106,9 @@ void OpenUrlJobTest::initTestCase()
ksycoca_ms_between_checks = 0; // need it to check the ksycoca mtime
m_fakeService = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + QLatin1Char('/') + s_tempServiceName;
writeApplicationDesktopFile(m_fakeService);
// not using %d because of remote urls
const QByteArray cmd = QByteArray("echo %u > " + QFile::encodeName(m_tempDir.path()) + "/dest");
writeApplicationDesktopFile(m_fakeService, cmd);
m_fakeService = QFileInfo(m_fakeService).canonicalFilePath();
m_filesToRemove.append(m_fakeService);
......@@ -221,7 +249,7 @@ void OpenUrlJobTest::refuseRunningNativeExecutables()
KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()), QStringLiteral("application/x-executable"), this);
QVERIFY(!job->exec());
QCOMPARE(job->error(), KJob::UserDefinedError);
QVERIFY2(job->errorString().contains("For safety it will not be started"), qPrintable(job->errorString()));
QVERIFY2(job->errorString().contains("For security reasons, launching executables is not allowed in this context."), qPrintable(job->errorString()));
}
void OpenUrlJobTest::refuseRunningRemoteNativeExecutables()
......@@ -230,7 +258,8 @@ void OpenUrlJobTest::refuseRunningRemoteNativeExecutables()
job->setRunExecutables(true); // even with this enabled, an error will occur
QVERIFY(!job->exec());
QCOMPARE(job->error(), KJob::UserDefinedError);
QVERIFY2(job->errorString().contains("For safety it will not be started"), qPrintable(job->errorString()));
QVERIFY2(job->errorString().contains("is located on a remote filesystem. For safety reasons it will not be started"),
qPrintable(job->errorString()));
}
KCONFIGCORE_EXPORT void loadUrlActionRestrictions(const KConfigGroup &cg);
......@@ -341,6 +370,111 @@ void OpenUrlJobTest::runNativeExecutable()
#endif
}
void OpenUrlJobTest::openOrExecuteScript_data()
{
QTest::addColumn<QString>("dialogResult");
QTest::newRow("execute_true") << "execute_true";
QTest::newRow("execute_false") << "execute_false";
QTest::newRow("canceled") << "canceled";
}
void OpenUrlJobTest::openOrExecuteScript()
{
#ifdef Q_OS_UNIX
QFETCH(QString, dialogResult);
// Given an executable shell script that copies "src" to "dest"
QTemporaryDir tempDir;
const QString dir = tempDir.path();
createSrcFile(dir + QLatin1String("/src"));
const QString scriptFile = dir + QLatin1String("/script.sh");
QFile file(scriptFile);
QVERIFY(file.open(QIODevice::WriteOnly));
file.write("#!/bin/sh\ncp src dest");
file.close();
// Set the executable bit, because OpenUrlJob will always open shell
// scripts that are not executable as text files
QVERIFY(file.setPermissions(QFile::ExeUser | file.permissions()));
KIO::setDefaultOpenOrExecuteFileHandler(&s_openOrExecuteFileHandler);
// When using OpenUrlJob to open the script
KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(scriptFile), QStringLiteral("application/x-shellscript"), this);
job->setShowOpenOrExecuteDialog(true);
// Then --- it depends on what the user says via the handler
if (dialogResult == QLatin1String("execute_true")) {
job->setRunExecutables(false); // Overriden by the user's choice
s_openOrExecuteFileHandler.setExecuteFile(true);
QVERIFY(job->exec());
// TRY because CommandLineLauncherJob finishes immediately, and tempDir
// will go out of scope and get deleted before the copy operation actually finishes
QTRY_VERIFY(QFileInfo::exists(dir + QLatin1String("/dest")));
} else if (dialogResult == QLatin1String("execute_false")) {
job->setRunExecutables(true); // Overriden by the user's choice
s_openOrExecuteFileHandler.setExecuteFile(false);
QVERIFY(job->exec());
const QString testOpen = m_tempDir.path() + QLatin1String("/dest"); // see the .desktop file in writeApplicationDesktopFile
QTRY_VERIFY(QFileInfo::exists(testOpen));
} else if (dialogResult == QLatin1String("canceled")) {
s_openOrExecuteFileHandler.setCanceled();
QVERIFY(!job->exec());
QCOMPARE(job->error(), KIO::ERR_USER_CANCELED);
}
#endif
}
void OpenUrlJobTest::openOrExecuteDesktop_data()
{
QTest::addColumn<QString>("dialogResult");
QTest::newRow("execute_true") << "execute_true";
QTest::newRow("execute_false") << "execute_false";
QTest::newRow("canceled") << "canceled";
}
void OpenUrlJobTest::openOrExecuteDesktop()
{
#ifdef Q_OS_UNIX
QFETCH(QString, dialogResult);
// Given a .desktop file, with an Exec line that copies "src" to "dest"
QTemporaryDir tempDir;
const QString dir = tempDir.path();
const QString desktopFile = dir + QLatin1String("/testopenorexecute.desktop");
createSrcFile(dir + QLatin1String("/src"));
const QByteArray cmd("cp " + QFile::encodeName(dir) + "/src " + QFile::encodeName(dir) + "/dest-open-or-execute-desktop");
writeApplicationDesktopFile(desktopFile, cmd);
KIO::setDefaultOpenOrExecuteFileHandler(&s_openOrExecuteFileHandler);
// When using OpenUrlJob to open the .desktop file
KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(desktopFile), QStringLiteral("application/x-desktop"), this);
job->setShowOpenOrExecuteDialog(true);
// Then --- it depends on what the user says via the handler
if (dialogResult == QLatin1String("execute_true")) {
job->setRunExecutables(false); // Overriden by the user's choice
s_openOrExecuteFileHandler.setExecuteFile(true);
QVERIFY2(job->exec(), qPrintable(job->errorString()));
// TRY because CommandLineLauncherJob finishes immediately, and tempDir
// will go out of scope and get deleted before the copy operation actually finishes
QTRY_VERIFY(QFileInfo::exists(dir + QLatin1String("/dest-open-or-execute-desktop")));
} if (dialogResult == QLatin1String("execute_false")) {
job->setRunExecutables(true); // Overriden by the user's choice
s_openOrExecuteFileHandler.setExecuteFile(false);
QVERIFY2(job->exec(), qPrintable(job->errorString()));
const QString testOpen = m_tempDir.path() + QLatin1String("/dest"); // see the .desktop file in writeApplicationDesktopFile
QTRY_VERIFY(QFileInfo::exists(testOpen));
} else if (dialogResult == QLatin1String("canceled")) {
s_openOrExecuteFileHandler.setCanceled();
QVERIFY(!job->exec());
QCOMPARE(job->error(), KIO::ERR_USER_CANCELED);
}
#endif
}
void OpenUrlJobTest::launchExternalBrowser_data()
{
QTest::addColumn<bool>("useBrowserApp");
......@@ -453,13 +587,13 @@ void OpenUrlJobTest::runDesktopFileDirectly()
QCOMPARE(readFile(dest), QString{});
}
void OpenUrlJobTest::writeApplicationDesktopFile(const QString &filePath)
void OpenUrlJobTest::writeApplicationDesktopFile(const QString &filePath, const QByteArray &command)
{
KDesktopFile file(filePath);
KConfigGroup group = file.desktopGroup();
group.writeEntry("Name", "KRunUnittestService");
group.writeEntry("MimeType", "text/plain;application/x-shellscript;x-scheme-handler/scheme");
group.writeEntry("Type", "Application");
group.writeEntry("Exec", QByteArray("echo %u > " + QFile::encodeName(m_tempDir.path()) + "/dest")); // not using %d because of remote urls
group.writeEntry("Exec", command);
QVERIFY(file.sync());
}
......@@ -47,6 +47,10 @@ private Q_SLOTS:
void runScript();
void runNativeExecutable_data();
void runNativeExecutable();
void openOrExecuteScript_data();
void openOrExecuteScript();
void openOrExecuteDesktop_data();
void openOrExecuteDesktop();
void launchExternalBrowser_data();
void launchExternalBrowser();
void nonExistingFile();
......@@ -58,7 +62,7 @@ private Q_SLOTS:
void runDesktopFileDirectly();
private:
void writeApplicationDesktopFile(const QString &filePath);
void writeApplicationDesktopFile(const QString &filePath, const QByteArray &cmd);
QStringList m_filesToRemove;
QTemporaryDir m_tempDir;
......
......@@ -171,7 +171,7 @@ void KFileFilterCombo::setMimeFilter(const QStringList &types,
continue;
}
if (type.name().startsWith(QLatin1String("all/"))) {
if (type.name().startsWith(QLatin1String("all/")) || type.isDefault()) {
hasAllFilesFilter = true;
continue;
}
......
......@@ -134,7 +134,6 @@ public:
QString findMatchingFilter(const QString &filter, const QString &filename) const;
void updateFilter();
void updateFilterText();
QList<QUrl> &parseSelectedUrls();
/**
* Parses the string "line" for files. If line doesn't contain any ", the
* whole line will be interpreted as one file. If the number of " is odd,
......@@ -222,9 +221,6 @@ public:
// the last selected url
QUrl url;
// the selected filenames in multiselection mode -- FIXME
QString filenames;
// now following all kind of widgets, that I need to rebuild
// the geometry management
QBoxLayout *boxLayout;
......@@ -1087,7 +1083,7 @@ void KFileWidget::slotOk()
void KFileWidget::accept()
{
d->inAccept = true; // parseSelectedUrls() checks that
d->inAccept = true;
*lastDirectory() = d->ops->url();
if (!d->fileClass.isEmpty()) {
......@@ -1701,7 +1697,7 @@ QList<QUrl> KFileWidget::selectedUrls() const
QList<QUrl> list;
if (d->inAccept) {
if (d->ops->mode() & KFile::Files) {
list = d->parseSelectedUrls();
list = d->urlList;
} else {
list.append(d->url);
}
......@@ -1709,41 +1705,6 @@ QList<QUrl> KFileWidget::selectedUrls() const
return list;
}
QList<QUrl> &KFileWidgetPrivate::parseSelectedUrls()
{
// qDebug();
if (filenames.isEmpty()) {
return urlList;
}
urlList.clear();
if (filenames.contains(QLatin1Char('/'))) { // assume _one_ absolute filename
QUrl u;
if (containsProtocolSection(filenames)) {
u = QUrl(filenames);
} else {
u.setPath(filenames);
}
if (u.isValid()) {
urlList.append(u);
} else
KMessageBox::error(q,
i18n("The chosen filenames do not\n"
"appear to be valid."),
i18n("Invalid Filenames"));
}
else {
urlList = tokenize(filenames);
}
filenames.clear(); // indicate that we parsed that one
return urlList;
}
// FIXME: current implementation drawback: a filename can't contain quotes
QList<QUrl> KFileWidgetPrivate::tokenize(const QString &line) const
{
......@@ -1832,7 +1793,7 @@ QStringList KFileWidget::selectedFiles() const
if (d->inAccept) {
if (d->ops->mode() & KFile::Files) {
const QList<QUrl> urls = d->parseSelectedUrls();
const QList<QUrl> urls = d->urlList;
QList<QUrl>::const_iterator it = urls.begin();
while (it != urls.end()) {
QUrl url = d->mostLocalUrl(*it);
......
......@@ -6,6 +6,7 @@ set(kiogui_SRCS
faviconrequestjob.cpp
openurljob.cpp
openwithhandlerinterface.cpp
openorexecutefileinterface.cpp
kprocessrunner.cpp
)
......
/* This file is part of the KDE libraries
Copyright (c) 2020 Ahmad Samir <a.samirh78@gmail.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License or ( at
your option ) version 3 or, at the discretion of KDE e.V. ( which shall
act as a proxy as in section 14 of the GPLv3 ), any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "openorexecutefileinterface.h"
using namespace KIO;
class KIO::OpenOrExecuteFileInterfacePrivate {};
OpenOrExecuteFileInterface::OpenOrExecuteFileInterface() = default;
OpenOrExecuteFileInterface::~OpenOrExecuteFileInterface() = default;
void OpenOrExecuteFileInterface::promptUserOpenOrExecute(KJob *job, const QString &mimetype)
{
Q_UNUSED(job)
Q_UNUSED(mimetype)
Q_EMIT canceled();
}
/* This file is part of the KDE libraries
Copyright (c) 2020 Ahmad Samir <a.samirh78@gmail.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License or ( at
your option ) version 3 or, at the discretion of KDE e.V. ( which shall
act as a proxy as in section 14 of the GPLv3 ), any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef OPENOREXECUTEFILEINTERFACE_H
#define OPENOREXECUTEFILEINTERFACE_H
#include <QObject>
#include <kiogui_export.h>
class KJob;
namespace KIO {
class OpenOrExecuteFileInterfacePrivate;
/**
* @class OpenOrExecuteFileInterface openorexecutefileinterface.h <KIO/OpenOrExecuteFileInterface>
* @brief The OpenOrExecuteFileInterface class allows OpenUrlJob to ask
* the user about how to handle various types of executable files, basically
* whether to run/execute the file, or in the case of text-based ones (shell
* scripts and .desktop files) open them as text.
*
* This extension mechanism for jobs is similar to KIO::JobUiDelegateExtension,
* OpenWithHandlerInterface and UntrustedProgramHandlerInterface.
*
* @since 5.73
*/
class KIOGUI_EXPORT OpenOrExecuteFileInterface : public QObject
{
Q_OBJECT
protected:
/**
* Constructor
*/
OpenOrExecuteFileInterface();
/**
* Destructor
*/
~OpenOrExecuteFileInterface() override;
public:
/**
* Show a dialog to ask the user how to handle various types of executable
* files, basically whether to run/execute the file, or in the case of text-based
* ones (shell scripts and .desktop files) open them as text.
*
* @param job the job calling this. This is useful if you need to
* get any of its properties
* @param mimetype the mimetype of the file being handled
*
* Implementations of this method must emit either executeFile or canceled.
*
* The default implementation in this base class simply emits canceled().
* Any application using KIO::JobUiDelegate (from KIOWidgets) will benefit
* from an automatically registered subclass which implements this method,
* which in turn uses ExecutableFileOpenDialog (from KIOWidgets).
*/
virtual void promptUserOpenOrExecute(KJob *job, const QString &mimetype);
Q_SIGNALS:
/**
* Emitted by promptUserOpenOrExecute() once the user chooses an action.
* @param enable \c true if the user selected to execute/run the file or
* \c false if the user selected to open the file as text (the latter is
* only valid for shell scripts and .desktop files)
*/
void executeFile(bool enable);
/**
* Emitted by promptUserOpenOrExecute() if user selects cancel.
*/
void canceled();
private:
QScopedPointer<OpenOrExecuteFileInterfacePrivate> d;
};
}
#endif // OPENOREXECUTEFILEINTERFACE_H
This diff is collapsed.
......@@ -101,6 +101,36 @@ public:
*/
void setRunExecutables(bool allow);
/**
* Set this to @c true if this class should show a dialog to ask the user about how
* to handle various types of executable files; note that executing/running remote
* files is disallowed as that is not secure (in the case of remote shell scripts
* and .desktop files, they are always opened as text in the default application):
* - For native binaries: whether to execute or cancel
* - For .exe files: whether to execute or cancel, ("execute" on Linux in this
* context means running the file with the default application (e.g. WINE))
* - For executable shell scripts: whether to execute the file or open it as
* text in the default application; note that if the file doesn't have the
* execute bit, it'll always be opened as text
* - For .desktop files: whether to run the file or open it as text in the default
* application; note that if the .desktop file is located in a non-standard
* location (on Linux standard locations are /usr/share/applications or
* ~/.local/share/applications) and does not have the execute bit, another dialog
* (see UntrustedProgramHandlerInterface) will be launched to ask the user whether
* they trust running the application (the one the .desktop file launches based on
* the Exec line)
*
* Note that the dialog, ExecutableFileOpenDialog (from KIOWidgets), provides an option
* to remember the last value used and not ask again, if that is set, then the dialog will
* not be shown.
*
* When set to @c true this will take precedence over setRunExecutables (the latter can be
* used to allow running executables without first asking the user for confirmation).
*
* @since 5.73
*/
void setShowOpenOrExecuteDialog(bool b);
/**
* Sets whether the external webbrowser setting should be honoured.
* This is enabled by default.
......
......@@ -37,7 +37,7 @@ Name[zh_TW]=以 root 身份執行動作。
Description=Root privileges are required to complete the action.
Description[ast]=Ríquense los privilexos de root pa completar l'aición.
Description[az]=Hərəkəti başa çatdırmaq üçün Root imtiyazları tələb olunur.
Description[ca]=Es requereixen privilegis d'administrador per completar l'acció.
Description[ca]=Es requereixen privilegis d'administrador per a completar l'acció.
Description[ca@valencia]=Es requereixen privilegis d'administrador per completar l'acció.
Description[cs]=Pro dokončení této činnosti jsou vyžadována práva uživatele root.
Description[da]=Root-privilegier kræves for at udføre handlingen.
......
......@@ -57,7 +57,7 @@ Exec=kio_http_cache_cleaner
Comment=Cleans up old entries from the HTTP cache
Comment[ar]=ينظّف خبيئة HTTP من المُدخلات القديمة
Comment[ast]=Llimpia les entraes vieyes de la caché HTTP
Comment[az]=Köhnə giriçləri HTTP keşindən təmizləyin
Comment[az]=Köhnə girişləri HTTP keşindən təmizləyin
Comment[ca]=Neteja les entrades antigues de la memòria cau HTTP
Comment[ca@valencia]=Neteja les entrades antigues de la memòria cau HTTP
Comment[cs]=Odstraňuje staré položky z HTTP cache
......
......@@ -2,7 +2,7 @@
Type=Service
Name=Remote URL Change Notifier
Name[ast]=Avisador de cambeos d'URLs remotes
Name[az]=URL Dəyiçikliklərini İzləmək
Name[az]=URL Dəyişikliklərini İzləmək
Name[bg]=Уведомител за промени в отдалечени URL-адреси
Name[bn]=দূরবর্তী ইউ-আর-এল পরিবর্তন নোটিফায়ার
Name[bs]=Izveštavač o izmjenama udaljenog URL‑a
......
......@@ -98,7 +98,7 @@ Name[zh_TW]=Windows 資源共享
Comment=Credentials used to access SMB shares
Comment[az]=SMB (Windows) paylaşımlarını əldə etmək üçün sənədlər
Comment[ca]=Credencials usades per accedir a les comparticions SMB
Comment[ca]=Credencials usades per a accedir a les comparticions SMB
Comment[ca@valencia]=Credencials usades per accedir a les comparticions SMB
Comment[da]=Akkreditiver som bruges til at tilgå delte SMB-ressourcer
Comment[de]=Anmeldungsdaten für den Zugriff auf SMB-Freigaben
......
......@@ -44,7 +44,7 @@
"Description[zh_TW]": "自動代理伺服器組態",
"Name": "Network Proxy Configuration",
"Name[ar]": "ضبط وسيط الشبكة",
"Name[az]": "Çəbəkə Proxy Ayarları",
"Name[az]": "Şəbəkə Proxy Ayarları",
"Name[ca@valencia]": "Configuració del servidor intermediari de la xarxa",
"Name[ca]": "Configuració del servidor intermediari de la xarxa",
"Name[cs]": "Nastavení proxy sítě",
......
......@@ -4,7 +4,7 @@
"Description[ar]": "يُوفّر سياسة شهادة SSL للتطبيقات",
"Description[az]": "Tətbiqlər üçün SSL Sertifikatlar Siyasəti",
"Description[ca@valencia]": "Proporciona la política pels certificats SSL a les aplicacions",
"Description[ca]": "Proporciona la política pels certificats SSL a les aplicacions",
"Description[ca]": "Proporciona a les aplicacions la política per als certificats SSL",
"Description[cs]": "Poskytuje politiky certifikátů SSL pro aplikace",
"Description[da]": "Leverer politik for SSL-certifikater til programmer",
"Description[de]": "SSL-Zertifikat-Regel den Anwendungen zur Verfügung stellen",
......@@ -46,7 +46,7 @@
"Name[ar]": "سياسة شهادة SSL",
"Name[az]": "SSl Sertifikat Siyasəti",
"Name[ca@valencia]": "Política pels certificats SSL",
"Name[ca]": "Política pels certificats SSL",
"Name[ca]": "Política per als certificats SSL",
"Name[cs]": "Chování certifikátů SSL",
"Name[da]": "Politik for SSL-certifikater",
"Name[de]": "SSL-Zertifikat-Regel",
......
......@@ -4,6 +4,7 @@ Name[ast]=Enllaz básicu a un ficheru o direutoriu…
Name[az]=Fayl və ya qovluğa əsas link ...
Name[ca]=Enllaç bàsic a un fitxer o directori...
Name[cs]=Základní odkaz na soubor nebo adresář...
Name[da]=Basalt link til fil eller mappe...
Name[de]=Einfache Verknüpfung zu Datei oder Ordner ...
Name[en_GB]=Basic Link to File or Directory...
Name[es]=Enlace básico a archivo o a directorio...
......
......@@ -4,6 +4,7 @@ Name=ArchWiki
Name[az]=ArchWiki
Name[ca]=ArchWiki
Name[cs]=ArchWiki
Name[da]=ArchWiki
Name[de]=ArchWiki
Name[en_GB]=ArchWiki
Name[es]=ArchWiki
......@@ -26,6 +27,7 @@ Query=https://wiki.archlinux.org/index.php?search=\\{@}
Query[az]=https://wiki.archlinux.org/index.php?search=\\{@}
Query[ca]=https://wiki.archlinux.org/index.php?search=\\{@}
Query[cs]=https://wiki.archlinux.org/index.php?search=\\{@}
Query[da]=https://wiki.archlinux.org/index.php?search=\\{@}
Query[de]=https://wiki.archlinux.org/index.php?search=\\{@}
Query[en_GB]=https://wiki.archlinux.org/index.php?search=\\{@}
Query[es]=https://wiki.archlinux.org/index.php?search=\\{@}
......
......@@ -3,6 +3,8 @@ Keys=bug,bugft,bugno,bugnr
Name=KDE Bugs
Name[az]=KDE Xətaları
Name[ca]=KDE Bugs
Name[cs]=Chyby KDE
Name[da]=KDE Bugs
Name[en_GB]=KDE Bugs
Name[es]=Fallos de KDE
Name[eu]=KDEren akatsak
......
......@@ -3,7 +3,10 @@ Keys=deepl
Name=DeepL
Name[az]=DeepL
Name[ca]=DeepL
Name[cs]=DeepL
Name[da]=DeepL
Name[es]=DeepL
Name[eu]=DeepL
Name[fr]=DeepL
Name[it]=DeepL
Name[ko]=DeepL
......@@ -18,7 +21,10 @@ Name[x-test]=xxDeepLxx
Query=https://www.deepl.com/translator#any/any/\\{@}
Query[az]=https://www.deepl.com/translator#any/any/\\{@}
Query[ca]=https://www.deepl.com/translator#any/any/\\{@}
Query[cs]=https://www.deepl.com/translator#any/any/\\{@}
Query[da]=https://www.deepl.com/translator#any/any/\\{@}
Query[es]=https://www.deepl.com/translator#any/any/\\{@}
Query[eu]=https://www.deepl.com/translator#any/any/\\{@}
Query[fr]=https://www.deepl.com/translator#any/any/\\{@}
Query[it]=https://www.deepl.com/translator#any/any/\\{@}
Query[ko]=https://www.deepl.com/translator#any/any/\\{@}
......
......@@ -79,6 +79,7 @@ Query[ast]=https://www.cnrtl.fr/definition/\\{@}
Query[az]=https://www.cnrtl.fr/definition/\\{@}
Query[ca]=https://www.cnrtl.fr/definition/\\{@}
Query[cs]=https://www.cnrtl.fr/definition/\\{@}
Query[da]=https://www.cnrtl.fr/definition/\\{@}
Query[de]=https://www.cnrtl.fr/definition/\\{@}
Query[en_GB]=https://www.cnrtl.fr/definition/\\{@}
Query[es]=https://www.cnrtl.fr/definition/\\{@}
......
......@@ -77,6 +77,7 @@ Query[ast]=https://my.magnatune.com/search?w=\\{@}&t=m&x=0&y=0
Query[az]=https://my.magnatune.com/search?w=\\{@}&t=m&x=0&y=0
Query[ca]=https://my.magnatune.com/search?w=\\{@}&t=m&x=0&y=0
Query[cs]=https://my.magnatune.com/search?w=\\{@}&t=m&x=0&y=0
Query[da]=https://my.magnatune.com/search?w=\\{@}&t=m&x=0&y=0
Query[de]=https://my.magnatune.com/search?w=\\{@}&t=m&x=0&y=0
Query[en_GB]=https://my.magnatune.com/search?w=\\{@}&t=m&x=0&y=0
Query[es]=https://my.magnatune.com/search?w=\\{@}&t=m&x=0&y=0
......
......@@ -11,7 +11,7 @@ Name[bg]=Търсене в Microsoft Developer Network
Name[bn]=মাইক্রোসফট ডেভেলপার নেটওয়ার্ক অনুসন্ধান
Name[bn_IN]=Microsoft Developer Network Search
Name[bs]=Maicrosoftova razvojna mreža (MSDN)
Name[ca]=Xarxa de cerca pel desenvolupador de Microsoft
Name[ca]=Xarxa de cerca per al desenvolupador de Microsoft
Name[ca@valencia]=Xarxa de cerca pel desenvolupador de Microsoft
Name[cs]=Vyhledávání v Microsoft Developer Network
Name[csb]=MSDN - dostónczi dlô programistów
......
......@@ -48,6 +48,8 @@ Name[zh_TW]=Qwant
Query=https://www.qwant.com/?q=\\{@}&client=kde&t=web
Query[az]=https://www.qwant.com/?q=\\{@}&client=kde&t=web
Query[ca]=https://www.qwant.com/?q=\\{@}&client=kde&t=web
Query[cs]=https://www.qwant.com/?q=\\{@}&client=kde&t=web
Query[da]=https://www.qwant.com/?q=\\{@}&client=kde&t=web
Query[en_GB]=https://www.qwant.com/?q=\\{@}&client=kde&t=web
Query[es]=https://www.qwant.com/?q=\\{@}&client=kde&t=web
Query[eu]=https://www.qwant.com/?q=\\{@}&client=kde&t=web
......
......@@ -47,6 +47,8 @@ Name[zh_TW]=Qwant Images
Query=https://www.qwant.com/?q=\\{@}&client=kde&t=images
Query[az]=https://www.qwant.com/?q=\\{@}&client=kde&t=images
Query[ca]=https://www.qwant.com/?q=\\{@}&client=kde&t=images
Query[cs]=https://www.qwant.com/?q=\\{@}&client=kde&t=images
Query[da]=https://www.qwant.com/?q=\\{@}&client=kde&t=images
Query[en_GB]=https://www.qwant.com/?q=\\{@}&client=kde&t=images
Query[es]=https://www.qwant.com/?q=\\{@}&client=kde&t=images
Query[eu]=https://www.qwant.com/?q=\\{@}&client=kde&t=images
......
......@@ -47,6 +47,8 @@ Name[zh_TW]=Qwant News
Query=https://www.qwant.com/?q=\\{@}&client=kde&t=news
Query[az]=https://www.qwant.com/?q=\\{@}&client=kde&t=news
Query[ca]=https://www.qwant.com/?q=\\{@}&client=kde&t=news
Query[cs]=https://www.qwant.com/?q=\\{@}&client=kde&t=news
Query[da]=https://www.qwant.com/?q=\\{@}&client=kde&t=news
Query[en_GB]=https://www.qwant.com/?q=\\{@}&client=kde&t=news
Query[es]=https://www.qwant.com/?q=\\{@}&client=kde&t=news
Query[eu]=https://www.qwant.com/?q=\\{@}&client=kde&t=news
......
......@@ -47,6 +47,8 @@ Name[zh_TW]=Qwant Shopping
Query=https://www.qwant.com/?q=\\{@}&client=kde&t=shopping
Query[az]=https://www.qwant.com/?q=\\{@}&client=kde&t=shopping
Query[ca]=https://www.qwant.com/?q=\\{@}&client=kde&t=shopping
Query[cs]=https://www.qwant.com/?q=\\{@}&client=kde&t=shopping
Query[da]=https://www.qwant.com/?q=\\{@}&client=kde&t=shopping
Query[en_GB]=https://www.qwant.com/?q=\\{@}&client=kde&t=shopping
Query[es]=https://www.qwant.com/?q=\\{@}&client=kde&t=shopping
Query[eu]=https://www.qwant.com/single?q=\\{@}&client=kde&source=shopping
......
......@@ -47,6 +47,8 @@ Name[zh_TW]=Qwant Social
Query=https://www.qwant.com/?q=\\{@}&client=kde&t=social
Query[az]=https://www.qwant.com/?q=\\{@}&client=kde&t=social
Query[ca]=https://www.qwant.com/?q=\\{@}&client=kde&t=social
Query[cs]=https://www.qwant.com/?q=\\{@}&client=kde&t=social
Query[da]=https://www.qwant.com/?q=\\{@}&client=kde&t=social
Query[en_GB]=https://www.qwant.com/?q=\\{@}&client=kde&t=social
Query[es]=https://www.qwant.com/?q=\\{@}&client=kde&t=social
Query[eu]=https://www.qwant.com/single?q=\\{@}&client=kde&source=social
......
......@@ -48,6 +48,8 @@ Name[zh_TW]=Qwant Videos
Query=https://www.qwant.com/?q=\\{@}&client=kde&t=videos
Query[az]=https://www.qwant.com/?q=\\{@}&client=kde&t=videos
Query[ca]=https://www.qwant.com/?q=\\{@}&client=kde&t=videos
Query[cs]=https://www.qwant.com/?q=\\{@}&client=kde&t=videos
Query[da]=https://www.qwant.com/?q=\\{@}&client=kde&t=videos
Query[en_GB]=https://www.qwant.com/?q=\\{@}&client=kde&t=videos
Query[es]=https://www.qwant.com/?q=\\{@}&client=kde&t=videos
Query[eu]=https://www.qwant.com/single?q=\\{@}&client=kde&source=videos
......
......@@ -97,6 +97,7 @@ Query[ast]=https://www.rae.es/drae/?val=\\{@}
Query[az]=https://www.rae.es/drae/?val=\\{@}
Query[ca]=https://www.rae.es/drae/?val=\\{@}
Query[cs]=https://www.rae.es/drae/?val=\\{@}
Query[da]=https://www.rae.es/drae/?val=\\{@}
Query[de]=https://www.rae.es/drae/?val=\\{@}
Query[en_GB]=https://www.rae.es/drae/?val=\\{@}
Query[es]=https://www.rae.es/drae/?val=\\{@}
......
......@@ -100,6 +100,7 @@ Query[ast]=https://www.voila.com/S/geek?an=1&kw=\\{@}&dt=*
Query[az]=https://www.voila.com/S/geek?an=1&kw=\\{@}&dt=*
Query[ca]=https://www.voila.com/S/geek?an=1&kw=\\{@}&dt=*
Query[cs]=https://www.voila.com/S/geek?an=1&kw=\\{@}&dt=*
Query[da]=https://www.voila.com/S/geek?an=1&kw=\\{@}&dt=*
Query[de]=https://www.voila.com/S/geek?an=1&kw=\\{@}&dt=*
Query[en_GB]=https://www.voila.com/S/geek?an=1&kw=\\{@}&dt=*
Query[es]=https://www.voila.com/S/geek?an=1&kw=\\{@}&dt=*
......
......@@ -60,6 +60,7 @@ set(kiowidgets_SRCS
renamefiledialog.cpp
widgetsuntrustedprogramhandler.cpp
widgetsopenwithhandler.cpp
widgetsopenorexecutefilehandler.cpp
)
if (NOT WIN32)
list(APPEND kiowidgets_SRCS
......
......@@ -26,6 +26,7 @@
#include "kiogui_export.h"
#include "widgetsuntrustedprogramhandler.h"
#include "widgetsopenwithhandler.h"
#include "widgetsopenorexecutefilehandler.h"
#include <KConfigGroup>
#include <KJob>
......@@ -54,6 +55,7 @@ public:
namespace KIO {
KIOGUI_EXPORT void setDefaultUntrustedProgramHandler(KIO::UntrustedProgramHandlerInterface *iface);
KIOGUI_EXPORT void setDefaultOpenWithHandler(KIO::OpenWithHandlerInterface *iface);
KIOGUI_EXPORT void setDefaultOpenOrExecuteFileHandler(KIO::OpenOrExecuteFileInterface *iface);
}
KIO::JobUiDelegate::JobUiDelegate()
......@@ -64,6 +66,8 @@ KIO::JobUiDelegate::JobUiDelegate()
KIO::setDefaultUntrustedProgramHandler(&s_handler);
static WidgetsOpenWithHandler s_openUrlHandler;
KIO::setDefaultOpenWithHandler(&s_openUrlHandler);
static WidgetsOpenOrExecuteFileHandler s_openOrExecuteFileHandler;
KIO::setDefaultOpenOrExecuteFileHandler(&s_openOrExecuteFileHandler);
}
KIO::JobUiDelegate::~JobUiDelegate()
......
......@@ -424,8 +424,8 @@ int KFileItemActions::addServiceActionsTo(QMenu *mainMenu)
QMenu *actionMenu = mainMenu;
int userItemCount = 0;
if (s.user.count() + s.userSubmenus.count() +
s.userPriority.count() + s.userPrioritySubmenus.count() > 1) {
// we have more than one item, so let's make a submenu
s.userPriority.count() + s.userPrioritySubmenus.count() > 3) {
// we have more than three items, so let's make a submenu
actionMenu = new QMenu(i18nc("@title:menu", "&Actions"), mainMenu);
actionMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-more-symbolic")));
actionMenu->menuAction()->setObjectName(QStringLiteral("actions_submenu")); // for the unittest
......
......@@ -4,7 +4,7 @@ X-KDE-ServiceType=KPropertiesDialog/Plugin
Comment=Plugin for the Properties Dialog
Comment[ar]=ملحقة لحواري الخصائص
Comment[az]=Xüsusiyyətlər Dialogu üçün plaqin
Comment[ca]=Connector pel diàleg de les propietats
Comment[ca]=Connector per al diàleg de les propietats
Comment[ca@valencia]=Connector pel diàleg de les propietats
Comment[cs]=Modul pro dialog vlastností
Comment[da]=Plugin til egenskaber-dialogen
......
......@@ -990,12 +990,12 @@ bool KRun::isExecutable(const QString &mimeTypeName)
{
QMimeDatabase db;
QMimeType mimeType = db.mimeTypeForName(mimeTypeName);
return (mimeType.inherits(QLatin1String("application/x-desktop")) ||
mimeType.inherits(QLatin1String("application/x-executable")) ||
return (mimeType.inherits(QStringLiteral("application/x-desktop")) ||
mimeType.inherits(QStringLiteral("application/x-executable")) ||
/* See https://bugs.freedesktop.org/show_bug.cgi?id=97226 */
mimeType.inherits(QLatin1String("application/x-sharedlib")) ||
mimeType.inherits(QLatin1String("application/x-ms-dos-executable")) ||
mimeType.inherits(QLatin1String("application/x-shellscript")));
mimeType.inherits(QStringLiteral("application/x-sharedlib")) ||
mimeType.inherits(QStringLiteral("application/x-ms-dos-executable")) ||
mimeType.inherits(QStringLiteral("application/x-shellscript")));
}
void KRun::setUrl(const QUrl &url)
......
......@@ -91,6 +91,80 @@ static KSqueezedTextLabel *createSqueezedLabel(QWidget *parent, const QString &t
return label;
}
enum CompareFilesResult {
Identical,
PartiallyIdentical,
Different
};
static CompareFilesResult compareFiles(const QString &filepath, const QString &secondFilePath)
{
const qint64 bufferSize = 4096; // 4kb
QFile f(filepath);
QFile f2(secondFilePath);
const auto fileSize = f.size();
if (fileSize != f2.size()) {
return CompareFilesResult::Different;
}
if (!f.open(QFile::ReadOnly)) {
qCWarning(KIO_WIDGETS) << "Could not open file for comparison:" << f.fileName();
return CompareFilesResult::Different;
}
if (!f2.open(QFile::ReadOnly)) {
f.close();
qCWarning(KIO_WIDGETS) << "Could not open file for comparison:" << f2.fileName();
return CompareFilesResult::Different;
}
QByteArray buffer(bufferSize, 0);
QByteArray buffer2(bufferSize, 0);
bool result = true;
auto seekFillBuffer = [bufferSize](qint64 pos, QFile &f, QByteArray &buffer){
auto ioresult = f.seek(pos);
int bytesRead;
if (ioresult) {
bytesRead = f.read(buffer.data(), bufferSize);
ioresult = bytesRead != -1;
}
if (!ioresult) {
qCWarning(KIO_WIDGETS) << "Could not read file for comparison:" << f.fileName();
return false;
}
return true;
};
// compare at the beginning of the files
result = result && seekFillBuffer(0, f, buffer);
result = result && seekFillBuffer(0, f2, buffer2);
result = result && buffer == buffer2;
if (result && fileSize > 2 * bufferSize) {
// compare the contents in the middle of the files
result = result && seekFillBuffer(fileSize / 2 - bufferSize / 2, f, buffer);
result = result && seekFillBuffer(fileSize / 2 - bufferSize / 2, f2, buffer2);
result = result && buffer == buffer2;
}
if (result && fileSize > bufferSize) {
// compare the contents at the end of the files
result = result && seekFillBuffer(fileSize - bufferSize, f, buffer);
result = result && seekFillBuffer(fileSize - bufferSize, f2, buffer2);
result = result && buffer == buffer2;
}
if (!result) {
return CompareFilesResult::Different;
}
if (fileSize <= bufferSize * 3) {
// for files smaller than bufferSize * 3, we in fact compared fully the files
return CompareFilesResult::Identical;
} else {
return CompareFilesResult::PartiallyIdentical;
}
}
/** @internal */
class Q_DECL_HIDDEN RenameDialog::RenameDialogPrivate
{
......@@ -190,9 +264,14 @@ RenameDialog::RenameDialog(QWidget *parent, const QString &_caption,
}
if (_options & RenameDialog_Overwrite) {
const QString text = (_options & RenameDialog_IsDirectory) ? i18nc("Write files into an existing folder", "&Write Into") : i18n("&Overwrite");
d->bOverwrite = new QPushButton(text, this);
d->bOverwrite->setToolTip(i18n("Files and folders will be copied into the existing directory, alongside its existing contents.\nYou will be prompted again in case of a conflict with an existing file in the directory."));
d->bOverwrite = new QPushButton(this);
KGuiItem::assign(d->bOverwrite, KStandardGuiItem::overwrite());
if (_options & RenameDialog_IsDirectory) {
d->bOverwrite->setText(i18nc("Write files into an existing folder", "&Write Into"));
d->bOverwrite->setIcon(QIcon());
d->bOverwrite->setToolTip(i18n("Files and folders will be copied into the existing directory, alongside its existing contents.\nYou will be prompted again in case of a conflict with an existing file in the directory."));
}
connect(d->bOverwrite, &QAbstractButton::clicked, this, &RenameDialog::overwritePressed);
}
......@@ -289,6 +368,7 @@ RenameDialog::RenameDialog(QWidget *parent, const QString &_caption,
destUrlLabel->setTextFormat(Qt::PlainText);
gridLayout->addWidget(destUrlLabel, gridRow, 2);
gridLayout->addWidget(d->m_srcArea, ++gridRow, 0, 2, 1);
// The labels containing previews or icons, and an arrow icon indicating
// direction from src to dest
......@@ -296,34 +376,67 @@ RenameDialog::RenameDialog(QWidget *parent, const QString &_caption,
: QStringLiteral("go-next");
const QPixmap pix = QIcon::fromTheme(arrowName).pixmap(d->m_srcPreview->height());
srcToDestArrow->setPixmap(pix);
gridLayout->addWidget(d->m_srcArea, ++gridRow, 0);
gridLayout->addWidget(srcToDestArrow, gridRow, 1);
gridLayout->addWidget(d->m_destArea, gridRow, 2);
QLabel *diffTitle = createLabel(parent, i18n("Differences"), true);
gridLayout->addWidget(diffTitle, ++gridRow, 1);
QLabel *srcDateLabel = createDateLabel(parent, d->srcItem);
gridLayout->addWidget(srcDateLabel, ++gridRow, 0);
QLabel *destDateLabel = createDateLabel(parent, d->destItem);
gridLayout->addWidget(destDateLabel, gridRow, 2);
if (mtimeDest > mtimeSrc) {
QLabel* warningLabel = createLabel(this, i18n("The destination is more recent."), true);
gridLayout->addWidget(warningLabel, ++gridRow, 2);
gridLayout->addWidget(createLabel(parent, QStringLiteral("The destination is <b>more recent</b>")), gridRow, 1);
}
QLabel *destDateLabel = createLabel(parent, i18n("Date: %1", d->destItem.timeString(KFileItem::ModificationTime)));
gridLayout->addWidget(destDateLabel, gridRow, 2);
QLabel *srcSizeLabel = createSizeLabel(parent, d->srcItem);
gridLayout->addWidget(srcSizeLabel, ++gridRow, 0);
QLabel *destSizeLabel = createSizeLabel(parent, d->destItem);
gridLayout->addWidget(destSizeLabel, gridRow, 2);
if (d->srcItem.size() != d->destItem.size()) {
QString text;
int diff = 0;
if (d->srcItem.size() > d->destItem.size()) {
text = i18n("The source file is bigger.");
diff = d->srcItem.size() - d->destItem.size();
text = i18n("The destination is <b>smaller by %1</b>", KIO::convertSize(diff));
} else {
text = i18n("The destination file is bigger.");
diff = d->destItem.size() - d->srcItem.size();
text = i18n("The destination is <b>bigger by %1</b>", KIO::convertSize(diff));
}
gridLayout->addWidget(createLabel(parent, text), gridRow, 1);
}
QLabel *destSizeLabel = createLabel(parent, i18n("Size: %1", KIO::convertSize(d->destItem.size())));
gridLayout->addWidget(destSizeLabel, gridRow, 2);
// check files contents for local files
if (d->dest.isLocalFile() && d->src.isLocalFile()) {
const CompareFilesResult CompareFilesResult = compareFiles(d->src.toLocalFile(), d->dest.toLocalFile());
QString text;
switch (CompareFilesResult) {
case CompareFilesResult::Identical: text = i18n("The files are identical."); break;
case CompareFilesResult::PartiallyIdentical: text = i18n("The files seem identical."); break;
case CompareFilesResult::Different: text = i18n("The files are different."); break;
}
QLabel* filesIdenticalLabel = createLabel(this, text, true);
if (CompareFilesResult == CompareFilesResult::PartiallyIdentical) {
QLabel* pixmapLabel = new QLabel(this);
pixmapLabel->setPixmap(QIcon::fromTheme(QStringLiteral("help-about")).pixmap(QSize(16,16)));
pixmapLabel->setToolTip(
i18n("The files are likely to be identical: they have the same size and their contents are the same at the beginning, middle and end.")
);
pixmapLabel->setCursor(Qt::WhatsThisCursor);
QHBoxLayout* hbox = new QHBoxLayout(this);
hbox->addWidget(filesIdenticalLabel);
hbox->addWidget(pixmapLabel);
gridLayout->addLayout(hbox, gridRow + 1, 1);
} else {
gridLayout->addWidget(filesIdenticalLabel, gridRow + 1, 1);
}
QLabel* warningLabel = createLabel(this, text, true);
gridLayout->addWidget(warningLabel, ++gridRow, 2);
}
} else {
......@@ -658,10 +771,11 @@ void RenameDialog::resizePanels()
QScrollArea *RenameDialog::createContainerLayout(QWidget *parent, const KFileItem &item, QLabel *preview)
{
Q_UNUSED(item)
#if 0 // PENDING
KFileItemList itemList;
itemList << item;
#if 0 // PENDING
// KFileMetaDataWidget was deprecated for a Nepomuk widget, which is itself deprecated...
// If we still want metadata shown, we need a plugin that fetches data from KFileMetaData::ExtractorCollection
KFileMetaDataWidget *metaWidget = new KFileMetaDataWidget(this);
......
/* This file is part of the KDE libraries
Copyright (c) 2020 Ahmad Samir <a.samirh78@gmail.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License or ( at
your option ) version 3 or, at the discretion of KDE e.V. ( which shall
act as a proxy as in section 14 of the GPLv3 ), any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "widgetsopenorexecutefilehandler.h"
#include "executablefileopendialog_p.h"
#include <KConfigGroup>
#include <KJobWidgets>
#include <KSharedConfig>
#include <QApplication>
#include <QMimeDatabase>
KIO::WidgetsOpenOrExecuteFileHandler::WidgetsOpenOrExecuteFileHandler()
: KIO::OpenOrExecuteFileInterface()
{
}
KIO::WidgetsOpenOrExecuteFileHandler::~WidgetsOpenOrExecuteFileHandler() = default;
static ExecutableFileOpenDialog::Mode promptMode(const QMimeType &mime)
{
// Note that ExecutableFileOpenDialog::OpenAsExecute isn't useful here as
// OpenUrlJob treats .exe (application/x-ms-dos-executable) files as executables
// that are only opened using the default application associated with that mime type
// e.g. WINE
if (mime.inherits(QStringLiteral("text/plain"))) {
return ExecutableFileOpenDialog::OpenOrExecute;
}
return ExecutableFileOpenDialog::OnlyExecute;
}
void KIO::WidgetsOpenOrExecuteFileHandler::promptUserOpenOrExecute(KJob *job, const QString &mimetype)
{
KConfigGroup cfgGroup(KSharedConfig::openConfig(QStringLiteral("kiorc")), "Executable scripts");
const QString value = cfgGroup.readEntry("behaviourOnLaunch", "alwaysAsk");
if (value != QLatin1String("alwaysAsk")) {
Q_EMIT executeFile(value == QLatin1String("execute"));
return;
}
QWidget *parentWidget = job ? KJobWidgets::window(job) : qApp->activeWindow();
QMimeDatabase db;
QMimeType mime = db.mimeTypeForName(mimetype);
ExecutableFileOpenDialog *dialog = new ExecutableFileOpenDialog(promptMode(mime), parentWidget);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, &QDialog::finished, this, [this, dialog, mime](const int result) {
if (result == ExecutableFileOpenDialog::Rejected) {
Q_EMIT canceled();
return;
}
const bool isExecute = result == ExecutableFileOpenDialog::ExecuteFile;
Q_EMIT executeFile(isExecute);
if (dialog->isDontAskAgainChecked()) {
KConfigGroup cfgGroup(KSharedConfig::openConfig(QStringLiteral("kiorc")), "Executable scripts");
cfgGroup.writeEntry("behaviourOnLaunch", isExecute ? "execute" : "open");
}
});
dialog->show();
}
/* This file is part of the KDE libraries
Copyright (c) 2020 Ahmad Samir <a.samirh78@gmail.com>
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License or ( at
your option ) version 3 or, at the discretion of KDE e.V. ( which shall
act as a proxy as in section 14 of the GPLv3 ), any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef WIDGETSOPENOREXECUTEFILEHANDLER_H
#define WIDGETSOPENOREXECUTEFILEHANDLER_H
#include "openorexecutefileinterface.h"
namespace KIO {
// TODO KF6: Make KIO::JobUiDelegate inherit from WidgetsOpenOrExecuteFileHandler
// (or even merge the two classes)
// so that setDelegate(new KIO::JobUiDelegate) invokes the dialog boxes on error
// and when showing ExecutableFileOpenDialog.
class WidgetsOpenOrExecuteFileHandler : public OpenOrExecuteFileInterface
{
public:
WidgetsOpenOrExecuteFileHandler();
~WidgetsOpenOrExecuteFileHandler() override;
void promptUserOpenOrExecute(KJob *job, const QString &mimetype) override;
private:
// Note: no d pointer because it's not exported at this point
};
}
#endif // WIDGETSOPENOREXECUTEFILEHANDLER_H