Skip to content

Task 390571 add panel file manager dock plugin#3934

Open
add-uos wants to merge 2 commits into
linuxdeepin:fashion-modelfrom
add-uos:task-390571-add-panel-file-manager-dock-plugin
Open

Task 390571 add panel file manager dock plugin#3934
add-uos wants to merge 2 commits into
linuxdeepin:fashion-modelfrom
add-uos:task-390571-add-panel-file-manager-dock-plugin

Conversation

@add-uos

@add-uos add-uos commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

No description provided.

Implement a new dde-shell dock panel plugin that provides file
browsing capabilities directly from the dock panel, featuring
directory navigation, icon view modes and color theme support.

新增dde-shell任务栏文件管理面板插件,支持目录浏览、图标视图
模式切换和颜色主题配置。

Log: 新增任务栏文件管理面板插件
PMS: TASK-390571
Influence: 用户可以从任务栏面板直接浏览文件目录,提升文件访问效率。

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @add-uos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: add-uos

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@github-actions

Copy link
Copy Markdown
  • 检测到debian目录文件有变更: debian/dde-file-manager-dock-plugin.install,debian/control,debian/dde-desktop.install

@github-actions

Copy link
Copy Markdown
  • 敏感词检查失败, 检测到1个文件存在敏感词
详情
{
    "debian/control": [
        {
            "line": "Homepage: http://www.deepin.org",
            "line_number": 59,
            "rule": "S35",
            "reason": "Url link | 6fe814dfb7"
        }
    ]
}

Implement a new dde-shell dock panel plugin (org.deepin.ds.filemanager)
that provides file browsing capabilities directly from the taskbar dock
panel. The plugin displays real file thumbnails in a 2x2 grid on the
dock icon and a popup file browser with thumbnail previews.

Changes:
- Add PinnedItemIcon and TaskIcon QML components for dock icon rendering
- Integrate DThumbnailProvider for async file thumbnail generation
- Replace Canvas 2D hand-drawn icons with real file icon previews
- Add OpacityMask rounded-corner thumbnail rendering in popup views
- Add DirectoryModel with thumbnailUrl role and async refresh signal
- Add previewIconNames property for dock icon composite preview
- Remove colorTheme/gridCount properties and right-click color menu
- Package as separate deb: dde-file-manager-dock-plugin

新增dde-shell任务栏文件管理面板插件,支持从任务栏直接浏览文件目录,
显示真实文件缩略图预览,提供网格/列表视图切换功能。

Log: 新增任务栏文件管理面板插件
PMS: TASK-390571
Influence: 用户可以从任务栏面板直接浏览文件目录,提升文件访问效率。
@add-uos
add-uos force-pushed the task-390571-add-panel-file-manager-dock-plugin branch from 912c17f to 9f15254 Compare July 14, 2026 12:01
@github-actions

Copy link
Copy Markdown
  • 检测到debian目录文件有变更: debian/dde-file-manager-dock-plugin.install,debian/control,debian/dde-desktop.install

@github-actions

Copy link
Copy Markdown
  • 敏感词检查失败, 检测到1个文件存在敏感词
详情
{
    "debian/control": [
        {
            "line": "Homepage: http://www.deepin.org",
            "line_number": 59,
            "rule": "S35",
            "reason": "Url link | 6fe814dfb7"
        }
    ]
}

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

deepin pr auto review

★ 总体评分:55分

■ 【总体评价】

代码实现了 dde-shell 文件管理器 dock 插件的核心功能,但存在 DBus 路径构造错误和 .desktop 文件安全验证缺失
逻辑因 DBus 路径未转义导致 AM1 启动失败回退至 openUrl,且 .desktop 文件任意执行风险扣15分,性能与质量问题再扣15分

■ 【详细分析】

  • 1.语法逻辑(存在严重错误)✕

