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 + 修改密碼 + + +