From 2c19a9ba03aab7603d232f21ace3b59bc9360d5b Mon Sep 17 00:00:00 2001 From: fuleyi Date: Wed, 3 Dec 2025 14:19:33 +0800 Subject: [PATCH] feat: enhance login reminder with configurable settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Added DConfig support for LoginReminder configuration with default value false 2. Implemented password expiration check as fallback when LoginReminder is disabled 3. Replaced systemd service with desktop autostart file for better session integration 4. Added translation support with multiple language files (en_US, zh_CN, zh_HK, zh_TW) 5. Added D-Bus service watcher to wait for notification service availability 6. Improved notification content with detailed login information and password warnings 7. Added action button to directly open password change page in control center Log: Added configurable login reminder feature with password expiration warnings Influence: 1. Test login reminder functionality with DConfig enabled and disabled states 2. Verify password expiration notifications work independently 3. Check translation loading for different locales 4. Test notification action button opens control center password page 5. Verify autostart behavior after system login 6. Test D-Bus service dependency handling feat: 增强登录提醒功能并支持配置设置 1. 添加 DConfig 支持登录提醒配置,默认值为关闭 2. 实现密码过期检查作为登录提醒禁用时的备用功能 3. 将 systemd 服务替换为桌面自启动文件以实现更好的会话集成 4. 添加多语言翻译支持(英文、简体中文、繁体中文香港、繁体中文台湾) 5. 添加 D-Bus 服务监视器以等待通知服务可用 6. 改进通知内容,包含详细的登录信息和密码警告 7. 添加操作按钮可直接打开控制中心的密码修改页面 Log: 新增可配置的登录提醒功能,包含密码过期警告 Influence: 1. 测试 DConfig 启用和禁用状态下的登录提醒功能 2. 验证密码过期通知是否独立工作 3. 检查不同语言环境的翻译加载 4. 测试通知操作按钮是否打开控制中心密码页面 5. 验证系统登录后的自启动行为 6. 测试 D-Bus 服务依赖处理 --- .tx/transifex.yaml | 8 + CMakeLists.txt | 2 + lupdate.sh | 6 + misc/CMakeLists.txt | 6 + misc/dconf/org.deepin.dde.session.json | 17 ++ tools/dde-login-reminder/CMakeLists.txt | 23 +- .../dde-login-reminder.desktop | 10 + .../dde-login-reminder.service | 10 - tools/dde-login-reminder/main.cpp | 197 ++++++++++++++++-- .../translations/dde-login-reminder.ts | 78 +++++++ .../translations/dde-login-reminder_zh_CN.ts | 78 +++++++ .../translations/dde-login-reminder_zh_HK.ts | 78 +++++++ .../translations/dde-login-reminder_zh_TW.ts | 78 +++++++ 13 files changed, 560 insertions(+), 31 deletions(-) create mode 100644 .tx/transifex.yaml create mode 100755 lupdate.sh create mode 100644 misc/dconf/org.deepin.dde.session.json create mode 100644 tools/dde-login-reminder/dde-login-reminder.desktop delete mode 100644 tools/dde-login-reminder/dde-login-reminder.service create mode 100644 tools/dde-login-reminder/translations/dde-login-reminder.ts create mode 100644 tools/dde-login-reminder/translations/dde-login-reminder_zh_CN.ts create mode 100644 tools/dde-login-reminder/translations/dde-login-reminder_zh_HK.ts create mode 100644 tools/dde-login-reminder/translations/dde-login-reminder_zh_TW.ts diff --git a/.tx/transifex.yaml b/.tx/transifex.yaml new file mode 100644 index 0000000..e4d97ba --- /dev/null +++ b/.tx/transifex.yaml @@ -0,0 +1,8 @@ +filters: +- filter_type: file + source_file: tools/dde-login-reminder/translations/dde-login-reminder.ts + file_format: QT + source_language: en_US + translation_files_expression: tools/dde-login-reminder/translations/dde-login-reminder_.ts +settings: + pr_branch_name: transifex_update_ diff --git a/CMakeLists.txt b/CMakeLists.txt index 14df601..da4da45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ include(GNUInstallDirs) set(QT_VERSION_MAJOR 6) set(DTK_VERSION_MAJOR 6) +find_package(DtkBuildHelper REQUIRED) + macro(install_symlink filepath wantsdir) file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/link/${wantsdir}/) execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/lib/systemd/user/${filepath} ${PROJECT_BINARY_DIR}/link/${wantsdir}/${filepath}) diff --git a/lupdate.sh b/lupdate.sh new file mode 100755 index 0000000..4e18683 --- /dev/null +++ b/lupdate.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# 更新 dde-login-reminder 翻译文件 +lupdate tools/dde-login-reminder -ts -no-obsolete translations/dde-login-reminder.ts + +# tx push -s -f --branch m20 diff --git a/misc/CMakeLists.txt b/misc/CMakeLists.txt index 4eef91c..21fbccf 100644 --- a/misc/CMakeLists.txt +++ b/misc/CMakeLists.txt @@ -21,3 +21,9 @@ install( install(FILES ${XSESSION} DESTINATION /etc/X11/Xsession.d/) install(FILES ${PROFILE} DESTINATION /etc/profile.d) + +# Install dconf configuration files +dtk_add_config_meta_files( + APPID org.deepin.dde.session + FILES ${CMAKE_CURRENT_SOURCE_DIR}/dconf/org.deepin.dde.session.json +) diff --git a/misc/dconf/org.deepin.dde.session.json b/misc/dconf/org.deepin.dde.session.json new file mode 100644 index 0000000..18cdd70 --- /dev/null +++ b/misc/dconf/org.deepin.dde.session.json @@ -0,0 +1,17 @@ +{ + "magic": "dsg.config.meta", + "version": "1.0", + "contents": { + "LoginReminder": { + "value": false, + "serial": 0, + "flags": [], + "name": "login reminder", + "name[zh_CN]": "登录提醒", + "description": "Display login reminder notification including login information, password expiration warning and failed login attempts", + "description[zh_CN]": "显示登录提醒通知,包括登录信息、密码过期警告和登录失败次数", + "permissions": "readwrite", + "visibility": "public" + } + } +} diff --git a/tools/dde-login-reminder/CMakeLists.txt b/tools/dde-login-reminder/CMakeLists.txt index 28deb44..6aa517d 100644 --- a/tools/dde-login-reminder/CMakeLists.txt +++ b/tools/dde-login-reminder/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network DBus REQUIRED) +find_package(Dtk${DTK_VERSION_MAJOR} COMPONENTS Core REQUIRED) find_package(PkgConfig REQUIRED) @@ -21,17 +22,27 @@ target_link_libraries(${BIN_NAME} Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::DBus Qt${QT_VERSION_MAJOR}::Network + Dtk${DTK_VERSION_MAJOR}::Core ) target_include_directories(${BIN_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/src/utils ) +target_compile_definitions(${BIN_NAME} PRIVATE + CMAKE_INSTALL_FULL_DATADIR="${CMAKE_INSTALL_FULL_DATADIR}" +) + install(TARGETS ${BIN_NAME} DESTINATION bin) -set(SYSTEMD_FILES - dde-login-reminder.service -) -install(FILES ${SYSTEMD_FILES} DESTINATION lib/systemd/user/) -install(DIRECTORY DESTINATION lib/systemd/user/dde-osd.target.wants/) -install_symlink(dde-login-reminder.service dde-osd.target.wants) +# Install desktop file to autostart directory +install(FILES dde-login-reminder.desktop DESTINATION /etc/xdg/autostart/) + +# Translation files +file(GLOB TS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS LinguistTools) +if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND AND TS_FILES) + qt_add_translation(QM_FILES ${TS_FILES}) + add_custom_target(${BIN_NAME}_translations ALL DEPENDS ${QM_FILES}) + install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/${BIN_NAME}/translations) +endif() diff --git a/tools/dde-login-reminder/dde-login-reminder.desktop b/tools/dde-login-reminder/dde-login-reminder.desktop new file mode 100644 index 0000000..82f02aa --- /dev/null +++ b/tools/dde-login-reminder/dde-login-reminder.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=DDE Login Reminder +Comment=Display login reminder notification +Exec=/usr/bin/dde-login-reminder +Icon=preferences-system +Terminal=false +Categories=System; +X-Deepin-AutoStart=true +NoDisplay=true diff --git a/tools/dde-login-reminder/dde-login-reminder.service b/tools/dde-login-reminder/dde-login-reminder.service deleted file mode 100644 index 0551dc8..0000000 --- a/tools/dde-login-reminder/dde-login-reminder.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=dde-login-reminder service -CollectMode=inactive-or-failed - -Wants=dde-osd.target -After=dde-osd.target - -[Service] -Type=simple -ExecStart=/usr/bin/dde-login-reminder diff --git a/tools/dde-login-reminder/main.cpp b/tools/dde-login-reminder/main.cpp index 8660b8e..51c8ac7 100644 --- a/tools/dde-login-reminder/main.cpp +++ b/tools/dde-login-reminder/main.cpp @@ -2,26 +2,45 @@ // // SPDX-License-Identifier: GPL-3.0-or-later +#include #include +#include #include #include +#include #include +#include +#include +#include +#include #include -#include "utils.h" #include "loginreminderinfo.h" +DCORE_USE_NAMESPACE + +// 前置声明 +int checkPasswordExpired(); int start() { qDebug() << "login reminder init"; -#if 0 - // TODO has no 'com.deepin.dde.startdde' gsetting - if (!Utils::SettingValue("com.deepin.dde.startdde", QByteArray(), "login-reminder", false).toBool()) - return 0; -#endif - // 登录后展示横幅通知信息 + + // 读取 DConfig 配置,检查 LoginReminder 是否开启 + DConfig *config = DConfig::create("org.deepin.dde.session", "org.deepin.dde.session"); + bool loginReminderEnabled = false; // 默认关闭 + if (config && config->isValid()) { + loginReminderEnabled = config->value("LoginReminder", false).toBool(); + qDebug() << "LoginReminder enabled:" << loginReminderEnabled; + } + + // 如果 login-reminder 没有开启,仅检查密码过期情况 + if (!loginReminderEnabled) { + qDebug() << "LoginReminder is disabled, only checking password expiration"; + return checkPasswordExpired(); + } + QDBusInterface userInter("org.deepin.dde.Accounts1", QString("/org/deepin/dde/Accounts1/User%1").arg(getuid()), "org.deepin.dde.Accounts1.User", QDBusConnection::systemBus()); QDBusPendingReply reply = userInter.call("GetReminderInfo"); if (reply.isError()) { @@ -69,11 +88,18 @@ int start() const QString &address = (info.CurrentLogin.Address == "0.0.0.0") ? getFirstIpAddress() : info.CurrentLogin.Address; QString body = QString("%1 %2 %3").arg(info.Username).arg(address).arg(currentLoginTime); int daysLeft = info.spent.LastChange + info.spent.Max - int(QDateTime::currentSecsSinceEpoch() / SECONDS_PER_DAY); + bool needNotify = false; if ((info.spent.Max != -1) && (info.spent.Warn != -1) && (info.spent.Warn > daysLeft)) { + needNotify = true; body += " " + QString(QObject::tr("Your password will expire in %1 days")).arg(daysLeft); } - body += "\n" + QString(QObject::tr("%1 login failures since the last successful login")).arg(info.FailCountSinceLastLogin); - + if (info.FailCountSinceLastLogin > 0) { + needNotify = true; + body += "\n" + QString(QObject::tr("%1 login failures since the last successful login")).arg(info.FailCountSinceLastLogin); + } + if (!needNotify) { + return 0; + } const QString &lastLoginTime = info.LastLogin.Time.left(QString("yyyy-MM-dd hh:mm:ss").length()); QString content; content += QString("

%1

").arg(info.Username); @@ -95,7 +121,7 @@ int start() uint notifyId = notifyIdReply.value(); // 5秒后自动关闭通知,避免在通知中心中显示 - sleep(5); + sleep(10); QDBusPendingReply<> closeReply = notifyInter->call("CloseNotification", notifyId); if (closeReply.isError()) { @@ -107,13 +133,154 @@ int start() return 0; } +int checkPasswordExpired() { + qDebug() << "check password expired"; + + // 调用 PasswordExpiredInfo 获取密码过期信息 + QDBusInterface userInter("org.deepin.dde.Accounts1", + QString("/org/deepin/dde/Accounts1/User%1").arg(getuid()), + "org.deepin.dde.Accounts1.User", + QDBusConnection::systemBus()); + + QDBusMessage reply = userInter.call("PasswordExpiredInfo"); + if (reply.type() == QDBusMessage::ErrorMessage) { + qWarning() << "failed to retrieve password expired info, error: " << reply.errorMessage(); + return -1; + } + + // 解析返回值: (Int32 expiredStatus, Int64 dayLeft) + QList args = reply.arguments(); + if (args.size() < 2) { + qWarning() << "invalid reply from PasswordExpiredInfo"; + return -1; + } + + int expiredStatus = args[0].toInt(); + qint64 dayLeft = args[1].toLongLong(); + + qDebug() << "Password expired status:" << expiredStatus << ", days left:" << dayLeft; + + // expiredStatus: 0=正常, 1=即将过期, 2=已过期 + if (expiredStatus == 0) { + qDebug() << "Password is valid, no notification needed"; + return 0; + } + + // 构建通知内容 + QString title = QObject::tr("Password Expiration Warning"); + QString body; + QString content; + + if (expiredStatus == 2) { + // 密码已过期 + body = QObject::tr("Your password has expired. Please change it immediately."); + content = QString("

%1

").arg(QObject::tr("Your password has expired!")); + content += QString("

%1

").arg(QObject::tr("For security reasons, please change your password immediately.")); + } else if (expiredStatus == 1 && dayLeft > 0) { + // 密码即将过期 + body = QObject::tr("Your password will expire in %1 days. Please change it soon.").arg(dayLeft); + content = QString("

%1

").arg(QObject::tr("Password Expiration Warning")); + content += QString("

%1

").arg(QObject::tr("Your password will expire in %1 days.").arg(dayLeft)); + content += QString("

%1

").arg(QObject::tr("Please change your password as soon as possible.")); + } else { + // 其他情况 + qDebug() << "Unknown password status, no notification"; + return 0; + } + + // 发送通知 + QDBusInterface *notifyInter = new QDBusInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications"); + QVariantMap hints; + // 点击通知后打开控制中心的账户密码修改页面 + hints.insert(QString("x-deepin-action-change_password"), + QVariant(QString("dbus-send,--print-reply,--dest=org.deepin.dde.ControlCenter1,/org/deepin/dde/ControlCenter1,org.deepin.dde.ControlCenter1.ShowPage,string:accountsloginMethodItempassword"))); + + QDBusPendingReply notifyIdReply = notifyInter->call("Notify", + "dde-control-center", + uint(0), + "preferences-system", + title, + body, + QStringList() << "change_password" << QObject::tr("Change Password"), + hints, + int(0)); + if (notifyIdReply.isError()) { + qWarning() << "failed to call notification, error: " << notifyIdReply.error().message(); + return -1; + } + uint notifyId = notifyIdReply.value(); + + // 10秒后自动关闭通知 + sleep(5); + + QDBusPendingReply<> closeReply = notifyInter->call("CloseNotification", notifyId); + if (closeReply.isError()) { + qWarning() << "failed to close notification, error: " << closeReply.error().message(); + return -1; + } + + qDebug() << "password expired check finished"; + return 0; +} + int main(int argc, char *argv[]) { - Q_UNUSED(argc); - Q_UNUSED(argv); + QCoreApplication app(argc, argv); + app.setOrganizationName("deepin"); + app.setApplicationName("dde-login-reminder"); + + // 加载翻译文件 + QTranslator *translator = new QTranslator(&app); + QString locale = QLocale::system().name(); + + // 尝试多个可能的翻译路径 + QStringList translationPaths; + translationPaths << QString(CMAKE_INSTALL_FULL_DATADIR) + "/dde-login-reminder/translations" + << "/usr/share/dde-login-reminder/translations" + << "/usr/local/share/dde-login-reminder/translations"; + + bool loaded = false; + for (const QString &path : translationPaths) { + if (translator->load(QString("dde-login-reminder_%1").arg(locale), path)) { + app.installTranslator(translator); + qDebug() << "Loaded translation for locale:" << locale << "from" << path; + loaded = true; + break; + } + } + + if (!loaded) { + qDebug() << "Failed to load translation for locale:" << locale; + qDebug() << "Tried paths:" << translationPaths; + } registerLoginReminderInfoMetaType(); - return 0; - // return start(); -} + // 检查 org.deepin.dde.Notification1 服务是否已启动 + QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface(); + if (interface->isServiceRegistered("org.deepin.dde.Notification1")) { + // 服务已启动,直接执行 + qDebug() << "Notification service already available, starting immediately"; + int result = start(); + return result; + } + + // 服务未启动,动态监听服务注册 + qDebug() << "Notification service not available, waiting for it to start..."; + QDBusServiceWatcher *watcher = new QDBusServiceWatcher( + "org.deepin.dde.Notification1", + QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForRegistration, + &app + ); + + QObject::connect(watcher, &QDBusServiceWatcher::serviceRegistered, [&app](const QString &serviceName) { + qDebug() << "Notification service registered:" << serviceName; + int result = start(); + app.exit(result); + }); + + return app.exec(); +} \ No newline at end of file diff --git a/tools/dde-login-reminder/translations/dde-login-reminder.ts b/tools/dde-login-reminder/translations/dde-login-reminder.ts new file mode 100644 index 0000000..761e4ff --- /dev/null +++ b/tools/dde-login-reminder/translations/dde-login-reminder.ts @@ -0,0 +1,78 @@ + + + + + QObject + + + Login Reminder + Login Reminder + + + + Your password will expire in %1 days + Your password will expire in %1 days + + + + %1 login failures since the last successful login + %1 login failures since the last successful login + + + + Login time: %1 + Login time: %1 + + + + Last login: %1 + Last login: %1 + + + + Details + Details + + + + + Password Expiration Warning + Password Expiration Warning + + + + Your password has expired. Please change it immediately. + Your password has expired. Please change it immediately. + + + + Your password has expired! + Your password has expired! + + + + For security reasons, please change your password immediately. + For security reasons, please change your password immediately. + + + + Your password will expire in %1 days. Please change it soon. + Your password will expire in %1 days. Please change it soon. + + + + Your password will expire in %1 days. + Your password will expire in %1 days. + + + + Please change your password as soon as possible. + Please change your password as soon as possible. + + + + Change Password + Change Password + + + diff --git a/tools/dde-login-reminder/translations/dde-login-reminder_zh_CN.ts b/tools/dde-login-reminder/translations/dde-login-reminder_zh_CN.ts new file mode 100644 index 0000000..bffcb81 --- /dev/null +++ b/tools/dde-login-reminder/translations/dde-login-reminder_zh_CN.ts @@ -0,0 +1,78 @@ + + + + + QObject + + + Login Reminder + 登录提醒 + + + + Your password will expire in %1 days + 您的密码将在 %1 天后过期 + + + + %1 login failures since the last successful login + 自上次成功登录以来有 %1 次登录失败 + + + + Login time: %1 + 登录时间:%1 + + + + Last login: %1 + 上次登录:%1 + + + + Details + 详情 + + + + + Password Expiration Warning + 密码过期警告 + + + + Your password has expired. Please change it immediately. + 您的密码已过期,请立即修改。 + + + + Your password has expired! + 您的密码已过期! + + + + For security reasons, please change your password immediately. + 出于安全考虑,请立即修改您的密码。 + + + + Your password will expire in %1 days. Please change it soon. + 您的密码将在 %1 天后过期,请尽快修改。 + + + + Your password will expire in %1 days. + 您的密码将在 %1 天后过期。 + + + + Please change your password as soon as possible. + 请尽快修改您的密码。 + + + + Change Password + 修改密码 + + + diff --git a/tools/dde-login-reminder/translations/dde-login-reminder_zh_HK.ts b/tools/dde-login-reminder/translations/dde-login-reminder_zh_HK.ts new file mode 100644 index 0000000..a90bd24 --- /dev/null +++ b/tools/dde-login-reminder/translations/dde-login-reminder_zh_HK.ts @@ -0,0 +1,78 @@ + + + + + QObject + + + Login Reminder + 登入提醒 + + + + Your password will expire in %1 days + 您的密碼將在 %1 天後過期 + + + + %1 login failures since the last successful login + 自上次成功登入以來有 %1 次登入失敗 + + + + Login time: %1 + 登入時間:%1 + + + + Last login: %1 + 上次登入:%1 + + + + Details + 詳情 + + + + + Password Expiration Warning + 密碼過期警告 + + + + Your password has expired. Please change it immediately. + 您的密碼已過期,請立即修改。 + + + + Your password has expired! + 您的密碼已過期! + + + + For security reasons, please change your password immediately. + 出於安全考慮,請立即修改您的密碼。 + + + + Your password will expire in %1 days. Please change it soon. + 您的密碼將在 %1 天後過期,請儘快修改。 + + + + Your password will expire in %1 days. + 您的密碼將在 %1 天後過期。 + + + + Please change your password as soon as possible. + 請儘快修改您的密碼。 + + + + Change Password + 修改密碼 + + + diff --git a/tools/dde-login-reminder/translations/dde-login-reminder_zh_TW.ts b/tools/dde-login-reminder/translations/dde-login-reminder_zh_TW.ts new file mode 100644 index 0000000..b4019bf --- /dev/null +++ b/tools/dde-login-reminder/translations/dde-login-reminder_zh_TW.ts @@ -0,0 +1,78 @@ + + + + + QObject + + + Login Reminder + 登入提醒 + + + + Your password will expire in %1 days + 您的密碼將在 %1 天後過期 + + + + %1 login failures since the last successful login + 自上次成功登入以來有 %1 次登入失敗 + + + + Login time: %1 + 登入時間:%1 + + + + Last login: %1 + 上次登入:%1 + + + + Details + 詳情 + + + + + Password Expiration Warning + 密碼過期警告 + + + + Your password has expired. Please change it immediately. + 您的密碼已過期,請立即修改。 + + + + Your password has expired! + 您的密碼已過期! + + + + For security reasons, please change your password immediately. + 出於安全考慮,請立即修改您的密碼。 + + + + Your password will expire in %1 days. Please change it soon. + 您的密碼將在 %1 天後過期,請儘快修改。 + + + + Your password will expire in %1 days. + 您的密碼將在 %1 天後過期。 + + + + Please change your password as soon as possible. + 請儘快修改您的密碼。 + + + + Change Password + 修改密碼 + + +