filemanagerplugin.cppopenFile 函数中,objectPath 使用 QStringLiteral("/org/desktopspec/ApplicationManager1/%1").arg(appId) 构造,而 appId 来自 desktopInfo.completeBaseName(),通常包含 .(如 org.deepin.editor)。DBus 对象路径规范不允许 . 字符,导致路径非法,QDBusInterface 始终无效,AM1 启动永远失败并回退至 openUrlfilemanager.qmlonDroppeddrop.urls[0].toString().replace(/^file:\/\//, "") 未解码 URL 编码字符(如 %20),导致含空格或特殊字符的路径解析错误。directorymodel.hEntry 结构体中 bool isDir 未初始化默认值,存在未定义行为风险。
潜在问题:DBus 路径非法导致 .desktop 文件无法通过 AM1 启动,核心功能失效;URL 编码未解码导致拖放含特殊字符路径的文件时导航失败;isDir 未初始化可能在异常路径下产生未定义行为
建议:对 appId 进行 DBus 路径转义(. 替换为 _2e 等);在 C++ 端添加 Q_INVOKABLE 方法使用 QUrl::toLocalFile() 正确解析 URL;将 bool isDir 改为 bool isDir = false

  • 2.代码质量(一般)✕

filemanager.qml 中存在大量硬编码尺寸(如 48040090100)和颜色值(如 #0096C7#FFFFFF),缺乏常量定义或主题引用。directorymodel.cppiconToDataUrl 方法缺少缓存机制,每次调用都重新生成 base64 数据。filemanagerplugin.cppopenFile 中 DBus 调用失败后仅打印日志,缺少面向用户的状态反馈。directorymodel.cpploadDirectoryQSettings 解析 .desktop 文件未处理 Name[%1] 的回退逻辑不完整(未考虑 Name[zh] 等无地区后缀的情况)。
潜在问题:硬编码值导致维护困难和主题适配问题;缺少缓存导致重复计算;用户无法感知启动失败原因
建议:将尺寸和颜色提取为属性或常量;为 iconToDataUrl 添加 QHash<QString, QString> 缓存;完善 .desktop 文件本地化名称的回退逻辑

  • 3.代码性能(存在性能问题)✕

directorymodel.cpploadDirectory 中对每个文件调用 iconToDataUrl,该方法执行 QIcon::fromThemeQPixmap::pixmapQBuffer base64 编码,对于包含大量文件的目录会产生显著性能开销。filemanagerplugin.cpppreviewIconNames 每次调用都通过 m_directoryModel->get(i) 创建 QVariantMap,虽然只取前4项但仍有不必要的对象分配。directorymodel.cpponThumbnailGenerated 中对 m_entries 进行线性扫描匹配路径,当条目较多时效率较低。
潜在问题:大目录加载时 UI 卡顿;频繁的 QVariantMap 创建增加 GC 压力
建议:为 iconToDataUrl 添加基于 iconName 的静态缓存;previewIconNames 直接访问 m_entries 内部数据而非通过 get()onThumbnailGenerated 使用 QHash<QString, int> 建立路径到索引的映射

  • 4.代码安全(存在1个安全漏洞)✕

漏洞对比统计:新增漏洞 1 个,减少漏洞 0 个,持平 0 个
存在 1 个安全漏洞(中危1个),openFile 函数对 .desktop 文件缺少来源验证和内容审查,恶意文件可通过下载目录或共享文件夹传入,用户双击即触发任意命令执行

  • 安全漏洞1(中危):任意命令执行 在 FileManagerPlugin::openFile (filemanagerplugin.cpp) 中,函数对 .desktop 文件直接调用 ApplicationManager1 的 Launch 方法传递 filePath,或回退至 QDesktopServices::openUrl,未验证文件是否位于可信应用目录、是否具备执行权限或 Exec 字段内容是否安全。输入源为用户浏览目录中的任意 .desktop 文件,触发方式为用户在 dock 弹窗中双击文件,危害为执行 Exec 字段中的任意系统命令,可导致数据泄露、系统破坏或权限提升 ——非常重要

  • 建议:在调用 AM1 Launch 前验证 .desktop 文件是否位于可信路径(/usr/share/applications/~/.local/share/applications/);对不可信路径的 .desktop 文件弹出确认对话框或拒绝执行;使用 QStandardPaths 构建可信路径列表

■ 【改进建议代码示例】

// filemanagerplugin.cpp - 修复 openFile 函数
void FileManagerPlugin::openFile(const QString &filePath)
{
    QFileInfo fileInfo(filePath);
    if (!fileInfo.exists() || !fileInfo.isReadable()) {
        qCWarning(fileManagerPluginLog) << "File does not exist or not readable:" << filePath;
        return;
    }

    if (filePath.endsWith(QLatin1String(".desktop"), Qt::CaseInsensitive)) {
        // 安全检查:验证 .desktop 文件是否位于可信应用目录
        static const QStringList trustedPaths = {
            QStringLiteral("/usr/share/applications/"),
            QStringLiteral("/usr/local/share/applications/"),
            QDir::homePath() + QStringLiteral("/.local/share/applications/")
        };

        const QString canonicalPath = fileInfo.canonicalFilePath();
        bool isTrusted = false;
        for (const auto &trustedPath : trustedPaths) {
            if (canonicalPath.startsWith(trustedPath)) {
                isTrusted = true;
                break;
            }
        }

        if (!isTrusted) {
            qCWarning(fileManagerPluginLog) << "Refusing to launch untrusted .desktop file:" << canonicalPath;
            QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
            return;
        }

        QString appId = fileInfo.completeBaseName();
        // 转义 DBus 对象路径中的非法字符(. -> _2e, - -> _2d)
        QString escapedAppId = appId;
        escapedAppId.replace(QLatin1Char('.'), QLatin1String("_2e"));
        escapedAppId.replace(QLatin1Char('-'), QLatin1String("_2d"));

        QString objectPath = QStringLiteral("/org/desktopspec/ApplicationManager1/%1")
                                 .arg(escapedAppId);

        QDBusInterface iface(
            QStringLiteral("org.desktopspec.ApplicationManager1"),
            objectPath,
            QStringLiteral("org.desktopspec.ApplicationManager1.Application"),
            QDBusConnection::sessionBus());

        if (iface.isValid()) {
            QVariantMap options {
                { QStringLiteral("_launch_type"), QStringLiteral("dde-file-manager") }
            };
            QDBusReply<void> reply = iface.call(QStringLiteral("Launch"),
                                                filePath,
                                                QStringList(),
                                                options);
            if (reply.isValid())
                return;

            qCWarning(fileManagerPluginLog) << "AM1 Launch failed:" << reply.error().message()
                                            << "falling back to openUrl";
        }
    }

    QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
}

// filemanagerplugin.h - 添加 URL 解析方法
// 在 public 部分添加:
Q_INVOKABLE QString urlToLocalPath(const QString &urlString) const;

// filemanagerplugin.cpp - 实现 URL 解析
QString FileManagerPlugin::urlToLocalPath(const QString &urlString) const
{
    QUrl url(urlString);
    if (url.isLocalFile())
        return url.toLocalFile();
    return urlString;
}

// directorymodel.h - 修复 Entry 结构体初始化
struct Entry {
    QString name;
    QString path;
    QString iconName;
    QString iconUrl;
    bool isDir = false;  // 添加默认初始化
    FileType fileType = GenericFile;
    QString thumbnailUrl;
};

// directorymodel.cpp - 添加 iconToDataUrl 缓存
QString DirectoryModel::iconToDataUrl(const QString &iconName, int size)
{
    static QHash<QString, QString> cache;
    const QString cacheKey = QStringLiteral("%1_%2").arg(iconName).arg(size);

    auto it = cache.constFind(cacheKey);
    if (it != cache.constEnd())
        return it.value();

    QIcon icon;
    if (iconName.startsWith(QLatin1Char('/')))
        icon = QIcon(iconName);
    if (icon.isNull())
        icon = QIcon::fromTheme(iconName);
    if (icon.isNull())
        icon = QIcon::fromTheme(QStringLiteral("text-x-generic"));
    if (icon.isNull()) {
        cache.insert(cacheKey, QString());
        return QString();
    }

    QPixmap pm = icon.pixmap(size, size);
    if (pm.isNull()) {
        cache.insert(cacheKey, QString());
        return QString();
    }

    QByteArray ba;
    QBuffer buf(&ba);
    buf.open(QIODevice::WriteOnly);
    pm.save(&buf, "PNG");
    QString result = QStringLiteral("data:image/png;base64,") + ba.toBase64();
    cache.insert(cacheKey, result);
    return result;
}
// filemanager.qml - 修复 onDropped 中的 URL 解析
onDropped: function (drop) {
    dragHovering = false
    if (drop.urls.length === 0)
        return
    // 使用 C++ 端的 QUrl::toLocalFile 正确解析路径
    var localPath = Applet.urlToLocalPath(drop.urls[0].toString())
    if (!localPath)
        return
    if (Applet.isDirectory(localPath) || Applet.isFile(localPath)) {
        var dir = Applet.isDirectory(localPath) ? localPath 
                                                  : localPath.substring(0, localPath.lastIndexOf("/"))
        if (dir)
            Applet.navigateTo(dir)
    }
    drop.accepted = true
}

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

@add-uos: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
github-pr-review-ci 9f15254 link true /test github-pr-review-ci

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

Comment thread debian/control
Comment on lines +91 to +92
Package: dde-file-manager-dock-plugin
Architecture: any

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

control.in也要写

usr/share/applications/dde-computer.desktop
usr/share/applications/dde-trash.desktop
usr/share/applications/dde-home.desktop
usr/share/dde-shell/*/*.json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

桌面插件org.deepin.ds.desktop.so匹配不了吧

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants