From 66bf1dc6fa28095ca59ec5c814e2d35d50be294b Mon Sep 17 00:00:00 2001 From: Timmy38 <101416770+Timmy38@users.noreply.github.com> Date: Mon, 8 Jun 2026 11:45:11 +0200 Subject: [PATCH 01/36] =?UTF-8?q?N=C2=B09179=20Fix=20tests=20for=20unatten?= =?UTF-8?q?ded=20install=20backward=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApplicationInstallerSequencerTest.php | 29 ++++++++++++++++--- .../sequencers/DataAuditSequencerTest.php | 16 ++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/tests/php-unit-tests/unitary-tests/setup/sequencers/ApplicationInstallerSequencerTest.php b/tests/php-unit-tests/unitary-tests/setup/sequencers/ApplicationInstallerSequencerTest.php index 5cd4b3bca4..7793a92ecc 100644 --- a/tests/php-unit-tests/unitary-tests/setup/sequencers/ApplicationInstallerSequencerTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/sequencers/ApplicationInstallerSequencerTest.php @@ -439,6 +439,27 @@ class ApplicationInstallerSequencerTest extends ItopTestCase $this->assertEquals($expected, $this->oSequencer->GetStepNames()); } + public function testDefaultOptionalStepsWhenUsingOldParameters() + { + $aAdditionalParams = [ + 'optional_steps' => null, + ]; + $this->GivenApplicationInstallSequencer($aAdditionalParams, true); + + $expected = [ + '', + 'log-parameters', + 'migrate-before', + 'db-schema', + 'migrate-after', + 'after-db-create', + 'load-data', + 'create-config', + 'commit', + ]; + $this->assertEquals($expected, $this->oSequencer->GetStepNames()); + } + public static function WithoutOneStepProvider() { return [ @@ -482,13 +503,13 @@ class ApplicationInstallerSequencerTest extends ItopTestCase public function testGetStepNamesWithOnlyMandatorySteps() { - $this->GivenApplicationInstallSequencer([], true); + $aAdditionalParams = [ + 'optional_steps' => [], + ]; + $this->GivenApplicationInstallSequencer($aAdditionalParams, true); $expected = [ '', - 'log-parameters', - 'migrate-before', 'db-schema', - 'migrate-after', 'after-db-create', 'load-data', 'create-config', diff --git a/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php b/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php index 97dc0bac9a..6f81e0b232 100644 --- a/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/sequencers/DataAuditSequencerTest.php @@ -290,6 +290,22 @@ class DataAuditSequencerTest extends ItopTestCase $this->assertEquals($expected, $oSequencer->GetStepNames()); } + public function testDefaultOptionalStepsWhenUsingOldParameters() + { + $oRunTimeEnvironment = $this->createMock(\RunTimeEnvironment::class); + $oParams = $this->GivenParams(); + $oParams->Set('optional_steps', null); + $oSequencer = new DataAuditSequencer($oParams, $oRunTimeEnvironment); + + $expected = [ + '', + 'copy', + 'compile', + 'complete', + ]; + $this->assertEquals($expected, $oSequencer->GetStepNames()); + } + private function GivenParams(array $aAdditionalParams = []): PHPParameters { $oParams = new PHPParameters(); From 4371ddfbbd85f765abf5d94f89137e7250d346c4 Mon Sep 17 00:00:00 2001 From: Purple Grape Date: Mon, 8 Jun 2026 19:21:12 +0800 Subject: [PATCH 02/36] =?UTF-8?q?N=C2=B09663=20-=20improved=20translation?= =?UTF-8?q?=20for=20zh=5Fcn=20(#913)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...dict.combodo-backoffice-darkmoon-theme.php | 2 +- ...ackoffice-fullmoon-high-contrast-theme.php | 2 +- ...fullmoon-protanopia-deuteranopia-theme.php | 2 +- ...o-backoffice-fullmoon-tritanopia-theme.php | 2 +- .../zh_cn.dict.combodo-db-tools.php | 19 +- .../zh_cn.dict.itop-attachments.php | 56 +- .../dictionaries/zh_cn.dict.itop-backup.php | 17 +- .../zh_cn.dict.itop-bridge-cmdb-services.php | 20 +- .../zh_cn.dict.itop-bridge-cmdb-ticket.php | 19 +- .../zh_cn.dict.itop-change-mgmt-itil.php | 3 +- .../zh_cn.dict.itop-change-mgmt.php | 2 +- .../zh_cn.dict.itop-config-mgmt.php | 584 ++++---- .../dictionaries/zh_cn.dict.itop-config.php | 7 +- .../zh_cn.dict.itop-container-mgmt.php | 173 +++ .../zh_cn.dict.itop-core-update.php | 21 +- .../zh_cn.dict.itop-faq-light.php | 7 +- .../dictionaries/zh_cn.dict.itop-flow-map.php | 91 ++ .../zh_cn.dict.itop-hub-connector.php | 6 + .../zh_cn.dict.itop-incident-mgmt-itil.php | 18 +- .../zh_cn.dict.itop-knownerror-mgmt.php | 15 +- .../zh_cn.dict.itop-oauth-client.php | 6 +- .../zh_cn.dict.itop-portal-base.php | 6 +- .../dictionaries/zh_cn.dict.itop-portal.php | 2 + .../zh_cn.dict.itop-problem-mgmt.php | 4 + .../zh_cn.dict.itop-request-mgmt-itil.php | 20 +- .../zh_cn.dict.itop-request-mgmt.php | 32 +- .../zh_cn.dict.itop-service-mgmt-provider.php | 108 +- .../zh_cn.dict.itop-service-mgmt.php | 136 +- .../zh_cn.dict.itop-storage-mgmt.php | 34 +- .../zh_cn.dict.itop-structure.php | 80 +- .../zh_cn.dict.itop-themes-compat.php | 2 +- .../dictionaries/zh_cn.dict.itop-tickets.php | 175 +-- .../zh_cn.dict.itop-virtualization-mgmt.php | 27 +- .../zh_cn.dictionary.itop.orm-document.php | 5 +- .../zh_cn.dictionary.itop.uniqueness-rule.php | 3 +- .../links/zh_cn.dictionary.itop.links.php | 68 +- .../zh_cn.dictionary.itop.newsroom.php | 16 +- .../zh_cn.dictionary.itop.welcome-popup.php | 53 +- .../zh_cn.dictionary.itop.activity-panel.php | 11 +- .../zh_cn.dictionary.itop.navigation-menu.php | 9 +- .../login/zh_cn.dictionary.itop.login.php | 2 +- ...n.dictionary.itop.notifications-center.php | 22 +- .../zh_cn.dictionary.itop.preferences.php | 71 +- dictionaries/zh_cn.dictionary.itop.core.php | 375 ++--- dictionaries/zh_cn.dictionary.itop.ui.php | 1203 +++++++++-------- 45 files changed, 2040 insertions(+), 1496 deletions(-) create mode 100644 datamodels/2.x/itop-container-mgmt/dictionaries/zh_cn.dict.itop-container-mgmt.php create mode 100644 datamodels/2.x/itop-flow-map/dictionaries/zh_cn.dict.itop-flow-map.php diff --git a/datamodels/2.x/combodo-backoffice-darkmoon-theme/dictionaries/zh_cn.dict.combodo-backoffice-darkmoon-theme.php b/datamodels/2.x/combodo-backoffice-darkmoon-theme/dictionaries/zh_cn.dict.combodo-backoffice-darkmoon-theme.php index 45857d5b93..608d28d354 100644 --- a/datamodels/2.x/combodo-backoffice-darkmoon-theme/dictionaries/zh_cn.dict.combodo-backoffice-darkmoon-theme.php +++ b/datamodels/2.x/combodo-backoffice-darkmoon-theme/dictionaries/zh_cn.dict.combodo-backoffice-darkmoon-theme.php @@ -22,5 +22,5 @@ * along with iTop. If not, see */ Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'theme:darkmoon' => '暗月', + 'theme:darkmoon' => 'Dark moon', ]); diff --git a/datamodels/2.x/combodo-backoffice-fullmoon-high-contrast-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-high-contrast-theme.php b/datamodels/2.x/combodo-backoffice-fullmoon-high-contrast-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-high-contrast-theme.php index de73ad883e..d1325947c3 100644 --- a/datamodels/2.x/combodo-backoffice-fullmoon-high-contrast-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-high-contrast-theme.php +++ b/datamodels/2.x/combodo-backoffice-fullmoon-high-contrast-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-high-contrast-theme.php @@ -23,5 +23,5 @@ */ Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'theme:fullmoon-high-contrast' => 'Fullmoon (High contrast)~~', + 'theme:fullmoon-high-contrast' => 'Fullmoon (高对比度)', ]); diff --git a/datamodels/2.x/combodo-backoffice-fullmoon-protanopia-deuteranopia-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-protanopia-deuteranopia-theme.php b/datamodels/2.x/combodo-backoffice-fullmoon-protanopia-deuteranopia-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-protanopia-deuteranopia-theme.php index 3d59b91f7e..c2d3913c18 100644 --- a/datamodels/2.x/combodo-backoffice-fullmoon-protanopia-deuteranopia-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-protanopia-deuteranopia-theme.php +++ b/datamodels/2.x/combodo-backoffice-fullmoon-protanopia-deuteranopia-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-protanopia-deuteranopia-theme.php @@ -23,5 +23,5 @@ */ Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'theme:fullmoon-protanopia-deuteranopia' => 'Fullmoon (Protanopia & Deuteranopia)~~', + 'theme:fullmoon-protanopia-deuteranopia' => 'Fullmoon (红绿色盲 & 绿色色盲)', ]); diff --git a/datamodels/2.x/combodo-backoffice-fullmoon-tritanopia-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-tritanopia-theme.php b/datamodels/2.x/combodo-backoffice-fullmoon-tritanopia-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-tritanopia-theme.php index 1075b9c9f8..16ad899878 100644 --- a/datamodels/2.x/combodo-backoffice-fullmoon-tritanopia-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-tritanopia-theme.php +++ b/datamodels/2.x/combodo-backoffice-fullmoon-tritanopia-theme/dictionaries/zh_cn.dict.combodo-backoffice-fullmoon-tritanopia-theme.php @@ -23,5 +23,5 @@ */ Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'theme:fullmoon-tritanopia' => 'Fullmoon (Tritanopia)~~', + 'theme:fullmoon-tritanopia' => 'Fullmoon (蓝黄色盲)', ]); diff --git a/datamodels/2.x/combodo-db-tools/dictionaries/zh_cn.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/dictionaries/zh_cn.dict.combodo-db-tools.php index 25efc74abe..4726e700fa 100644 --- a/datamodels/2.x/combodo-db-tools/dictionaries/zh_cn.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/dictionaries/zh_cn.dict.combodo-db-tools.php @@ -4,7 +4,7 @@ * Localized data * * @copyright Copyright (C) 2010-2024 Combodo SAS - * @license https://opensource.org/licenses/AGPL-3.0 + * @license http://opensource.org/licenses/AGPL-3.0 * * This file is part of iTop. * @@ -21,14 +21,15 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + // Database inconsistencies Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dictionary entries go here - 'Menu:DBToolsMenu' => '数据库工具', + 'Menu:DBToolsMenu' => '数据库完整性', 'DBTools:Class' => '类型', - 'DBTools:Title' => '数据库维护工具', + 'DBTools:Title' => '数据库完整性检查', 'DBTools:ErrorsFound' => '发现错误', - 'DBTools:Indication' => '重要: 修复数据库错误后, 可能会出现新的不一致, 您必须重新运行一次分析.', + 'DBTools:Indication' => '重要提示: 修复数据库错误后, 可能会出现新的不一致, 您必须重新运行一次分析.', 'DBTools:Disclaimer' => '免责申明: 在应用修复之前, 应先备份数据库', 'DBTools:Error' => '错误', 'DBTools:Count' => '个数', @@ -42,17 +43,20 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'DBTools:IntegrityCheck' => '完整性检查', 'DBTools:FetchCheck' => '提取检查 (耗时长)', 'DBTools:SelectAnalysisType' => '请选择分析类型', + 'DBTools:Analyze' => '分析', 'DBTools:Details' => '显示详情', 'DBTools:ShowAll' => '显示所有错误', + 'DBTools:Inconsistencies' => '数据库不一致', 'DBTools:DetailedErrorTitle' => '%2$s个错误在类型%1$s: %3$s', 'DBTools:DetailedErrorLimit' => '列表限制为%1$s错误', + 'DBAnalyzer-Integrity-OrphanRecord' => '孤立记录位于 `%1$s`, 其应该有副本位于表 `%2$s`', 'DBAnalyzer-Integrity-InvalidExtKey' => '无效的外键%1$s (列: `%2$s.%3$s`)', 'DBAnalyzer-Integrity-MissingExtKey' => '外键丢失%1$s (列: `%2$s.%3$s`)', 'DBAnalyzer-Integrity-InvalidValue' => '无效的值%1$s (列: `%2$s.%3$s`)', - 'DBAnalyzer-Integrity-UsersWithoutProfile' => '一些用户账号没有角色', + 'DBAnalyzer-Integrity-UsersWithoutProfile' => '有的账号没有角色', 'DBAnalyzer-Integrity-HKInvalid' => '损坏的层级链 `%1$s`', 'DBAnalyzer-Fetch-Count-Error' => '读取计数出错于 `%1$s`, %2$d个记录已读取 / %3$d已', 'DBAnalyzer-Integrity-FinalClass' => '字段 `%2$s`.`%1$s` 必须是相同的值, 而不是 `%3$s`.`%1$s`', @@ -70,19 +74,24 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'DBTools:LostAttachments' => '附件缺失', 'DBTools:LostAttachments:Disclaimer' => '可以在此搜索数据库中丢失或错放的附件. 这不是数据恢复工具, 其无法恢复已删除的数据.', + 'DBTools:LostAttachments:Button:Analyze' => '分析', 'DBTools:LostAttachments:Button:Restore' => '还原', 'DBTools:LostAttachments:Button:Restore:Confirm' => '此操作无法回退, 请确认是否继续还原.', 'DBTools:LostAttachments:Button:Busy' => '请稍候...', + 'DBTools:LostAttachments:Step:Analyze' => '首先, 通过分析数据库来搜索丢失或误挪动的附件.', + 'DBTools:LostAttachments:Step:AnalyzeResults' => '分析结果:', 'DBTools:LostAttachments:Step:AnalyzeResults:None' => '非常好! 所有附件都是正常的.', 'DBTools:LostAttachments:Step:AnalyzeResults:Some' => '某些附件 (%1$d) 看起来放错了位置. 请检查下面的列表并选择要挪动的文件.', 'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => '文件名', 'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => '当前位置', 'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => '移动到...', + 'DBTools:LostAttachments:Step:RestoreResults' => '还原结果:', 'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d的附件被还原.', + 'DBTools:LostAttachments:StoredAsInlineImage' => '存储为内嵌图像', 'DBTools:LostAttachments:History' => '附件 "%1$s" 已使用数据库工具还原', ]); diff --git a/datamodels/2.x/itop-attachments/dictionaries/zh_cn.dict.itop-attachments.php b/datamodels/2.x/itop-attachments/dictionaries/zh_cn.dict.itop-attachments.php index 20bb894679..9368be4466 100644 --- a/datamodels/2.x/itop-attachments/dictionaries/zh_cn.dict.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/dictionaries/zh_cn.dict.itop-attachments.php @@ -1,15 +1,23 @@ '附件 (%1$d)', 'Attachments:EmptyTabTitle' => '附件', @@ -39,10 +47,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Attachment' => '附件', - 'Class:Attachment+' => '', + 'Class:Attachment+' => '文件: 链接到单一对象的文本或图片. 它无法被修改,只能被删除. 附件无法在编辑界面之外创建.', 'Class:Attachment/Attribute:expire' => '过期', 'Class:Attachment/Attribute:expire+' => '', - 'Class:Attachment/Attribute:temp_id' => '临时编号', + 'Class:Attachment/Attribute:temp_id' => '临时id', 'Class:Attachment/Attribute:temp_id+' => '', 'Class:Attachment/Attribute:item_class' => '项目类型', 'Class:Attachment/Attribute:item_class+' => '', @@ -69,11 +77,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Attachment/Attribute:creation_date' => '创建日期', - 'Class:Attachment/Attribute:creation_date+' => '~~', - 'Class:Attachment/Attribute:user_id' => '用户编号', - 'Class:Attachment/Attribute:user_id+' => '~~', - 'Class:Attachment/Attribute:contact_id' => '联系人编号', - 'Class:Attachment/Attribute:contact_id+' => '~~', + 'Class:Attachment/Attribute:creation_date+' => '', + 'Class:Attachment/Attribute:user_id' => '用户id', + 'Class:Attachment/Attribute:user_id+' => '', + 'Class:Attachment/Attribute:contact_id' => '联系人id', + 'Class:Attachment/Attribute:contact_id+' => '', ]); // @@ -81,15 +89,15 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:TriggerOnAttachmentDownload' => '触发器 (于对象附件下载时)', - 'Class:TriggerOnAttachmentDownload+' => '触发器于指定类型 [子类型] 对象附件下载时', - 'Class:TriggerOnAttachmentCreate' => 'Trigger (on object\'s attachment creation)~~', - 'Class:TriggerOnAttachmentCreate+' => 'Trigger on object\'s attachment creation~~', - 'Class:TriggerOnAttachmentCreate/Attribute:file_in_email' => 'Add file in email~~', - 'Class:TriggerOnAttachmentCreate/Attribute:file_in_email+' => 'If checked, the file will be automatically attached to the email when an email action is triggered~~', - 'Class:TriggerOnAttachmentDelete' => 'Trigger (on object\'s attachment deletion)~~', - 'Class:TriggerOnAttachmentDelete+' => 'Trigger on object\'s attachment deletion~~', - 'Class:TriggerOnAttachmentDelete/Attribute:file_in_email' => 'Add deleted file in email~~', - 'Class:TriggerOnAttachmentDelete/Attribute:file_in_email+' => 'If checked, the deleted file will be automatically attached to the email when an email action is triggered~~', - 'Class:TriggerOnObject:TriggerClassAttachment/ReadOnlyMessage' => 'Trigger on object is not allowed on class Attachment. Please use specific trigger~~', + 'Class:TriggerOnAttachmentDownload' => '触发器 (对象附件被下载时)', + 'Class:TriggerOnAttachmentDownload+' => '触发器基于指定类型 [或子类型] 对象附件被下载时', + 'Class:TriggerOnAttachmentCreate' => '触发器 (对象附件被创建时)', + 'Class:TriggerOnAttachmentCreate+' => '触发器 (对象附件被创建时)', + 'Class:TriggerOnAttachmentDelete' => '触发器 (对象附件被删除时)', + 'Class:TriggerOnAttachmentDelete+' => '触发器 (对象附件被删除时)', + 'Class:TriggerOnAttachmentCreate/Attribute:file_in_email' => '在邮件里添加文件', + 'Class:TriggerOnAttachmentCreate/Attribute:file_in_email+' => '如果勾选,文件将在触发邮件操作时自动附加到邮件中', + 'Class:TriggerOnAttachmentDelete/Attribute:file_in_email' => '在邮件里添加已删除的文件', + 'Class:TriggerOnAttachmentDelete/Attribute:file_in_email+' => '如果勾选,已删除的文件将在触发邮件操作时自动附加到邮件中', + 'Class:TriggerOnObject:TriggerClassAttachment/ReadOnlyMessage' => '此触发器不允许用于附件. 请使用特定的触发器', ]); diff --git a/datamodels/2.x/itop-backup/dictionaries/zh_cn.dict.itop-backup.php b/datamodels/2.x/itop-backup/dictionaries/zh_cn.dict.itop-backup.php index 8837151e60..3b982705b7 100644 --- a/datamodels/2.x/itop-backup/dictionaries/zh_cn.dict.itop-backup.php +++ b/datamodels/2.x/itop-backup/dictionaries/zh_cn.dict.itop-backup.php @@ -21,25 +21,28 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'bkp-backup-running' => '备份正在进行, 请稍候...', 'bkp-restore-running' => '还原正在进行, 请稍等...', - 'Menu:BackupStatus' => '定时备份', - 'bkp-status-title' => '定时备份', + + 'Menu:BackupStatus' => '备份', + 'bkp-status-title' => '备份', 'bkp-status-checks' => '设置与检查', 'bkp-mysqldump-ok' => '已找到 mysqldump : %1$s', - 'bkp-mysqldump-notfound' => 'mysqldump找不到: %1$s - 请确认它安装在正确的路径, 或者调整'.ITOP_APPLICATION_SHORT.'配置文件的参数mysql_bindir.', - 'bkp-mysqldump-issue' => 'mysqldump无法运行 (retcode=%1$d): 请确认它安装在正确的路径, 或者调整'.ITOP_APPLICATION_SHORT.'配置文件的参数mysql_bindir', + 'bkp-mysqldump-notfound' => 'mysqldump 找不到: %1$s - 请确认它安装在正确的路径, 或者调整'.ITOP_APPLICATION_SHORT.'配置文件的参数 mysql_bindir.', + 'bkp-mysqldump-issue' => 'mysqldump 无法运行 (retcode=%1$d): 请确认它安装在正确的路径, 或者调整'.ITOP_APPLICATION_SHORT.'配置文件的参数 mysql_bindir', 'bkp-missing-dir' => '目标目录%1$s找不到', 'bkp-free-disk-space' => '%1$s可用空间位于%2$s', 'bkp-dir-not-writeable' => '%1$s没有写入权限', 'bkp-wrong-format-spec' => '当前文件名格式错误 (%1$s). 默认格式应该是: %2$s', 'bkp-name-sample' => '备份文件将以数据库名, 日期和时间进行命名. 例如: %1$s', 'bkp-week-days' => '在每个%1$s的%2$s进行备份', - 'bkp-retention' => '最多%1$d份备份文件在目标目录.', + 'bkp-retention' => '在目标目录中最多保留%1$d份备份文件.', 'bkp-next-to-delete' => '当下一次备份时将被删除 (参阅设置 "retention_count")', 'bkp-table-file' => '文件', - 'bkp-table-file+' => '只有扩展名是.zip的文件才被认为是备份文件', + 'bkp-table-file+' => '只有扩展名是.zip的文件才被认为是有效的备份文件', 'bkp-table-size' => '大小', 'bkp-table-size+' => '', 'bkp-table-actions' => '操作', @@ -47,7 +50,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'bkp-status-backups-auto' => '定时备份', 'bkp-status-backups-manual' => '手动备份', 'bkp-status-backups-none' => '尚未开始备份', - 'bkp-next-backup' => '下一次备份将发生在%1$s (%2$s) 的%3$s', + 'bkp-next-backup' => '下一次备份即将发生在%1$s (%2$s) 的%3$s', 'bkp-next-backup-unknown' => '下一次备份尚未被计划.', 'bkp-button-backup-now' => '立即备份!', 'bkp-button-restore-now' => '还原!', diff --git a/datamodels/2.x/itop-bridge-cmdb-services/dictionaries/zh_cn.dict.itop-bridge-cmdb-services.php b/datamodels/2.x/itop-bridge-cmdb-services/dictionaries/zh_cn.dict.itop-bridge-cmdb-services.php index 7f04afd1a0..2d0935f517 100644 --- a/datamodels/2.x/itop-bridge-cmdb-services/dictionaries/zh_cn.dict.itop-bridge-cmdb-services.php +++ b/datamodels/2.x/itop-bridge-cmdb-services/dictionaries/zh_cn.dict.itop-bridge-cmdb-services.php @@ -17,10 +17,11 @@ // You should have received a copy of the GNU Affero General Public License // along with iTop. If not, see /** - * @copyright Copyright (C) 2010-2024 Combodo SAS - * @license https://opensource.org/licenses/AGPL-3.0 - * @author Benjamin Planque +* @author Benjamin Planque +* @copyright Copyright (C) 2010-2024 Combodo SAS +* @license http://opensource.org/licenses/AGPL-3.0 */ + ////////////////////////////////////////////////////////////////////// // Note: The classes have been grouped by categories: bizmodel ////////////////////////////////////////////////////////////////////// @@ -31,8 +32,9 @@ // // Class: lnkFunctionalCIToProviderContract // + Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkFunctionalCIToProviderContract' => '关联功能配置项/供应商合同', + 'Class:lnkFunctionalCIToProviderContract' => '链接 功能配置项/供应商合同', 'Class:lnkFunctionalCIToProviderContract+' => '', 'Class:lnkFunctionalCIToProviderContract/Name' => '%1$s / %2$s', 'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_id' => '供应商合同', @@ -50,9 +52,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkFunctionalCIToService' => '关联 功能配置项/服务', - 'Class:lnkFunctionalCIToService+' => '', - 'Class:lnkFunctionalCIToService/Name' => '%1$s / %2$s~~', + 'Class:lnkFunctionalCIToService' => '链接 功能配置项/服务', + 'Class:lnkFunctionalCIToService+' => 'This link models the functional CIs that are required to deliver a Service and whose malfunction would affect the quality of the Service.~~', + 'Class:lnkFunctionalCIToService/Name' => '%1$s / %2$s', 'Class:lnkFunctionalCIToService/Attribute:service_id' => '服务', 'Class:lnkFunctionalCIToService/Attribute:service_id+' => '', 'Class:lnkFunctionalCIToService/Attribute:service_name' => '服务名称', @@ -80,7 +82,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Document/Attribute:contracts_list' => '合同', - 'Class:Document/Attribute:contracts_list+' => '此文档关联的所有合同', + 'Class:Document/Attribute:contracts_list+' => '此文档相关的所有合同', 'Class:Document/Attribute:services_list' => '服务', - 'Class:Document/Attribute:services_list+' => '此文档关联的所有服务', + 'Class:Document/Attribute:services_list+' => '此文档相关的所有服务', ]); diff --git a/datamodels/2.x/itop-bridge-cmdb-ticket/dictionaries/zh_cn.dict.itop-bridge-cmdb-ticket.php b/datamodels/2.x/itop-bridge-cmdb-ticket/dictionaries/zh_cn.dict.itop-bridge-cmdb-ticket.php index 5ec96a13f3..6f062cf60c 100644 --- a/datamodels/2.x/itop-bridge-cmdb-ticket/dictionaries/zh_cn.dict.itop-bridge-cmdb-ticket.php +++ b/datamodels/2.x/itop-bridge-cmdb-ticket/dictionaries/zh_cn.dict.itop-bridge-cmdb-ticket.php @@ -17,10 +17,11 @@ // You should have received a copy of the GNU Affero General Public License // along with iTop. If not, see /** - * @copyright Copyright (C) 2010-2024 Combodo SAS - * @license https://opensource.org/licenses/AGPL-3.0 - * @author Benjamin Planque - */ +* @author Benjamin Planque +* @copyright Copyright (C) 2010-2024 Combodo SAS +* @license http://opensource.org/licenses/AGPL-3.0 +*/ + ////////////////////////////////////////////////////////////////////// // Note: The classes have been grouped by categories: bizmodel ////////////////////////////////////////////////////////////////////// @@ -28,19 +29,21 @@ // Classes in 'bizmodel' ////////////////////////////////////////////////////////////////////// // + // // Class: lnkFunctionalCIToTicket // + Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkFunctionalCIToTicket' => '关联 功能配置项/工单', - 'Class:lnkFunctionalCIToTicket+' => '', + 'Class:lnkFunctionalCIToTicket' => '链接 功能配置项/工单', + 'Class:lnkFunctionalCIToTicket+' => 'This link models the functional CIs that are concerned by a Ticket (such as a User Request, an Incident, a Change, ...)~~', 'Class:lnkFunctionalCIToTicket/Name' => '%1$s / %2$s', 'Class:lnkFunctionalCIToTicket/Attribute:ticket_id' => '工单', 'Class:lnkFunctionalCIToTicket/Attribute:ticket_id+' => '', 'Class:lnkFunctionalCIToTicket/Attribute:ticket_ref' => '工单编号', 'Class:lnkFunctionalCIToTicket/Attribute:ticket_ref+' => '', 'Class:lnkFunctionalCIToTicket/Attribute:ticket_title' => '工单标题', - 'Class:lnkFunctionalCIToTicket/Attribute:ticket_title+' => '~~', + 'Class:lnkFunctionalCIToTicket/Attribute:ticket_title+' => '', 'Class:lnkFunctionalCIToTicket/Attribute:functionalci_id' => '配置项', 'Class:lnkFunctionalCIToTicket/Attribute:functionalci_id+' => '', 'Class:lnkFunctionalCIToTicket/Attribute:functionalci_name' => '配置项名称', @@ -50,7 +53,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkFunctionalCIToTicket/Attribute:impact_code' => '影响', 'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:manual' => '手动添加', 'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:computed' => '自动添加', - 'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:not_impacted' => '不通知', + 'Class:lnkFunctionalCIToTicket/Attribute:impact_code/Value:not_impacted' => '未被影响', ]); // diff --git a/datamodels/2.x/itop-change-mgmt-itil/dictionaries/zh_cn.dict.itop-change-mgmt-itil.php b/datamodels/2.x/itop-change-mgmt-itil/dictionaries/zh_cn.dict.itop-change-mgmt-itil.php index 4e1c7a2012..6e7283ba2e 100644 --- a/datamodels/2.x/itop-change-mgmt-itil/dictionaries/zh_cn.dict.itop-change-mgmt-itil.php +++ b/datamodels/2.x/itop-change-mgmt-itil/dictionaries/zh_cn.dict.itop-change-mgmt-itil.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:ChangeManagement' => '变更管理', 'Menu:Change:Overview' => '概况', @@ -125,7 +126,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Change/Attribute:parent_name' => '变更编号', 'Class:Change/Attribute:parent_name+' => '', 'Class:Change/Attribute:related_request_list' => '相关需求', - 'Class:Change/Attribute:related_request_list+' => '此变更相关的所有用户需求', + 'Class:Change/Attribute:related_request_list+' => '此变更相关的所有需求', 'Class:Change/Attribute:related_problems_list' => '相关问题', 'Class:Change/Attribute:related_problems_list+' => '此变更相关的所有问题', 'Class:Change/Attribute:related_incident_list' => '相关事件', diff --git a/datamodels/2.x/itop-change-mgmt/dictionaries/zh_cn.dict.itop-change-mgmt.php b/datamodels/2.x/itop-change-mgmt/dictionaries/zh_cn.dict.itop-change-mgmt.php index 7422b88832..894f6b51f2 100644 --- a/datamodels/2.x/itop-change-mgmt/dictionaries/zh_cn.dict.itop-change-mgmt.php +++ b/datamodels/2.x/itop-change-mgmt/dictionaries/zh_cn.dict.itop-change-mgmt.php @@ -109,7 +109,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Change/Attribute:fallback_plan' => '回滚计划', 'Class:Change/Attribute:fallback_plan+' => '', 'Class:Change/Attribute:related_request_list' => '相关需求', - 'Class:Change/Attribute:related_request_list+' => '此变更相关的所有用户需求', + 'Class:Change/Attribute:related_request_list+' => '此变更相关的所有需求', 'Class:Change/Attribute:related_incident_list' => '相关事件', 'Class:Change/Attribute:related_incident_list+' => '此变更相关的所有事件', 'Class:Change/Attribute:related_problems_list' => '相关问题', diff --git a/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php b/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php index ab8fb2b4a9..a3874dd015 100644 --- a/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php +++ b/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php @@ -4,7 +4,7 @@ * Localized data * * @copyright Copyright (C) 2010-2024 Combodo SAS - * @license https://opensource.org/licenses/AGPL-3.0 + * @license http://opensource.org/licenses/AGPL-3.0 * * This file is part of iTop. * @@ -21,23 +21,25 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + ////////////////////////////////////////////////////////////////////// // Relations ////////////////////////////////////////////////////////////////////// // + Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Relation:impacts/Description' => '被影响的元素', - 'Relation:impacts/DownStream' => '影响...', - 'Relation:impacts/DownStream+' => '被影响的元素', - 'Relation:impacts/UpStream' => '依赖于...', - 'Relation:impacts/UpStream+' => '此元素依赖的元素...', + 'Relation:impacts/Description' => '被影响的元素', + 'Relation:impacts/DownStream' => '影响...', + 'Relation:impacts/DownStream+' => '被影响的元素', + 'Relation:impacts/UpStream' => '依赖于...', + 'Relation:impacts/UpStream+' => '此元素依赖的元素...', // Legacy entries 'Relation:depends on/Description' => '此元素依赖的元素...', - 'Relation:depends on/DownStream' => '依赖于...', - 'Relation:depends on/UpStream' => '影响...', - 'Relation:impacts/LoadData' => '加载数据', - 'Relation:impacts/NoFilteredData' => 'please select objects and load data~~', - 'Relation:impacts/FilteredData' => 'Filtered data~~', + 'Relation:depends on/DownStream' => '依赖于...', + 'Relation:depends on/UpStream' => '影响...', + 'Relation:impacts/LoadData' => '加载数据', + 'Relation:impacts/NoFilteredData' => '请选择对象并加载数据', + 'Relation:impacts/FilteredData' => '已筛选的数据', ]); // Dictionnay conventions @@ -55,26 +57,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ ////////////////////////////////////////////////////////////////////// // Classes in 'bizmodel' ////////////////////////////////////////////////////////////////////// -// -// Dictionnay conventions -// Class: -// Class:+ -// Class:/Attribute: -// Class:/Attribute:+ -// Class:/Attribute:/Value: -// Class:/Attribute:/Value:+ -// Class:/Stimulus: -// Class:/Stimulus:+ -// Class:/UniquenessRule: -// Class:/UniquenessRule:+ - -////////////////////////////////////////////////////////////////////// -// Note: The classes have been grouped by categories: bizmodel -////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////// -// Classes in 'bizmodel' -////////////////////////////////////////////////////////////////////// // // @@ -82,12 +65,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkContactToFunctionalCI' => '关联联系人/功能项', - 'Class:lnkContactToFunctionalCI+' => '', + 'Class:lnkContactToFunctionalCI' => '链接 联系人/功能配置项', + 'Class:lnkContactToFunctionalCI+' => '管理联系人与功能配置项的链接. 它可以是一个团队的职责范围, 分配工单, 或者将特定设备(如PC或电话)分配给对应的人员, 以管理资产.', 'Class:lnkContactToFunctionalCI/Name' => '%1$s / %2$s', - 'Class:lnkContactToFunctionalCI/Attribute:functionalci_id' => '功能项', + 'Class:lnkContactToFunctionalCI/Attribute:functionalci_id' => '功能配置项', 'Class:lnkContactToFunctionalCI/Attribute:functionalci_id+' => '', - 'Class:lnkContactToFunctionalCI/Attribute:functionalci_name' => '功能项名称', + 'Class:lnkContactToFunctionalCI/Attribute:functionalci_name' => '功能配置项名称', 'Class:lnkContactToFunctionalCI/Attribute:functionalci_name+' => '', 'Class:lnkContactToFunctionalCI/Attribute:contact_id' => '联系人', 'Class:lnkContactToFunctionalCI/Attribute:contact_id+' => '', @@ -101,7 +84,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:FunctionalCI' => '功能配置项', - 'Class:FunctionalCI+' => '', + 'Class:FunctionalCI+' => '用于CMDB的抽象类, 用于分组大多数配置项类型.', 'Class:FunctionalCI/Attribute:name' => '名称', 'Class:FunctionalCI/Attribute:name+' => '', 'Class:FunctionalCI/Attribute:description' => '描述', @@ -110,7 +93,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:FunctionalCI/Attribute:org_id+' => '', 'Class:FunctionalCI/Attribute:organization_name' => '组织名称', 'Class:FunctionalCI/Attribute:organization_name+' => '通用名', - 'Class:FunctionalCI/Attribute:business_criticity' => '业务级别', + 'Class:FunctionalCI/Attribute:business_criticity' => '业务关键性', 'Class:FunctionalCI/Attribute:business_criticity+' => '', 'Class:FunctionalCI/Attribute:business_criticity/Value:high' => '高', 'Class:FunctionalCI/Attribute:business_criticity/Value:high+' => '高', @@ -120,10 +103,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:FunctionalCI/Attribute:business_criticity/Value:medium+' => '中', 'Class:FunctionalCI/Attribute:move2production' => '投产日期', 'Class:FunctionalCI/Attribute:move2production+' => '', + 'Class:FunctionalCI/Attribute:groups_list' => '分组', + 'Class:FunctionalCI/Attribute:groups_list+' => '分组可用作标记, 用于记录环境、项目 (迁移、升级、安全) 等等…', 'Class:FunctionalCI/Attribute:contacts_list' => '联系人', 'Class:FunctionalCI/Attribute:contacts_list+' => '此配置项的所有联系人', 'Class:FunctionalCI/Attribute:documents_list' => '文档', - 'Class:FunctionalCI/Attribute:documents_list+' => '此配置项关联的所有文档', + 'Class:FunctionalCI/Attribute:documents_list+' => '此配置项相关的所有文档', 'Class:FunctionalCI/Attribute:applicationsolution_list' => '应用方案', 'Class:FunctionalCI/Attribute:applicationsolution_list+' => '此配置项依赖的所有应用方案', 'Class:FunctionalCI/Attribute:softwares_list' => '软件', @@ -140,7 +125,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PhysicalDevice' => '物理设备', - 'Class:PhysicalDevice+' => '', + 'Class:PhysicalDevice+' => '物理型配置项的抽象. 物理设备可以被定位. 它通常有品牌和型号.', 'Class:PhysicalDevice/ComplementaryName' => '%1$s - %2$s', 'Class:PhysicalDevice/Attribute:serialnumber' => '序列号', 'Class:PhysicalDevice/Attribute:serialnumber+' => '', @@ -164,7 +149,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PhysicalDevice/Attribute:brand_name+' => '', 'Class:PhysicalDevice/Attribute:model_id' => '型号', 'Class:PhysicalDevice/Attribute:model_id+' => '', - 'Class:PhysicalDevice/Attribute:model_name' => '型号名称', + 'Class:PhysicalDevice/Attribute:model_end_of_support' => '型号过保日期', + 'Class:PhysicalDevice/Attribute:model_end_of_support+' => '当硬件型号不再被制造商支持时,如果此信息在型号上有所记录.', + 'Class:PhysicalDevice/Attribute:model_name' => '型号名称', 'Class:PhysicalDevice/Attribute:model_name+' => '', 'Class:PhysicalDevice/Attribute:asset_number' => '资产编号', 'Class:PhysicalDevice/Attribute:asset_number+' => '', @@ -180,7 +167,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Rack' => '机架', - 'Class:Rack+' => '', + 'Class:Rack+' => '一种用于安装数据中心设备的开放式框架.', 'Class:Rack/ComplementaryName' => '%1$s - %2$s', 'Class:Rack/Attribute:nb_u' => '机架高度', 'Class:Rack/Attribute:nb_u+' => '', @@ -207,7 +194,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Phone' => '电话', - 'Class:Phone+' => '', + 'Class:Phone+' => '终端用户设备.带线的座机', ]); // @@ -216,7 +203,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:MobilePhone' => '手机', - 'Class:MobilePhone+' => '', + 'Class:MobilePhone+' => '终端用户设备.无线电话', 'Class:MobilePhone/Attribute:imei' => 'IMEI', 'Class:MobilePhone/Attribute:imei+' => '', 'Class:MobilePhone/Attribute:hw_pin' => '硬件 PIN 码', @@ -229,7 +216,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:IPPhone' => 'IP 电话', - 'Class:IPPhone+' => '', + 'Class:IPPhone+' => '用于电话的物理设备,连接到网络', ]); // @@ -238,7 +225,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Tablet' => '平板', - 'Class:Tablet+' => '', + 'Class:Tablet+' => '终端用户设备.例如 iPad, Galaxy Note/Tab Nexus, Kindle...', ]); // @@ -247,10 +234,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ConnectableCI' => '可连接的配置项', - 'Class:ConnectableCI+' => '物理配置项', + 'Class:ConnectableCI+' => '可联网的物理设备', 'Class:ConnectableCI/ComplementaryName' => '%1$s - %2$s', 'Class:ConnectableCI/Attribute:networkdevice_list' => '网络设备', - 'Class:ConnectableCI/Attribute:networkdevice_list+' => '所有连接到这台设备的网络设备', + 'Class:ConnectableCI/Attribute:networkdevice_list+' => '连接到这台设备的所有网络设备', 'Class:ConnectableCI/Attribute:physicalinterface_list' => '网卡', 'Class:ConnectableCI/Attribute:physicalinterface_list+' => '所有物理网卡', ]); @@ -261,7 +248,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DatacenterDevice' => '数据中心设备', - 'Class:DatacenterDevice+' => '', + 'Class:DatacenterDevice+' => '安装在数据中心的联网物理设备, 通常安装在机架或机柜中. 它包括物理机, 网络设备, 存储系统, SAN 交换机, 磁带库, NAS 设备, 等等.', 'Class:DatacenterDevice/ComplementaryName' => '%1$s - %2$s', 'Class:DatacenterDevice/Attribute:rack_id' => '机架', 'Class:DatacenterDevice/Attribute:rack_id+' => '', @@ -283,10 +270,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DatacenterDevice/Attribute:powerB_id+' => '', 'Class:DatacenterDevice/Attribute:powerB_name' => '备电源名称', 'Class:DatacenterDevice/Attribute:powerB_name+' => '', - 'Class:DatacenterDevice/Attribute:fiberinterfacelist_list' => '光纤接口', + 'Class:DatacenterDevice/Attribute:fiberinterfacelist_list' => '光口', 'Class:DatacenterDevice/Attribute:fiberinterfacelist_list+' => '此设备的所有光纤接口', 'Class:DatacenterDevice/Attribute:san_list' => 'SAN', - 'Class:DatacenterDevice/Attribute:san_list+' => '所有连接到这台设备的SAN交换机', + 'Class:DatacenterDevice/Attribute:san_list+' => '连接到这台设备的所有SAN交换机', 'Class:DatacenterDevice/Attribute:redundancy' => '冗余', 'Class:DatacenterDevice/Attribute:redundancy/count' => '此设备运行正常至少需要一路电源 (主或备)', // Unused yet @@ -300,7 +287,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:NetworkDevice' => '网络设备', - 'Class:NetworkDevice+' => '', + 'Class:NetworkDevice+' => '任何类型的网络设备: 路由器, 交换机, 集线器, 负载均衡器, 防火墙…', 'Class:NetworkDevice/ComplementaryName' => '%1$s - %2$s', 'Class:NetworkDevice/Attribute:networkdevicetype_id' => '网络设备类型', 'Class:NetworkDevice/Attribute:networkdevicetype_id+' => '', @@ -312,6 +299,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:NetworkDevice/Attribute:iosversion_id+' => '', 'Class:NetworkDevice/Attribute:iosversion_name' => 'IOS版本名称', 'Class:NetworkDevice/Attribute:iosversion_name+' => '', + 'Class:NetworkDevice/Attribute:ios_end_of_support' => 'IOS过保时间', + 'Class:NetworkDevice/Attribute:ios_end_of_support+' => '厂家不再为该IOS版本提供修复的时间.', 'Class:NetworkDevice/Attribute:ram' => '内存', 'Class:NetworkDevice/Attribute:ram+' => '', ]); @@ -321,20 +310,22 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:Server' => '服务器', - 'Class:Server+' => '', + 'Class:Server' => '物理机', + 'Class:Server+' => '用于管理集中式物理资源或服务的数据中心设备. 它通常安装了特定的 OS 版本并运行各类软件.', 'Class:Server/ComplementaryName' => '%1$s - %2$s', - 'Class:Server/Attribute:osfamily_id' => '操作系统家族', + 'Class:Server/Attribute:osfamily_id' => 'OS 家族', 'Class:Server/Attribute:osfamily_id+' => '', - 'Class:Server/Attribute:osfamily_name' => '操作系统家族名称', + 'Class:Server/Attribute:osfamily_name' => 'OS 家族名称', 'Class:Server/Attribute:osfamily_name+' => '', - 'Class:Server/Attribute:osversion_id' => '操作系统版本', + 'Class:Server/Attribute:osversion_id' => 'OS 版本', 'Class:Server/Attribute:osversion_id+' => '', - 'Class:Server/Attribute:osversion_name' => '操作系统版本名称', + 'Class:Server/Attribute:osversion_name' => 'OS 版本名称', 'Class:Server/Attribute:osversion_name+' => '', - 'Class:Server/Attribute:oslicence_id' => '操作系统许可证', + 'Class:Server/Attribute:os_end_of_support' => 'OS 过保时间', + 'Class:Server/Attribute:os_end_of_support+' => '厂商不再为该操作系统版本提供补丁的时间.', + 'Class:Server/Attribute:oslicence_id' => 'OS 许可证', 'Class:Server/Attribute:oslicence_id+' => '', - 'Class:Server/Attribute:oslicence_name' => '操作系统许可证名称', + 'Class:Server/Attribute:oslicence_name' => 'OS 许可证名称', 'Class:Server/Attribute:oslicence_name+' => '', 'Class:Server/Attribute:cpu' => 'CPU', 'Class:Server/Attribute:cpu+' => '', @@ -350,7 +341,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:StorageSystem' => '存储系统', - 'Class:StorageSystem+' => '', + 'Class:StorageSystem+' => '存储系统可以使用光纤或以太网连接. 存储系统以逻辑卷为单位进行管理.', 'Class:StorageSystem/ComplementaryName' => '%1$s - %2$s', 'Class:StorageSystem/Attribute:logicalvolume_list' => '逻辑卷', 'Class:StorageSystem/Attribute:logicalvolume_list+' => '此存储系统包含的所有逻辑卷', @@ -362,7 +353,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SANSwitch' => 'SAN交换机', - 'Class:SANSwitch+' => '', + 'Class:SANSwitch+' => 'SAN交换机是指兼容光纤通道协议的网络交换机, 通常用于存储网络. 它是一个数据中心设备.', 'Class:SANSwitch/ComplementaryName' => '%1$s - %2$s', 'Class:SANSwitch/Attribute:datacenterdevice_list' => '设备', 'Class:SANSwitch/Attribute:datacenterdevice_list+' => '连接到此SAN交换机的所有设备', @@ -374,7 +365,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:TapeLibrary' => '磁带库', - 'Class:TapeLibrary+' => '', + 'Class:TapeLibrary+' => '托管了多个磁带(含盒式磁带)的数据中心设备. 磁带库通常用于数据备份或归档.', 'Class:TapeLibrary/ComplementaryName' => '%1$s - %2$s', 'Class:TapeLibrary/Attribute:tapes_list' => '磁带', 'Class:TapeLibrary/Attribute:tapes_list+' => '此磁带库里的所有磁带', @@ -386,7 +377,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:NAS' => 'NAS', - 'Class:NAS+' => '', + 'Class:NAS+' => '数据中心里联网的大容量存储设备. 在 '.ITOP_APPLICATION_SHORT.' 中 NAS (Network-attached storage) 用于托管 NAS 文件系统.', 'Class:NAS/ComplementaryName' => '%1$s - %2$s', 'Class:NAS/Attribute:nasfilesystem_list' => '文件系统', 'Class:NAS/Attribute:nasfilesystem_list+' => '此NAS里的所有文件系统', @@ -398,17 +389,19 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PC' => 'PC', - 'Class:PC+' => '', + 'Class:PC+' => '可连接的配置项. 个人计算机 (PC) 是一种物理设备,可以是台式机或笔记本电脑,安装了操作系统并设计用于运行软件实例.', 'Class:PC/ComplementaryName' => '%1$s - %2$s', - 'Class:PC/Attribute:osfamily_id' => '操作系统家族', + 'Class:PC/Attribute:osfamily_id' => 'OS 家族', 'Class:PC/Attribute:osfamily_id+' => '', - 'Class:PC/Attribute:osfamily_name' => '操作系统家族名称', + 'Class:PC/Attribute:osfamily_name' => 'OS 家族名称', 'Class:PC/Attribute:osfamily_name+' => '', - 'Class:PC/Attribute:osversion_id' => '操作系统版本', + 'Class:PC/Attribute:osversion_id' => 'OS 版本', 'Class:PC/Attribute:osversion_id+' => '', - 'Class:PC/Attribute:osversion_name' => '操作系统版本名称', + 'Class:PC/Attribute:osversion_name' => 'OS 版本名称', 'Class:PC/Attribute:osversion_name+' => '', - 'Class:PC/Attribute:cpu' => 'CPU', + 'Class:PC/Attribute:os_end_of_support' => 'OS 过保时间', + 'Class:PC/Attribute:os_end_of_support+' => '厂商不再为该操作系统版本提供补丁的时间.', + 'Class:PC/Attribute:cpu' => 'CPU', 'Class:PC/Attribute:cpu+' => '', 'Class:PC/Attribute:ram' => '内存', 'Class:PC/Attribute:ram+' => '', @@ -426,7 +419,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Printer' => '打印机', - 'Class:Printer+' => '', + 'Class:Printer+' => '可连接的配置项. 可以联网或连接到PC的物理设备.', 'Class:Printer/ComplementaryName' => '%1$s - %2$s', ]); @@ -436,7 +429,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PowerConnection' => '供电线路', - 'Class:PowerConnection+' => '', + 'Class:PowerConnection+' => '用于电力供应的物理设备的抽象.', 'Class:PowerConnection/ComplementaryName' => '%1$s - %2$s', ]); @@ -446,7 +439,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PowerSource' => '电源', - 'Class:PowerSource+' => '', + 'Class:PowerSource+' => '物理电源连接. 用于记录数据中心的任何类型的电源 (主电源入口, 断路器…) ,但不是 PDU.', 'Class:PowerSource/ComplementaryName' => '%1$s - %2$s', 'Class:PowerSource/Attribute:pdus_list' => 'PDU', 'Class:PowerSource/Attribute:pdus_list+' => '使用此电源的所有 PDU', @@ -458,7 +451,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PDU' => 'PDU', - 'Class:PDU+' => '', + 'Class:PDU+' => '电力供应连接. PDU (Power Distribution Unit) 是一种配备了多个输出的电力分配设备,特别是为数据中心内的服务器机架和网络设备机架供电.', 'Class:PDU/ComplementaryName' => '%1$s - %2$s - %3$s - %4$s', 'Class:PDU/Attribute:rack_id' => '机架', 'Class:PDU/Attribute:rack_id+' => '', @@ -476,7 +469,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Peripheral' => '配件', - 'Class:Peripheral+' => '', + 'Class:Peripheral+' => '物理设备, 用于记录任何类型的计算机外设. +例如: 外部硬盘, 扫描仪, 输入设备 (轨迹球, 条码扫描仪), 等等…', 'Class:Peripheral/ComplementaryName' => '%1$s - %2$s', ]); @@ -486,7 +480,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Enclosure' => '机柜', - 'Class:Enclosure+' => '', + 'Class:Enclosure+' => '一种安装在机架内的封闭式柜子, 用于托管IT设备,如刀片服务器、网络设备...', 'Class:Enclosure/ComplementaryName' => '%1$s - %2$s - %3$s', 'Class:Enclosure/Attribute:rack_id' => '机架', 'Class:Enclosure/Attribute:rack_id+' => '', @@ -504,13 +498,13 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ApplicationSolution' => '应用方案', - 'Class:ApplicationSolution+' => '', + 'Class:ApplicationSolution+' => '应用方案描述了复杂应用是如何由多个基本组件之间组装(或依赖)的. 应用方案的主要信息是其关系列表.', 'Class:ApplicationSolution/Attribute:functionalcis_list' => '配置项', 'Class:ApplicationSolution/Attribute:functionalcis_list+' => '此应用方案包含的所有配置项', 'Class:ApplicationSolution/Attribute:businessprocess_list' => '业务流程', - 'Class:ApplicationSolution/Attribute:businessprocess_list+' => '所有依赖此应用方案的业务流程', - 'Class:ApplicationSolution/Attribute:logo' => 'Logo~~', - 'Class:ApplicationSolution/Attribute:logo+' => 'Used as object icon when displayed within impact analysis graphs~~', + 'Class:ApplicationSolution/Attribute:businessprocess_list+' => '依赖此应用方案的所有业务流程', + 'Class:ApplicationSolution/Attribute:logo' => 'Logo', + 'Class:ApplicationSolution/Attribute:logo+' => '用于在显示影响分析图表时作为对象图标', 'Class:ApplicationSolution/Attribute:status' => '状态', 'Class:ApplicationSolution/Attribute:status+' => '', 'Class:ApplicationSolution/Attribute:status/Value:active' => '启用', @@ -529,11 +523,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:BusinessProcess' => '业务流程', - 'Class:BusinessProcess+' => '', + 'Class:BusinessProcess+' => '业务流程用于描述运营过程中的高级流程或重要应用. 它与应用方案非常类似, 但是为了描述更高层次的应用或整个组织的流程.', 'Class:BusinessProcess/Attribute:applicationsolutions_list' => '应用方案', - 'Class:BusinessProcess/Attribute:applicationsolutions_list+' => '所有影响此业务流程的应用方案', - 'Class:BusinessProcess/Attribute:logo' => 'Logo~~', - 'Class:BusinessProcess/Attribute:logo+' => 'Used as object icon when displayed within impact analysis graphs~~', + 'Class:BusinessProcess/Attribute:applicationsolutions_list+' => '影响此业务流程的所有应用方案', + 'Class:BusinessProcess/Attribute:logo' => 'Logo', + 'Class:BusinessProcess/Attribute:logo+' => '用于在显示影响分析图表时作为对象图标', 'Class:BusinessProcess/Attribute:status' => '状态', 'Class:BusinessProcess/Attribute:status+' => '', 'Class:BusinessProcess/Attribute:status/Value:active' => '启用', @@ -542,22 +536,64 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:BusinessProcess/Attribute:status/Value:inactive+' => '停用', ]); +// +// Class: Software +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:Software' => '软件', + 'Class:Software+' => '软件是软件清单中的一个基本项目. 它有一个特定的版本. 在 '.ITOP_APPLICATION_SHORT.' 中, 软件分为: DB服务器、中间件、PC软件、Web服务器和其它软件.', + 'Class:Software/ComplementaryName' => '%1$s - %2$s', + 'Class:Software/Attribute:name' => '名称', + 'Class:Software/Attribute:name+' => '', + 'Class:Software/Attribute:vendor' => '厂商', + 'Class:Software/Attribute:vendor+' => '', + 'Class:Software/Attribute:version' => '版本', + 'Class:Software/Attribute:version+' => '', + 'Class:Software/Attribute:end_of_support' => '过保日期', + 'Class:Software/Attribute:end_of_support+' => '厂家提供的最后一个支持日期,此后不再提供此软件版本的补丁.', + 'Class:Software/Attribute:documents_list' => '文档', + 'Class:Software/Attribute:documents_list+' => '此软件相关的所有文档', + 'Class:Software/Attribute:logo' => 'Logo', + 'Class:Software/Attribute:logo+' => '用于在显示影响分析图表时作为所有使用了此软件的软件实例对象的图标', + 'Class:Software/Attribute:type' => '类型', + 'Class:Software/Attribute:type+' => '', + 'Class:Software/Attribute:type/Value:DBServer' => 'DB 服务器', + 'Class:Software/Attribute:type/Value:DBServer+' => 'DB 服务器', + 'Class:Software/Attribute:type/Value:Middleware' => '中间件', + 'Class:Software/Attribute:type/Value:Middleware+' => '中间件', + 'Class:Software/Attribute:type/Value:OtherSoftware' => '其它软件', + 'Class:Software/Attribute:type/Value:OtherSoftware+' => '其它软件', + 'Class:Software/Attribute:type/Value:PCSoftware' => 'PC 软件', + 'Class:Software/Attribute:type/Value:PCSoftware+' => 'PC 软件', + 'Class:Software/Attribute:type/Value:WebServer' => 'Web 服务器', + 'Class:Software/Attribute:type/Value:WebServer+' => 'Web 服务器', + 'Class:Software/Attribute:softwareinstance_list' => '软件实例', + 'Class:Software/Attribute:softwareinstance_list+' => '此软件的所有实例', + 'Class:Software/Attribute:softwarepatch_list' => '软件补丁', + 'Class:Software/Attribute:softwarepatch_list+' => '此软件的所有补丁', + 'Class:Software/Attribute:softwarelicence_list' => '软件许可证', + 'Class:Software/Attribute:softwarelicence_list+' => '此软件的所有许可证', +]); + // // Class: SoftwareInstance // Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SoftwareInstance' => '软件实例', - 'Class:SoftwareInstance+' => '', + 'Class:SoftwareInstance+' => '在设备(物理机, PC, 虚拟机)上部署软件的抽象. 在 '.ITOP_APPLICATION_SHORT.' 中, 软件实例分为: DB服务器, 中间件, PC软件, Web服务器和其它软件.', 'Class:SoftwareInstance/Attribute:system_id' => '系统', - 'Class:SoftwareInstance/Attribute:system_id+' => '', + 'Class:SoftwareInstance/Attribute:system_id+' => '系统可以是物理机, 虚拟机, PC, ...', 'Class:SoftwareInstance/Attribute:system_name' => '系统名称', 'Class:SoftwareInstance/Attribute:system_name+' => '', 'Class:SoftwareInstance/Attribute:software_id' => '软件', 'Class:SoftwareInstance/Attribute:software_id+' => '', 'Class:SoftwareInstance/Attribute:software_name' => '软件名称', 'Class:SoftwareInstance/Attribute:software_name+' => '', - 'Class:SoftwareInstance/Attribute:softwarelicence_id' => '软件许可证', + 'Class:SoftwareInstance/Attribute:software_end_of_support' => '软件过保时间', + 'Class:SoftwareInstance/Attribute:software_end_of_support+' => '厂商为此软件版本提供补丁的最后日期.', + 'Class:SoftwareInstance/Attribute:softwarelicence_id' => '软件许可证', 'Class:SoftwareInstance/Attribute:softwarelicence_id+' => '', 'Class:SoftwareInstance/Attribute:softwarelicence_name' => '许可证名称', 'Class:SoftwareInstance/Attribute:softwarelicence_name+' => '', @@ -577,7 +613,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Middleware' => '中间件', - 'Class:Middleware+' => '', + 'Class:Middleware+' => '向其它软件提供服务的软件实例 (例如: Tomcat, JBoss, Talend, Microsoft BizTalk, IBM Websphere 或 Lotus Domino), 通常安装在特定系统(PC, 物理机或虚拟机)上.', 'Class:Middleware/Attribute:middlewareinstance_list' => '中间件实例', 'Class:Middleware/Attribute:middlewareinstance_list+' => '此中间件的所有实例', ]); @@ -587,9 +623,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:DBServer' => '数据库服务器', - 'Class:DBServer+' => '', - 'Class:DBServer/Attribute:dbschema_list' => '数据库', + 'Class:DBServer' => 'DB 服务器', + 'Class:DBServer+' => '提供数据库服务的软件实例 (例如: MySQL 8.0, Oracle, SQL Server, DB2…), 通常安装在特定系统(PC, 物理机或虚拟机)上.', + 'Class:DBServer/Attribute:dbschema_list' => '数据库架构', 'Class:DBServer/Attribute:dbschema_list+' => '此数据库服务器上的所有数据库架构', ]); @@ -598,8 +634,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:WebServer' => 'Web服务器', - 'Class:WebServer+' => '', + 'Class:WebServer' => 'Web 服务器', + 'Class:WebServer+' => '提供网页服务的软件实例 (例如: Apache 2.4, Nginx 1.29.4, IIS 7.0), 通常安装在特定系统(PC, 物理机或虚拟机)上.', 'Class:WebServer/Attribute:webapp_list' => 'Web应用', 'Class:WebServer/Attribute:webapp_list+' => '此web服务器上的所有web应用', ]); @@ -610,7 +646,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PCSoftware' => 'PC 软件', - 'Class:PCSoftware+' => '', + 'Class:PCSoftware+' => '安装在PC上的软件实例 (例如: MS Office, Adobe Photoshop 或 Filezilla).', ]); // @@ -619,7 +655,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:OtherSoftware' => '其它软件', - 'Class:OtherSoftware+' => '', + 'Class:OtherSoftware+' => '不包含在清单之内的任何类型的软件实例: PC软件, 中间件, DB服务器或 Web服务器.', ]); // @@ -628,10 +664,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:MiddlewareInstance' => '中间件实例', - 'Class:MiddlewareInstance+' => '', + 'Class:MiddlewareInstance+' => '由中间件提供服务的功能配置项.', 'Class:MiddlewareInstance/ComplementaryName' => '%1$s - %2$s', - 'Class:MiddlewareInstance/Attribute:logo' => 'Logo~~', - 'Class:MiddlewareInstance/Attribute:logo+' => 'Used as object icon when displayed within impact analysis graphs~~', + 'Class:MiddlewareInstance/Attribute:logo' => 'Logo', + 'Class:MiddlewareInstance/Attribute:logo+' => '用于在影响分析图表中显示为对象图标', 'Class:MiddlewareInstance/Attribute:middleware_id' => '中间件', 'Class:MiddlewareInstance/Attribute:middleware_id+' => '', 'Class:MiddlewareInstance/Attribute:middleware_name' => '名称', @@ -643,10 +679,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:DatabaseSchema' => '数据库', - 'Class:DatabaseSchema+' => '', + 'Class:DatabaseSchema' => '数据库架构', + 'Class:DatabaseSchema+' => 'DB 服务器上运行的逻辑数据库实例.', 'Class:DatabaseSchema/ComplementaryName' => '%1$s - %2$s', - 'Class:DatabaseSchema/Attribute:dbserver_id' => '数据库服务器', + 'Class:DatabaseSchema/Attribute:dbserver_id' => 'DB 服务器', 'Class:DatabaseSchema/Attribute:dbserver_id+' => '', 'Class:DatabaseSchema/Attribute:dbserver_name' => '名称', 'Class:DatabaseSchema/Attribute:dbserver_name+' => '', @@ -658,14 +694,14 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:WebApplication' => 'Web 应用', - 'Class:WebApplication+' => '', + 'Class:WebApplication+' => '可使用网页浏览器访问的 web 应用实例, 它运行在特定的 Web 服务器实例之上. 例如, 您正在使用的这个iTop.', 'Class:WebApplication/ComplementaryName' => '%1$s - %2$s', 'Class:WebApplication/Attribute:webserver_id' => 'Web服务器', 'Class:WebApplication/Attribute:webserver_id+' => '', 'Class:WebApplication/Attribute:webserver_name' => '名称', 'Class:WebApplication/Attribute:webserver_name+' => '', - 'Class:WebApplication/Attribute:logo' => 'Logo~~', - 'Class:WebApplication/Attribute:logo+' => 'Used as object icon when displayed within impact analysis graphs~~', + 'Class:WebApplication/Attribute:logo' => 'Logo', + 'Class:WebApplication/Attribute:logo+' => '用于在影响分析图表中显示为对象图标', 'Class:WebApplication/Attribute:url' => 'URL', 'Class:WebApplication/Attribute:url+' => '', ]); @@ -676,7 +712,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:VirtualDevice' => '虚拟设备', - 'Class:VirtualDevice+' => '', + 'Class:VirtualDevice+' => '用于服务器虚拟化的抽象类 (宿主机和虚拟机).', 'Class:VirtualDevice/Attribute:status' => '状态', 'Class:VirtualDevice/Attribute:status+' => '', 'Class:VirtualDevice/Attribute:status/Value:implementation' => '生效', @@ -697,7 +733,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:VirtualHost' => '宿主机', - 'Class:VirtualHost+' => '', + 'Class:VirtualHost+' => '对虚拟设备(虚拟机监视器, 集群,...)的抽象, 用于托管虚拟机.', 'Class:VirtualHost/Attribute:virtualmachine_list' => '虚拟机', 'Class:VirtualHost/Attribute:virtualmachine_list+' => '此宿主机托管的所有虚拟机', ]); @@ -707,8 +743,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:Hypervisor' => '虚拟机管理器', - 'Class:Hypervisor+' => '', + 'Class:Hypervisor' => 'Hypervisor', + 'Class:Hypervisor+' => '虚拟化主机. 运行虚拟化软件(MS Hyper-V, VMWare ESX, Xen, 等.), 运行在物理服务器上并支持创建虚拟机.', 'Class:Hypervisor/Attribute:farm_id' => '集群', 'Class:Hypervisor/Attribute:farm_id+' => '', 'Class:Hypervisor/Attribute:farm_name' => '名称', @@ -725,13 +761,13 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Farm' => '集群', - 'Class:Farm+' => '', - 'Class:Farm/Attribute:hypervisor_list' => '虚拟机管理器', - 'Class:Farm/Attribute:hypervisor_list+' => '集群由哪些虚拟机管理器组成', + 'Class:Farm+' => '虚拟化的主机. 集群由一组虚拟化监视器组成, 它们通过共享存储资源为托管的虚拟机提供容错能力.', + 'Class:Farm/Attribute:hypervisor_list' => 'Hypervisor', + 'Class:Farm/Attribute:hypervisor_list+' => '集群由哪些 Hypervisor 组成', 'Class:Farm/Attribute:redundancy' => '高可用性', - 'Class:Farm/Attribute:redundancy/disabled' => '所有虚拟机管理器正常, 集群才正常', - 'Class:Farm/Attribute:redundancy/count' => '至少%1$s个虚拟机管理器是正常的, 集群才是正常的', - 'Class:Farm/Attribute:redundancy/percent' => '至少%1$s %%的虚拟机管理器是正常的, 集群才正常', + 'Class:Farm/Attribute:redundancy/disabled' => '所有 Hypervisor 正常, 集群才正常', + 'Class:Farm/Attribute:redundancy/count' => '至少%1$s个 Hypervisor 是正常的, 集群才是正常的', + 'Class:Farm/Attribute:redundancy/percent' => '至少%1$s %%的 Hypervisor 是正常的, 集群才正常', ]); // @@ -740,21 +776,23 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:VirtualMachine' => '虚拟机', - 'Class:VirtualMachine+' => '', + 'Class:VirtualMachine+' => '与物理机类似的虚拟设备,它既可以托管在 Hypervisor 上,也可以托管在集群上.', 'Class:VirtualMachine/ComplementaryName' => '%1$s - %2$s', 'Class:VirtualMachine/Attribute:virtualhost_id' => '宿主机', 'Class:VirtualMachine/Attribute:virtualhost_id+' => '', 'Class:VirtualMachine/Attribute:virtualhost_name' => '名称', 'Class:VirtualMachine/Attribute:virtualhost_name+' => '', - 'Class:VirtualMachine/Attribute:osfamily_id' => '操作系统家族', + 'Class:VirtualMachine/Attribute:osfamily_id' => 'OS 家族', 'Class:VirtualMachine/Attribute:osfamily_id+' => '', 'Class:VirtualMachine/Attribute:osfamily_name' => '名称', 'Class:VirtualMachine/Attribute:osfamily_name+' => '', - 'Class:VirtualMachine/Attribute:osversion_id' => '操作系统版本', + 'Class:VirtualMachine/Attribute:osversion_id' => 'OS 版本', 'Class:VirtualMachine/Attribute:osversion_id+' => '', 'Class:VirtualMachine/Attribute:osversion_name' => '名称', 'Class:VirtualMachine/Attribute:osversion_name+' => '', - 'Class:VirtualMachine/Attribute:oslicence_id' => '操作系统许可证', + 'Class:VirtualMachine/Attribute:os_end_of_support' => 'OS 过保日期', + 'Class:VirtualMachine/Attribute:os_end_of_support+' => '厂商不再支持该操作系统版本时的日期.', + 'Class:VirtualMachine/Attribute:oslicence_id' => 'OS 许可证', 'Class:VirtualMachine/Attribute:oslicence_id+' => '', 'Class:VirtualMachine/Attribute:oslicence_name' => '名称', 'Class:VirtualMachine/Attribute:oslicence_name+' => '', @@ -762,7 +800,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:VirtualMachine/Attribute:cpu+' => '', 'Class:VirtualMachine/Attribute:ram' => '内存', 'Class:VirtualMachine/Attribute:ram+' => '', - 'Class:VirtualMachine/Attribute:managementip' => '管理IP', + 'Class:VirtualMachine/Attribute:managementip' => '管理 IP', 'Class:VirtualMachine/Attribute:managementip+' => '', 'Class:VirtualMachine/Attribute:logicalinterface_list' => '网卡', 'Class:VirtualMachine/Attribute:logicalinterface_list+' => '所有逻辑网卡', @@ -774,7 +812,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:LogicalVolume' => '逻辑卷', - 'Class:LogicalVolume+' => '', + 'Class:LogicalVolume+' => '存储系统里的基本存储单元. 它可以被多个服务器和虚拟设备访问.', 'Class:LogicalVolume/Attribute:name' => '名称', 'Class:LogicalVolume/Attribute:name+' => '', 'Class:LogicalVolume/Attribute:lun_id' => 'LUN ID', @@ -800,8 +838,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkServerToVolume' => '关联服务器/逻辑卷', - 'Class:lnkServerToVolume+' => '', + 'Class:lnkServerToVolume' => '链接 服务器/逻辑卷', + 'Class:lnkServerToVolume+' => '这是多对多的关系, 某台服务器使用了一个存储卷(存储系统中的一个存储单元). 其它服务器也可以使用相同的逻辑卷.', 'Class:lnkServerToVolume/Name' => '%1$s / %2$s', 'Class:lnkServerToVolume/Attribute:volume_id' => '逻辑卷', 'Class:lnkServerToVolume/Attribute:volume_id+' => '', @@ -820,8 +858,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkVirtualDeviceToVolume' => '关联虚拟设备/逻辑卷', - 'Class:lnkVirtualDeviceToVolume+' => '', + 'Class:lnkVirtualDeviceToVolume' => '链接 虚拟设备/逻辑卷', + 'Class:lnkVirtualDeviceToVolume+' => '这是多对多的关系, 某个虚拟设备使用了一个逻辑卷(存储系统中的一个存储单元). 其它虚拟设备也可以使用相同的逻辑卷.', 'Class:lnkVirtualDeviceToVolume/Name' => '%1$s / %2$s', 'Class:lnkVirtualDeviceToVolume/Attribute:volume_id' => '逻辑卷', 'Class:lnkVirtualDeviceToVolume/Attribute:volume_id+' => '', @@ -840,8 +878,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkSanToDatacenterDevice' => '关联 SAN/数据中心设备', - 'Class:lnkSanToDatacenterDevice+' => '', + 'Class:lnkSanToDatacenterDevice' => '链接 SAN交换机/数据中心设备', + 'Class:lnkSanToDatacenterDevice+' => '这是多对多的关系, SAN交换机与数据中心设备(服务器、网络设备等)之间存在的网络连接.', 'Class:lnkSanToDatacenterDevice/Name' => '%1$s / %2$s', 'Class:lnkSanToDatacenterDevice/Attribute:san_id' => 'SAN 交换机', 'Class:lnkSanToDatacenterDevice/Attribute:san_id+' => '', @@ -863,7 +901,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Tape' => '磁带', - 'Class:Tape+' => 'A Tape (or cartridge) within '.ITOP_APPLICATION_SHORT.' is a removable piece of storage part of a Tape Library~~', + 'Class:Tape+' => '磁带(或盒式磁带) 在 ' . ITOP_APPLICATION_SHORT . ' 中是磁带库的一部分,可移除的存储介质.', 'Class:Tape/Attribute:name' => '名称', 'Class:Tape/Attribute:name+' => '', 'Class:Tape/Attribute:description' => '描述', @@ -882,7 +920,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:NASFileSystem' => 'NAS 文件系统', - 'Class:NASFileSystem+' => '', + 'Class:NASFileSystem+' => '表示托管在特定 NAS (Network Attached Storage) 内的共享文件系统.', 'Class:NASFileSystem/Attribute:name' => '名称', 'Class:NASFileSystem/Attribute:name+' => '', 'Class:NASFileSystem/Attribute:description' => '描述', @@ -897,55 +935,17 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:NASFileSystem/Attribute:nas_name+' => '', ]); -// -// Class: Software -// - -Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:Software' => '软件', - 'Class:Software+' => '', - 'Class:Software/ComplementaryName' => '%1$s - %2$s', - 'Class:Software/Attribute:name' => '名称', - 'Class:Software/Attribute:name+' => '', - 'Class:Software/Attribute:vendor' => '厂商', - 'Class:Software/Attribute:vendor+' => '', - 'Class:Software/Attribute:version' => '版本', - 'Class:Software/Attribute:version+' => '', - 'Class:Software/Attribute:documents_list' => '文档', - 'Class:Software/Attribute:documents_list+' => '此软件的所有文档', - 'Class:Software/Attribute:logo' => 'Logo~~', - 'Class:Software/Attribute:logo+' => 'Used as icon for all Software Instance objects using this Software, when displayed within impact analysis graphs~~', - 'Class:Software/Attribute:type' => '类型', - 'Class:Software/Attribute:type+' => '', - 'Class:Software/Attribute:type/Value:DBServer' => '数据库服务器', - 'Class:Software/Attribute:type/Value:DBServer+' => '数据库服务器', - 'Class:Software/Attribute:type/Value:Middleware' => '中间件', - 'Class:Software/Attribute:type/Value:Middleware+' => '中间件', - 'Class:Software/Attribute:type/Value:OtherSoftware' => '其它软件', - 'Class:Software/Attribute:type/Value:OtherSoftware+' => '其它软件', - 'Class:Software/Attribute:type/Value:PCSoftware' => 'PC软件', - 'Class:Software/Attribute:type/Value:PCSoftware+' => 'PC软件', - 'Class:Software/Attribute:type/Value:WebServer' => 'Web服务器', - 'Class:Software/Attribute:type/Value:WebServer+' => 'Web服务器', - 'Class:Software/Attribute:softwareinstance_list' => '软件实例', - 'Class:Software/Attribute:softwareinstance_list+' => '此软件的所有实例', - 'Class:Software/Attribute:softwarepatch_list' => '软件补丁', - 'Class:Software/Attribute:softwarepatch_list+' => '此软件的所有补丁', - 'Class:Software/Attribute:softwarelicence_list' => '软件许可证', - 'Class:Software/Attribute:softwarelicence_list+' => '此软件的所有许可证', -]); - // // Class: Patch // Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Patch' => '补丁', - 'Class:Patch+' => '', + 'Class:Patch+' => '抽象类,用于对系统或软件提供的补丁、热修复、安全修复或软件服务包.', 'Class:Patch/Attribute:name' => '名称', 'Class:Patch/Attribute:name+' => '', 'Class:Patch/Attribute:documents_list' => '文档', - 'Class:Patch/Attribute:documents_list+' => '此补丁关联的所有文档', + 'Class:Patch/Attribute:documents_list+' => '此补丁相关的所有文档', 'Class:Patch/Attribute:description' => '描述', 'Class:Patch/Attribute:description+' => '', 'Class:Patch/Attribute:finalclass' => '补丁类型', @@ -957,13 +957,13 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:OSPatch' => '操作系统补丁', - 'Class:OSPatch+' => '', + 'Class:OSPatch' => 'OS 补丁', + 'Class:OSPatch+' => '针对特定操作系统的补丁、热修复、安全修复或服务包.', 'Class:OSPatch/Attribute:functionalcis_list' => '设备', 'Class:OSPatch/Attribute:functionalcis_list+' => '已安装此补丁的所有系统', - 'Class:OSPatch/Attribute:osversion_id' => '操作系统版本', + 'Class:OSPatch/Attribute:osversion_id' => 'OS 版本', 'Class:OSPatch/Attribute:osversion_id+' => '', - 'Class:OSPatch/Attribute:osfamily_id' => 'OS Family~~', + 'Class:OSPatch/Attribute:osfamily_id' => 'OS 家族', 'Class:OSPatch/Attribute:osfamily_id+' => '', 'Class:OSPatch/Attribute:osversion_name' => '名称', 'Class:OSPatch/Attribute:osversion_name+' => '', @@ -975,7 +975,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SoftwarePatch' => '软件补丁', - 'Class:SoftwarePatch+' => '', + 'Class:SoftwarePatch+' => '针对特定软件的补丁、热修复、安全修复或服务包.', 'Class:SoftwarePatch/Attribute:software_id' => '软件', 'Class:SoftwarePatch/Attribute:software_id+' => '', 'Class:SoftwarePatch/Attribute:software_name' => '名称', @@ -990,11 +990,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Licence' => '许可证', - 'Class:Licence+' => '', + 'Class:Licence+' => '抽象类.针对特定操作系统版本或软件版本的许可证合同', 'Class:Licence/Attribute:name' => '名称', 'Class:Licence/Attribute:name+' => '', 'Class:Licence/Attribute:documents_list' => '文档', - 'Class:Licence/Attribute:documents_list+' => '此许可证关联的所有文档', + 'Class:Licence/Attribute:documents_list+' => '此许可证相关的所有文档', 'Class:Licence/Attribute:org_id' => '组织', 'Class:Licence/Attribute:org_id+' => '', 'Class:Licence/Attribute:organization_name' => '组织名称', @@ -1024,13 +1024,13 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:OSLicence' => '操作系统许可证', - 'Class:OSLicence+' => '', + 'Class:OSLicence' => 'OS 许可证', + 'Class:OSLicence+' => '针对特定操作系统的许可证合同. 该许可证与操作系统相关联 (例如 Windows 2008 R2) 并可以与多个物理机或虚拟机关联.', 'Class:OSLicence/ComplementaryName' => '%1$s - %2$s', - 'Class:OSLicence/Attribute:osversion_id' => '操作系统版本', + 'Class:OSLicence/Attribute:osversion_id' => 'OS版本', 'Class:OSLicence/Attribute:osversion_id+' => '', - 'Class:OSLicence/Attribute:osfamily_id' => 'OS Family~~', - 'Class:OSLicence/Attribute:osfamily_id+' => '~~', + 'Class:OSLicence/Attribute:osfamily_id' => 'OS家族', + 'Class:OSLicence/Attribute:osfamily_id+' => '', 'Class:OSLicence/Attribute:osversion_name' => '名称', 'Class:OSLicence/Attribute:osversion_name+' => '', 'Class:OSLicence/Attribute:virtualmachines_list' => '虚拟机', @@ -1045,7 +1045,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SoftwareLicence' => '软件许可证', - 'Class:SoftwareLicence+' => '', + 'Class:SoftwareLicence+' => '针对特定软件的许可证合同. 该许可证与某个软件相关联 (例如 MS Office 2010) 并可以与该软件的多个实例关联.', 'Class:SoftwareLicence/ComplementaryName' => '%1$s - %2$s', 'Class:SoftwareLicence/Attribute:software_id' => '软件', 'Class:SoftwareLicence/Attribute:software_id+' => '', @@ -1060,8 +1060,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkDocumentToLicence' => '关联文档/许可证', - 'Class:lnkDocumentToLicence+' => '', + 'Class:lnkDocumentToLicence' => '链接 文档/许可证', + 'Class:lnkDocumentToLicence+' => 'Link used when a Document is applicable to a License.~~', 'Class:lnkDocumentToLicence/Name' => '%1$s / %2$s', 'Class:lnkDocumentToLicence/Attribute:licence_id' => '许可证', 'Class:lnkDocumentToLicence/Attribute:licence_id+' => '', @@ -1078,14 +1078,18 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:OSVersion' => '操作系统版本', - 'Class:OSVersion+' => '', - 'Class:OSVersion/Attribute:osfamily_id' => '操作系统家族', + 'Class:OSVersion' => 'OS 版本', + 'Class:OSVersion+' => '分类. 计算机 (物理机, 虚拟机 或 PC) 的 "OS 版本" 对应的可能的值. OS 版本以 OS 家族为单位进行组织.', + 'Class:OSVersion/Attribute:osfamily_id' => 'OS 家族', 'Class:OSVersion/Attribute:osfamily_id+' => '', 'Class:OSVersion/Attribute:osfamily_name' => '名称', 'Class:OSVersion/Attribute:osfamily_name+' => '', - 'Class:OSVersion/UniquenessRule:name_osfamily+' => 'Name must be unique in the OS family~~', - 'Class:OSVersion/UniquenessRule:name_osfamily' => 'this OS version already exists within the OS family~~', + 'Class:OSVersion/Attribute:end_of_support' => '过保日期', + 'Class:OSVersion/Attribute:end_of_support+' => 'The date after which the editor ceases to provide patches for this OS version.~~', + 'Class:OSVersion/Attribute:ospatches_list' => 'OS 补丁', + 'Class:OSVersion/Attribute:ospatches_list+' => 'All the OS patches for this OS version~~', + 'Class:OSVersion/UniquenessRule:name_osfamily+' => 'OS 家族的名称必须唯一', + 'Class:OSVersion/UniquenessRule:name_osfamily' => '此 OS 版本已在 OS 家族中存在~~', ]); // @@ -1093,10 +1097,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:OSFamily' => '操作系统家族', - 'Class:OSFamily+' => '', - 'Class:OSFamily/UniquenessRule:name+' => 'Name must be unique~~', - 'Class:OSFamily/UniquenessRule:name' => 'this OS family already exists~~', + 'Class:OSFamily' => 'OS 家族', + 'Class:OSFamily+' => '分类. 物理机、虚拟机、PC 的 "OS 家族" 属性可能的值列表.', + 'Class:OSFamily/Attribute:osversions_list' => 'OS 版本', + 'Class:OSFamily/Attribute:osversions_list+' => '此 OS 家族的所有 OS 版本', + 'Class:OSFamily/UniquenessRule:name+' => '名称必须唯一', + 'Class:OSFamily/UniquenessRule:name' => '此 OS 家族已存在', ]); // @@ -1105,10 +1111,14 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Brand' => '品牌', - 'Class:Brand+' => '', - 'Class:Brand/Attribute:logo' => 'Logo', + 'Class:Brand+' => '分类. 物理设备 "品牌" 的可能的值列表.', + 'Class:Brand/Attribute:iosversions_list' => 'IOS 版本', + 'Class:Brand/Attribute:iosversions_list+' => '此品牌的所有 IOS 版本', + 'Class:Brand/Attribute:logo' => 'Logo', 'Class:Brand/Attribute:logo+' => '', - 'Class:Brand/Attribute:physicaldevices_list' => '物理设备', + 'Class:Brand/Attribute:models_list' => '型号', + 'Class:Brand/Attribute:models_list+' => '此品牌的所有型号', + 'Class:Brand/Attribute:physicaldevices_list' => '物理设备', 'Class:Brand/Attribute:physicaldevices_list+' => '此品牌的所有物理设备', 'Class:Brand/UniquenessRule:name+' => '名称必须唯一', 'Class:Brand/UniquenessRule:name' => '此品牌已存在', @@ -1119,8 +1129,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:Model' => '型号', - 'Class:Model+' => '', + 'Class:Model' => '设备型号', + 'Class:Model+' => '分类. 物理设备型号属性可能的值列表. 每个型号只属于一个品牌,通常适用于一种单一类别的物理设备.', 'Class:Model/ComplementaryName' => '%1$s - %2$s', 'Class:Model/Attribute:brand_id' => '品牌', 'Class:Model/Attribute:brand_id+' => '', @@ -1130,14 +1140,16 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Model/Attribute:picture+' => '', 'Class:Model/Attribute:type' => '设备类型', 'Class:Model/Attribute:type+' => '', - 'Class:Model/Attribute:type/Value:PowerSource' => '电源', + 'Class:Model/Attribute:end_of_support' => '过保日期', + 'Class:Model/Attribute:end_of_support+' => '厂商提供补丁和支持的最后时间.', + 'Class:Model/Attribute:type/Value:PowerSource' => '电源', 'Class:Model/Attribute:type/Value:PowerSource+' => '电源', 'Class:Model/Attribute:type/Value:DiskArray' => '磁盘阵列', 'Class:Model/Attribute:type/Value:DiskArray+' => '磁盘阵列', 'Class:Model/Attribute:type/Value:Enclosure' => '机柜', 'Class:Model/Attribute:type/Value:Enclosure+' => '机柜', - 'Class:Model/Attribute:type/Value:IPPhone' => 'IP电话', - 'Class:Model/Attribute:type/Value:IPPhone+' => 'IP电话', + 'Class:Model/Attribute:type/Value:IPPhone' => 'IP 电话', + 'Class:Model/Attribute:type/Value:IPPhone+' => 'IP 电话', 'Class:Model/Attribute:type/Value:MobilePhone' => '手机', 'Class:Model/Attribute:type/Value:MobilePhone+' => '手机', 'Class:Model/Attribute:type/Value:NAS' => 'NAS', @@ -1154,10 +1166,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Model/Attribute:type/Value:Printer+' => '打印机', 'Class:Model/Attribute:type/Value:Rack' => '机架', 'Class:Model/Attribute:type/Value:Rack+' => '机架', - 'Class:Model/Attribute:type/Value:SANSwitch' => 'SAN交换机', - 'Class:Model/Attribute:type/Value:SANSwitch+' => 'SAN交换机', - 'Class:Model/Attribute:type/Value:Server' => '服务器', - 'Class:Model/Attribute:type/Value:Server+' => '服务器', + 'Class:Model/Attribute:type/Value:SANSwitch' => 'SAN 交换机', + 'Class:Model/Attribute:type/Value:SANSwitch+' => '光纤交换机', + 'Class:Model/Attribute:type/Value:Server' => '物理机', + 'Class:Model/Attribute:type/Value:Server+' => '物理机', 'Class:Model/Attribute:type/Value:StorageSystem' => '存储系统', 'Class:Model/Attribute:type/Value:StorageSystem+' => '存储系统', 'Class:Model/Attribute:type/Value:Tablet' => '平板', @@ -1178,9 +1190,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:NetworkDeviceType' => '网络设备类型', - 'Class:NetworkDeviceType+' => '', - 'Class:NetworkDeviceType/Attribute:logo' => 'Logo~~', - 'Class:NetworkDeviceType/Attribute:logo+' => 'Used as icon for all Network Device of this type, when displayed in console (details, summary card and impact analysis graphs)~~', + 'Class:NetworkDeviceType+' => '分类. 网络设备 "类型" 的可能的值 (例如:路由器、交换机、防火墙等).', + 'Class:NetworkDeviceType/Attribute:logo' => 'Logo', + 'Class:NetworkDeviceType/Attribute:logo+' => '用于此类型网络设备的图标,当在控制台中显示时 (详情、摘要卡片和影响分析图表)', 'Class:NetworkDeviceType/Attribute:networkdevicesdevices_list' => '网络设备', 'Class:NetworkDeviceType/Attribute:networkdevicesdevices_list+' => '此类型的所有网络设备', ]); @@ -1191,13 +1203,17 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:IOSVersion' => 'IOS 版本', - 'Class:IOSVersion+' => '', + 'Class:IOSVersion+' => '分类. 网络设备操作系统的版本可能的值 (IOS 来自 Cisco 的 Internetwork Operating System).', 'Class:IOSVersion/Attribute:brand_id' => '品牌', 'Class:IOSVersion/Attribute:brand_id+' => '', 'Class:IOSVersion/Attribute:brand_name' => '名称', 'Class:IOSVersion/Attribute:brand_name+' => '', - 'Class:IOSVersion/UniquenessRule:name_brand+' => 'Name must be unique in the brand~~', - 'Class:IOSVersion/UniquenessRule:name_brand' => 'this IOS version already exists for this brand~~', + 'Class:IOSVersion/Attribute:end_of_support' => '过保日期', + 'Class:IOSVersion/Attribute:end_of_support+' => '厂商提供补丁的最后时间.', + 'Class:IOSVersion/Attribute:networkdevices_list' => '网络设备', + 'Class:IOSVersion/Attribute:networkdevices_list+' => '运行此 IOS 版本的所有网络设备', + 'Class:IOSVersion/UniquenessRule:name_brand+' => '名称在品牌中必须唯一', + 'Class:IOSVersion/UniquenessRule:name_brand' => '此 IOS 版本已存在于此品牌', ]); // @@ -1205,8 +1221,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkDocumentToPatch' => '关联文档/补丁', - 'Class:lnkDocumentToPatch+' => '', + 'Class:lnkDocumentToPatch' => '链接 文档/补丁', + 'Class:lnkDocumentToPatch+' => 'Link used when a Document is applicable to a Patch.~~', 'Class:lnkDocumentToPatch/Name' => '%1$s / %2$s', 'Class:lnkDocumentToPatch/Attribute:patch_id' => '补丁', 'Class:lnkDocumentToPatch/Attribute:patch_id+' => '', @@ -1223,8 +1239,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkSoftwareInstanceToSoftwarePatch' => ' 关联软件实例/软件补丁', - 'Class:lnkSoftwareInstanceToSoftwarePatch+' => '', + 'Class:lnkSoftwareInstanceToSoftwarePatch' => '链接 软件实例/软件补丁', + 'Class:lnkSoftwareInstanceToSoftwarePatch+' => 'This link indicates that a software patch has been applied to a software instance.~~', 'Class:lnkSoftwareInstanceToSoftwarePatch/Name' => '%1$s / %2$s', 'Class:lnkSoftwareInstanceToSoftwarePatch/Attribute:softwarepatch_id' => '软件补丁', 'Class:lnkSoftwareInstanceToSoftwarePatch/Attribute:softwarepatch_id+' => '', @@ -1241,16 +1257,16 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkFunctionalCIToOSPatch' => '关联功能项/操作系统补丁', - 'Class:lnkFunctionalCIToOSPatch+' => '', + 'Class:lnkFunctionalCIToOSPatch' => '链接 功能配置项/OS 补丁', + 'Class:lnkFunctionalCIToOSPatch+' => 'Models the deployment of an OS Patch on a device.~~', 'Class:lnkFunctionalCIToOSPatch/Name' => '%1$s / %2$s', - 'Class:lnkFunctionalCIToOSPatch/Attribute:ospatch_id' => '操作系统补丁', + 'Class:lnkFunctionalCIToOSPatch/Attribute:ospatch_id' => 'OS 补丁', 'Class:lnkFunctionalCIToOSPatch/Attribute:ospatch_id+' => '', - 'Class:lnkFunctionalCIToOSPatch/Attribute:ospatch_name' => '操作系统补丁名称', + 'Class:lnkFunctionalCIToOSPatch/Attribute:ospatch_name' => 'OS 补丁名称', 'Class:lnkFunctionalCIToOSPatch/Attribute:ospatch_name+' => '', - 'Class:lnkFunctionalCIToOSPatch/Attribute:functionalci_id' => '功能项', + 'Class:lnkFunctionalCIToOSPatch/Attribute:functionalci_id' => '功能配置项', 'Class:lnkFunctionalCIToOSPatch/Attribute:functionalci_id+' => '', - 'Class:lnkFunctionalCIToOSPatch/Attribute:functionalci_name' => '功能项名称', + 'Class:lnkFunctionalCIToOSPatch/Attribute:functionalci_name' => '功能配置项名称', 'Class:lnkFunctionalCIToOSPatch/Attribute:functionalci_name+' => '', ]); @@ -1259,8 +1275,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkDocumentToSoftware' => '关联文档/软件', - 'Class:lnkDocumentToSoftware+' => '', + 'Class:lnkDocumentToSoftware' => '链接 文档/软件', + 'Class:lnkDocumentToSoftware+' => 'Link used when a Document is applicable to Software.~~', 'Class:lnkDocumentToSoftware/Name' => '%1$s / %2$s', 'Class:lnkDocumentToSoftware/Attribute:software_id' => '软件', 'Class:lnkDocumentToSoftware/Attribute:software_id+' => '', @@ -1278,7 +1294,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Subnet' => '子网', - 'Class:Subnet+' => '', + 'Class:Subnet+' => 'IP 网段, 由 IP 地址和掩码定义', 'Class:Subnet/Name' => '%1$s/%2$s', 'Class:Subnet/ComplementaryName' => '%1$s - %2$s', 'Class:Subnet/Attribute:description' => '描述', @@ -1303,8 +1319,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:VLAN' => 'VLAN', - 'Class:VLAN+' => '', - 'Class:VLAN/Attribute:vlan_tag' => 'VLAN 标记', + 'Class:VLAN+' => 'VLAN 即虚拟局域网,用于以逻辑方式对局域网内的网络、子网和物理接口进行分组.', + 'Class:VLAN/Attribute:vlan_tag' => 'VLAN 标签', 'Class:VLAN/Attribute:vlan_tag+' => '', 'Class:VLAN/Attribute:description' => '描述', 'Class:VLAN/Attribute:description+' => '', @@ -1323,8 +1339,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkSubnetToVLAN' => '关联子网/VLAN', - 'Class:lnkSubnetToVLAN+' => '', + 'Class:lnkSubnetToVLAN' => '链接 子网/VLAN', + 'Class:lnkSubnetToVLAN+' => '这是多对多的关系, 某个 VLAN 包含一个子网. 其它 VLAN 也可以包含相同的子网. 并且一个 VLAN 可以跨越多个子网.', 'Class:lnkSubnetToVLAN/Name' => '%1$s / %2$s', 'Class:lnkSubnetToVLAN/Attribute:subnet_id' => '子网', 'Class:lnkSubnetToVLAN/Attribute:subnet_id+' => '', @@ -1334,7 +1350,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkSubnetToVLAN/Attribute:subnet_name+' => '', 'Class:lnkSubnetToVLAN/Attribute:vlan_id' => 'VLAN', 'Class:lnkSubnetToVLAN/Attribute:vlan_id+' => '', - 'Class:lnkSubnetToVLAN/Attribute:vlan_tag' => 'VLAN 标记', + 'Class:lnkSubnetToVLAN/Attribute:vlan_tag' => 'VLAN 标签', 'Class:lnkSubnetToVLAN/Attribute:vlan_tag+' => '', ]); @@ -1344,7 +1360,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:NetworkInterface' => '网卡', - 'Class:NetworkInterface+' => '', + 'Class:NetworkInterface+' => '对所有网络接口的抽象.', 'Class:NetworkInterface/Attribute:name' => '名称', 'Class:NetworkInterface/Attribute:name+' => '', 'Class:NetworkInterface/Attribute:finalclass' => '网卡类型', @@ -1357,9 +1373,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:IPInterface' => 'IP 接口', - 'Class:IPInterface+' => '', + 'Class:IPInterface+' => '抽象类. 一种具有 IP 地址的网络接口类型', 'Class:IPInterface/Attribute:ipaddress' => 'IP 地址', 'Class:IPInterface/Attribute:ipaddress+' => '', + 'Class:IPInterface/Attribute:macaddress' => 'MAC地址', 'Class:IPInterface/Attribute:macaddress+' => '', 'Class:IPInterface/Attribute:comment' => '备注', @@ -1378,13 +1395,17 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PhysicalInterface' => '物理网卡', - 'Class:PhysicalInterface+' => '', + 'Class:PhysicalInterface+' => '基于物理网络接口的IP接口类型 (例如,以太网卡).', 'Class:PhysicalInterface/Name' => '%2$s %1$s', 'Class:PhysicalInterface/Attribute:connectableci_id' => '设备', 'Class:PhysicalInterface/Attribute:connectableci_id+' => '', 'Class:PhysicalInterface/Attribute:connectableci_name' => '设备名称', 'Class:PhysicalInterface/Attribute:connectableci_name+' => '', - 'Class:PhysicalInterface/Attribute:vlans_list' => 'VLAN', + 'Class:PhysicalInterface/Attribute:org_id' => '组织', + 'Class:PhysicalInterface/Attribute:org_id+' => '', + 'Class:PhysicalInterface/Attribute:location_id' => '位置', + 'Class:PhysicalInterface/Attribute:location_id+' => '', + 'Class:PhysicalInterface/Attribute:vlans_list' => 'VLAN', 'Class:PhysicalInterface/Attribute:vlans_list+' => '', ]); @@ -1393,8 +1414,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkPhysicalInterfaceToVLAN' => '关联物理网卡/VLAN', - 'Class:lnkPhysicalInterfaceToVLAN+' => '', + 'Class:lnkPhysicalInterfaceToVLAN' => '链接 物理网卡/VLAN', + 'Class:lnkPhysicalInterfaceToVLAN+' => 'This link indicates when a network interface is part of a VLAN (虚拟局域网).~~', 'Class:lnkPhysicalInterfaceToVLAN/Name' => '%1$s %2$s / %3$s', 'Class:lnkPhysicalInterfaceToVLAN/Attribute:physicalinterface_id' => '物理网卡', 'Class:lnkPhysicalInterfaceToVLAN/Attribute:physicalinterface_id+' => '', @@ -1406,7 +1427,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkPhysicalInterfaceToVLAN/Attribute:physicalinterface_device_name+' => '', 'Class:lnkPhysicalInterfaceToVLAN/Attribute:vlan_id' => 'VLAN', 'Class:lnkPhysicalInterfaceToVLAN/Attribute:vlan_id+' => '', - 'Class:lnkPhysicalInterfaceToVLAN/Attribute:vlan_tag' => 'VLAN 标记', + 'Class:lnkPhysicalInterfaceToVLAN/Attribute:vlan_tag' => 'VLAN 标签', 'Class:lnkPhysicalInterfaceToVLAN/Attribute:vlan_tag+' => '', ]); @@ -1416,7 +1437,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:LogicalInterface' => '逻辑网卡', - 'Class:LogicalInterface+' => '', + 'Class:LogicalInterface+' => '不与物理网卡直接对应的IP接口,其关联是动态的,通常用于虚拟机.', 'Class:LogicalInterface/Attribute:virtualmachine_id' => '虚拟机', 'Class:LogicalInterface/Attribute:virtualmachine_id+' => '', 'Class:LogicalInterface/Attribute:virtualmachine_name' => '虚拟机名称', @@ -1428,8 +1449,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:FiberChannelInterface' => '光纤接口', - 'Class:FiberChannelInterface+' => '', + 'Class:FiberChannelInterface' => '光口', + 'Class:FiberChannelInterface+' => '主要用于存储系统的一种高速网络接口.', 'Class:FiberChannelInterface/Attribute:speed' => '速率', 'Class:FiberChannelInterface/Attribute:speed+' => '', 'Class:FiberChannelInterface/Attribute:topology' => '拓扑', @@ -1447,8 +1468,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkConnectableCIToNetworkDevice' => '关联可连接项/网络设备', - 'Class:lnkConnectableCIToNetworkDevice+' => '', + 'Class:lnkConnectableCIToNetworkDevice' => '链接 可连接项/网络设备', + 'Class:lnkConnectableCIToNetworkDevice+' => 'Defines on which network equipment a device is connected.~~', 'Class:lnkConnectableCIToNetworkDevice/Name' => '%1$s / %2$s', 'Class:lnkConnectableCIToNetworkDevice/Attribute:networkdevice_id' => '网络设备', 'Class:lnkConnectableCIToNetworkDevice/Attribute:networkdevice_id+' => '', @@ -1475,16 +1496,16 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkApplicationSolutionToFunctionalCI' => '关联应用方案/功能项', - 'Class:lnkApplicationSolutionToFunctionalCI+' => '', + 'Class:lnkApplicationSolutionToFunctionalCI' => '链接 应用方案/功能配置项', + 'Class:lnkApplicationSolutionToFunctionalCI+' => 'Models the membership of a device to an Application Solution. The meaning of this relationship varies depending on the types of Application Solution.~~', 'Class:lnkApplicationSolutionToFunctionalCI/Name' => '%1$s / %2$s', 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:applicationsolution_id' => '应用方案', 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:applicationsolution_id+' => '', 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:applicationsolution_name' => '应用方案名称', 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:applicationsolution_name+' => '', - 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_id' => '功能项', + 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_id' => '功能配置项', 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_id+' => '', - 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_name' => '功能项名称', + 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_name' => '功能配置项名称', 'Class:lnkApplicationSolutionToFunctionalCI/Attribute:functionalci_name+' => '', ]); @@ -1493,8 +1514,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkApplicationSolutionToBusinessProcess' => '关联应用方案/业务流程', - 'Class:lnkApplicationSolutionToBusinessProcess+' => '', + 'Class:lnkApplicationSolutionToBusinessProcess' => '链接 应用方案/业务流程', + 'Class:lnkApplicationSolutionToBusinessProcess+' => 'Models the relationship between an Application Solution and a Business Process.~~', 'Class:lnkApplicationSolutionToBusinessProcess/Name' => '%1$s / %2$s', 'Class:lnkApplicationSolutionToBusinessProcess/Attribute:businessprocess_id' => '业务流程', 'Class:lnkApplicationSolutionToBusinessProcess/Attribute:businessprocess_id+' => '', @@ -1512,7 +1533,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Group' => '配置组', - 'Class:Group+' => '', + 'Class:Group+' => '配置组旨在为任何项目定义明确的配置项集合. 与应用方案不同,配置组不会受到其组件的影响,也不会影响它的组件. 例如,在进行操作系统迁移时,配置组可以方便地收集"待迁移的服务器". 随着迁移的进行,迁移完成的服务器将从配置组中移除.', 'Class:Group/ComplementaryName' => '%1$s - %2$s', 'Class:Group/Attribute:name' => '名称', 'Class:Group/Attribute:name+' => '', @@ -1537,7 +1558,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Group/Attribute:parent_name' => '名称', 'Class:Group/Attribute:parent_name+' => '', 'Class:Group/Attribute:ci_list' => '关联的配置项', - 'Class:Group/Attribute:ci_list+' => '此组关联的所有配置项', + 'Class:Group/Attribute:ci_list+' => '此配置组相关的所有配置项', 'Class:Group/Attribute:parent_id_friendlyname' => '上级配置组', 'Class:Group/Attribute:parent_id_friendlyname+' => '', ]); @@ -1547,8 +1568,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkGroupToCI' => '关联配置组/配置项', - 'Class:lnkGroupToCI+' => '', + 'Class:lnkGroupToCI' => '链接 配置组/配置项', + 'Class:lnkGroupToCI+' => 'This link indicates when a Functional CI is part of a Group.~~', 'Class:lnkGroupToCI/Name' => '%1$s / %2$s', 'Class:lnkGroupToCI/Attribute:group_id' => '组', 'Class:lnkGroupToCI/Attribute:group_id+' => '', @@ -1562,20 +1583,38 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkGroupToCI/Attribute:reason+' => '', ]); +// +// Class: lnkDocumentToFunctionalCI +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:lnkDocumentToFunctionalCI' => '链接 文档/功能配置项', + 'Class:lnkDocumentToFunctionalCI+' => 'Link used when a Document is applicable to a Functional CI.~~', + 'Class:lnkDocumentToFunctionalCI/Name' => '%1$s / %2$s', + 'Class:lnkDocumentToFunctionalCI/Attribute:functionalci_id' => '功能配置项', + 'Class:lnkDocumentToFunctionalCI/Attribute:functionalci_id+' => '', + 'Class:lnkDocumentToFunctionalCI/Attribute:functionalci_name' => '功能配置项名称', + 'Class:lnkDocumentToFunctionalCI/Attribute:functionalci_name+' => '', + 'Class:lnkDocumentToFunctionalCI/Attribute:document_id' => '文档', + 'Class:lnkDocumentToFunctionalCI/Attribute:document_id+' => '', + 'Class:lnkDocumentToFunctionalCI/Attribute:document_name' => '文档名称', + 'Class:lnkDocumentToFunctionalCI/Attribute:document_name+' => '', +]); + // Add translation for Fieldsets Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'ConfigMgmt:baseinfo' => 'General~~', - 'ConfigMgmt:moreinfo' => 'CI specifics~~', - 'Storage:moreinfo' => 'Storage specifics~~', - 'ConfigMgmt:otherinfo' => 'Description~~', - 'ConfigMgmt:dates' => 'Dates~~', - 'Software:moreinfo' => 'Software specifics~~', - 'Phone:moreinfo' => 'Phone specifics~~', + 'ConfigMgmt:baseinfo' => '概况', + 'ConfigMgmt:moreinfo' => '配置项详情', + 'ConfigMgmt:otherinfo' => '描述', + 'ConfigMgmt:dates' => '日期', + 'Storage:moreinfo' => '存储详情', + 'Software:moreinfo' => '软件详情', + 'Phone:moreinfo' => '电话详情', 'Server:baseinfo' => '基本信息', + 'Server:moreinfo' => '设备详情', 'Server:Date' => '日期', - 'Server:moreinfo' => '更多信息', - 'Server:otherinfo' => '其它信息', + 'Server:otherinfo' => '描述', 'Server:power' => '电力供应', 'Class:Subnet/Tab:IPUsage' => 'IP 使用率', 'Class:Subnet/Tab:IPUsage+' => '子网中哪些 IP 在使用或可用~', @@ -1586,24 +1625,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Document:PreviewTab' => '预览', ]); -// -// Class: lnkDocumentToFunctionalCI -// - -Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkDocumentToFunctionalCI' => '关联文档/功能项', - 'Class:lnkDocumentToFunctionalCI+' => '', - 'Class:lnkDocumentToFunctionalCI/Name' => '%1$s / %2$s', - 'Class:lnkDocumentToFunctionalCI/Attribute:functionalci_id' => '功能项', - 'Class:lnkDocumentToFunctionalCI/Attribute:functionalci_id+' => '', - 'Class:lnkDocumentToFunctionalCI/Attribute:functionalci_name' => '功能项名称', - 'Class:lnkDocumentToFunctionalCI/Attribute:functionalci_name+' => '', - 'Class:lnkDocumentToFunctionalCI/Attribute:document_id' => '文档', - 'Class:lnkDocumentToFunctionalCI/Attribute:document_id+' => '', - 'Class:lnkDocumentToFunctionalCI/Attribute:document_name' => '文档名称', - 'Class:lnkDocumentToFunctionalCI/Attribute:document_name+' => '', -]); - // // Application Menu // @@ -1649,19 +1670,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:ConfigManagement:Misc' => '杂项', 'Menu:Group' => '配置组', 'Menu:Group+' => '配置组', - 'Menu:OSVersion' => 'OS版本', + 'Menu:OSVersion' => 'OS 版本', 'Menu:OSVersion+' => '', 'Menu:Software' => '软件清单', 'Menu:Software+' => '软件清单', ]); - -// -// Class: PhysicalInterface -// - -Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:PhysicalInterface/Attribute:org_id' => 'Organization~~', - 'Class:PhysicalInterface/Attribute:org_id+' => '~~', - 'Class:PhysicalInterface/Attribute:location_id' => 'Location~~', - 'Class:PhysicalInterface/Attribute:location_id+' => '~~', -]); diff --git a/datamodels/2.x/itop-config/dictionaries/zh_cn.dict.itop-config.php b/datamodels/2.x/itop-config/dictionaries/zh_cn.dict.itop-config.php index ec3308126a..8650e7cb2c 100644 --- a/datamodels/2.x/itop-config/dictionaries/zh_cn.dict.itop-config.php +++ b/datamodels/2.x/itop-config/dictionaries/zh_cn.dict.itop-config.php @@ -21,9 +21,10 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Menu:ConfigFileEditor' => 'Plain text editor~~', - 'Menu:ConfigEditor' => '编辑配置文件', + + 'Menu:ConfigFileEditor' => '纯文本编辑器', 'itop-config/Operation:Edit/Title' => '配置文件编辑器', 'config-edit-intro' => '编辑配置文件时请务必格外小心.', 'config-apply' => '应用', @@ -34,7 +35,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'config-no-change' => '没有变化: 配置文件将保持不变.', 'config-reverted' => '配置文件已恢复.', 'config-parse-error' => '第%2$d行: %1$s.
配置文件尚未更新.', - 'config-current-line' => '正在编辑第%1$s行', + 'config-current-line' => '正在编辑第 %1$s 行', 'config-saved-warning-db-password' => '保存成功, 但因为数据库密码中包含不支持的字符, 配置文件备份不会成功.', 'config-error-transaction' => '错误: 无效的事务编号. 配置没有被更新.', 'config-error-file-changed' => '错误: 配置文件在您打开以后已被更改, 无法保存. 请刷新并再次保存.', diff --git a/datamodels/2.x/itop-container-mgmt/dictionaries/zh_cn.dict.itop-container-mgmt.php b/datamodels/2.x/itop-container-mgmt/dictionaries/zh_cn.dict.itop-container-mgmt.php new file mode 100644 index 0000000000..6293a6f002 --- /dev/null +++ b/datamodels/2.x/itop-container-mgmt/dictionaries/zh_cn.dict.itop-container-mgmt.php @@ -0,0 +1,173 @@ + '基本信息', + 'Container:moreinfo' => '容器详情', + 'Container:otherinfo' => '日期和描述', +]); + +// +// Class Container Image +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:ContainerImage/Name' => '%1$s %2$s', + 'Class:ContainerImage/ComplementaryName' => '%1$s - %2$s', + 'Class:ContainerImage' => '容器镜像', + 'Class:ContainerImage+' => '准备作为容器启动的软件镜像', + 'Class:ContainerImage/Attribute:name' => '名称', + 'Class:ContainerImage/Attribute:name+' => '', + 'Class:ContainerImage/Attribute:version' => '版本', + 'Class:ContainerImage/Attribute:version+' => '', + 'Class:ContainerImage/Attribute:description' => '描述', + 'Class:ContainerImage/Attribute:description+' => '', + 'Class:ContainerImage/Attribute:publisher' => '发布者', + 'Class:ContainerImage/Attribute:publisher+' => '镜像的发布者. 例如: php, nginx, ...', + 'Class:ContainerImage/Attribute:image' => '镜像', + 'Class:ContainerImage/Attribute:image+' => '在对应的托管平台上检索镜像的详细信息', + 'Class:ContainerImage/Attribute:type_id' => '类型', + 'Class:ContainerImage/Attribute:type_id+' => 'Type d\image~~', + 'Class:ContainerImage/Attribute:software_id' => '软件', + 'Class:ContainerImage/Attribute:software_id+' => '', + 'Class:ContainerImage/Attribute:containerapplications_list' => '容器化应用', + 'Class:ContainerImage/Attribute:containerapplications_list+' => '此镜像所对应的应用程序', +]); + +// +// Class Container Application +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:ContainerApplication/Name' => '%1$s', + 'Class:ContainerApplication/ComplementaryName' => '%1$s', + 'Class:ContainerApplication' => '容器化应用程序', + 'Class:ContainerApplication+' => '部署在容器平台上的应用程序', + 'Class:ContainerApplication/Attribute:descriptor' => 'Deployment file', + 'Class:ContainerApplication/Attribute:descriptor+' => 'File describing how to deploy the application on the container platform (e.g., Docker Compose, Helm Chart, etc.)', + 'Class:ContainerApplication/Attribute:containervirtualhost_id' => 'Container Host', + 'Class:ContainerApplication/Attribute:containervirtualhost_id+' => 'Container Platform on which the application is running', + 'Class:ContainerApplication/Attribute:logo' => 'Logo', + 'Class:ContainerApplication/Attribute:logo+' => 'Used as object icon when this ContainerApplication is displayed within impact analysis graphs', + 'Class:ContainerApplication/Attribute:containertype_id' => '容器类型', + 'Class:ContainerApplication/Attribute:containertype_id+' => '容器化使用的技术', + 'Class:ContainerApplication/Attribute:containerimages_list' => '容器镜像', + 'Class:ContainerApplication/Attribute:containerimages_list+' => 'Software images used to build the containerized application', + +]); + +// +// Class: lnkContainerApplicationToImage +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:lnkContainerApplicationToImage' => '链接 容器应用/容器镜像', + 'Class:lnkContainerApplicationToImage+' => '', + 'Class:lnkContainerApplicationToImage/Name' => '%1$s / %2$s', + 'Class:lnkContainerApplicationToImage/Name+' => '', + 'Class:lnkContainerApplicationToImage/Attribute:containerapplication_id' => '容器化应用程序', + 'Class:lnkContainerApplicationToImage/Attribute:containerapplication_id+' => '使用此镜像的应用程序', + 'Class:lnkContainerApplicationToImage/Attribute:containerimage_id' => '容器镜像', + 'Class:lnkContainerApplicationToImage/Attribute:containerimage_id+' => '用于构建容器化应用程序的软件镜像', +]); + +// +// Class Container Virtual Host +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:ContainerVirtualHost/Name' => '%1$s', + 'Class:ContainerVirtualHost/ComplementaryName' => '', + 'Class:ContainerVirtualHost' => '容器平台', + 'Class:ContainerVirtualHost+' => 'Platform on which applications run as containers', + 'Class:ContainerVirtualHost/Attribute:containertype_id' => '容器类型', + 'Class:ContainerVirtualHost/Attribute:containertype_id+' => '用于交付容器的技术', + 'Class:ContainerVirtualHost/Attribute:status' => '状态', + 'Class:ContainerVirtualHost/Attribute:status+' => '容器平台的状态', + 'Class:ContainerVirtualHost/Attribute:containerapplications_list' => '应用程序', + 'Class:ContainerVirtualHost/Attribute:containerapplications_list+' => '在此容器环境中运行的应用程序', +]); + +// +// Class Container Host +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:ContainerHost/Name' => '%1$s', + 'Class:ContainerHost/ComplementaryName' => '%1$s-%2$s', + 'Class:ContainerHost' => '容器宿主机', + 'Class:ContainerHost+' => '托管容器的宿主机. 它是容器平台的基本元素', + 'Class:ContainerHost/Attribute:containercluster_id' => '容器集群', + 'Class:ContainerHost/Attribute:containercluster_id+' => '', + 'Class:ContainerHost/Attribute:role' => '角色', + 'Class:ContainerHost/Attribute:role+' => '主机在集群中的角色:主节点或工作节点. 独立运行时不在集群中.', + 'Class:ContainerHost/Attribute:system_id' => '系统', + 'Class:ContainerHost/Attribute:system_id+' => '系统可以是物理机, 虚拟机, 云平台, ...', + 'Class:ContainerHost/Attribute:role/Value:master' => '主节点', + 'Class:ContainerHost/Attribute:role/Value:worker' => '工作节点', + 'Class:ContainerHost/Attribute:role/Value:standalone' => '独立运行', +]); + +// +// Class Container Cluster +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:ContainerCluster/Name' => '%1$s', + 'Class:ContainerCluster/ComplementaryName' => '', + 'Class:ContainerCluster' => '容器集群', + 'Class:ContainerCluster+' => '由一组容器宿主机组成的容器平台', + 'Class:ContainerCluster/Attribute:redundancy' => '冗余配置', + 'Class:ContainerCluster/Attribute:redundancy/disabled' => '当所有主机都在运行时, 集群才是正常的', + 'Class:ContainerCluster/Attribute:redundancy/count' => '当至少 %1$s 个主机在运行时, 集群才是正常的', + 'Class:ContainerCluster/Attribute:redundancy/percent' => '当至少 %1$s %% 的在主机运行时,集群才是正常的', + 'Class:ContainerCluster/Attribute:containerhosts_list' => '容器宿主机', + 'Class:ContainerCluster/Attribute:containerhosts_list+' => '此集群的主机', +]); + +// +// Class Container Type +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:ContainerType/Name' => '%1$s', + 'Class:ContainerType/ComplementaryName' => '', + 'Class:ContainerType' => '容器类型', + 'Class:ContainerType+' => '用于交付容器的技术', +]); + +// +// Class Container Type +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:ContainerImageType/Name' => '%1$s', + 'Class:ContainerImageType/ComplementaryName' => '', + 'Class:ContainerImageType' => '容器镜像类型', + 'Class:ContainerImageType+' => '容器镜像的分类', +]); + +// +// Class Cloud, Server and Virtual Machine +// + +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'Class:Cloud/Attribute:containerhosts_list' => '容器宿主机', + 'Class:Cloud/Attribute:containerhosts_list+' => '运行在此云平台上的容器宿主机列表', + 'Class:Server/Attribute:containerhosts_list' => '容器宿主机', + 'Class:Server/Attribute:containerhosts_list+' => '运行在此物理机上的容器宿主机列表', + 'Class:VirtualMachine/Attribute:containerhosts_list' => '容器宿主机', + 'Class:VirtualMachine/Attribute:containerhosts_list+' => '运行在此虚拟机上的容器宿主机列表', + 'Class:Software/Attribute:containerimages_list' => '容器镜像', + 'Class:Software/Attribute:containerimages_list+' => '运行此软件的容器镜像列表', +]); diff --git a/datamodels/2.x/itop-core-update/dictionaries/zh_cn.dict.itop-core-update.php b/datamodels/2.x/itop-core-update/dictionaries/zh_cn.dict.itop-core-update.php index 433c7f610a..b5dd0aca59 100644 --- a/datamodels/2.x/itop-core-update/dictionaries/zh_cn.dict.itop-core-update.php +++ b/datamodels/2.x/itop-core-update/dictionaries/zh_cn.dict.itop-core-update.php @@ -26,12 +26,14 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'itop-core-update:UI:SelectUpdateFile' => '应用升级', 'itop-core-update:UI:ConfirmUpdate' => ' 升级', 'itop-core-update:UI:UpdateCoreFiles' => '应用升级', - 'iTopUpdate:UI:MaintenanceModeActive' => '此应用当前维护中, 不允许任何用户访问. 必须运行安装或恢复归档来使其处于正常模式.', + 'iTopUpdate:UI:MaintenanceModeActive' => '此应用当前维护中, 不允许任何用户访问. 必须运行安装向导或恢复归档来使其处于正常模式.', 'itop-core-update:UI:UpdateDone' => '应用升级', + 'itop-core-update/Operation:SelectUpdateFile/Title' => '应用升级', 'itop-core-update/Operation:ConfirmUpdate/Title' => '请确认升级应用', 'itop-core-update/Operation:UpdateCoreFiles/Title' => '应用正在升级', 'itop-core-update/Operation:UpdateDone/Title' => '应用升级完毕', + 'iTopUpdate:UI:SelectUpdateFile' => '请选择要上传的升级文件', 'iTopUpdate:UI:CheckUpdate' => '校验升级文件', 'iTopUpdate:UI:ConfirmInstallFile' => '即将安装 %1$s', @@ -50,6 +52,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'iTopUpdate:UI:UploadArchive' => '请选择要上传的软件包', 'iTopUpdate:UI:ServerFile' => '服务器上的软件包路径已存在', 'iTopUpdate:UI:WarningReadOnlyDuringUpdate' => '升级期间, 应用会变成只读状态.', + 'iTopUpdate:UI:Status' => '状态', 'iTopUpdate:UI:Action' => '升级', 'iTopUpdate:UI:Setup' => ITOP_APPLICATION_SHORT.'安装', @@ -61,8 +64,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'iTopUpdate:UI:ItopDiskSpace' => ITOP_APPLICATION_SHORT.'的磁盘空间', 'iTopUpdate:UI:DBDiskSpace' => '数据库的磁盘空间', 'iTopUpdate:UI:FileUploadMaxSize' => '文件上传大小上限', - 'iTopUpdate:UI:PostMaxSize' => 'PHP ini值post_max_size: %1$s', - 'iTopUpdate:UI:UploadMaxFileSize' => 'PHP ini 值 upload_max_filesize: %1$s', + + 'iTopUpdate:UI:PostMaxSize' => 'PHP ini 的值 post_max_size: %1$s', + 'iTopUpdate:UI:UploadMaxFileSize' => 'PHP ini 的值 upload_max_filesize: %1$s', + 'iTopUpdate:UI:CanCoreUpdate:Loading' => '正在检查文件', 'iTopUpdate:UI:CanCoreUpdate:Error' => '文件检查失败 (%1$s)', 'iTopUpdate:UI:CanCoreUpdate:ErrorFileNotExist' => '文件检查失败 (%1$s 文件不存在)', @@ -70,11 +75,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'iTopUpdate:UI:CanCoreUpdate:Yes' => '应用可以升级', 'iTopUpdate:UI:CanCoreUpdate:No' => '应用无法升级: %1$s', 'iTopUpdate:UI:CanCoreUpdate:Warning' => '警告: 应用升级可能失败: %1$s', - 'iTopUpdate:UI:CannotUpdateUseSetup' => '检测到一些文件被修改, 无法进行局部升级.
请按照指南一步步操作以手动升级系统. 您必须使用安装已升级应用.', + 'iTopUpdate:UI:CannotUpdateUseSetup' => '检测到一些文件被修改, 无法进行局部升级.
请按照流程一步步操作以手动升级系统. 您必须使用安装向导来升级应用.', 'iTopUpdate:UI:CheckInProgress' => '完整性检查中, 请稍候', - 'iTopUpdate:UI:SetupLaunch' => '启动'.ITOP_APPLICATION_SHORT.'安装', - 'iTopUpdate:UI:SetupLaunchConfirm' => '将启动'.ITOP_APPLICATION_SHORT.'安装, 确定吗?', - 'iTopUpdate:UI:FastSetupLaunch' => 'Fast Setup~~', + + 'iTopUpdate:UI:SetupLaunch' => '启动'.ITOP_APPLICATION_SHORT.'安装向导', + 'iTopUpdate:UI:SetupLaunchConfirm' => '即将启动'.ITOP_APPLICATION_SHORT.'安装向导, 请确定?', + 'iTopUpdate:UI:FastSetupLaunch' => '快速安装', // Setup Messages 'iTopUpdate:UI:SetupMessage:Ready' => '准备开始', @@ -101,6 +107,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'iTopUpdate:Error:InvalidToken' => '无效的 token', 'iTopUpdate:Error:UpdateFailed' => '升级失败', 'iTopUpdate:Error:FileUploadMaxSizeTooSmall' => '上传上限太小. 请调整 PHP 配置.', + 'iTopUpdate:UI:RestoreArchive' => '您可以从归档文件 \'%1$s\' 还原应用程序', 'iTopUpdate:UI:RestoreBackup' => '您可以从 \'%1$s\' 还原数据库', 'iTopUpdate:UI:UpdateDone' => '升级成功', diff --git a/datamodels/2.x/itop-faq-light/dictionaries/zh_cn.dict.itop-faq-light.php b/datamodels/2.x/itop-faq-light/dictionaries/zh_cn.dict.itop-faq-light.php index 22f187e0fe..a29112a002 100644 --- a/datamodels/2.x/itop-faq-light/dictionaries/zh_cn.dict.itop-faq-light.php +++ b/datamodels/2.x/itop-faq-light/dictionaries/zh_cn.dict.itop-faq-light.php @@ -4,7 +4,7 @@ * Localized data * * @copyright Copyright (C) 2010-2024 Combodo SAS - * @license https://opensource.org/licenses/AGPL-3.0 + * @license http://opensource.org/licenses/AGPL-3.0 * * This file is part of iTop. * @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + // Dictionnay conventions // Class: // Class:+ @@ -30,10 +31,12 @@ // Class:/Attribute:/Value:+ // Class:/Stimulus: // Class:/Stimulus:+ + ////////////////////////////////////////////////////////////////////// // Classes in 'bizmodel' ////////////////////////////////////////////////////////////////////// // + // Dictionnay conventions // Class: // Class:+ @@ -43,9 +46,11 @@ // Class:/Attribute:/Value:+ // Class:/Stimulus: // Class:/Stimulus:+ + // // Class: FAQ // + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:FAQ' => 'FAQ', 'Class:FAQ+' => '常见问题', diff --git a/datamodels/2.x/itop-flow-map/dictionaries/zh_cn.dict.itop-flow-map.php b/datamodels/2.x/itop-flow-map/dictionaries/zh_cn.dict.itop-flow-map.php new file mode 100644 index 0000000000..863ceef69b --- /dev/null +++ b/datamodels/2.x/itop-flow-map/dictionaries/zh_cn.dict.itop-flow-map.php @@ -0,0 +1,91 @@ + 'DataFlows between CIs~~', + 'Relation:dataflows/DownStream' => 'Outbound flows...', + 'Relation:dataflows/DownStream+' => 'Outbound flows map from', + 'Relation:dataflows/UpStream' => 'Inbound flows...', + 'Relation:dataflows/UpStream+' => 'Inbound flows map to', + + 'Class:FunctionalCI/Attribute:dataflows' => '数据流', + 'Class:FunctionalCI/Attribute:dataflows+' => '该对象作为源或目标的数据流', + 'FunctionalCI:DataFlow:Title' => '数据流', + 'FunctionalCI:DataFlow:Inbound' => '入站数据流', + 'FunctionalCI:DataFlow:Outbound' => '出站数据流', + + 'DataFlow:moreinfo' => '数据流详情', + + 'Class:DataFlow' => '数据流', + 'Class:DataFlow+' => 'For application flow for example~~', + 'Class:DataFlow/Name' => '%1$s', + 'Class:DataFlow/Attribute:name' => '名称', + 'Class:DataFlow/Attribute:name+' => '已传输的数据', + 'Class:DataFlow/Attribute:source_id' => '数据源', + 'Class:DataFlow/Attribute:source_id+' => '数据流的源头配置项', + 'Class:DataFlow/Attribute:source_impact' => '数据源影响?', + 'Class:DataFlow/Attribute:source_impact+' => '数据源是否影响数据流?', + 'Class:DataFlow/Attribute:source_impact/Value:yes' => '是', + 'Class:DataFlow/Attribute:source_impact/Value:yes+' => '如果数据源失效,数据流将受到影响', + 'Class:DataFlow/Attribute:source_impact/Value:no' => '否', + 'Class:DataFlow/Attribute:source_impact/Value:no+' => '如果数据源失效,数据流不受影响', + 'Class:DataFlow/Attribute:destination_id' => '目标', + 'Class:DataFlow/Attribute:destination_id+' => '数据流的目标配置项', + 'Class:DataFlow/Attribute:destination_impact' => '目标影响', + 'Class:DataFlow/Attribute:destination_impact+' => '目标是否受数据流影响?', + 'Class:DataFlow/Attribute:destination_impact/Value:yes' => '是', + 'Class:DataFlow/Attribute:destination_impact/Value:yes+' => '如果数据流停止,目标将受到影响', + 'Class:DataFlow/Attribute:destination_impact/Value:no' => '否', + 'Class:DataFlow/Attribute:destination_impact/Value:no+' => '如果数据流停止,目标不受影响', + 'Class:DataFlow/Attribute:dataflowtype_id' => '数据流类型', + 'Class:DataFlow/Attribute:dataflowtype_id+' => '数据流的分类', + 'Class:DataFlow/Attribute:status' => '状态', + 'Class:DataFlow/Attribute:status+' => '', + 'Class:DataFlow/Attribute:status/Value:active' => '启用', + 'Class:DataFlow/Attribute:status/Value:inactive' => '停用', + 'Class:DataFlow/Attribute:execution_frequency' => '执行频率', + 'Class:DataFlow/Attribute:execution_frequency+' => '数据流执行的频率', + 'Class:DataFlow/Attribute:execution_frequency/Value:realtime' => '实时', + 'Class:DataFlow/Attribute:execution_frequency/Value:realtime+' => '', + 'Class:DataFlow/Attribute:execution_frequency/Value:ondemand' => '按需', + 'Class:DataFlow/Attribute:execution_frequency/Value:ondemand+' => '即时执行,不按计划进行', + 'Class:DataFlow/Attribute:execution_frequency/Value:hourly' => '每小时', + 'Class:DataFlow/Attribute:execution_frequency/Value:hourly+' => '', + 'Class:DataFlow/Attribute:execution_frequency/Value:daily' => '每天', + 'Class:DataFlow/Attribute:execution_frequency/Value:daily+' => '', + 'Class:DataFlow/Attribute:execution_frequency/Value:weekly' => '每周', + 'Class:DataFlow/Attribute:execution_frequency/Value:weekly+' => '', + 'Class:DataFlow/Attribute:execution_frequency/Value:monthly' => '每月', + 'Class:DataFlow/Attribute:execution_frequency/Value:monthly+' => '', + 'Class:DataFlow/Attribute:execution_frequency/Value:yearly' => '每年', + 'Class:DataFlow/Attribute:execution_frequency/Value:yearly+' => '', + 'Class:DataFlow/Attribute:documents_list+' => '例如: 技术规范, 操作手册等.', + 'Class:DataFlow/Attribute:contacts_list+' => '例如: 数据流所有者, 技术支持等.', + 'Class:DataFlow/Error:CheckSource' => 'The source of a data flow cannot be a data flow itself. Choose another source CI than %1$s~~', + 'Class:DataFlow/Error:CheckDestination' => 'The destination of a data flow cannot be a data flow itself. Choose another destination CI than %1$s~~', + + 'Class:DataFlowType' => '数据流类型', + 'Class:DataFlowType+' => '数据流的分类', + +/* + 'Class:DataFlow/Attribute:source_id_friendlyname' => 'source_id_friendlyname', + 'Class:DataFlow/Attribute:source_id_friendlyname+' => 'Full name', + 'Class:DataFlow/Attribute:source_id_finalclass_recall' => 'source_id->CI sub-class', + 'Class:DataFlow/Attribute:source_id_finalclass_recall+' => 'Name of the final class', + 'Class:DataFlow/Attribute:source_id_obsolescence_flag' => 'source_id->Obsolete', + 'Class:DataFlow/Attribute:source_id_obsolescence_flag+' => 'Computed dynamically on other attributes', + 'Class:DataFlow/Attribute:destination_id_friendlyname' => 'destination_id_friendlyname', + 'Class:DataFlow/Attribute:destination_id_friendlyname+' => 'Full name', + 'Class:DataFlow/Attribute:destination_id_finalclass_recall' => 'destination_id->CI sub-class', + 'Class:DataFlow/Attribute:destination_id_finalclass_recall+' => 'Name of the final class', + 'Class:DataFlow/Attribute:destination_id_obsolescence_flag' => 'destination_id->Obsolete', + 'Class:DataFlow/Attribute:destination_id_obsolescence_flag+' => 'Computed dynamically on other attributes', +*/ +]); diff --git a/datamodels/2.x/itop-hub-connector/dictionaries/zh_cn.dict.itop-hub-connector.php b/datamodels/2.x/itop-hub-connector/dictionaries/zh_cn.dict.itop-hub-connector.php index 0f3c4866e2..eb0b3e17bc 100644 --- a/datamodels/2.x/itop-hub-connector/dictionaries/zh_cn.dict.itop-hub-connector.php +++ b/datamodels/2.x/itop-hub-connector/dictionaries/zh_cn.dict.itop-hub-connector.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dictionary entries go here 'Menu:iTopHub' => 'iTop Hub', @@ -39,6 +40,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'iTopHub:AutoSubmit' => '不再询问. 下次自动进入iTop Hub.', 'UI:About:RemoteExtensionSource' => 'iTop Hub', 'iTopHub:Explanation' => '点击这个按钮您将被引导至iTop Hub.', + 'iTopHub:BackupFreeDiskSpaceIn' => '%1$s 可用磁盘空间位于 %2$s.', 'iTopHub:FailedToCheckFreeDiskSpace' => '检查可用磁盘空间失败.', 'iTopHub:BackupOk' => '备份成功.', @@ -48,6 +50,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'iTopHub:CompiledOK' => '编译成功.', 'iTopHub:ConfigurationSafelyReverted' => '安装时发生错误!
系统配置将不会改变.', 'iTopHub:FailAuthent' => '认证失败.', + 'iTopHub:InstalledExtensions' => '本机已安装的扩展', 'iTopHub:ExtensionCategory:Manual' => '手动安装的扩展', 'iTopHub:ExtensionCategory:Manual+' => '下列已安装的扩展是手动将文件放置到 %1$s 目录的:', @@ -57,6 +60,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'iTopHub:NoExtensionInThisCategory+' => '浏览 iTop Hub, 去寻找符合您喜欢的扩展吧.', 'iTopHub:ExtensionNotInstalled' => '未安装', 'iTopHub:GetMoreExtensions' => '从 iTop Hub 获取扩展...', + 'iTopHub:LandingWelcome' => '恭喜! 下列来自 iTop Hub 的扩展已被下载并安装到本机.', 'iTopHub:GoBackToITopBtn' => '返回'.ITOP_APPLICATION_SHORT, 'iTopHub:Uncompressing' => '扩展解压中...', @@ -65,6 +69,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'iTopHub:DBBackupSentence' => '在升级之前,备份数据库和'.ITOP_APPLICATION_SHORT.'配置文件', 'iTopHub:DeployBtn' => '安装!', 'iTopHub:DatabaseBackupProgress' => '实例备份...', + 'iTopHub:InstallationEffect:Install' => '版本: %1$s 将被安装.', 'iTopHub:InstallationEffect:NoChange' => '版本: %1$s 已安装. 保持不变.', 'iTopHub:InstallationEffect:Upgrade' => '将从版本 %1$s 升级到版本 %2$s.', @@ -74,6 +79,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'iTopHub:InstallationEffect:MissingDependencies' => '扩展无法安装, 因为未知的依赖.', 'iTopHub:InstallationEffect:MissingDependencies_Details' => '此扩展依赖模块: %1$s', 'iTopHub:InstallationProgress:InstallationSuccessful' => '安装成功!', + 'iTopHub:InstallationStatus:Installed_Version' => '%1$s 版本: %2$s.', 'iTopHub:InstallationStatus:Installed' => '已安装', 'iTopHub:InstallationStatus:Version_NotInstalled' => '版本 %1$s 未被 安装.', diff --git a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/zh_cn.dict.itop-incident-mgmt-itil.php b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/zh_cn.dict.itop-incident-mgmt-itil.php index 3fb0d9b570..bea50accd1 100644 --- a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/zh_cn.dict.itop-incident-mgmt-itil.php +++ b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/zh_cn.dict.itop-incident-mgmt-itil.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:IncidentManagement' => '事件管理', 'Menu:IncidentManagement+' => '事件管理', @@ -130,7 +131,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Incident/Attribute:servicesubcategory_id+' => '', 'Class:Incident/Attribute:servicesubcategory_name' => '子服务名称', 'Class:Incident/Attribute:servicesubcategory_name+' => '', - 'Class:Incident/Attribute:escalation_flag' => '是否升级', + 'Class:Incident/Attribute:escalation_flag' => '升级标识', 'Class:Incident/Attribute:escalation_flag+' => '', 'Class:Incident/Attribute:escalation_flag/Value:no' => '否', 'Class:Incident/Attribute:escalation_flag/Value:no+' => '否', @@ -150,10 +151,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Incident/Attribute:tto+' => '响应时间', 'Class:Incident/Attribute:ttr' => 'TTR', 'Class:Incident/Attribute:ttr+' => '解决时限', - 'Class:Incident/Attribute:tto_time_spent' => 'TTO time spent~~', - 'Class:Incident/Attribute:tto_time_spent+' => '~~', - 'Class:Incident/Attribute:ttr_time_spent' => 'TTR time spent~~', - 'Class:Incident/Attribute:ttr_time_spent+' => '~~', + 'Class:Incident/Attribute:tto_time_spent' => 'TTO 耗时', + 'Class:Incident/Attribute:tto_time_spent+' => 'TTO 耗时', + 'Class:Incident/Attribute:ttr_time_spent' => 'TTR 耗时', + 'Class:Incident/Attribute:ttr_time_spent+' => 'TTR 耗时', 'Class:Incident/Attribute:tto_escalation_deadline' => 'TTO截止日期', 'Class:Incident/Attribute:tto_escalation_deadline+' => '', 'Class:Incident/Attribute:sla_tto_passed' => 'SLA TTO合格', @@ -169,7 +170,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Incident/Attribute:time_spent' => '耗时', 'Class:Incident/Attribute:time_spent+' => '', 'Class:Incident/Attribute:resolution_code' => '解决方式', - 'Class:Incident/Attribute:resolution_code+' => '', + 'Class:Incident/Attribute:resolution_code+' => '为解决这个事件而做了什么?', 'Class:Incident/Attribute:resolution_code/Value:assistance' => '外部支持', 'Class:Incident/Attribute:resolution_code/Value:assistance+' => '外部支持', 'Class:Incident/Attribute:resolution_code/Value:bug fixed' => '缺陷修复', @@ -204,7 +205,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Incident/Attribute:related_request_list+' => '此事件相关的所有需求', 'Class:Incident/Attribute:child_incidents_list' => '子事件', 'Class:Incident/Attribute:child_incidents_list+' => '此事件相关的所有衍生事件', - 'Class:Incident/Attribute:public_log' => '评论', + 'Class:Incident/Attribute:public_log' => '公共日志', 'Class:Incident/Attribute:public_log+' => '', 'Class:Incident/Attribute:user_satisfaction' => '用户满意度', 'Class:Incident/Attribute:user_satisfaction+' => '', @@ -218,7 +219,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Incident/Attribute:user_satisfaction/Value:4+' => '非常不满意', 'Class:Incident/Attribute:user_comment' => '用户评论', 'Class:Incident/Attribute:user_comment+' => '', - 'Class:Incident/Attribute:parent_incident_id_friendlyname' => '父级事件名称', + 'Class:Incident/Attribute:parent_incident_id_friendlyname' => '父级事件昵称', 'Class:Incident/Attribute:parent_incident_id_friendlyname+' => '', 'Class:Incident/Stimulus:ev_assign' => '分配', 'Class:Incident/Stimulus:ev_assign+' => '', @@ -239,6 +240,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Incident/Stimulus:ev_reopen' => '重新打开', 'Class:Incident/Stimulus:ev_reopen+' => '', 'Class:Incident/Error:CannotAssignParentIncidentIdToSelf' => '无法分配父级事件给自己', + 'Class:Incident/Method:ResolveChildTickets' => '解决子工单', 'Class:Incident/Method:ResolveChildTickets+' => '递归解决子工单 (自动解决), 并调整相关字段与父级工单保持一致: 服务, 团队, 办理人, 解决方案', 'Tickets:Related:OpenIncidents' => '打开的事件', diff --git a/datamodels/2.x/itop-knownerror-mgmt/dictionaries/zh_cn.dict.itop-knownerror-mgmt.php b/datamodels/2.x/itop-knownerror-mgmt/dictionaries/zh_cn.dict.itop-knownerror-mgmt.php index 514b8f71fc..794567b434 100644 --- a/datamodels/2.x/itop-knownerror-mgmt/dictionaries/zh_cn.dict.itop-knownerror-mgmt.php +++ b/datamodels/2.x/itop-knownerror-mgmt/dictionaries/zh_cn.dict.itop-knownerror-mgmt.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + // Dictionnay conventions // Class: // Class:+ @@ -30,10 +31,12 @@ // Class:/Attribute:/Value:+ // Class:/Stimulus: // Class:/Stimulus:+ + ////////////////////////////////////////////////////////////////////// // Classes in 'bizmodel' ////////////////////////////////////////////////////////////////////// // + // Dictionnay conventions // Class: // Class:+ @@ -43,9 +46,11 @@ // Class:/Attribute:/Value:+ // Class:/Stimulus: // Class:/Stimulus:+ + // // Class: KnownError // + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:KnownError' => '已知错误', 'Class:KnownError+' => '记录一个已知错误', @@ -96,8 +101,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkErrorToFunctionalCI' => '关联已知问题/功能配置项', - 'Class:lnkErrorToFunctionalCI+' => '已知问题和功能配置项之间的关联', + 'Class:lnkErrorToFunctionalCI' => '链接 已知问题/功能配置项', + 'Class:lnkErrorToFunctionalCI+' => '已知问题和功能配置项之间的链接', 'Class:lnkErrorToFunctionalCI/Name' => '%1$s / %2$s', 'Class:lnkErrorToFunctionalCI/Attribute:functionalci_id' => '配置项', 'Class:lnkErrorToFunctionalCI/Attribute:functionalci_id+' => '', @@ -116,8 +121,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkDocumentToError' => '关联文档/已知问题', - 'Class:lnkDocumentToError+' => '文档和已知问题之间的关联', + 'Class:lnkDocumentToError' => '链接 文档/已知问题', + 'Class:lnkDocumentToError+' => '文档和已知问题之间的链接', 'Class:lnkDocumentToError/Name' => '%1$s / %2$s', 'Class:lnkDocumentToError/Attribute:document_id' => '文档', 'Class:lnkDocumentToError/Attribute:document_id+' => '', @@ -127,7 +132,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkDocumentToError/Attribute:error_id+' => '', 'Class:lnkDocumentToError/Attribute:error_name' => '已知问题名称', 'Class:lnkDocumentToError/Attribute:error_name+' => '', - 'Class:lnkDocumentToError/Attribute:link_type' => '关联类型', + 'Class:lnkDocumentToError/Attribute:link_type' => '链接类型', 'Class:lnkDocumentToError/Attribute:link_type+' => '', ]); diff --git a/datamodels/2.x/itop-oauth-client/dictionaries/zh_cn.dict.itop-oauth-client.php b/datamodels/2.x/itop-oauth-client/dictionaries/zh_cn.dict.itop-oauth-client.php index 048ad3b9e8..ae7cdd07c5 100644 --- a/datamodels/2.x/itop-oauth-client/dictionaries/zh_cn.dict.itop-oauth-client.php +++ b/datamodels/2.x/itop-oauth-client/dictionaries/zh_cn.dict.itop-oauth-client.php @@ -6,13 +6,16 @@ * @copyright Copyright (C) 2010-2024 Combodo SAS * @license https://opensource.org/licenses/AGPL-3.0 */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:CreateMailbox' => '创建邮箱...', 'Menu:OAuthClient' => 'OAuth Mail Access~~', 'Menu:OAuthClient+' => '', 'Menu:GenerateTokens' => '生成访问令牌...', 'Menu:RegenerateTokens' => '重新生成访问令牌...', + 'itop-oauth-client/Operation:CreateMailBox/Title' => '邮箱创建', + 'itop-oauth-client:UsedForSMTP' => '此OAuth客户端用户SMTP', 'itop-oauth-client:TestSMTP' => '发送测试邮件', 'itop-oauth-client:MissingOAuthClient' => '没有Oauth客户端给用户%1$s', @@ -21,7 +24,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'itop-oauth-client:Message:TokenCreated' => '访问令牌已生成', 'itop-oauth-client:Message:TokenRecreated' => '访问令牌已重新生成', 'itop-oauth-client:Message:TokenError' => '由于服务错误没有生成访问令牌', + 'OAuthClient:Name/UseForSMTPMustBeUnique' => '此组合登录 (%1$s) 和使用于SMTP (%2$s) 已经在OAuth客户端使用', + 'OAuthClient:baseinfo' => '基本信息', 'OAuthClient:scope' => '范围', ]); @@ -59,7 +64,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ This url must be copied in the OAuth2 configuration of the provider Erase the field to recalculate default value EOF - , 'Class:OAuthClient/Attribute:mailbox_list' => '邮箱列表', 'Class:OAuthClient/Attribute:mailbox_list+' => '~~', diff --git a/datamodels/2.x/itop-portal-base/dictionaries/zh_cn.dict.itop-portal-base.php b/datamodels/2.x/itop-portal-base/dictionaries/zh_cn.dict.itop-portal-base.php index 87b44e1b46..ec00c5bec1 100644 --- a/datamodels/2.x/itop-portal-base/dictionaries/zh_cn.dict.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/dictionaries/zh_cn.dict.itop-portal-base.php @@ -1,8 +1,7 @@ '%1$s 用户门户', @@ -84,7 +84,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Brick:Portal:UserProfile:Password:ConfirmPassword' => '确认密码', 'Brick:Portal:UserProfile:Password:CantChangeContactAdministrator' => '要修改密码, 请联系管理员', 'Brick:Portal:UserProfile:Password:CantChangeForUnknownReason' => '无法修改密码, 请联系管理员', - 'Brick:Portal:UserProfile:PersonalInformations:Title' => '人员信息', + 'Brick:Portal:UserProfile:PersonalInformations:Title' => '个体信息', 'Brick:Portal:UserProfile:Photo:Title' => '头像', ]); diff --git a/datamodels/2.x/itop-portal/dictionaries/zh_cn.dict.itop-portal.php b/datamodels/2.x/itop-portal/dictionaries/zh_cn.dict.itop-portal.php index dd0993287f..f9ec027e4e 100644 --- a/datamodels/2.x/itop-portal/dictionaries/zh_cn.dict.itop-portal.php +++ b/datamodels/2.x/itop-portal/dictionaries/zh_cn.dict.itop-portal.php @@ -21,6 +21,7 @@ * @license https://opensource.org/licenses/AGPL-3.0 * @author Benjamin Planque */ + ////////////////////////////////////////////////////////////////////// // Note: The classes have been grouped by categories: bizmodel ////////////////////////////////////////////////////////////////////// @@ -28,6 +29,7 @@ // Classes in 'bizmodel' ////////////////////////////////////////////////////////////////////// // + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'portal:itop-portal' => '标准门户', // This is the portal name that will be displayed in portal dispatcher (eg. URL in menus) 'Page:DefaultTitle' => '%1$s - 用户门户', diff --git a/datamodels/2.x/itop-problem-mgmt/dictionaries/zh_cn.dict.itop-problem-mgmt.php b/datamodels/2.x/itop-problem-mgmt/dictionaries/zh_cn.dict.itop-problem-mgmt.php index 1cf347a8b0..44c288b791 100644 --- a/datamodels/2.x/itop-problem-mgmt/dictionaries/zh_cn.dict.itop-problem-mgmt.php +++ b/datamodels/2.x/itop-problem-mgmt/dictionaries/zh_cn.dict.itop-problem-mgmt.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + // Dictionnay conventions // Class: // Class:+ @@ -30,10 +31,12 @@ // Class:/Attribute:/Value:+ // Class:/Stimulus: // Class:/Stimulus:+ + ////////////////////////////////////////////////////////////////////// // Classes in 'bizmodel' ////////////////////////////////////////////////////////////////////// // + // Dictionnay conventions // Class: // Class:+ @@ -43,6 +46,7 @@ // Class:/Attribute:/Value:+ // Class:/Stimulus: // Class:/Stimulus:+ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:ProblemManagement' => '问题管理', 'Menu:ProblemManagement+' => '问题管理', diff --git a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/zh_cn.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/zh_cn.dict.itop-request-mgmt-itil.php index 406e9f02a4..f9207e8019 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/zh_cn.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/zh_cn.dict.itop-request-mgmt-itil.php @@ -4,6 +4,7 @@ * @copyright Copyright (C) 2010-2024 Combodo SAS * @license http://opensource.org/licenses/AGPL-3.0 */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:RequestManagement' => '服务台', 'Menu:RequestManagement+' => '', @@ -13,10 +14,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:UserRequest:Provider+' => '', 'Menu:UserRequest:Overview' => '概况', 'Menu:UserRequest:Overview+' => '', - 'Menu:NewUserRequest' => '新建用户需求', - 'Menu:NewUserRequest+' => '新建用户需求工单', - 'Menu:SearchUserRequests' => '搜索用户需求', - 'Menu:SearchUserRequests+' => '搜索用户需求', + 'Menu:NewUserRequest' => '新建需求', + 'Menu:NewUserRequest+' => '新建需求工单', + 'Menu:SearchUserRequests' => '搜索需求', + 'Menu:SearchUserRequests+' => '搜索需求', 'Menu:UserRequest:Shortcuts' => '快捷方式', 'Menu:UserRequest:Shortcuts+' => '', 'Menu:UserRequest:MyRequests' => '分配给我的需求', @@ -53,7 +54,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:UserRequest' => '用户需求', + 'Class:UserRequest' => '需求', 'Class:UserRequest+' => '', 'Class:UserRequest/Attribute:status' => '状态', 'Class:UserRequest/Attribute:status+' => '', @@ -135,7 +136,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:UserRequest/Attribute:servicesubcategory_id+' => '', 'Class:UserRequest/Attribute:servicesubcategory_name' => '子服务名称', 'Class:UserRequest/Attribute:servicesubcategory_name+' => '', - 'Class:UserRequest/Attribute:escalation_flag' => '升级标签', + 'Class:UserRequest/Attribute:escalation_flag' => '升级标识', 'Class:UserRequest/Attribute:escalation_flag+' => '', 'Class:UserRequest/Attribute:escalation_flag/Value:no' => '否', 'Class:UserRequest/Attribute:escalation_flag/Value:no+' => '否', @@ -211,7 +212,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:UserRequest/Attribute:parent_incident_ref+' => '~~', 'Class:UserRequest/Attribute:related_request_list' => '子需求', 'Class:UserRequest/Attribute:related_request_list+' => '此父级需求相关的所有衍生需求', - 'Class:UserRequest/Attribute:public_log' => '评论', + 'Class:UserRequest/Attribute:public_log' => '公共日志', 'Class:UserRequest/Attribute:public_log+' => '', 'Class:UserRequest/Attribute:user_satisfaction' => '用户满意度', 'Class:UserRequest/Attribute:user_satisfaction+' => '', @@ -225,7 +226,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:UserRequest/Attribute:user_satisfaction/Value:4+' => '非常不满意', 'Class:UserRequest/Attribute:user_comment' => '用户评论', 'Class:UserRequest/Attribute:user_comment+' => '', - 'Class:UserRequest/Attribute:parent_request_id_friendlyname' => 'parent_request_id_friendlyname', + 'Class:UserRequest/Attribute:parent_request_id_friendlyname' => '父级需求昵称', 'Class:UserRequest/Attribute:parent_request_id_friendlyname+' => '', 'Class:UserRequest/Stimulus:ev_assign' => '分配', 'Class:UserRequest/Stimulus:ev_assign+' => '', @@ -252,12 +253,13 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:UserRequest/Stimulus:ev_wait_for_approval' => '等待批准', 'Class:UserRequest/Stimulus:ev_wait_for_approval+' => '', 'Class:UserRequest/Error:CannotAssignParentRequestIdToSelf' => '无法分配父级需求给自己', + 'Class:UserRequest/Method:ResolveChildTickets' => '解决子工单', 'Class:UserRequest/Method:ResolveChildTickets+' => '递归解决子工单 (自动解决), 并调整相关字段与父级工单保持一致: 服务, 团队, 办理人, 解决方案信息', ]); Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Organization:Overview:UserRequests' => '此组织的所有用户需求', + 'Organization:Overview:UserRequests' => '此组织的所有需求', 'Organization:Overview:MyUserRequests' => '我在此组织发起的需求', 'Organization:Overview:Tickets' => '此组织内的所有工单', ]); diff --git a/datamodels/2.x/itop-request-mgmt/dictionaries/zh_cn.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/dictionaries/zh_cn.dict.itop-request-mgmt.php index 6d09fa082d..c086bbc094 100644 --- a/datamodels/2.x/itop-request-mgmt/dictionaries/zh_cn.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/dictionaries/zh_cn.dict.itop-request-mgmt.php @@ -4,6 +4,7 @@ * @copyright Copyright (C) 2010-2024 Combodo SAS * @license http://opensource.org/licenses/AGPL-3.0 */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:RequestManagement' => '服务台', 'Menu:RequestManagement+' => '', @@ -13,10 +14,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:UserRequest:Provider+' => '', 'Menu:UserRequest:Overview' => '概况', 'Menu:UserRequest:Overview+' => '', - 'Menu:NewUserRequest' => '新建用户需求', - 'Menu:NewUserRequest+' => '新建用户需求工单', - 'Menu:SearchUserRequests' => '搜索用户需求', - 'Menu:SearchUserRequests+' => '搜索用户需求', + 'Menu:NewUserRequest' => '新建需求', + 'Menu:NewUserRequest+' => '新建需求工单', + 'Menu:SearchUserRequests' => '搜索需求', + 'Menu:SearchUserRequests+' => '搜索需求', 'Menu:UserRequest:Shortcuts' => '快捷方式', 'Menu:UserRequest:Shortcuts+' => '', 'Menu:UserRequest:MyRequests' => '分配给我的需求', @@ -57,7 +58,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:UserRequest' => '用户需求', + 'Class:UserRequest' => '需求', 'Class:UserRequest+' => '', 'Class:UserRequest/Attribute:status' => '状态', 'Class:UserRequest/Attribute:status+' => '', @@ -141,7 +142,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:UserRequest/Attribute:servicesubcategory_id+' => '', 'Class:UserRequest/Attribute:servicesubcategory_name' => '子服务名称', 'Class:UserRequest/Attribute:servicesubcategory_name+' => '', - 'Class:UserRequest/Attribute:escalation_flag' => '是否升级', + 'Class:UserRequest/Attribute:escalation_flag' => '升级标识', 'Class:UserRequest/Attribute:escalation_flag+' => '', 'Class:UserRequest/Attribute:escalation_flag/Value:no' => '否', 'Class:UserRequest/Attribute:escalation_flag/Value:no+' => '否', @@ -151,7 +152,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:UserRequest/Attribute:escalation_reason+' => '', 'Class:UserRequest/Attribute:assignment_date' => '分配日期', 'Class:UserRequest/Attribute:assignment_date+' => '', - 'Class:UserRequest/Attribute:resolution_date' => '开始时间', + 'Class:UserRequest/Attribute:resolution_date' => '解决日期', 'Class:UserRequest/Attribute:resolution_date+' => '', 'Class:UserRequest/Attribute:last_pending_date' => '最近待定日期', 'Class:UserRequest/Attribute:last_pending_date+' => '', @@ -161,9 +162,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:UserRequest/Attribute:tto+' => '响应时间', 'Class:UserRequest/Attribute:ttr' => 'TTR', 'Class:UserRequest/Attribute:ttr+' => '解决时限', - 'Class:UserRequest/Attribute:tto_time_spent' => 'TTO time spent~~', + 'Class:UserRequest/Attribute:tto_time_spent' => 'TTO 耗时', 'Class:UserRequest/Attribute:tto_time_spent+' => '~~', - 'Class:UserRequest/Attribute:ttr_time_spent' => 'TTR time spent~~', + 'Class:UserRequest/Attribute:ttr_time_spent' => 'TTR 耗时', 'Class:UserRequest/Attribute:ttr_time_spent+' => '~~', 'Class:UserRequest/Attribute:tto_escalation_deadline' => 'TTO截止日期', 'Class:UserRequest/Attribute:tto_escalation_deadline+' => '', @@ -213,7 +214,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:UserRequest/Attribute:parent_change_ref+' => '', 'Class:UserRequest/Attribute:related_request_list' => '衍生事件', 'Class:UserRequest/Attribute:related_request_list+' => '此事件相关的所有子事件', - 'Class:UserRequest/Attribute:public_log' => '评论', + 'Class:UserRequest/Attribute:public_log' => '公共日志', 'Class:UserRequest/Attribute:public_log+' => '', 'Class:UserRequest/Attribute:user_satisfaction' => '用户满意度', 'Class:UserRequest/Attribute:user_satisfaction+' => '', @@ -227,7 +228,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:UserRequest/Attribute:user_satisfaction/Value:4+' => '', 'Class:UserRequest/Attribute:user_comment' => '用户留言', 'Class:UserRequest/Attribute:user_comment+' => '', - 'Class:UserRequest/Attribute:parent_request_id_friendlyname' => 'parent_request_id_friendlyname', + 'Class:UserRequest/Attribute:parent_request_id_friendlyname' => '父级需求昵称', 'Class:UserRequest/Attribute:parent_request_id_friendlyname+' => '', 'Class:UserRequest/Stimulus:ev_assign' => '分配', 'Class:UserRequest/Stimulus:ev_assign+' => '', @@ -271,20 +272,21 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Portal:UserRequest:MoreInfo' => '更多详细信息', 'Portal:Details-Service-Element' => '服务元素', 'Portal:NoClosedTicket' => '没有关闭的需求', - 'Portal:NoService' => '', + 'Portal:NoService' => '没有可用的服务', 'Portal:ListOpenProblems' => '处理中的问题', 'Portal:ShowProblem' => '问题', 'Portal:ShowFaqs' => 'FAQ', 'Portal:NoOpenProblem' => '没有打开的问题', 'Portal:SelectLanguage' => '更改您的语言', 'Portal:LanguageChangedTo_Lang' => '语言更改为', - 'Portal:ChooseYourFavoriteLanguage' => '选择您喜欢的语言', + 'Portal:ChooseYourFavoriteLanguage' => '请选择您喜欢的语言', + 'Class:UserRequest/Method:ResolveChildTickets' => '解决子工单', 'Class:UserRequest/Method:ResolveChildTickets+' => '递归解决子工单 (自动解决), 并调整相关字段与父级工单保持一致: 服务, 团队, 办理人, 解决方案', ]); Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Organization:Overview:UserRequests' => '来自此组织的用户需求', - 'Organization:Overview:MyUserRequests' => '我在此组织的用户需求', + 'Organization:Overview:UserRequests' => '来自此组织的需求', + 'Organization:Overview:MyUserRequests' => '我在此组织的需求', 'Organization:Overview:Tickets' => '来自此组织的工单', ]); diff --git a/datamodels/2.x/itop-service-mgmt-provider/dictionaries/zh_cn.dict.itop-service-mgmt-provider.php b/datamodels/2.x/itop-service-mgmt-provider/dictionaries/zh_cn.dict.itop-service-mgmt-provider.php index d4461e97e2..323a48498a 100644 --- a/datamodels/2.x/itop-service-mgmt-provider/dictionaries/zh_cn.dict.itop-service-mgmt-provider.php +++ b/datamodels/2.x/itop-service-mgmt-provider/dictionaries/zh_cn.dict.itop-service-mgmt-provider.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + // Dictionnay conventions // Class: // Class:+ @@ -30,6 +31,7 @@ // Class:/Attribute:/Value:+ // Class:/Stimulus: // Class:/Stimulus:+ + // // Menu, fieldsets, UI, messages translations // @@ -41,6 +43,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI-ServiceManagementMenu-ContractsBySrvLevel' => '合同 (按服务等级)', 'UI-ServiceManagementMenu-ContractsByStatus' => '合同 (按状态)', 'UI-ServiceManagementMenu-ContractsEndingIn30Days' => '近30天内截止的合同', + 'Menu:ProviderContract' => '供应商合同', 'Menu:ProviderContract+' => '供应商合同', 'Menu:CustomerContract' => '客户合同', @@ -55,18 +58,18 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:SLT+' => '服务等级目标', 'Menu:DeliveryModel' => '交付模式', 'Menu:DeliveryModel+' => '交付模式', - 'Menu:ServiceFamily' => '服务系列', - 'Menu:ServiceFamily+' => '服务系列', - 'Menu:ServiceCatalog' => 'Service catalog~~', - 'Menu:ServiceCatalog+' => 'Define the service elements of your offering~~', - 'UI-ServiceCatalogMenu-Title' => 'Service catalog~~', - 'UI-ServiceCatalogMenu-NotInPortal' => 'Not displayed in User Portal~~', - 'UI-ServiceCatalogMenu-OnlyProductionInPortal' => 'Only Service and Subcategory on production are visible in User Portal~~', - 'UI-ServiceCatalogMenu-UnusedService' => 'Services not used by any Customers~~', - 'UI-ServiceCatalogMenu-ServiceWithoutFamilyNotInPortal' => 'Services without Service Family are not visible in User Portal~~', - 'UI-ServiceCatalogMenu-SLTBySLA' => 'Count SLTs on each SLA~~', - 'UI-ServiceCatalogMenu-ContractByService' => 'Count Contracts using a Service~~', - 'UI-ServiceCatalogMenu-ContractBySLA' => 'Count Contracts using an SLA~~', + 'Menu:ServiceFamily' => '服务家族', + 'Menu:ServiceFamily+' => '服务家族', + 'Menu:ServiceCatalog' => '服务清单', + 'Menu:ServiceCatalog+' => '定义所有提供的服务', + 'UI-ServiceCatalogMenu-Title' => '服务清单', + 'UI-ServiceCatalogMenu-NotInPortal' => '在用户门户中不可见', + 'UI-ServiceCatalogMenu-OnlyProductionInPortal' => '只有生产状态的服务和子服务才会在用户门户中可见', + 'UI-ServiceCatalogMenu-UnusedService' => '未被任何客户使用的服务', + 'UI-ServiceCatalogMenu-ServiceWithoutFamilyNotInPortal' => '没有服务家族的服务在用户门户中不可见', + 'UI-ServiceCatalogMenu-SLTBySLA' => '统计 SLA/SLT', + 'UI-ServiceCatalogMenu-ContractByService' => '统计 服务/合同', + 'UI-ServiceCatalogMenu-ContractBySLA' => '统计 SLA/合同', 'Contract:baseinfo' => '常规信息', 'Contract:moreinfo' => '合同信息', @@ -90,7 +93,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ContractType' => '合同类型', - 'Class:ContractType+' => '', + 'Class:ContractType+' => '用于对客户和供应商合同进行分类.', ]); // @@ -99,7 +102,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Contract' => '合同', - 'Class:Contract+' => '', + 'Class:Contract+' => '用于处理不同合同类型的抽象类.', 'Class:Contract/Attribute:name' => '名称', 'Class:Contract/Attribute:name+' => '', 'Class:Contract/Attribute:org_id' => '组织', @@ -138,8 +141,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Contract/Attribute:provider_name+' => '', 'Class:Contract/Attribute:status' => '状态', 'Class:Contract/Attribute:status+' => '', - 'Class:Contract/Attribute:status/Value:implementation' => '启用', - 'Class:Contract/Attribute:status/Value:implementation+' => '启用', + 'Class:Contract/Attribute:status/Value:implementation' => '生效', + 'Class:Contract/Attribute:status/Value:implementation+' => '生效', 'Class:Contract/Attribute:status/Value:obsolete' => '废弃', 'Class:Contract/Attribute:status/Value:obsolete+' => '废弃', 'Class:Contract/Attribute:status/Value:production' => '正式', @@ -154,7 +157,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:CustomerContract' => '客户合同', - 'Class:CustomerContract+' => '', + 'Class:CustomerContract+' => 'Agreement between a client and a provider for the delivery of services with an optional level of commitment (SLA, Coverage Window).~~', 'Class:CustomerContract/Attribute:services_list' => '服务', 'Class:CustomerContract/Attribute:services_list+' => '此合同包含的所有服务', 'Class:CustomerContract/Attribute:functionalcis_list' => '配置项', @@ -169,7 +172,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ProviderContract' => '供应商合同', - 'Class:ProviderContract+' => '', + 'Class:ProviderContract+' => 'Agreement between an external provider and an internal organization.~~', 'Class:ProviderContract/Attribute:functionalcis_list' => '配置项', 'Class:ProviderContract/Attribute:functionalcis_list+' => '此合同包含的所有配置项', 'Class:ProviderContract/Attribute:sla' => 'SLA', @@ -183,8 +186,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkContactToContract' => '关联联系人/合同', - 'Class:lnkContactToContract+' => '', + 'Class:lnkContactToContract' => '链接 联系人/合同', + 'Class:lnkContactToContract+' => 'Manages key contacts on each Customer or Provider Contract.~~', 'Class:lnkContactToContract/Name' => '%1$s / %2$s', 'Class:lnkContactToContract/Attribute:contract_id' => '合同', 'Class:lnkContactToContract/Attribute:contract_id+' => '', @@ -201,8 +204,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkContractToDocument' => '关联合同/文档', - 'Class:lnkContractToDocument+' => '', + 'Class:lnkContractToDocument' => '链接 合同/文档', + 'Class:lnkContractToDocument+' => 'Link used when a Document is applicable to a Contract.~~', 'Class:lnkContractToDocument/Name' => '%1$s / %2$s', 'Class:lnkContractToDocument/Attribute:contract_id' => '合同', 'Class:lnkContractToDocument/Attribute:contract_id+' => '', @@ -220,7 +223,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkFunctionalCIToProviderContract' => '关联功能配置项/供应商合同', - 'Class:lnkFunctionalCIToProviderContract+' => '', + 'Class:lnkFunctionalCIToProviderContract+' => 'This link models the Functional CIs that are supported by an external company through a Provider Contract.~~', 'Class:lnkFunctionalCIToProviderContract/Name' => '%1$s / %2$s', 'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_id' => '供应商合同', 'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_id+' => '', @@ -237,8 +240,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:ServiceFamily' => '服务系列', - 'Class:ServiceFamily+' => '', + 'Class:ServiceFamily' => '服务家族', + 'Class:ServiceFamily+' => '服务架构的最高层级.在用户门户中对外提供服务时需要.', 'Class:ServiceFamily/Attribute:name' => '名称', 'Class:ServiceFamily/Attribute:name+' => '', 'Class:ServiceFamily/Attribute:icon' => '图标', @@ -253,7 +256,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Service' => '服务', - 'Class:Service+' => '', + 'Class:Service+' => '服务由组织提供,并通过客户合同订阅. 它必须包含至少一个子服务.', 'Class:Service/ComplementaryName' => '%1$s - %2$s', 'Class:Service/Attribute:name' => '名称', 'Class:Service/Attribute:name+' => '', @@ -263,18 +266,18 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Service/Attribute:organization_name+' => '', 'Class:Service/Attribute:description' => '描述', 'Class:Service/Attribute:description+' => '', - 'Class:Service/Attribute:servicefamily_id' => '服务系列', + 'Class:Service/Attribute:servicefamily_id' => '服务家族', 'Class:Service/Attribute:servicefamily_id+' => 'Required for this service to be visible on User Portal~~', - 'Class:Service/Attribute:servicefamily_name' => '服务系列名称', + 'Class:Service/Attribute:servicefamily_name' => '服务家族名称', 'Class:Service/Attribute:servicefamily_name+' => '', 'Class:Service/Attribute:documents_list' => '文档', 'Class:Service/Attribute:documents_list+' => '此服务相关的所有文档', 'Class:Service/Attribute:contacts_list' => '联系人', 'Class:Service/Attribute:contacts_list+' => '此服务相关的所有联系人', 'Class:Service/Attribute:status' => '状态', - 'Class:Service/Attribute:status+' => 'By default only Service in production are visible by Portal users~~', - 'Class:Service/Attribute:status/Value:implementation' => '启用', - 'Class:Service/Attribute:status/Value:implementation+' => '启用', + 'Class:Service/Attribute:status+' => '默认情况下,只有生产状态的服务才会在用户门户中可见', + 'Class:Service/Attribute:status/Value:implementation' => '生效', + 'Class:Service/Attribute:status/Value:implementation+' => '生效', 'Class:Service/Attribute:status/Value:obsolete' => '废弃', 'Class:Service/Attribute:status/Value:obsolete+' => '', 'Class:Service/Attribute:status/Value:production' => '生产', @@ -292,8 +295,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkDocumentToService' => '关联文档/服务', - 'Class:lnkDocumentToService+' => '', + 'Class:lnkDocumentToService' => '链接 文档/服务', + 'Class:lnkDocumentToService+' => 'Link used when a Document is applicable to a Service.~~', 'Class:lnkDocumentToService/Name' => '%1$s / %2$s', 'Class:lnkDocumentToService/Attribute:service_id' => '服务', 'Class:lnkDocumentToService/Attribute:service_id+' => '', @@ -310,8 +313,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkContactToService' => '关联联系人/服务', - 'Class:lnkContactToService+' => '', + 'Class:lnkContactToService' => '链接 联系人/服务', + 'Class:lnkContactToService+' => 'Ideal for defining the Team to which Tickets created on the related Service will be assigned (automatically or manually).~~', 'Class:lnkContactToService/Name' => '%1$s / %2$s', 'Class:lnkContactToService/Attribute:service_id' => '服务', 'Class:lnkContactToService/Attribute:service_id+' => '', @@ -329,7 +332,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ServiceSubcategory' => '子服务', - 'Class:ServiceSubcategory+' => '', + 'Class:ServiceSubcategory+' => '服务架构的最低层级. 用户需求通常与某个子服务相关联.', 'Class:ServiceSubcategory/ComplementaryName' => '%1$s - %2$s', 'Class:ServiceSubcategory/Attribute:name' => '名称', 'Class:ServiceSubcategory/Attribute:name+' => '', @@ -341,8 +344,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ServiceSubcategory/Attribute:service_name+' => '', 'Class:ServiceSubcategory/Attribute:status' => '状态', 'Class:ServiceSubcategory/Attribute:status+' => '', - 'Class:ServiceSubcategory/Attribute:status/Value:implementation' => '启用', - 'Class:ServiceSubcategory/Attribute:status/Value:implementation+' => '启用', + 'Class:ServiceSubcategory/Attribute:status/Value:implementation' => '生效', + 'Class:ServiceSubcategory/Attribute:status/Value:implementation+' => '生效', 'Class:ServiceSubcategory/Attribute:status/Value:obsolete' => '废弃', 'Class:ServiceSubcategory/Attribute:status/Value:obsolete+' => '废弃', 'Class:ServiceSubcategory/Attribute:status/Value:production' => '生产', @@ -363,7 +366,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SLA' => 'SLA', - 'Class:SLA+' => '', + 'Class:SLA+' => 'Service Level Agreement (SLA) 适用于客户订阅的服务,并通过 SLT 进行衡量和考核.', 'Class:SLA/Attribute:name' => '名称', 'Class:SLA/Attribute:name+' => '', 'Class:SLA/Attribute:description' => '描述', @@ -385,7 +388,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SLT' => 'SLT', - 'Class:SLT+' => '', + 'Class:SLT+' => '服务水平目标位于服务水平协议(SLA)之下. 它定义了(TTO 或 TTR)指标的最大时限, 需求类型 (事件或服务需求) 和优先级.', 'Class:SLT/Attribute:name' => '名称', 'Class:SLT/Attribute:name+' => '', 'Class:SLT/Attribute:priority' => '优先级', @@ -425,8 +428,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkSLAToSLT' => '关联 SLA / SLT', - 'Class:lnkSLAToSLT+' => '', + 'Class:lnkSLAToSLT' => '链接 SLA/SLT', + 'Class:lnkSLAToSLT+' => 'This link indicates that an SLT is included in the Service Level Agreement (SLA). An SLA usually contains several SLTs. An SLT can be reused as is by several SLAs (seldom).~~', 'Class:lnkSLAToSLT/Name' => '%1$s / %2$s', 'Class:lnkSLAToSLT/Attribute:sla_id' => 'SLA', 'Class:lnkSLAToSLT/Attribute:sla_id+' => '', @@ -453,8 +456,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkCustomerContractToService' => '关联客户合同/服务', - 'Class:lnkCustomerContractToService+' => '', + 'Class:lnkCustomerContractToService' => '链接 客户合同/服务', + 'Class:lnkCustomerContractToService+' => 'A single line of a Customer Contract, specifying the Service provided and, for this service, the subscribed commitment levels (Service Level Agreement and Coverage Window).~~', 'Class:lnkCustomerContractToService/Name' => '%1$s / %2$s', 'Class:lnkCustomerContractToService/Attribute:customercontract_id' => '客户合同', 'Class:lnkCustomerContractToService/Attribute:customercontract_id+' => '', @@ -477,8 +480,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkCustomerContractToProviderContract' => '关联 客户合同/供应商合同', - 'Class:lnkCustomerContractToProviderContract+' => '', + 'Class:lnkCustomerContractToProviderContract' => '链接 客户合同/供应商合同', + 'Class:lnkCustomerContractToProviderContract+' => 'This link models when a Provider Contract contributes to the delivery of a Customer Contract.~~', 'Class:lnkCustomerContractToProviderContract/Name' => '%1$s / %2$s', 'Class:lnkCustomerContractToProviderContract/Attribute:customercontract_id' => '客户合同', 'Class:lnkCustomerContractToProviderContract/Attribute:customercontract_id+' => '', @@ -495,8 +498,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkCustomerContractToFunctionalCI' => '关联客户合同/功能配置项', - 'Class:lnkCustomerContractToFunctionalCI+' => '', + 'Class:lnkCustomerContractToFunctionalCI' => '链接 客户合同/功能配置项', + 'Class:lnkCustomerContractToFunctionalCI+' => 'This link models the equipment (Functional CI) covered by a Customer Contract.~~', 'Class:lnkCustomerContractToFunctionalCI/Name' => '%1$s / %2$s', 'Class:lnkCustomerContractToFunctionalCI/Attribute:customercontract_id' => '客户合同', 'Class:lnkCustomerContractToFunctionalCI/Attribute:customercontract_id+' => '', @@ -514,7 +517,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DeliveryModel' => '交付模式', - 'Class:DeliveryModel+' => '', + 'Class:DeliveryModel+' => '交付模式指定了可以分配工单的团队;它必须在联系人选项卡中包含至少一个团队. +每个客户组织都必须有定义好的交付模式.', 'Class:DeliveryModel/Attribute:name' => '名称', 'Class:DeliveryModel/Attribute:name+' => '', 'Class:DeliveryModel/Attribute:org_id' => '组织', @@ -524,7 +528,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DeliveryModel/Attribute:description' => '描述', 'Class:DeliveryModel/Attribute:description+' => '', 'Class:DeliveryModel/Attribute:contacts_list' => '联系人', - 'Class:DeliveryModel/Attribute:contacts_list+' => '此交付模式的所有联系人 (包括团队和人员)', + 'Class:DeliveryModel/Attribute:contacts_list+' => '此交付模式的所有联系人 (包括团队和个体)', 'Class:DeliveryModel/Attribute:customers_list' => '客户', 'Class:DeliveryModel/Attribute:customers_list+' => '使用此交付模式的所有客户', ]); @@ -534,8 +538,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkDeliveryModelToContact' => '关联 交付模式/联系人', - 'Class:lnkDeliveryModelToContact+' => '', + 'Class:lnkDeliveryModelToContact' => '链接 交付模式/联系人', + 'Class:lnkDeliveryModelToContact+' => 'This link specifies the role of a Team (more rarely a Person) within a Delivery Model.~~', 'Class:lnkDeliveryModelToContact/Name' => '%1$s / %2$s', 'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_id' => '交付模式', 'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_id+' => '', diff --git a/datamodels/2.x/itop-service-mgmt/dictionaries/zh_cn.dict.itop-service-mgmt.php b/datamodels/2.x/itop-service-mgmt/dictionaries/zh_cn.dict.itop-service-mgmt.php index 05d3883f2a..227d78747d 100644 --- a/datamodels/2.x/itop-service-mgmt/dictionaries/zh_cn.dict.itop-service-mgmt.php +++ b/datamodels/2.x/itop-service-mgmt/dictionaries/zh_cn.dict.itop-service-mgmt.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + // Dictionnay conventions // Class: // Class:+ @@ -30,7 +31,10 @@ // Class:/Attribute:/Value:+ // Class:/Stimulus: // Class:/Stimulus:+ + +// // Menu, fieldsets, UI, messages translations +// Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:ServiceManagement' => '服务管理', 'Menu:ServiceManagement+' => '服务管理概况', @@ -39,6 +43,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI-ServiceManagementMenu-ContractsBySrvLevel' => '合同 (按服务等级)', 'UI-ServiceManagementMenu-ContractsByStatus' => '合同 (按状态)', 'UI-ServiceManagementMenu-ContractsEndingIn30Days' => '未来30天内截止的合同', + 'Menu:ProviderContract' => '供应商合同', 'Menu:ProviderContract+' => '供应商合同', 'Menu:CustomerContract' => '客户合同', @@ -53,18 +58,18 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:SLT+' => '服务等级目标', 'Menu:DeliveryModel' => '交付模式', 'Menu:DeliveryModel+' => '交付模式', - 'Menu:ServiceFamily' => '服务系列', - 'Menu:ServiceFamily+' => '服务系列', - 'Menu:ServiceCatalog' => 'Service catalog~~', - 'Menu:ServiceCatalog+' => 'Define the service elements of your offering~~', - 'UI-ServiceCatalogMenu-Title' => 'Service catalog~~', - 'UI-ServiceCatalogMenu-NotInPortal' => 'Not displayed in User Portal~~', - 'UI-ServiceCatalogMenu-OnlyProductionInPortal' => 'Only Service and Subcategory on production are visible in User Portal~~', - 'UI-ServiceCatalogMenu-UnusedService' => 'Services not used by any Customers~~', - 'UI-ServiceCatalogMenu-ServiceWithoutFamilyNotInPortal' => 'Services without Service Family are not visible in User Portal~~', - 'UI-ServiceCatalogMenu-SLTBySLA' => 'Count SLTs on each SLA~~', - 'UI-ServiceCatalogMenu-ContractByService' => 'Count Contracts using a Service~~', - 'UI-ServiceCatalogMenu-ContractBySLA' => 'Count Contracts using an SLA~~', + 'Menu:ServiceFamily' => '服务家族', + 'Menu:ServiceFamily+' => '服务家族', + 'Menu:ServiceCatalog' => '服务清单', + 'Menu:ServiceCatalog+' => '定义可提供的服务', + 'UI-ServiceCatalogMenu-Title' => '服务清单', + 'UI-ServiceCatalogMenu-NotInPortal' => '在用户门户中不可见', + 'UI-ServiceCatalogMenu-OnlyProductionInPortal' => '只有生产状态的服务和子服务才会在用户门户中可见', + 'UI-ServiceCatalogMenu-UnusedService' => '未被任何客户使用的服务', + 'UI-ServiceCatalogMenu-ServiceWithoutFamilyNotInPortal' => '没有服务家族的服务在用户门户中不可见', + 'UI-ServiceCatalogMenu-SLTBySLA' => '统计 SLA/SLT', + 'UI-ServiceCatalogMenu-ContractByService' => '统计 服务/合同', + 'UI-ServiceCatalogMenu-ContractBySLA' => '统计 SLA/合同', 'Menu:Procedure' => '流程清单', 'Menu:Procedure+' => '所有流程清单', @@ -79,8 +84,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Organization/Attribute:deliverymodel_id' => '交付模式', - 'Class:Organization/Attribute:deliverymodel_id+' => 'This is required for Tickets handling. -The delivery model specifies the teams to which tickets can be assigned.~~', + 'Class:Organization/Attribute:deliverymodel_id+' => '工单处理必备. +交付模式指定了工单可以分配到的团队.', 'Class:Organization/Attribute:deliverymodel_name' => '交付模式名称', ]); @@ -90,7 +95,7 @@ The delivery model specifies the teams to which tickets can be assigned.~~', Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ContractType' => '合同类型', - 'Class:ContractType+' => '', + 'Class:ContractType+' => '用于对客户和供应商合同进行分类.', ]); // @@ -99,7 +104,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Contract' => '合同', - 'Class:Contract+' => '', + 'Class:Contract+' => '用于处理不同合同类型的抽象类.', 'Class:Contract/Attribute:name' => '名称', 'Class:Contract/Attribute:name+' => '', 'Class:Contract/Attribute:org_id' => '客户', @@ -138,8 +143,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Contract/Attribute:provider_name+' => '通用名称', 'Class:Contract/Attribute:status' => '状态', 'Class:Contract/Attribute:status+' => '', - 'Class:Contract/Attribute:status/Value:implementation' => '启用', - 'Class:Contract/Attribute:status/Value:implementation+' => '启用', + 'Class:Contract/Attribute:status/Value:implementation' => '生效', + 'Class:Contract/Attribute:status/Value:implementation+' => '生效', 'Class:Contract/Attribute:status/Value:obsolete' => '废弃', 'Class:Contract/Attribute:status/Value:obsolete+' => '废弃', 'Class:Contract/Attribute:status/Value:production' => '生产', @@ -153,7 +158,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:CustomerContract' => '客户合同', - 'Class:CustomerContract+' => '', + 'Class:CustomerContract+' => 'Agreement between a client and a provider for the delivery of services with an optional level of commitment (SLA, Coverage Window).~~', 'Class:CustomerContract/Attribute:services_list' => '服务', 'Class:CustomerContract/Attribute:services_list+' => '此合同包含的所有服务', ]); @@ -164,7 +169,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ProviderContract' => '供应商合同', - 'Class:ProviderContract+' => '', + 'Class:ProviderContract+' => 'Agreement between an external provider and an internal organization.~~', 'Class:ProviderContract/Attribute:functionalcis_list' => '配置项', 'Class:ProviderContract/Attribute:functionalcis_list+' => '此供应商合同包含的所有配置项', 'Class:ProviderContract/Attribute:sla' => 'SLA', @@ -184,8 +189,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkContactToContract' => '关联 联系人/合同', - 'Class:lnkContactToContract+' => '', + 'Class:lnkContactToContract' => '链接 联系人/合同', + 'Class:lnkContactToContract+' => 'Manages key contacts on each customer or provider contract.~~', 'Class:lnkContactToContract/Name' => '%1$s / %2$s', 'Class:lnkContactToContract/Attribute:contract_id' => '合同', 'Class:lnkContactToContract/Attribute:contract_id+' => '', @@ -202,8 +207,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkContractToDocument' => '关联合同/文档', - 'Class:lnkContractToDocument+' => '', + 'Class:lnkContractToDocument' => '链接 合同/文档', + 'Class:lnkContractToDocument+' => 'Link used when a Document is applicable to a Contract.~~', 'Class:lnkContractToDocument/Name' => '%1$s / %2$s', 'Class:lnkContractToDocument/Attribute:contract_id' => '合同', 'Class:lnkContractToDocument/Attribute:contract_id+' => '', @@ -220,8 +225,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:ServiceFamily' => '服务系列', - 'Class:ServiceFamily+' => '', + 'Class:ServiceFamily' => '服务家族', + 'Class:ServiceFamily+' => '服务架构的最高层级.在用户门户中对外提供服务时需要.', 'Class:ServiceFamily/Attribute:name' => '名称', 'Class:ServiceFamily/Attribute:name+' => '', 'Class:ServiceFamily/Attribute:icon' => '图标', @@ -236,7 +241,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Service' => '服务', - 'Class:Service+' => '', + 'Class:Service+' => '服务由组织提供并通过客户合同订阅.它必须包含至少一个子服务.', 'Class:Service/ComplementaryName' => '%1$s - %2$s', 'Class:Service/Attribute:name' => '名称', 'Class:Service/Attribute:name+' => '', @@ -244,9 +249,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Service/Attribute:org_id+' => '', 'Class:Service/Attribute:organization_name' => '供应商名称', 'Class:Service/Attribute:organization_name+' => '', - 'Class:Service/Attribute:servicefamily_id' => '服务系列', + 'Class:Service/Attribute:servicefamily_id' => '服务家族', 'Class:Service/Attribute:servicefamily_id+' => 'Required for this service to be visible on User Portal~~', - 'Class:Service/Attribute:servicefamily_name' => '服务系列名称', + 'Class:Service/Attribute:servicefamily_name' => '服务家族名称', 'Class:Service/Attribute:servicefamily_name+' => '', 'Class:Service/Attribute:description' => '描述', 'Class:Service/Attribute:description+' => '', @@ -255,9 +260,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Service/Attribute:contacts_list' => '联系人', 'Class:Service/Attribute:contacts_list+' => '此服务相关的所有联系人', 'Class:Service/Attribute:status' => '状态', - 'Class:Service/Attribute:status+' => 'By default only Service in production are visible by Portal users~~', - 'Class:Service/Attribute:status/Value:implementation' => '启用', - 'Class:Service/Attribute:status/Value:implementation+' => '启用', + 'Class:Service/Attribute:status+' => '默认情况下,只有生产状态的服务才会在用户门户中可见', + 'Class:Service/Attribute:status/Value:implementation' => '生效', + 'Class:Service/Attribute:status/Value:implementation+' => '生效', 'Class:Service/Attribute:status/Value:obsolete' => '废弃', 'Class:Service/Attribute:status/Value:obsolete+' => '', 'Class:Service/Attribute:status/Value:production' => '生产', @@ -279,8 +284,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkDocumentToService' => '关联文档/服务', - 'Class:lnkDocumentToService+' => '', + 'Class:lnkDocumentToService' => '链接 文档/服务', + 'Class:lnkDocumentToService+' => 'Link used when a Document is applicable to a Service.~~', 'Class:lnkDocumentToService/Name' => '%1$s / %2$s', 'Class:lnkDocumentToService/Attribute:service_id' => '服务', 'Class:lnkDocumentToService/Attribute:service_id+' => '', @@ -297,8 +302,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkContactToService' => '关联联系人/服务', - 'Class:lnkContactToService+' => '', + 'Class:lnkContactToService' => '链接 联系人/服务', + 'Class:lnkContactToService+' => 'Ideal for defining the team to which Tickets created on the related Service will be assigned (automatically or manually).~~', 'Class:lnkContactToService/Name' => '%1$s / %2$s', 'Class:lnkContactToService/Attribute:service_id' => '服务', 'Class:lnkContactToService/Attribute:service_id+' => '', @@ -316,7 +321,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ServiceSubcategory' => '子服务', - 'Class:ServiceSubcategory+' => '', + 'Class:ServiceSubcategory+' => '服务架构的最低层级. 用户需求通常与某个子服务相关联.', 'Class:ServiceSubcategory/ComplementaryName' => '%1$s - %2$s', 'Class:ServiceSubcategory/Attribute:name' => '名称', 'Class:ServiceSubcategory/Attribute:name+' => '', @@ -334,8 +339,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ServiceSubcategory/Attribute:request_type/Value:service_request+' => '服务需求', 'Class:ServiceSubcategory/Attribute:status' => '状态', 'Class:ServiceSubcategory/Attribute:status+' => '', - 'Class:ServiceSubcategory/Attribute:status/Value:implementation' => '启用', - 'Class:ServiceSubcategory/Attribute:status/Value:implementation+' => '启用', + 'Class:ServiceSubcategory/Attribute:status/Value:implementation' => '生效', + 'Class:ServiceSubcategory/Attribute:status/Value:implementation+' => '生效', 'Class:ServiceSubcategory/Attribute:status/Value:obsolete' => '废弃', 'Class:ServiceSubcategory/Attribute:status/Value:obsolete+' => '废弃', 'Class:ServiceSubcategory/Attribute:status/Value:production' => '生产', @@ -348,7 +353,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SLA' => 'SLA', - 'Class:SLA+' => '', + 'Class:SLA+' => 'Service Level Agreement (SLA) 适用于客户订阅的服务,并通过 SLT 进行衡量和考核.', 'Class:SLA/Attribute:name' => '名称', 'Class:SLA/Attribute:name+' => '', 'Class:SLA/Attribute:description' => '描述', @@ -361,7 +366,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SLA/Attribute:slts_list+' => '此SLA包含的所有服务等级目标', 'Class:SLA/Attribute:customercontracts_list' => '客户合同', 'Class:SLA/Attribute:customercontracts_list+' => '使用此SLA的所有客户合同', - 'Class:SLA/Error:UniqueLnkCustomerContractToService' => '不能保存客户合同%1$s与服务%2$s的关联: SLA已存在', + 'Class:SLA/Error:UniqueLnkCustomerContractToService' => '无法保存客户合同%1$s与服务%2$s的链接: SLA已存在', ]); // @@ -370,7 +375,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SLT' => 'SLT', - 'Class:SLT+' => '', + 'Class:SLT+' => '服务水平目标(SLT)位于服务水平协议(SLA)之下. 它定义了(TTO 或 TTR)指标的最大时限, 需求类型 (事件或服务需求) 和优先级.', 'Class:SLT/Attribute:name' => '名称', 'Class:SLT/Attribute:name+' => '', 'Class:SLT/Attribute:priority' => '优先级', @@ -404,7 +409,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SLT/Attribute:unit/Value:minutes' => '分钟', 'Class:SLT/Attribute:unit/Value:minutes+' => '分钟', 'Class:SLT/Attribute:slas_list' => 'SLA', - 'Class:SLT/Attribute:slas_list+' => '所有使用此 SLT 的 SLA', + 'Class:SLT/Attribute:slas_list+' => '使用此 SLT 的所有 SLA', ]); // @@ -412,8 +417,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkSLAToSLT' => '关联SLA/SLT', - 'Class:lnkSLAToSLT+' => '', + 'Class:lnkSLAToSLT' => '链接 SLA/SLT', + 'Class:lnkSLAToSLT+' => 'This link indicates that an SLT is included in the Service Level Agreement (SLA). An SLA usually contains several SLTs. An SLT can be reused as is by several SLAs (seldom).~~', 'Class:lnkSLAToSLT/Name' => '%1$s / %2$s', 'Class:lnkSLAToSLT/Attribute:sla_id' => 'SLA', 'Class:lnkSLAToSLT/Attribute:sla_id+' => '', @@ -440,8 +445,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkCustomerContractToService' => '关联客户合同/服务', - 'Class:lnkCustomerContractToService+' => '', + 'Class:lnkCustomerContractToService' => '链接 客户合同/服务', + 'Class:lnkCustomerContractToService+' => 'A single line of a customer contract, specifying the Service provided and, for this service, the subscribed commitment levels (Service Level Aggrement and Coverage Window).~~', 'Class:lnkCustomerContractToService/Name' => '%1$s / %2$s', 'Class:lnkCustomerContractToService/Attribute:customercontract_id' => '客户合同', 'Class:lnkCustomerContractToService/Attribute:customercontract_id+' => '', @@ -455,6 +460,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkCustomerContractToService/Attribute:sla_id+' => '', 'Class:lnkCustomerContractToService/Attribute:sla_name' => 'SLA名称', 'Class:lnkCustomerContractToService/Attribute:sla_name+' => '', + 'Class:lnkCustomerContractToService/Attribute:provider_id' => 'Provider id~~', + 'Class:lnkCustomerContractToService/Attribute:provider_id+' => '', ]); // @@ -462,8 +469,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkProviderContractToService' => '关联供应商合同/服务', - 'Class:lnkProviderContractToService+' => '', + 'Class:lnkProviderContractToService' => '链接 供应商合同/服务', + 'Class:lnkProviderContractToService+' => 'This link can model that a provider contract enables the delivery of a Service.~~', 'Class:lnkProviderContractToService/Name' => '%1$s / %2$s', 'Class:lnkProviderContractToService/Attribute:service_id' => '服务', 'Class:lnkProviderContractToService/Attribute:service_id+' => '', @@ -473,6 +480,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkProviderContractToService/Attribute:providercontract_id+' => '', 'Class:lnkProviderContractToService/Attribute:providercontract_name' => '供应商合同名称', 'Class:lnkProviderContractToService/Attribute:providercontract_name+' => '', + 'Class:lnkProviderContractToService/Attribute:provider_id' => 'Provider id~~', + 'Class:lnkProviderContractToService/Attribute:provider_id+' => '', ]); // @@ -481,7 +490,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DeliveryModel' => '交付模式', - 'Class:DeliveryModel+' => '', + 'Class:DeliveryModel+' => '交付模式指定了可以分配工单的团队;它必须在联系人选项卡中包含至少一个团队. +每个客户组织都必须有定义好的交付模式.', 'Class:DeliveryModel/Attribute:name' => '名称', 'Class:DeliveryModel/Attribute:name+' => 'Don\'t forget to add teams to this delivery model~~', 'Class:DeliveryModel/Attribute:org_id' => '组织', @@ -491,9 +501,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DeliveryModel/Attribute:description' => '描述', 'Class:DeliveryModel/Attribute:description+' => '', 'Class:DeliveryModel/Attribute:contacts_list' => '联系人', - 'Class:DeliveryModel/Attribute:contacts_list+' => '此交付模式相关的所有联系人 (包括团队和人员)', + 'Class:DeliveryModel/Attribute:contacts_list+' => '此交付模式相关的所有联系人 (包括团队和个体)', 'Class:DeliveryModel/Attribute:customers_list' => '客户', - 'Class:DeliveryModel/Attribute:customers_list+' => '所有使用此交付模式的客户', + 'Class:DeliveryModel/Attribute:customers_list+' => '使用此交付模式的所有客户', ]); // @@ -501,8 +511,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkDeliveryModelToContact' => '关联交付模式/联系人', - 'Class:lnkDeliveryModelToContact+' => '', + 'Class:lnkDeliveryModelToContact' => '链接 交付模式/联系人', + 'Class:lnkDeliveryModelToContact+' => 'This link specifies the role of a Team (more rarely a Person) within a Delivery Model.~~', 'Class:lnkDeliveryModelToContact/Name' => '%1$s / %2$s', 'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_id' => '交付模式', 'Class:lnkDeliveryModelToContact/Attribute:deliverymodel_id+' => '', @@ -539,21 +549,3 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkContractToDocument/Attribute:provider_id' => 'Provider id~~', 'Class:lnkContractToDocument/Attribute:provider_id+' => '~~', ]); - -// -// Class: lnkCustomerContractToService -// - -Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkCustomerContractToService/Attribute:provider_id' => 'Provider id~~', - 'Class:lnkCustomerContractToService/Attribute:provider_id+' => '~~', -]); - -// -// Class: lnkProviderContractToService -// - -Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkProviderContractToService/Attribute:provider_id' => 'Provider id~~', - 'Class:lnkProviderContractToService/Attribute:provider_id+' => '~~', -]); diff --git a/datamodels/2.x/itop-storage-mgmt/dictionaries/zh_cn.dict.itop-storage-mgmt.php b/datamodels/2.x/itop-storage-mgmt/dictionaries/zh_cn.dict.itop-storage-mgmt.php index 6f33c74572..c523361045 100644 --- a/datamodels/2.x/itop-storage-mgmt/dictionaries/zh_cn.dict.itop-storage-mgmt.php +++ b/datamodels/2.x/itop-storage-mgmt/dictionaries/zh_cn.dict.itop-storage-mgmt.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dictionary entries go here ]); @@ -30,12 +31,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:NASFileSystem/Attribute:org_id' => 'Org id~~', - 'Class:NASFileSystem/Attribute:org_id+' => '~~', - 'Class:NASFileSystem/Attribute:location_id' => 'Location id~~', - 'Class:NASFileSystem/Attribute:location_id+' => '~~', - 'Class:NASFileSystem/Attribute:location_name' => 'Location name~~', - 'Class:NASFileSystem/Attribute:location_name+' => '~~', + 'Class:NASFileSystem/Attribute:org_id' => '组织 ID', + 'Class:NASFileSystem/Attribute:org_id+' => '', + 'Class:NASFileSystem/Attribute:location_id' => '位置 ID', + 'Class:NASFileSystem/Attribute:location_id+' => '', + 'Class:NASFileSystem/Attribute:location_name' => '位置名称', + 'Class:NASFileSystem/Attribute:location_name+' => '', ]); // @@ -43,10 +44,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:FiberChannelInterface/Attribute:org_id' => 'Org id~~', - 'Class:FiberChannelInterface/Attribute:org_id+' => '~~', - 'Class:FiberChannelInterface/Attribute:location_id' => 'Location id~~', - 'Class:FiberChannelInterface/Attribute:location_id+' => '~~', + 'Class:FiberChannelInterface/Name' => '%2$s %1$s', + 'Class:FiberChannelInterface/Attribute:org_id' => '组织 ID', + 'Class:FiberChannelInterface/Attribute:org_id+' => '', + 'Class:FiberChannelInterface/Attribute:location_id' => '位置 ID', + 'Class:FiberChannelInterface/Attribute:location_id+' => '', ]); // @@ -54,10 +56,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:LogicalVolume/Attribute:org_id' => 'Org id~~', - 'Class:LogicalVolume/Attribute:org_id+' => '~~', - 'Class:LogicalVolume/Attribute:location_id' => 'Location id~~', - 'Class:LogicalVolume/Attribute:location_id+' => '~~', - 'Class:LogicalVolume/Attribute:location_name' => 'Location name~~', - 'Class:LogicalVolume/Attribute:location_name+' => '~~', + 'Class:LogicalVolume/Attribute:org_id' => '组织 ID', + 'Class:LogicalVolume/Attribute:org_id+' => '', + 'Class:LogicalVolume/Attribute:location_id' => '位置 ID', + 'Class:LogicalVolume/Attribute:location_id+' => '', + 'Class:LogicalVolume/Attribute:location_name' => '位置名称', + 'Class:LogicalVolume/Attribute:location_name+' => '', ]); diff --git a/datamodels/2.x/itop-structure/dictionaries/zh_cn.dict.itop-structure.php b/datamodels/2.x/itop-structure/dictionaries/zh_cn.dict.itop-structure.php index 2e88b43bba..a92a704301 100644 --- a/datamodels/2.x/itop-structure/dictionaries/zh_cn.dict.itop-structure.php +++ b/datamodels/2.x/itop-structure/dictionaries/zh_cn.dict.itop-structure.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + // Dictionnary conventions // Class: // Class:+ @@ -32,6 +33,7 @@ // Class:/Stimulus:+ // Class:/UniquenessRule: // Class:/UniquenessRule:+ + ////////////////////////////////////////////////////////////////////// // Note: The classes have been grouped by categories: bizmodel ////////////////////////////////////////////////////////////////////// @@ -39,12 +41,14 @@ // Classes in 'bizmodel' ////////////////////////////////////////////////////////////////////// // + // // Class: Organization // + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Organization' => '组织', - 'Class:Organization+' => '', + 'Class:Organization+' => 'It can be a customer, a provider, your company or departments within your company. Organizations can be structured hierarchically. Users can be limited to objects belonging to some organizations only.~~', 'Class:Organization/Attribute:name' => '名称', 'Class:Organization/Attribute:name+' => '常用名称', 'Class:Organization/Attribute:code' => '编码', @@ -91,7 +95,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Location/Attribute:org_name' => '所属组织名称', 'Class:Location/Attribute:org_name+' => '', 'Class:Location/Attribute:address' => '地址', - 'Class:Location/Attribute:address+' => '地址', + 'Class:Location/Attribute:address+' => '邮政地址', 'Class:Location/Attribute:postal_code' => '邮编', 'Class:Location/Attribute:postal_code+' => '邮政编码', 'Class:Location/Attribute:city' => '城市', @@ -110,7 +114,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Contact' => '联系人', - 'Class:Contact+' => '', + 'Class:Contact+' => '抽象类. 联系人可以与工单和功能配置项关联,用于不同的目的,例如事件分配和通知.', 'Class:Contact/ComplementaryName' => '%1$s - %2$s', 'Class:Contact/Attribute:name' => '名称', 'Class:Contact/Attribute:name+' => '', @@ -129,7 +133,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Contact/Attribute:phone' => '电话', 'Class:Contact/Attribute:phone+' => '', 'Class:Contact/Attribute:notify' => '通知', - 'Class:Contact/Attribute:notify+' => '', + 'Class:Contact/Attribute:notify+' => 'Flag which can be used by each notification~~', 'Class:Contact/Attribute:notify/Value:no' => '否', 'Class:Contact/Attribute:notify/Value:no+' => '否', 'Class:Contact/Attribute:notify/Value:yes' => '是', @@ -137,7 +141,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Contact/Attribute:function' => '职责', 'Class:Contact/Attribute:function+' => '', 'Class:Contact/Attribute:cis_list' => '配置项', - 'Class:Contact/Attribute:cis_list+' => '此联系人关联的所有配置项', + 'Class:Contact/Attribute:cis_list+' => '此联系人相关的所有配置项', 'Class:Contact/Attribute:finalclass' => '联系人类型', 'Class:Contact/Attribute:finalclass+' => '根本属性的名称', ]); @@ -147,9 +151,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:Person' => '人员', - 'Class:Person+' => '', - 'Class:Person/ComplementaryName' => '%1$s - %2$s~~', + 'Class:Person' => '个体', + 'Class:Person+' => 'A type of Contact used to describe physical persons. Persons can be grouped into Teams. Persons can be linked to other configuration items (eg. to describe who to contact in case of incident with an application). +Other usage: the caller of a User request is a Person as well as the agent assigned to resolve it.~~', + 'Class:Person/ComplementaryName' => '%1$s - %2$s', 'Class:Person/Attribute:name' => '姓', 'Class:Person/Attribute:name+' => '', 'Class:Person/Attribute:first_name' => '名', @@ -167,11 +172,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Person/Attribute:manager_name' => '名称', 'Class:Person/Attribute:manager_name+' => '', 'Class:Person/Attribute:team_list' => '团队', - 'Class:Person/Attribute:team_list+' => '这人员归属的所有团队', + 'Class:Person/Attribute:team_list+' => '这个体归属的所有团队', 'Class:Person/Attribute:tickets_list' => '工单', 'Class:Person/Attribute:tickets_list+' => '此人发起的所有工单', + 'Class:Person/Attribute:tickets_list/UI:Links:Create:Modal:Title' => 'Create a %4$s for %2$s~~', 'Class:Person/Attribute:user_list' => '用户', - 'Class:Person/Attribute:user_list+' => '所有关联到此人员的用户', + 'Class:Person/Attribute:user_list+' => '此个体相关的所有用户', 'Class:Person/Attribute:manager_id_friendlyname' => '直属上级姓名', 'Class:Person/Attribute:manager_id_friendlyname+' => '', 'Class:Person/Attribute:picture' => '头像', @@ -180,7 +186,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Person/UniquenessRule:employee_number' => '\'$this->org_name$\' 内已经有人占用了这个员工号', 'Class:Person/UniquenessRule:name+' => '同一组织内的员工姓名必须唯一', 'Class:Person/UniquenessRule:name' => '\'$this->org_name$\' 内已经有人叫这个名字', - 'Class:Person/Error:ChangingOrgDenied' => '无法移动此人员到组织 \'%1$s\' 因为这将终端其用户门户的访问, 其关联的用户没有被授权访问此组织', + 'Class:Person/Error:ChangingOrgDenied' => '无法移动此个体到组织 \'%1$s\' 因为这将终端其用户门户的访问, 其关联的用户没有被授权访问此组织', ]); // @@ -189,19 +195,19 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Team' => '团队', - 'Class:Team+' => '', + 'Class:Team+' => 'A type of Contact. Often used to group Persons, but not only. Teams are expected to watch Tickets dispatched to them, and assign it to an agent, usually a member of that team.~~', 'Class:Team/ComplementaryName' => '%1$s - %2$s', 'Class:Team/Attribute:persons_list' => '成员', 'Class:Team/Attribute:persons_list+' => '此团队包含的所有成员', - 'Class:Team/Attribute:overview' => 'Overview~~', - 'Team:Overview' => 'Tickets and CIs managed by the team~~', - 'Team:Overview:ActiveTickets' => 'Active tickets~~', - 'Team:Overview:FunctionalCIs-ByType' => 'Functional CIs~~', - 'Team:Overview:UserRequest-ByStatus' => 'User requests by status~~', - 'Team:Overview:UserRequest-ClosedByMonth' => 'User requests closed in last 12 months~~', - 'Team:Overview:UserRequest-ClosedByAgent' => 'User requests closed by agent in last 12 months~~', + 'Class:Team/Attribute:overview' => '概览', + 'Team:Overview' => '此团队管理的工单和配置项', + 'Team:Overview:ActiveTickets' => '活跃的工单', + 'Team:Overview:FunctionalCIs-ByType' => '功能配置项', + 'Team:Overview:UserRequest-ByStatus' => '用户请求状态', + 'Team:Overview:UserRequest-ClosedByMonth' => '过去12个月关闭的需求', + 'Team:Overview:UserRequest-ClosedByAgent' => '过去12个月由办理人关闭的需求', 'Class:Team/Attribute:tickets_list' => '工单', - 'Class:Team/Attribute:tickets_list+' => '此团队的所有工单', + 'Class:Team/Attribute:tickets_list+' => '此团队分配到的所有工单', ]); // @@ -210,7 +216,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Document' => '文档', - 'Class:Document+' => '', + 'Class:Document+' => 'Abstract class. A document that can be shared across multiple objects, making it easy and quick to retrieve from all relevant locations.~~', 'Class:Document/ComplementaryName' => '%1$s - %2$s - %3$s', 'Class:Document/Attribute:name' => '名称', 'Class:Document/Attribute:name+' => '', @@ -235,7 +241,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Document/Attribute:status/Value:published' => '正式', 'Class:Document/Attribute:status/Value:published+' => '', 'Class:Document/Attribute:cis_list' => '配置项', - 'Class:Document/Attribute:cis_list+' => '此文档关联的所有配置项', + 'Class:Document/Attribute:cis_list+' => '此文档相关的所有配置项', 'Class:Document/Attribute:finalclass' => '文档类型', 'Class:Document/Attribute:finalclass+' => '根本属性的名称', ]); @@ -246,7 +252,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DocumentFile' => '文档文件', - 'Class:DocumentFile+' => '', + 'Class:DocumentFile+' => 'It\'s a type of Document which includes an uploaded file (in any format: Word, PDF, Spreadsheet, etc.).~~', 'Class:DocumentFile/Attribute:file' => '文件', 'Class:DocumentFile/Attribute:file+' => '', ]); @@ -257,7 +263,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DocumentNote' => '文档笔记', - 'Class:DocumentNote+' => '', + 'Class:DocumentNote+' => 'Used to store a text document. HTML formatting is supported using the WYSIWYG editor. Search can be performed on its content.~~', 'Class:DocumentNote/Attribute:text' => '正文', 'Class:DocumentNote/Attribute:text+' => '', ]); @@ -268,7 +274,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DocumentWeb' => '文档网页', - 'Class:DocumentWeb+' => '', + 'Class:DocumentWeb+' => 'Hyperlinks to external applications or documents, acting as pointers to external resources. You cannot search in their content from '.ITOP_APPLICATION_SHORT, 'Class:DocumentWeb/Attribute:url' => 'URL', 'Class:DocumentWeb/Attribute:url+' => '', ]); @@ -279,10 +285,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Typology' => '拓扑', - 'Class:Typology+' => '', + 'Class:Typology+' => 'Abstract class. ExternalKeyAttribute to a Typology subclass are used in place of an EnumAttribute, to have more dynamic values.~~', 'Class:Typology/Attribute:name' => '名称', 'Class:Typology/Attribute:name+' => '', - 'Class:Typology/Attribute:finalclass' => '拓扑类型', + 'Class:Typology/Attribute:finalclass' => '拓扑子类型', 'Class:Typology/Attribute:finalclass+' => '根本属性的名称', ]); @@ -292,7 +298,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DocumentType' => '文档类型', - 'Class:DocumentType+' => '', + 'Class:DocumentType+' => 'Typology. A classification system used to organize and logically group documents~~', ]); // @@ -301,7 +307,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ContactType' => '联系人类型', - 'Class:ContactType+' => '', + 'Class:ContactType+' => 'Typology to organize your Contacts and group them logically for you.~~', ]); // @@ -309,15 +315,15 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkPersonToTeam' => '关联 人员/团队', - 'Class:lnkPersonToTeam+' => '', + 'Class:lnkPersonToTeam' => '链接 个体/团队', + 'Class:lnkPersonToTeam+' => 'This link indicates when a Person is a member of a Team.~~', 'Class:lnkPersonToTeam/Name' => '%1$s / %2$s', 'Class:lnkPersonToTeam/Name+' => '', 'Class:lnkPersonToTeam/Attribute:team_id' => '团队', 'Class:lnkPersonToTeam/Attribute:team_id+' => '', 'Class:lnkPersonToTeam/Attribute:team_name' => '团队名称', 'Class:lnkPersonToTeam/Attribute:team_name+' => '', - 'Class:lnkPersonToTeam/Attribute:person_id' => '人员', + 'Class:lnkPersonToTeam/Attribute:person_id' => '个体', 'Class:lnkPersonToTeam/Attribute:person_id+' => '', 'Class:lnkPersonToTeam/Attribute:person_name' => '姓名', 'Class:lnkPersonToTeam/Attribute:person_name+' => '', @@ -338,7 +344,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:Catalogs+' => '数据类型', 'Menu:Audit' => '审计', 'Menu:Audit+' => '审计', - 'Menu:CSVImport' => 'CSV导入', + 'Menu:CSVImport' => 'CSV 导入', 'Menu:CSVImport+' => '批量创建或更新', 'Menu:Organization' => '组织', 'Menu:Organization+' => '所有组织', @@ -351,8 +357,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Menu:Contact' => '联系人', 'Menu:Contact+' => '联系人', 'Menu:Contact:Count' => '%1$d 个联系人', - 'Menu:Person' => '人员', - 'Menu:Person+' => '所有人员', + 'Menu:Person' => '个体', + 'Menu:Person+' => '所有个体', 'Menu:Team' => '团队', 'Menu:Team+' => '所有团队', 'Menu:Document' => '文档', @@ -383,6 +389,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Themes Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'theme:fullmoon' => '满月', - 'theme:test-red' => '测试 (红色)', + 'theme:fullmoon' => 'Full moon', + 'theme:test-red' => 'Test instance (Red)', ]); diff --git a/datamodels/2.x/itop-themes-compat/dictionaries/zh_cn.dict.itop-themes-compat.php b/datamodels/2.x/itop-themes-compat/dictionaries/zh_cn.dict.itop-themes-compat.php index 29768e7f8e..4b95080264 100644 --- a/datamodels/2.x/itop-themes-compat/dictionaries/zh_cn.dict.itop-themes-compat.php +++ b/datamodels/2.x/itop-themes-compat/dictionaries/zh_cn.dict.itop-themes-compat.php @@ -7,5 +7,5 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'theme:light-grey' => '淡灰 (废弃)', + 'theme:light-grey' => 'Light Grey (废弃)', ]); diff --git a/datamodels/2.x/itop-tickets/dictionaries/zh_cn.dict.itop-tickets.php b/datamodels/2.x/itop-tickets/dictionaries/zh_cn.dict.itop-tickets.php index 1a6d64b8e8..f146894e42 100644 --- a/datamodels/2.x/itop-tickets/dictionaries/zh_cn.dict.itop-tickets.php +++ b/datamodels/2.x/itop-tickets/dictionaries/zh_cn.dict.itop-tickets.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + // Dictionnay conventions // Class: // Class:+ @@ -30,12 +31,14 @@ // Class:/Attribute:/Value:+ // Class:/Stimulus: // Class:/Stimulus:+ + // // Class: Ticket // + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Ticket' => '工单', - 'Class:Ticket+' => '', + 'Class:Ticket+' => '抽象类', 'Class:Ticket/Attribute:ref' => '编号', 'Class:Ticket/Attribute:ref+' => '', 'Class:Ticket/Attribute:org_id' => '组织', @@ -66,7 +69,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Ticket/Attribute:last_update+' => '', 'Class:Ticket/Attribute:close_date' => '关闭日期', 'Class:Ticket/Attribute:close_date+' => '', - 'Class:Ticket/Attribute:private_log' => '私信', + 'Class:Ticket/Attribute:private_log' => '私有日志', 'Class:Ticket/Attribute:private_log+' => '', 'Class:Ticket/Attribute:contacts_list' => '联系人', 'Class:Ticket/Attribute:contacts_list+' => '此工单相关的所有联系人', @@ -92,8 +95,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkContactToTicket' => '关联联系人/工单', - 'Class:lnkContactToTicket+' => '', + 'Class:lnkContactToTicket' => '链接 联系人/工单', + 'Class:lnkContactToTicket+' => 'Allows linking several Contacts to notify them about the progress of Tickets, in addition to the requester and the agent.~~', 'Class:lnkContactToTicket/Name' => '%1$s / %2$s', 'Class:lnkContactToTicket/Attribute:ticket_id' => '工单', 'Class:lnkContactToTicket/Attribute:ticket_id+' => '', @@ -119,7 +122,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:WorkOrder' => '工作任务', - 'Class:WorkOrder+' => '', + 'Class:WorkOrder+' => 'A task required for the resolution of a Ticket, assigned to a Team or even an agent.~~', 'Class:WorkOrder/Attribute:name' => '名称', 'Class:WorkOrder/Attribute:name+' => '', 'Class:WorkOrder/Attribute:status' => '状态', @@ -154,95 +157,95 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Fieldset translation Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Ticket:baseinfo' => '基本信息', - 'Ticket:date' => '日期信息', - 'Ticket:contact' => '联系人', - 'Ticket:moreinfo' => '更多信息', - 'Ticket:relation' => '相关信息', - 'Ticket:log' => '日志', - 'Ticket:Type' => '风险评估', - 'Ticket:support' => '支持信息', - 'Ticket:resolution' => '解决方案', - 'Ticket:SLA' => 'SLA 报告', - 'WorkOrder:Details' => '详情', - 'WorkOrder:Moreinfo' => '更多信息', - 'Tickets:ResolvedFrom' => '由%1$s自动解决', - 'Class:cmdbAbstractObject/Method:Set' => '设置', - 'Class:cmdbAbstractObject/Method:Set+' => '填写固定值', - 'Class:cmdbAbstractObject/Method:Set/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:Set/Param:1+' => '填写当前对象', - 'Class:cmdbAbstractObject/Method:Set/Param:2' => '值', - 'Class:cmdbAbstractObject/Method:Set/Param:2+' => '要设置的值', - 'Class:cmdbAbstractObject/Method:SetCurrentDate' => '设置为当前日期', - 'Class:cmdbAbstractObject/Method:SetCurrentDate+' => '填写当前日期和时间', - 'Class:cmdbAbstractObject/Method:SetCurrentDate/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:SetCurrentDate/Param:1+' => '填写当前对象', - 'Class:cmdbAbstractObject/Method:SetCurrentDateIfNull' => '为空则设置为当前日期', - 'Class:cmdbAbstractObject/Method:SetCurrentDateIfNull+' => '设置空字段为当前日期和时间', - 'Class:cmdbAbstractObject/Method:SetCurrentDateIfNull/Param:1' => '目标字段', + 'Ticket:baseinfo' => '基本信息', + 'Ticket:date' => '日期信息', + 'Ticket:contact' => '联系人', + 'Ticket:moreinfo' => '更多信息', + 'Ticket:relation' => '相关信息', + 'Ticket:log' => '日志', + 'Ticket:Type' => '风险评估', + 'Ticket:support' => '支持信息', + 'Ticket:resolution' => '解决方案', + 'Ticket:SLA' => 'SLA 报告', + 'WorkOrder:Details' => '详情', + 'WorkOrder:Moreinfo' => '更多信息', + 'Tickets:ResolvedFrom' => '由%1$s自动解决', + 'Class:cmdbAbstractObject/Method:Set' => '设置', + 'Class:cmdbAbstractObject/Method:Set+' => '填写固定值', + 'Class:cmdbAbstractObject/Method:Set/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:Set/Param:1+' => '填写当前对象', + 'Class:cmdbAbstractObject/Method:Set/Param:2' => '值', + 'Class:cmdbAbstractObject/Method:Set/Param:2+' => '要设置的值', + 'Class:cmdbAbstractObject/Method:SetCurrentDate' => '设置为当前日期', + 'Class:cmdbAbstractObject/Method:SetCurrentDate+' => '填写当前日期和时间', + 'Class:cmdbAbstractObject/Method:SetCurrentDate/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:SetCurrentDate/Param:1+' => '填写当前对象', + 'Class:cmdbAbstractObject/Method:SetCurrentDateIfNull' => '为空则设置为当前日期', + 'Class:cmdbAbstractObject/Method:SetCurrentDateIfNull+' => '设置空字段为当前日期和时间', + 'Class:cmdbAbstractObject/Method:SetCurrentDateIfNull/Param:1' => '目标字段', 'Class:cmdbAbstractObject/Method:SetCurrentDateIfNull/Param:1+' => '当前对象中要设置的字段', - 'Class:cmdbAbstractObject/Method:SetCurrentUser' => '设置为当前用户', - 'Class:cmdbAbstractObject/Method:SetCurrentUser+' => '填写当前登录用户', - 'Class:cmdbAbstractObject/Method:SetCurrentUser/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:SetCurrentUser/Param:1+' => '当前对象中要设置的字段. 如果此字段为字符串则使用显示名称, 否则将使用标识符. 显示名称为关联到用户的人员的姓名, 如果没有关联人员则为登录名.', - 'Class:cmdbAbstractObject/Method:SetCurrentPerson' => '设置为当前人员', - 'Class:cmdbAbstractObject/Method:SetCurrentPerson+' => '设置字段为当前登录的人员 (此 "人员" 关联到当前登录的 "用户").', - 'Class:cmdbAbstractObject/Method:SetCurrentPerson/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:SetCurrentPerson/Param:1+' => '填写当前对象, 如果填写字符串则是昵称.', - 'Class:cmdbAbstractObject/Method:SetElapsedTime' => '设置已过时间', - 'Class:cmdbAbstractObject/Method:SetElapsedTime+' => '设置字段为当前时间针对另一个字段设置的日期所用时长 (秒)', - 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:1+' => '当前对象中要设置的字段', - 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:2' => '参考字段', - 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:2+' => '此字段来自获取相关日期的字段', - 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:3' => '工作时间', - 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:3+' => '若留空则取决于标准工作时间场景, 或者设置为 "DefaultWorkingTimeComputer" 来强制要求24x7场景', - 'Class:cmdbAbstractObject/Method:SetIfNull' => '为空时设置', - 'Class:cmdbAbstractObject/Method:SetIfNull+' => '仅当字段为空时设置, 使用此固定值', - 'Class:cmdbAbstractObject/Method:SetIfNull/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:SetIfNull/Param:1+' => '当前对象里要设置的字段', - 'Class:cmdbAbstractObject/Method:SetIfNull/Param:2' => '值', - 'Class:cmdbAbstractObject/Method:SetIfNull/Param:2+' => '要设置的值', - 'Class:cmdbAbstractObject/Method:AddValue' => '加上值', - 'Class:cmdbAbstractObject/Method:AddValue+' => '给字段加上一个固定值', - 'Class:cmdbAbstractObject/Method:AddValue/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:AddValue/Param:1+' => '当前对象里要修改的字段', - 'Class:cmdbAbstractObject/Method:AddValue/Param:2' => '值', - 'Class:cmdbAbstractObject/Method:AddValue/Param:2+' => '要加上的数值, 可以为负', - 'Class:cmdbAbstractObject/Method:SetComputedDate' => '设置计算的日期', - 'Class:cmdbAbstractObject/Method:SetComputedDate+' => '设置字段为按规则根据另一个字段计算的日期', - 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:1+' => '当前对象里要设置的字段', - 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:2' => '修饰符', - 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:2+' => '要修改源日期的文本修饰符, 例如 "+3 days"', - 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:3' => '源字段', - 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:3+' => '作为源值应用修饰符逻辑的字段', - 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull' => '若空则设置计算的日期', - 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull+' => '为空时设置字段为按规则根据另一个字段计算的日期', + 'Class:cmdbAbstractObject/Method:SetCurrentUser' => '设置为当前用户', + 'Class:cmdbAbstractObject/Method:SetCurrentUser+' => '填写当前登录用户', + 'Class:cmdbAbstractObject/Method:SetCurrentUser/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:SetCurrentUser/Param:1+' => '当前对象中要设置的字段. 如果此字段为字符串则使用昵称, 否则将使用标识符. 昵称为关联用户的名称, 否则会使用登录名.', + 'Class:cmdbAbstractObject/Method:SetCurrentPerson' => '设置为当前用户', + 'Class:cmdbAbstractObject/Method:SetCurrentPerson+' => '设置字段为当前登录的用户 (此 "个体" 关联到当前登录的 "用户").', + 'Class:cmdbAbstractObject/Method:SetCurrentPerson/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:SetCurrentPerson/Param:1+' => '填写当前对象, 如果填写字符串则是昵称.', + 'Class:cmdbAbstractObject/Method:SetElapsedTime' => '设置已过时间', + 'Class:cmdbAbstractObject/Method:SetElapsedTime+' => '设置字段为当前时间针对另一个字段设置的日期所用时长 (秒)', + 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:1+' => '当前对象中要设置的字段', + 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:2' => '参考字段', + 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:2+' => '此字段来自获取相关日期的字段', + 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:3' => '工作时间', + 'Class:cmdbAbstractObject/Method:SetElapsedTime/Param:3+' => '若留空则取决于标准工作时间场景, 或者设置为 "DefaultWorkingTimeComputer" 来强制要求24x7场景', + 'Class:cmdbAbstractObject/Method:SetIfNull' => '为空时设置', + 'Class:cmdbAbstractObject/Method:SetIfNull+' => '仅当字段为空时设置, 使用此固定值', + 'Class:cmdbAbstractObject/Method:SetIfNull/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:SetIfNull/Param:1+' => '当前对象里要设置的字段', + 'Class:cmdbAbstractObject/Method:SetIfNull/Param:2' => '值', + 'Class:cmdbAbstractObject/Method:SetIfNull/Param:2+' => '要设置的值', + 'Class:cmdbAbstractObject/Method:AddValue' => '加上值', + 'Class:cmdbAbstractObject/Method:AddValue+' => '给字段加上一个固定值', + 'Class:cmdbAbstractObject/Method:AddValue/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:AddValue/Param:1+' => '当前对象里要修改的字段', + 'Class:cmdbAbstractObject/Method:AddValue/Param:2' => '值', + 'Class:cmdbAbstractObject/Method:AddValue/Param:2+' => '要加上的数值, 可以为负', + 'Class:cmdbAbstractObject/Method:SetComputedDate' => '设置计算的日期', + 'Class:cmdbAbstractObject/Method:SetComputedDate+' => '设置字段为按规则根据另一个字段计算的日期', + 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:1+' => '当前对象里要设置的字段', + 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:2' => '修饰符', + 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:2+' => '要修改源日期的文本修饰符, 例如 "+3 days"', + 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:3' => '源字段', + 'Class:cmdbAbstractObject/Method:SetComputedDate/Param:3+' => '作为源值应用修饰符逻辑的字段', + 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull' => '若空则设置计算的日期', + 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull+' => '为空时设置字段为按规则根据另一个字段计算的日期', 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull/Param:1' => '目标字段', 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull/Param:1+' => '当前对象中要设置的字段', 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull/Param:2' => '修饰符', 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull/Param:2+' => '要修改源日期的文本修饰符, 例如 "+3 days"', 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull/Param:3' => '源字段', 'Class:cmdbAbstractObject/Method:SetComputedDateIfNull/Param:3+' => '作为源值应用修饰符逻辑的字段', - 'Class:cmdbAbstractObject/Method:Reset' => '重置', - 'Class:cmdbAbstractObject/Method:Reset+' => '重置为默认值', - 'Class:cmdbAbstractObject/Method:Reset/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:Reset/Param:1+' => '填写当前对象', - 'Class:cmdbAbstractObject/Method:Copy' => '复制', - 'Class:cmdbAbstractObject/Method:Copy+' => '复制当前值到另外一个地方', - 'Class:cmdbAbstractObject/Method:Copy/Param:1' => '目标字段', - 'Class:cmdbAbstractObject/Method:Copy/Param:1+' => '填写当前对象', - 'Class:cmdbAbstractObject/Method:Copy/Param:2' => '源字段', - 'Class:cmdbAbstractObject/Method:Copy/Param:2+' => '此字段从当前对象获取值', - 'Class:cmdbAbstractObject/Method:ApplyStimulus' => '使用调整', - 'Class:cmdbAbstractObject/Method:ApplyStimulus+' => '当前对象中要应用的特定调整', + 'Class:cmdbAbstractObject/Method:Reset' => '重置', + 'Class:cmdbAbstractObject/Method:Reset+' => '重置为默认值', + 'Class:cmdbAbstractObject/Method:Reset/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:Reset/Param:1+' => '填写当前对象', + 'Class:cmdbAbstractObject/Method:Copy' => '复制', + 'Class:cmdbAbstractObject/Method:Copy+' => '复制当前值到另外一个地方', + 'Class:cmdbAbstractObject/Method:Copy/Param:1' => '目标字段', + 'Class:cmdbAbstractObject/Method:Copy/Param:1+' => '填写当前对象', + 'Class:cmdbAbstractObject/Method:Copy/Param:2' => '源字段', + 'Class:cmdbAbstractObject/Method:Copy/Param:2+' => '此字段从当前对象获取值', + 'Class:cmdbAbstractObject/Method:ApplyStimulus' => '使用调整', + 'Class:cmdbAbstractObject/Method:ApplyStimulus+' => '当前对象中要应用的特定调整', 'Class:cmdbAbstractObject/Method:ApplyStimulus/Param:1' => '调整编码', 'Class:cmdbAbstractObject/Method:ApplyStimulus/Param:1+' => '当前对象的有效调整编码', - 'Class:ResponseTicketTTO/Interface:iMetricComputer' => 'TTO', - 'Class:ResponseTicketTTO/Interface:iMetricComputer+' => '响应时限', - 'Class:ResponseTicketTTR/Interface:iMetricComputer' => 'TTR', - 'Class:ResponseTicketTTR/Interface:iMetricComputer+' => '解决时限', + 'Class:ResponseTicketTTO/Interface:iMetricComputer' => 'TTO', + 'Class:ResponseTicketTTO/Interface:iMetricComputer+' => '响应时限', + 'Class:ResponseTicketTTR/Interface:iMetricComputer' => 'TTR', + 'Class:ResponseTicketTTR/Interface:iMetricComputer+' => '解决时限', ]); // @@ -251,5 +254,5 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Ticket/Attribute:team_email' => 'Team email~~', - 'Class:Ticket/Attribute:team_email+' => '~~', + 'Class:Ticket/Attribute:team_email+' => '', ]); diff --git a/datamodels/2.x/itop-virtualization-mgmt/dictionaries/zh_cn.dict.itop-virtualization-mgmt.php b/datamodels/2.x/itop-virtualization-mgmt/dictionaries/zh_cn.dict.itop-virtualization-mgmt.php index 4a9c70a89d..864e8059a0 100644 --- a/datamodels/2.x/itop-virtualization-mgmt/dictionaries/zh_cn.dict.itop-virtualization-mgmt.php +++ b/datamodels/2.x/itop-virtualization-mgmt/dictionaries/zh_cn.dict.itop-virtualization-mgmt.php @@ -21,14 +21,15 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + // // Fieldsets for Virtualization classes // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Virtualization:baseinfo' => 'General~~', - 'Virtualization:moreinfo' => 'Virtualization specifics~~', - 'Virtualization:otherinfo' => 'Dates and description~~', + 'Virtualization:baseinfo' => '基本信息', + 'Virtualization:moreinfo' => '虚拟化详情', + 'Virtualization:otherinfo' => '日期和描述', ]); // @@ -38,13 +39,14 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Cloud/Name' => '%1$s', 'Class:Cloud/ComplementaryName' => '%1$s-%2$s', - 'Class:Cloud' => 'Cloud~~', - 'Class:Cloud+' => 'A Virtual Host operated by a Cloud provider. It can host Virtual Machines and Container Hosts.~~', - 'Class:Cloud/Attribute:logo' => 'Logo~~', - 'Class:Cloud/Attribute:logo+' => 'Used as object icon when this Cloud is displayed within impact analysis graphs~~', - 'Class:Cloud/Attribute:provider_id+' => 'Who provides the cloud~~', - 'Class:Cloud/Attribute:location_id' => 'Location~~', - 'Class:Cloud/Attribute:location_id+' => 'Where is located the cloud~~', + 'Class:Cloud' => '云平台', + 'Class:Cloud+' => '由云供应商运营的虚拟化主机. 它可以托管虚拟机和容器宿主机.', + 'Class:Cloud/Attribute:provider_id' => '供应商', + 'Class:Cloud/Attribute:logo' => 'Logo', + 'Class:Cloud/Attribute:logo+' => '在影响分析图中显示此云平台时用作对象图标~~', + 'Class:Cloud/Attribute:provider_id+' => '谁提供云平台', + 'Class:Cloud/Attribute:location_id' => '位置', + 'Class:Cloud/Attribute:location_id+' => '云平台的位置', ]); // @@ -52,6 +54,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:LogicalInterface/Attribute:org_id' => 'Organization~~', - 'Class:LogicalInterface/Attribute:org_id+' => '~~', + 'Class:LogicalInterface/Name' => '%2$s %1$s', + 'Class:LogicalInterface/Attribute:org_id' => '组织', + 'Class:LogicalInterface/Attribute:org_id+' => '', ]); diff --git a/dictionaries/core/orm-value/orm-document/zh_cn.dictionary.itop.orm-document.php b/dictionaries/core/orm-value/orm-document/zh_cn.dictionary.itop.orm-document.php index 028ce545ad..5fe24b2df6 100644 --- a/dictionaries/core/orm-value/orm-document/zh_cn.dictionary.itop.orm-document.php +++ b/dictionaries/core/orm-value/orm-document/zh_cn.dictionary.itop.orm-document.php @@ -4,7 +4,7 @@ * Localized data * * @copyright Copyright (C) 2010-2024 Combodo SAS - * @license https://opensource.org/licenses/AGPL-3.0 + * @license http://opensource.org/licenses/AGPL-3.0 * * This file is part of iTop. * @@ -21,7 +21,8 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Core:ormValue:ormDocument:DownloadsCount' => '%1s~~', + 'Core:ormValue:ormDocument:DownloadsCount' => '%1s', 'Core:ormValue:ormDocument:DownloadsCount+' => '已下载%1$s次', ]); diff --git a/dictionaries/core/uniqueness-rule/zh_cn.dictionary.itop.uniqueness-rule.php b/dictionaries/core/uniqueness-rule/zh_cn.dictionary.itop.uniqueness-rule.php index 93dc748fea..3a53cb112b 100644 --- a/dictionaries/core/uniqueness-rule/zh_cn.dictionary.itop.uniqueness-rule.php +++ b/dictionaries/core/uniqueness-rule/zh_cn.dictionary.itop.uniqueness-rule.php @@ -21,6 +21,7 @@ * You should have received a copy of the GNU Affero General Public License * along with iTop. If not, see */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:cmdbAbstractObject/UniquenessRule:no_duplicate' => '%1$s: %2$s 已关联至 %3$s: %4$s, 不允许重复关联.', + 'Class:cmdbAbstractObject/UniquenessRule:no_duplicate' => '%1$s: %2$s 已链接到 %3$s: %4$s, 不允许重复链接.', ]); diff --git a/dictionaries/ui/application/links/zh_cn.dictionary.itop.links.php b/dictionaries/ui/application/links/zh_cn.dictionary.itop.links.php index 78a6304093..900526c8a8 100644 --- a/dictionaries/ui/application/links/zh_cn.dictionary.itop.links.php +++ b/dictionaries/ui/application/links/zh_cn.dictionary.itop.links.php @@ -17,58 +17,60 @@ * * You should have received a copy of the GNU Affero General Public License */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Placeholders - // $%1s : host object class name - // $%2s : host object friendlyname - // $%3s : current tab name - // $%4s : remote object class name - // $%5s : remote object friendlyname - 'UI:Links:Object:New:Modal:Title' => '创建对象', + // $%1s : host object class name + // $%2s : host object friendlyname + // $%3s : current tab name + // $%4s : remote object class name + // $%5s : remote object friendlyname + + 'UI:Links:Object:New:Modal:Title' => '创建对象', // Create - 'UI:Links:Create:Button' => '创建', - 'UI:Links:Create:Button+' => '创建一个 %4$s', - 'UI:Links:Create:Modal:Title' => '创建一个 %4$s 至 %2$s', + 'UI:Links:Create:Button' => '创建', + 'UI:Links:Create:Button+' => '创建一个 %4$s', + 'UI:Links:Create:Modal:Title' => '创建一个 %4$s 至 %2$s', // Add - 'UI:Links:Add:Button' => '添加', - 'UI:Links:Add:Button+' => '添加一个 %4$s', - 'UI:Links:Add:Modal:Title' => '添加一个 %4$s 至 %2$s', + 'UI:Links:Add:Button' => '添加', + 'UI:Links:Add:Button+' => '添加一个 %4$s', + 'UI:Links:Add:Modal:Title' => '添加一个 %4$s 至 %2$s', // Modify link - 'UI:Links:ModifyLink:Button' => '修改', - 'UI:Links:ModifyLink:Button+' => '修改此关联', - 'UI:Links:ModifyLink:Modal:Title' => '修改 %2$s 和 %5$s 的关联', + 'UI:Links:ModifyLink:Button' => '修改', + 'UI:Links:ModifyLink:Button+' => '修改这个链接', + 'UI:Links:ModifyLink:Modal:Title' => '修改 %2$s 与 %5$s 之间的链接', // Modify object - 'UI:Links:ModifyObject:Button' => '修改', - 'UI:Links:ModifyObject:Button+' => '修改此对象', + 'UI:Links:ModifyObject:Button' => '修改', + 'UI:Links:ModifyObject:Button+' => '修改这个对象', 'UI:Links:ModifyObject:Modal:Title' => '%5$s', // Remove - 'UI:Links:Remove:Button' => '移除', - 'UI:Links:Remove:Button+' => '移除此 %4$s', - 'UI:Links:Remove:Modal:Title' => '从%1$s 移除 %4$s', - 'UI:Links:Remove:Modal:Message' => '请确认从 %2$s 移除 %5$s ?', + 'UI:Links:Remove:Button' => '移除', + 'UI:Links:Remove:Button+' => '移除这个 %4$s', + 'UI:Links:Remove:Modal:Title' => '从%1$s 移除 %4$s', + 'UI:Links:Remove:Modal:Message' => '请确认从 %2$s 中移除 %5$s ?', // Delete - 'UI:Links:Delete:Button' => '删除', - 'UI:Links:Delete:Button+' => '删除此 %4$s', - 'UI:Links:Delete:Modal:Title' => '删除 %4$s', - 'UI:Links:Delete:Modal:Message' => '请确认删除 %5$s?', + 'UI:Links:Delete:Button' => '删除', + 'UI:Links:Delete:Button+' => '删除此 %4$s', + 'UI:Links:Delete:Modal:Title' => '删除 %4$s', + 'UI:Links:Delete:Modal:Message' => '请确认删除 %5$s?', // Bulk - 'UI:Links:Bulk:LinkWillBeCreatedForAllObjects' => '添加至所有对象', + 'UI:Links:Bulk:LinkWillBeCreatedForAllObjects' => '添加至所有对象', 'UI:Links:Bulk:LinkWillBeDeletedFromAllObjects' => '从所有对象删除', - 'UI:Links:Bulk:LinkWillBeCreatedFor1Object' => '添加至一个对象', - 'UI:Links:Bulk:LinkWillBeDeletedFrom1Object' => '从一个对象移除', - 'UI:Links:Bulk:LinkWillBeCreatedForXObjects' => '添加 {count} 个对象', - 'UI:Links:Bulk:LinkWillBeDeletedFromXObjects' => '移除 {count} 个对象', - 'UI:Links:Bulk:LinkExistForAllObjects' => '已关联所有对象', - 'UI:Links:Bulk:LinkExistForOneObject' => '已关联一个对象', - 'UI:Links:Bulk:LinkExistForXObjects' => '已关联 {count} 个对象', + 'UI:Links:Bulk:LinkWillBeCreatedFor1Object' => '添加至一个对象', + 'UI:Links:Bulk:LinkWillBeDeletedFrom1Object' => '从一个对象移除', + 'UI:Links:Bulk:LinkWillBeCreatedForXObjects' => '添加 {count} 个对象', + 'UI:Links:Bulk:LinkWillBeDeletedFromXObjects' => '移除 {count} 个对象', + 'UI:Links:Bulk:LinkExistForAllObjects' => '已关联所有对象', + 'UI:Links:Bulk:LinkExistForOneObject' => '已关联一个对象', + 'UI:Links:Bulk:LinkExistForXObjects' => '已关联 {count} 个对象', // New item 'UI:Links:NewItem' => '新建条目', diff --git a/dictionaries/ui/application/newsroom/zh_cn.dictionary.itop.newsroom.php b/dictionaries/ui/application/newsroom/zh_cn.dictionary.itop.newsroom.php index 98777e7c38..1b3fac16e3 100644 --- a/dictionaries/ui/application/newsroom/zh_cn.dictionary.itop.newsroom.php +++ b/dictionaries/ui/application/newsroom/zh_cn.dictionary.itop.newsroom.php @@ -17,25 +17,27 @@ * * You should have received a copy of the GNU Affero General Public License */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Newsroom:iTopNotification:Label' => ITOP_APPLICATION_SHORT, - 'UI:Newsroom:iTopNotification:ViewAllPage:Title' => 'Your '.ITOP_APPLICATION_SHORT.' news~~', - 'UI:Newsroom:iTopNotification:ViewAllPage:SubTitle' => 'Manage your news, flag them as read or unread, delete them, etc.~~', + 'UI:Newsroom:iTopNotification:ViewAllPage:Title' => '您的 '.ITOP_APPLICATION_SHORT.' 消息', + 'UI:Newsroom:iTopNotification:ViewAllPage:SubTitle' => '管理您的消息,将它们标记为已读或未读,甚至删除等等.', 'UI:Newsroom:iTopNotification:ViewAllPage:Read:Label' => '已读', 'UI:Newsroom:iTopNotification:ViewAllPage:Unread:Label' => '未读', - 'UI:Newsroom:iTopNotification:SelectMode:Label' => 'Select mode~~', + 'UI:Newsroom:iTopNotification:SelectMode:Label' => '请选择模式', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsRead:Label' => '全部标记为已读', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAllAsUnread:Label' => '全部标记为未读', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Label' => '全部删除', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Success:Message' => '全部 %1$s 条消息已被删除', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Title' => '删除全部消息', - 'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Message' => 'Are you sure you want to delete all news?~~', + 'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteAll:Confirmation:Message' => '您确定要删除所有消息吗?', + 'UI:Newsroom:iTopNotification:ViewAllPage:Empty:Title' => '没有消息, 已是最新!', // Actions - // - Unitary buttons + // - Unitary buttons 'UI:Newsroom:iTopNotification:ViewAllPage:Action:Delete:Label' => '删除这条消息', - 'UI:Newsroom:iTopNotification:ViewAllPage:Action:ViewObject:Label' => 'Go to the news url~~', + 'UI:Newsroom:iTopNotification:ViewAllPage:Action:ViewObject:Label' => '转到消息的链接', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsRead:Label' => '标记为已读', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkAsUnread:Label' => '标记为未读', // - Bulk buttons @@ -43,7 +45,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Newsroom:iTopNotification:ViewAllPage:Action:MarkSelectedAsUnread:Label' => '标记已选为未读', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Label' => '删除已选择', 'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Title' => '删除已选的消息', - 'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Message' => 'Are you sure you want to delete selected news?~~', + 'UI:Newsroom:iTopNotification:ViewAllPage:Action:DeleteSelected:Confirmation:Message' => '您确定要删除选中的消息吗?', // Feedback messages 'UI:Newsroom:iTopNotification:ViewAllPage:Action:InvalidAction:Message' => '无效操作: "%1$s"', diff --git a/dictionaries/ui/application/welcome-popup/zh_cn.dictionary.itop.welcome-popup.php b/dictionaries/ui/application/welcome-popup/zh_cn.dictionary.itop.welcome-popup.php index 44b9230d0f..a505d13714 100644 --- a/dictionaries/ui/application/welcome-popup/zh_cn.dictionary.itop.welcome-popup.php +++ b/dictionaries/ui/application/welcome-popup/zh_cn.dictionary.itop.welcome-popup.php @@ -4,6 +4,7 @@ * @copyright Copyright (C) 2010-2024 Combodo SAS * @license http://opensource.org/licenses/AGPL-3.0 */ + // UI elements Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:WelcomePopup:Button:RemindLater' => '以后再提醒我', @@ -14,42 +15,42 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Message Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:WelcomePopup:Message:320_01_Welcome:Title' => '欢迎使用 '.ITOP_APPLICATION_SHORT.' 3.2', - 'UI:WelcomePopup:Message:320_01_Welcome:Description' => '
Congratulations, you landed on '.ITOP_APPLICATION.' '.ITOP_VERSION_NAME.'!
+ 'UI:WelcomePopup:Message:320_01_Welcome:Description' => '
恭喜, 您已登录 '.ITOP_APPLICATION.' '.ITOP_VERSION_NAME.'!

-
We\'re excited to announce this new release.
-
In addition to introducing new features such as Newsroom, '.ITOP_APPLICATION_SHORT.' 3.2 includes critical security patches, enhanced accessibility and other significant improvements focused on providing you with stability and security.
+
很高兴向你宣布这个新版本.
+
新增了新闻室等新功能, '.ITOP_APPLICATION_SHORT.' 3.2 还包含了关键的安全补丁, 增强的可访问性以及其它重要的改进,旨在为您提供更好的稳定性和安全性.

-
Discover all of '.ITOP_APPLICATION_SHORT.'\'s exciting new features and stay up-to-date with important notifications with our new welcome pop-up!
-
We hope you\'ll enjoy this version as much as we enjoyed imagining and creating it.
+
发现 '.ITOP_APPLICATION_SHORT.' 所有令人兴奋的新功能,并通过我们的新欢迎弹窗保持与重要通知的同步!
+
我们希望你能像我们一样,从构思到创造,全程享受这个版本!

-
Customize your '.ITOP_APPLICATION_SHORT.' preferences for a personalized experience.
~~', - 'UI:WelcomePopup:Message:320_02_Newsroom:Title' => 'Say "Hello" to the newsroom~~', - 'UI:WelcomePopup:Message:320_02_Newsroom:Description' => '
Say goodbye to cluttered inboxes and hello to personalized alerts with '.ITOP_APPLICATION_SHORT.'\'s Newsroom!
-
Newsroom allows you to easily manage notifications within the platform, so you can stay on top of important updates without constantly checking your email. With the ability to mark messages as read or unread, and automatically delete old notifications, you have complete control over your notifications.
+
定制您的 '.ITOP_APPLICATION_SHORT.' 偏好设置,可以获得个性化的使用体验.
', + 'UI:WelcomePopup:Message:320_02_Newsroom:Title' => '向新闻室说 "Hello" ', + 'UI:WelcomePopup:Message:320_02_Newsroom:Description' => '
告别杂乱的收件箱,用 '.ITOP_APPLICATION_SHORT.' 新闻室迎接个性化的告警!
+
新闻室允许您轻松管理平台内的通知,因此您可以掌握重要更新而无需频繁查收电子邮件.通过将消息标记为已读或未读,并自动删除旧通知,您可以完全控制您的通知.

-
Try it out today and streamline your '.ITOP_APPLICATION_SHORT.'\'s communication experience!
~~', - 'UI:WelcomePopup:Message:320_03_NotificationsCenter:Title' => 'Notifications center~~', - 'UI:WelcomePopup:Message:320_03_NotificationsCenter:Description' => '
As we know your information intake is already at its max, you can now easily choose how you receive your notifications - via email, chat, or even the Newsroom feature
-
You don\'t want to receive a certain type of alerts? Nothing easier with these advanced customization capabilities giving you the flexibility to tailor your experience to your needs.
+
今天就试试,简化您的 '.ITOP_APPLICATION_SHORT.' 沟通体验!
', + 'UI:WelcomePopup:Message:320_03_NotificationsCenter:Title' => '通知中心', + 'UI:WelcomePopup:Message:320_03_NotificationsCenter:Description' => '
由于我们知道您的信息摄入量已经达到最大限度,现在您可以轻松选择如何接收通知 - 通过电子邮件、聊天,甚至新闻室功能
+
您不想接收某种类型的警报?使用这些高级自定义功能,您可以根据需要轻松定制体验.

-
Access your notifications center through the newsroom or through your preferences and avoid information overload on all your communication channels!
~~', - 'UI:WelcomePopup:Message:320_05_A11yThemes:Title' => 'Accessibility for '.ITOP_APPLICATION_SHORT.'\'s UI~~', - 'UI:WelcomePopup:Message:320_05_A11yThemes:Description' => '
To ensure '.ITOP_APPLICATION_SHORT.'\'s accessibility, our team has been working on new back-office themes. WCAG compliants, those UI focus on making it easier for users with visual impairments to use the solution: +
通过新闻室或您的偏好设置访问您的通知中心,避免所有通信渠道的信息过载!
', + 'UI:WelcomePopup:Message:320_05_A11yThemes:Title' => ITOP_APPLICATION_SHORT.' UI 的可访问性', + 'UI:WelcomePopup:Message:320_05_A11yThemes:Description' => '
为了确保 '.ITOP_APPLICATION_SHORT.' 的可访问性,我们的团队一直在开发新的后台主题.符合 WCAG 标准,这些 UI 主题可以帮助视力障碍用户更容易的使用:
    -
  • Color-blind theme: Designed to help users with colorblindness, this theme actually breaks down in two sub-themes to adapt to specific cases:
  • +
  • 色盲主题: 设计用于帮助色盲用户,此主题实际上分为两个子主题以适应特定情况:
    • -
    • One adapted to protanopia and deuteranopia
    • -
    • And another one for tritanopia
    • +
    • 一个适用于红绿色盲和绿色色盲
    • +
    • 另一个适用于黄蓝色盲

    -
  • High-contrast theme: Increased contrast to allow users an easier distinction between different elements on screen and avoid to rely on color schema to convey information. It can be helpful for users with different pathology from colorblindness to low vision issues.
  • +
  • 高对比度主题: 增加对比度以允许用户更容易区分屏幕上的不同元素,并避免依赖颜色方案传递信息.它可以帮助从色盲到弱视等不同病理的用户.
-
~~', - 'UI:WelcomePopup:Message:320_04_PowerfulNotifications_AdminOnly:Title' => 'Powerful notifications~~', - 'UI:WelcomePopup:Message:320_04_PowerfulNotifications_AdminOnly:Description' => '
'.ITOP_APPLICATION_SHORT.'\'s Newsroom gives you a new way to automate your alerts based on events with recurrence, so you can easily set up rules that work for you.
-
Our priority-based notifications sorting ensures that important messages are displayed first, while our URL customization options allow you to direct recipients to the right place.
+
', + 'UI:WelcomePopup:Message:320_04_PowerfulNotifications_AdminOnly:Title' => '强大的通知', + 'UI:WelcomePopup:Message:320_04_PowerfulNotifications_AdminOnly:Description' => '
'.ITOP_APPLICATION_SHORT.' 的新闻室为您提供了一种新的方法,可以 自动化 基于事件的告警 并支持重复设置, 因此您可以轻松设置适合您的规则.
+
我们的基于优先级的通知排序确保重要消息优先展示,同时,我们的 URL 自定义选项允许您将收件人引导到正确的位置.

-
With support for multiple languages, you have now complete control over your notifications display.
+
支持多语言,您现在可以完全控制通知显示.

-
Configure it today and see how much more efficient your alerts process can be!
~~', +
现在就配置它,看看您的警报流程可以变得多么高效!
', ]); diff --git a/dictionaries/ui/layouts/activity-panel/zh_cn.dictionary.itop.activity-panel.php b/dictionaries/ui/layouts/activity-panel/zh_cn.dictionary.itop.activity-panel.php index c4a1ce344b..dc7303f12a 100644 --- a/dictionaries/ui/layouts/activity-panel/zh_cn.dictionary.itop.activity-panel.php +++ b/dictionaries/ui/layouts/activity-panel/zh_cn.dictionary.itop.activity-panel.php @@ -17,13 +17,14 @@ * * You should have received a copy of the GNU Affero General Public License */ + // Activity panel Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Layout:ActivityPanel:SizeToggler:Expand:Tooltip' => '展开', - 'UI:Layout:ActivityPanel:SizeToggler:Reduce:Tooltip' => '减少', + 'UI:Layout:ActivityPanel:SizeToggler:Reduce:Tooltip' => '还原', 'UI:Layout:ActivityPanel:DisplayToggler:Close:Tooltip' => '关闭', - 'UI:Layout:ActivityPanel:LoadMoreEntries:Tooltip' => '加载更多', - 'UI:Layout:ActivityPanel:LoadAllEntries:Tooltip' => '全部加载', + 'UI:Layout:ActivityPanel:LoadMoreEntries:Tooltip' => '加载更多条目', + 'UI:Layout:ActivityPanel:LoadAllEntries:Tooltip' => '加载之前全部的条目', // Tabs 'UI:Layout:ActivityPanel:Tab:Activity:Title' => '活动', @@ -54,6 +55,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Layout:ActivityPanel:NoEntry:Placeholder:Hint' => '暂无任何活动', // Closed cover - 'UI:Layout:ActivityPanel:ClosedCover:Title' => '活动面板', - 'UI:Layout:ActivityPanel:ClosedCover:Tooltip' => '点击打开活动面板', + 'UI:Layout:ActivityPanel:ClosedCover:Title' => '侧面板', + 'UI:Layout:ActivityPanel:ClosedCover:Tooltip' => '点击打开侧面板', ]); diff --git a/dictionaries/ui/layouts/navigation-menu/zh_cn.dictionary.itop.navigation-menu.php b/dictionaries/ui/layouts/navigation-menu/zh_cn.dictionary.itop.navigation-menu.php index d59ef87db2..b02860fb05 100644 --- a/dictionaries/ui/layouts/navigation-menu/zh_cn.dictionary.itop.navigation-menu.php +++ b/dictionaries/ui/layouts/navigation-menu/zh_cn.dictionary.itop.navigation-menu.php @@ -17,17 +17,18 @@ * * You should have received a copy of the GNU Affero General Public License */ + // Navigation menu Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'UI:Layout:NavigationMenu:CompanyLogo:AltText' => '公司标志', + 'UI:Layout:NavigationMenu:CompanyLogo:AltText' => '公司 logo', 'UI:Layout:NavigationMenu:Silo:Label' => '请选择要筛选的组织', - 'UI:Layout:NavigationMenu:Toggler:Tooltip' => '展开/折叠', - 'UI:Layout:NavigationMenu:Toggler:TooltipWithSiloLabel' => '展开/折叠 (筛选%1$s)', + 'UI:Layout:NavigationMenu:Toggler:Tooltip' => '展开 / 折叠', + 'UI:Layout:NavigationMenu:Toggler:TooltipWithSiloLabel' => '展开 / 折叠 (筛选 %1$s)', 'UI:Layout:NavigationMenu:MenuFilter:Input:Placeholder' => '筛选器...', 'UI:Layout:NavigationMenu:MenuFilter:Input:Tooltip' => '请输入要筛选的关键字', 'UI:Layout:NavigationMenu:MenuFilter:Input:Hint' => '将会显示所有匹配的菜单', 'UI:Layout:NavigationMenu:MenuFilter:Placeholder:Hint' => '此菜单暂无筛选结果', - 'UI:Layout:NavigationMenu:UserInfo:WelcomeMessage:Text' => '您好, %1$s!', + 'UI:Layout:NavigationMenu:UserInfo:WelcomeMessage:Text' => 'Hi, %1$s!', 'UI:Layout:NavigationMenu:UserInfo:Picture:AltText' => '%1$s 的联系人头像', 'UI:Layout:NavigationMenu:UserMenu:Toggler:Label' => '打开用户菜单', 'UI:Layout:NavigationMenu:KeyboardShortcut:FocusFilter' => '筛选菜单', diff --git a/dictionaries/ui/pages/login/zh_cn.dictionary.itop.login.php b/dictionaries/ui/pages/login/zh_cn.dictionary.itop.login.php index 7415103a72..3f9bfd3a4a 100644 --- a/dictionaries/ui/pages/login/zh_cn.dictionary.itop.login.php +++ b/dictionaries/ui/pages/login/zh_cn.dictionary.itop.login.php @@ -23,7 +23,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:ResetPwd-Error-WrongLogin' => '\'%1$s\' 用户名无效', 'UI:ResetPwd-Error-NotPossible' => '外部账号不允许重置密码.', 'UI:ResetPwd-Error-FixedPwd' => '此账号不允许重置密码.', - 'UI:ResetPwd-Error-NoContact' => '此账号没有关联到人员.', + 'UI:ResetPwd-Error-NoContact' => '此账号没有关联到个体.', 'UI:ResetPwd-Error-NoEmailAtt' => '此账号未关联邮箱地址,请联系管理员.', 'UI:ResetPwd-Error-NoEmail' => '缺少邮箱地址. 请联系管理员.', 'UI:ResetPwd-Error-Send' => '邮件发送存在技术原因. 请联系管理员.', diff --git a/dictionaries/ui/pages/notifications-center/zh_cn.dictionary.itop.notifications-center.php b/dictionaries/ui/pages/notifications-center/zh_cn.dictionary.itop.notifications-center.php index de58611b6a..5815c45f11 100644 --- a/dictionaries/ui/pages/notifications-center/zh_cn.dictionary.itop.notifications-center.php +++ b/dictionaries/ui/pages/notifications-center/zh_cn.dictionary.itop.notifications-center.php @@ -19,15 +19,15 @@ */ Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'UI:NotificationsCenter:Page:Title' => 'Notifications center~~', - 'UI:NotificationsCenter:Panel:Title' => 'Notifications center~~', - 'UI:NotificationsCenter:Panel:SubTitle' => 'Manage Notifications that you have received : unsubscribe or limit them to a single channel~~', - 'UI:NotificationsCenter:Panel:Toolbar:ViewAllNews:Title' => 'View all my news~~', - 'UI:NotificationsCenter:Panel:Table:Channels' => 'Channels~~', - 'UI:NotificationsCenter:Unsubscribe:Success' => 'You have been successfully unsubscribed from the selected notifications.~~', - 'UI:NotificationsCenter:Unsubscribe:Error' => 'An error occurred while unsubscribing from the selected notifications.~~', - 'UI:NotificationsCenter:Subscribe:Success' => 'You have been successfully subscribed to the selected notifications.~~', - 'UI:NotificationsCenter:Subscribe:Error' => 'An error occurred while subscribing to the selected notifications.~~', - 'UI:NotificationsCenter:Channel:OutOf:Text' => '%1$s out of %2$s~~', - 'UI:NotificationsCenter:Advanced:Input:Label' => '%1$s: %2$s~~', + 'UI:NotificationsCenter:Page:Title' => '通知中心', + 'UI:NotificationsCenter:Panel:Title' => '通知中心', + 'UI:NotificationsCenter:Panel:SubTitle' => '管理收到的通知 : 取消订阅或限制到单一频道', + 'UI:NotificationsCenter:Panel:Toolbar:ViewAllNews:Title' => '查看所有新闻', + 'UI:NotificationsCenter:Panel:Table:Channels' => '频道', + 'UI:NotificationsCenter:Unsubscribe:Success' => '您已成功取消订阅所选通知.', + 'UI:NotificationsCenter:Unsubscribe:Error' => '取消订阅所选通知时发生错误.', + 'UI:NotificationsCenter:Subscribe:Success' => '您已成功订阅所选通知.', + 'UI:NotificationsCenter:Subscribe:Error' => '订阅所选通知时发生错误.', + 'UI:NotificationsCenter:Channel:OutOf:Text' => '%1$s / %2$s', + 'UI:NotificationsCenter:Advanced:Input:Label' => '%1$s: %2$s', ]); diff --git a/dictionaries/ui/pages/preferences/zh_cn.dictionary.itop.preferences.php b/dictionaries/ui/pages/preferences/zh_cn.dictionary.itop.preferences.php index 2ec082a1d6..25f880dc22 100644 --- a/dictionaries/ui/pages/preferences/zh_cn.dictionary.itop.preferences.php +++ b/dictionaries/ui/pages/preferences/zh_cn.dictionary.itop.preferences.php @@ -17,42 +17,43 @@ * * You should have received a copy of the GNU Affero General Public License */ + // Navigation menu Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'UI:Preferences:Title' => '首选项', - 'UI:Preferences:UserInterface:Title' => '用户界面', - 'UI:Preferences:General:Title' => '概况', - 'UI:Preferences:General:Theme' => '主题', - 'UI:Preferences:General:Theme:DefaultThemeLabel' => '%1$s (默认)', - 'UI:Favorites:General:ShowSummaryCards' => '显示汇总卡片', - 'UI:Favorites:General:ShowSummaryCards+' => '当鼠标移动到对象链接时, 显示此对象的简要汇总信息, 如果该类型支持', - 'UI:Preferences:Lists:Title' => '列表', - 'UI:Preferences:RichText:Title' => '富文本编辑器', - 'UI:Preferences:RichText:ToolbarState' => '工具栏默认状态', - 'UI:Preferences:RichText:ToolbarState:Expanded' => '展开', - 'UI:Preferences:RichText:ToolbarState:Collapsed' => '折叠', - 'UI:Preferences:ActivityPanel:Title' => '活动面板', - 'UI:Preferences:ActivityPanel:EntryFormOpened' => '默认打开录入表单', - 'UI:Preferences:ActivityPanel:EntryFormOpened+' => '在显示对象时是否打开录入表单. 如果不选择, 仍可以点击新建按钮打开录入表单', - 'UI:Preferences:PersonalizeKeyboardShortcuts:Title' => '键盘快捷键', - 'UI:Preferences:PersonalizeKeyboardShortcuts:Input:Hint' => '请输入键盘快捷键', - 'UI:Preferences:PersonalizeKeyboardShortcuts:Button:Tooltip' => '录制键盘快捷键', - 'UI:Preferences:PersonalizeKeyboardShortcuts:Button:Reset' => '重置', - 'UI:Preferences:PersonalizeKeyboardShortcuts:Button:Reset:Tooltip' => '还原到默认的键盘快捷键', - 'UI:Preferences:Tabs:Title' => '标签栏', - 'UI:Preferences:Tabs:Layout:Label' => '布局', - 'UI:Preferences:Tabs:Layout:Horizontal' => '水平', - 'UI:Preferences:Tabs:Layout:Vertical' => '垂直', - 'UI:Preferences:Tabs:Scrollable:Label' => '导航', - 'UI:Preferences:Tabs:Scrollable:Classic' => '经典', - 'UI:Preferences:Tabs:Scrollable:Scrollable' => '可滚动', - 'UI:Preferences:General:Toasts' => 'Toast notifications position~~', - 'UI:Preferences:General:Toasts:Bottom' => 'Bottom~~', - 'UI:Preferences:General:Toasts:Top' => 'Top~~', - 'UI:Preferences:ChooseAPlaceholder' => '用户的默认头像', - 'UI:Preferences:ChooseAPlaceholder+' => '选择一个占位图片, 将在用户联系人没有设定头像图片时显示', - 'UI:Preferences:ChooseAPlaceholder:Success:Message' => 'Your placeholder image has been successfully updated~~', - 'UI:Preferences:Notifications' => 'Notifications~~', - 'UI:Preferences:Notifications+' => 'Configure the notifications you want to receive on this page.~~', + 'UI:Preferences:Title' => '偏好设置', + 'UI:Preferences:UserInterface:Title' => '用户界面', + 'UI:Preferences:General:Title' => '概况', + 'UI:Preferences:General:Theme' => '主题', + 'UI:Preferences:General:Theme:DefaultThemeLabel' => '%1$s (默认)', + 'UI:Favorites:General:ShowSummaryCards' => '显示汇总卡片', + 'UI:Favorites:General:ShowSummaryCards+' => '当鼠标移动到对象链接时, 显示此对象的简要汇总信息, 如果该类型支持', + 'UI:Preferences:Lists:Title' => '列表', + 'UI:Preferences:RichText:Title' => '富文本编辑器', + 'UI:Preferences:RichText:ToolbarState' => '工具栏默认状态', + 'UI:Preferences:RichText:ToolbarState:Expanded' => '展开', + 'UI:Preferences:RichText:ToolbarState:Collapsed' => '折叠', + 'UI:Preferences:ActivityPanel:Title' => '侧面板', + 'UI:Preferences:ActivityPanel:EntryFormOpened' => '默认打开侧面板', + 'UI:Preferences:ActivityPanel:EntryFormOpened+' => '在显示对象时是否打开侧面板. 如果不选择, 仍可以点击按钮打开侧面板', + 'UI:Preferences:PersonalizeKeyboardShortcuts:Title' => '键盘快捷键', + 'UI:Preferences:PersonalizeKeyboardShortcuts:Input:Hint' => '请输入键盘快捷键', + 'UI:Preferences:PersonalizeKeyboardShortcuts:Button:Tooltip' => '录制键盘快捷键', + 'UI:Preferences:PersonalizeKeyboardShortcuts:Button:Reset' => '重置', + 'UI:Preferences:PersonalizeKeyboardShortcuts:Button:Reset:Tooltip' => '还原到默认的键盘快捷键', + 'UI:Preferences:Tabs:Title' => '标签栏', + 'UI:Preferences:Tabs:Layout:Label' => '布局', + 'UI:Preferences:Tabs:Layout:Horizontal' => '水平', + 'UI:Preferences:Tabs:Layout:Vertical' => '垂直', + 'UI:Preferences:Tabs:Scrollable:Label' => '导航', + 'UI:Preferences:Tabs:Scrollable:Classic' => '经典', + 'UI:Preferences:Tabs:Scrollable:Scrollable' => '可滚动', + 'UI:Preferences:General:Toasts' => '通知位置', + 'UI:Preferences:General:Toasts:Bottom' => '底部', + 'UI:Preferences:General:Toasts:Top' => '顶部', + 'UI:Preferences:ChooseAPlaceholder' => '用户的默认头像', + 'UI:Preferences:ChooseAPlaceholder+' => '请选择一个占位图片, 将在用户联系人没有设定头像图片时显示', + 'UI:Preferences:ChooseAPlaceholder:Success:Message' => '您的占位图片已成功更新', + 'UI:Preferences:Notifications' => '通知', + 'UI:Preferences:Notifications+' => '在 这里配置您想要收到的通知.', ]); diff --git a/dictionaries/zh_cn.dictionary.itop.core.php b/dictionaries/zh_cn.dictionary.itop.core.php index 91860e39d3..dc46bfeaef 100644 --- a/dictionaries/zh_cn.dictionary.itop.core.php +++ b/dictionaries/zh_cn.dictionary.itop.core.php @@ -4,52 +4,79 @@ * Localized data * * @copyright Copyright (C) 2010-2024 Combodo SAS - * @license https://opensource.org/licenses/AGPL-3.0 + * @license http://opensource.org/licenses/AGPL-3.0 * - */ -/** + * This file is part of iTop. * + * iTop is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * iTop 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with iTop. If not, see */ + Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Core:DeletedObjectLabel' => '%1s (已删除)', 'Core:DeletedObjectTip' => '对象已被删除于 %1$s (%2$s)', + 'Core:UnknownObjectLabel' => '找不到对象 (类型: %1$s, 编号: %2$d)', 'Core:UnknownObjectTip' => '对象没有找到. 其可能已经被删除并且日志已经被清除.', + 'Core:UniquenessDefaultError' => '唯一性规则 \'%1$s\' 错误', 'Core:CheckConsistencyError' => '一致性规则没有被遵守: %1$s', 'Core:CheckValueError' => '属性 \'%1$s\' (%2$s)的未知值: %3$s', + 'Core:AttributeLinkedSet' => '对象数组', 'Core:AttributeLinkedSet+' => '任何相同类型或子类型的对象', + 'Core:AttributeLinkedSetDuplicatesFound' => '字段 \'%1$s\' 内容重复: %2$s', + 'Core:AttributeDashboard' => '仪表盘', 'Core:AttributeDashboard+' => '', + 'Core:AttributePhoneNumber' => '电话号码', 'Core:AttributePhoneNumber+' => '', + 'Core:AttributeObsolescenceDate' => '报废日期', 'Core:AttributeObsolescenceDate+' => '', + 'Core:AttributeTagSet' => '清单', 'Core:AttributeTagSet+' => '', 'Core:AttributeSet:placeholder' => '请点击这里添加', - 'Core:Placeholder:CannotBeResolved' => '(%1$s : cannot be resolved)~~', + 'Core:Placeholder:CannotBeResolved' => '(%1$s : 无法解析)', 'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)', 'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s来自%3$s)', 'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s来自子类型', + 'Core:AttributeCaseLog' => '日志', 'Core:AttributeCaseLog+' => '', - 'Core:AttributeMetaEnum' => '计算枚举', - 'Core:AttributeMetaEnum+' => '~~', + + 'Core:AttributeMetaEnum' => '计算的数字', + 'Core:AttributeMetaEnum+' => '', + 'Core:AttributeLinkedSetIndirect' => '对象数组 (N-N)', 'Core:AttributeLinkedSetIndirect+' => '相同类型的任何对象 [子类型]', + 'Core:AttributeInteger' => '整数', 'Core:AttributeInteger+' => '整数(可以为负)', + 'Core:AttributeDecimal' => '小数', 'Core:AttributeDecimal+' => '小数(可以为负)', + 'Core:AttributeBoolean' => '布尔', 'Core:AttributeBoolean+' => '', 'Core:AttributeBoolean/Value:null' => '', 'Core:AttributeBoolean/Value:yes' => '是', 'Core:AttributeBoolean/Value:no' => '否', - 'Core:AttributeArchiveFlag' => '是否归档', + + 'Core:AttributeArchiveFlag' => '归档标识', 'Core:AttributeArchiveFlag/Value:yes' => '是', 'Core:AttributeArchiveFlag/Value:yes+' => '该对象仅在归档模式可见', 'Core:AttributeArchiveFlag/Value:no' => '否', @@ -57,46 +84,63 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Core:AttributeArchiveFlag/Label+' => '', 'Core:AttributeArchiveDate/Label' => '归档日期', 'Core:AttributeArchiveDate/Label+' => '', - 'Core:AttributeObsolescenceFlag' => '是否废弃', + + 'Core:AttributeObsolescenceFlag' => '废弃标识', 'Core:AttributeObsolescenceFlag/Value:yes' => '是', 'Core:AttributeObsolescenceFlag/Value:yes+' => '该对象排除在影响分析中, 并且在搜索结果中隐藏', 'Core:AttributeObsolescenceFlag/Value:no' => '否', - 'Core:AttributeObsolescenceFlag/Label' => '是否废弃', + 'Core:AttributeObsolescenceFlag/Label' => '废弃标识', 'Core:AttributeObsolescenceFlag/Label+' => '基于其他属性动态计算', 'Core:AttributeObsolescenceDate/Label' => '废弃时间', 'Core:AttributeObsolescenceDate/Label+' => '该对象被废弃的大概日期', + 'Core:AttributeString' => '字符串', 'Core:AttributeString+' => '字符串', + 'Core:AttributeClass' => '类', 'Core:AttributeClass+' => '', + 'Core:AttributeApplicationLanguage' => '用户语言', 'Core:AttributeApplicationLanguage+' => '语言和国家/地区(EN US)', + 'Core:AttributeFinalClass' => '类型 (自动)', 'Core:AttributeFinalClass+' => '对象真实的类型 (内核自动创建)', + 'Core:AttributePassword' => '密码', 'Core:AttributePassword+' => '外部设备的密码', + 'Core:AttributeEncryptedString' => '加密字符串', 'Core:AttributeEncryptedString+' => '使用本地密钥加密的字符串', 'Core:AttributeEncryptUnknownLibrary' => '未知的加密库 (%1$s)', 'Core:AttributeEncryptFailedToDecrypt' => '** 解密错误 **', + 'Core:AttributeText' => '文本', 'Core:AttributeText+' => '多行字符串', + 'Core:AttributeHTML' => 'HTML', 'Core:AttributeHTML+' => 'HTML字符串', + 'Core:AttributeEmailAddress' => '邮箱地址', 'Core:AttributeEmailAddress+' => 'Email 地址', + 'Core:AttributeIPAddress' => 'IP 地址', 'Core:AttributeIPAddress+' => 'IP 地址', + 'Core:AttributeOQL' => 'OQL', 'Core:AttributeOQL+' => '对象查询语言表达式', - 'Core:AttributeEnum' => '枚举', - 'Core:AttributeEnum+' => '预定义的字符串列表', + + 'Core:AttributeEnum' => '数字', + 'Core:AttributeEnum+' => '预定义的数字列表', + 'Core:AttributeTemplateString' => '字符模板', 'Core:AttributeTemplateString+' => '包含占位符的字符串', + 'Core:AttributeTemplateText' => '文本模板', 'Core:AttributeTemplateText+' => '包含占位符的文本', + 'Core:AttributeTemplateHTML' => 'HTML模板', 'Core:AttributeTemplateHTML+' => '含有占位符的HTML', + 'Core:AttributeDateTime' => '日期/时间', 'Core:AttributeDateTime+' => '日期和时间 (年-月-日 时:分:秒)', 'Core:AttributeDateTime?SmartSearch' => ' @@ -112,8 +156,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ [日期,日期]

-如果不写具体时间,则默认00:00:00 +如果省略时间,则默认为 00:00:00

', + 'Core:AttributeDate' => '日期', 'Core:AttributeDate+' => '日期 (年-月-日)', 'Core:AttributeDate?SmartSearch' => ' @@ -128,34 +173,47 @@ Operators:
<日期
[日期,日期]

', + 'Core:AttributeDeadline' => '截止日期', 'Core:AttributeDeadline+' => '日期, 显示与当前的相对时间', + 'Core:AttributeExternalKey' => '外键', - 'Core:AttributeExternalKey+' => '外部关联键', - 'Core:AttributeHierarchicalKey' => '层级键', - 'Core:AttributeHierarchicalKey+' => '关联到父级的外键', + 'Core:AttributeExternalKey+' => '外面(或外部)的键', + + 'Core:AttributeHierarchicalKey' => 'Hierarchical Key~~', + 'Core:AttributeHierarchicalKey+' => 'External (or foreign) key to the parent~~', + 'Core:AttributeExternalField' => '外部字段', - 'Core:AttributeExternalField+' => '映射到外键的字段', + 'Core:AttributeExternalField+' => 'Field mapped to an external key~~', + 'Core:AttributeURL' => 'URL', 'Core:AttributeURL+' => '绝对或相对的URL字符串', + 'Core:AttributeBlob' => 'Blob', 'Core:AttributeBlob+' => '任何二进制内容(文档)', + 'Core:AttributeOneWayPassword' => '单向密码', 'Core:AttributeOneWayPassword+' => '单向加密 (或哈希) 的密码', + 'Core:AttributeTable' => '表', 'Core:AttributeTable+' => '带索引的二维数组', + 'Core:AttributePropertySet' => '属性', 'Core:AttributePropertySet+' => '非类型化的属性列表 (名称和值)', - 'Core:AttributeFriendlyName' => '通用名称', + + 'Core:AttributeFriendlyName' => '昵称', 'Core:AttributeFriendlyName+' => '自动创建的属性; 显示名称基于多个属性计算', + 'Core:FriendlyName-Label' => '全称', 'Core:FriendlyName-Description' => '全称', + 'Core:AttributeTag' => '标签', - 'Core:AttributeTag+' => '标签', + 'Core:AttributeTag+' => '', + 'Core:Context=REST/JSON' => 'REST', 'Core:Context=Synchro' => '同步', 'Core:Context=Setup' => '安装向导', - 'Core:Context=GUI:Console' => '命令行', + 'Core:Context=GUI:Console' => '终端', 'Core:Context=CRON' => '定时任务', 'Core:Context=GUI:Portal' => '门户', ]); @@ -315,12 +373,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:EventNotification' => '通知事件', 'Class:EventNotification+' => '已发送通知的追踪', 'Class:EventNotification/Attribute:trigger_id' => '触发器', - 'Class:EventNotification/Attribute:trigger_id+' => '用户账号', - 'Class:EventNotification/Attribute:object_class' => 'Object class~~', - 'Class:EventNotification/Attribute:object_class+' => 'Object class (Same as trigger)~~', + 'Class:EventNotification/Attribute:trigger_id+' => '账号', + 'Class:EventNotification/Attribute:object_class' => '对象类型', + 'Class:EventNotification/Attribute:object_class+' => '对象类型 (与触发器相同)', 'Class:EventNotification/Attribute:action_id' => '用户', - 'Class:EventNotification/Attribute:action_id+' => '用户账号', - 'Class:EventNotification/Attribute:object_id' => '对象编号', + 'Class:EventNotification/Attribute:action_id+' => '账号', + 'Class:EventNotification/Attribute:object_id' => '对象id', 'Class:EventNotification/Attribute:object_id+' => '对象编号 (类型由触发器定义?)', ]); @@ -428,7 +486,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:EventNotificationNewsroom' => 'News sent~~', + 'Class:EventNotificationNewsroom' => '已发送的新闻', 'Class:EventNotificationNewsroom+' => '', 'Class:EventNotificationNewsroom/Attribute:title' => '标题', 'Class:EventNotificationNewsroom/Attribute:title+' => '', @@ -456,6 +514,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:EventNotificationNewsroom/Attribute:read_date+' => '', 'Class:EventNotificationNewsroom/Attribute:contact_id' => '联系人', 'Class:EventNotificationNewsroom/Attribute:contact_id+' => '', + 'Core:EventNotificationNewsroom:ErrorNotificationNotSent' => '通知未发送', + 'Core:EventNotificationNewsroom:ErrorOnDBInsert' => '保存通知时发生错误', ]); // @@ -463,23 +523,23 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:Action' => '自定义操作', - 'Class:Action+' => '用户定义的操作', - 'Class:Action/ComplementaryName' => '%1$s: %2$s', - 'Class:Action/Attribute:name' => '名称', - 'Class:Action/Attribute:name+' => 'Any value that is meaningful to distinguish this action from the others', - 'Class:Action/Attribute:description' => '描述', - 'Class:Action/Attribute:description+' => 'A longer explanation about the purpose of this action. For information only.', - 'Class:Action/Attribute:status' => '状态', - 'Class:Action/Attribute:status+' => '是否正式环境?', - 'Class:Action/Attribute:status/Value:test' => '正在测试', - 'Class:Action/Attribute:status/Value:test+' => '', - 'Class:Action/Attribute:status/Value:enabled' => '正式', - 'Class:Action/Attribute:status/Value:enabled+' => '', - 'Class:Action/Attribute:status/Value:disabled' => '停用', + 'Class:Action' => '自定义操作', + 'Class:Action+' => '用户定义的操作', + 'Class:Action/ComplementaryName' => '%1$s: %2$s', + 'Class:Action/Attribute:name' => '名称', + 'Class:Action/Attribute:name+' => 'Any value that is meaningful to distinguish this action from the others', + 'Class:Action/Attribute:description' => '描述', + 'Class:Action/Attribute:description+' => 'A longer explanation about the purpose of this action. For information only.', + 'Class:Action/Attribute:status' => '状态', + 'Class:Action/Attribute:status+' => '是否正式环境?', + 'Class:Action/Attribute:status/Value:test' => '正在测试', + 'Class:Action/Attribute:status/Value:test+' => '', + 'Class:Action/Attribute:status/Value:enabled' => '正式', + 'Class:Action/Attribute:status/Value:enabled+' => '', + 'Class:Action/Attribute:status/Value:disabled' => '停用', 'Class:Action/Attribute:status/Value:disabled+' => '', 'Class:Action/Attribute:trigger_list' => '相关的触发器', - 'Class:Action/Attribute:trigger_list+' => '此操作关联的触发器', + 'Class:Action/Attribute:trigger_list+' => '此操作相关的触发器', 'Class:Action/Attribute:asynchronous' => 'Asynchronous~~', 'Class:Action/Attribute:asynchronous+' => 'Whether this action should be executed in the background or not~~', 'Class:Action/Attribute:asynchronous/Value:use_global_setting' => '使用全局设置', @@ -499,10 +559,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:ActionNotification' => '通知', - 'Class:ActionNotification+' => '通知 (抽象)', + 'Class:ActionNotification' => '通知操作', + 'Class:ActionNotification+' => '通知操作 (抽象)', 'Class:ActionNotification/Attribute:language' => '语言', - 'Class:ActionNotification/Attribute:language+' => '', + 'Class:ActionNotification/Attribute:language+' => 'Language to use for placeholders ($xxx$) inside the message (state, importance, priority, etc)~~', ]); // @@ -510,15 +570,15 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkActionNotificationToContact' => 'Link ActionNotification / Contact~~', + 'Class:lnkActionNotificationToContact' => '链接 操作通知/联系人', 'Class:lnkActionNotificationToContact+' => 'Contact subscription to Notification Action~~', - 'Class:lnkActionNotificationToContact/Attribute:contact_id' => 'Contact~~', + 'Class:lnkActionNotificationToContact/Attribute:contact_id' => '联系人', 'Class:lnkActionNotificationToContact/Attribute:contact_id+' => 'Contact who subscribed (or not) to the notification~~', - 'Class:lnkActionNotificationToContact/Attribute:action_id' => 'Action~~', + 'Class:lnkActionNotificationToContact/Attribute:action_id' => '操作', 'Class:lnkActionNotificationToContact/Attribute:action_id+' => 'The notification that the contact received at least once, and to which he can subscribe or unsubscribe~~', - 'Class:lnkActionNotificationToContact/Attribute:trigger_id' => 'Trigger~~', + 'Class:lnkActionNotificationToContact/Attribute:trigger_id' => '触发器', 'Class:lnkActionNotificationToContact/Attribute:trigger_id+' => 'The trigger that fired the notification~~', - 'Class:lnkActionNotificationToContact/Attribute:subscribed' => 'Subscribed~~', + 'Class:lnkActionNotificationToContact/Attribute:subscribed' => '已订阅', 'Class:lnkActionNotificationToContact/Attribute:subscribed+' => 'If the contact unsubscribed (no) or is subscribed (yes and default) to the notification~~', ]); @@ -527,41 +587,43 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:ActionEmail' => '邮件通知', - 'Class:ActionEmail+' => '', - 'Class:ActionEmail/Attribute:status+' => '此状态将决定提醒谁: + 'Class:ActionEmail' => '邮件通知', + 'Class:ActionEmail+' => '', + 'Class:ActionEmail/Attribute:status+' => '此状态将决定提醒谁: - 测试中: 仅测试者, - 生产的: 所有人 (收件人, 抄送和密送) - 禁用的: 没有人', - 'Class:ActionEmail/Attribute:status/Value:test+' => '仅测试收件人会被通知', - 'Class:ActionEmail/Attribute:status/Value:enabled+' => '通知所有人, 包含抄送和秘抄', - 'Class:ActionEmail/Attribute:status/Value:disabled+' => '不发送邮件通知', - 'Class:ActionEmail/Attribute:test_recipient' => '测试收件人', - 'Class:ActionEmail/Attribute:test_recipient+' => '通知为测试中时将使用此邮件地址, 而不是收件人, 抄送和密送', - 'Class:ActionEmail/Attribute:from' => '发件人 (邮箱)', - 'Class:ActionEmail/Attribute:from+' => '固定的邮箱地址或者类似$this->agent_id->email$的占位符. + 'Class:ActionEmail/Attribute:status/Value:test+' => '仅测试收件人会被通知', + 'Class:ActionEmail/Attribute:status/Value:enabled+' => '通知所有人, 包含抄送和秘抄', + 'Class:ActionEmail/Attribute:status/Value:disabled+' => '不发送邮件通知', + 'Class:ActionEmail/Attribute:test_recipient' => '测试收件人', + 'Class:ActionEmail/Attribute:test_recipient+' => '通知为测试中时将使用此邮件地址, 而不是收件人, 抄送和密送', + 'Class:ActionEmail/Attribute:from' => '发件人 (邮箱)', + 'Class:ActionEmail/Attribute:from+' => '固定的邮箱地址或者类似$this->agent_id->email$的占位符. 有些邮件服务器可能不接收占位符.', - 'Class:ActionEmail/Attribute:from_label' => '发件人 (显示名)', - 'Class:ActionEmail/Attribute:from_label+' => '固定的显示名或者类似 $this->agent_id->friendlyname$ 的占位符', - 'Class:ActionEmail/Attribute:reply_to' => '回复至 (邮箱)', - 'Class:ActionEmail/Attribute:reply_to+' => '固定的邮箱地址或者类似$this->team_id->email$的占位符. + 'Class:ActionEmail/Attribute:from_label' => '发件人 (显示名)', + 'Class:ActionEmail/Attribute:from_label+' => '固定的显示名或者类似 $this->agent_id->friendlyname$ 的占位符', + 'Class:ActionEmail/Attribute:reply_to' => '回复至 (邮箱)', + 'Class:ActionEmail/Attribute:reply_to+' => '固定的邮箱地址或者类似$this->team_id->email$的占位符. 如果忽略则使用发件人 (邮箱).', - 'Class:ActionEmail/Attribute:reply_to_label' => '回复至 (显示名)', - 'Class:ActionEmail/Attribute:reply_to_label+' => '固定的显示名或者类似$this->team_id->friendlyname$的占位符. - 如果忽略则使用发件人 (显示名).', - 'Class:ActionEmail/Attribute:to' => '收件人', - 'Class:ActionEmail/Attribute:to+' => '收件人: 返回含有邮箱字段对象的OQL查询. + 'Class:ActionEmail/Attribute:reply_to_label' => '回复至 (显示名)', + 'Class:ActionEmail/Attribute:reply_to_label+' => '固定的显示名或者类似$this->team_id->friendlyname$的占位符. +如果忽略则使用发件人 (显示名).', + 'Class:ActionEmail/Attribute:to' => '收件人', + 'Class:ActionEmail/Attribute:to+' => 'To: an OQL query returning objects having an email field. +While editing, click on the magnifier to get pertinent examples. +You can use in the OQL :this->attribute_code with an attribute code of the object which triggered the Notification. Then test your OQL syntax using the play icon.~~', + 'Class:ActionEmail/Attribute:cc' => '抄送', + 'Class:ActionEmail/Attribute:cc+' => 'Carbon Copy: an OQL query returning objects having an email field. +While editing, click on the magnifier to get pertinent examples. +You can use in the OQL :this->attribute_code with an attribute code of the object which triggered the Notification. Then test your OQL syntax using the play icon.~~', + 'Class:ActionEmail/Attribute:bcc' => '密抄', + 'Class:ActionEmail/Attribute:bcc+' => '密送: 返回含有邮箱字段对象的OQL查询. 编辑时可点击放大镜图标获取参考示例', - 'Class:ActionEmail/Attribute:cc' => '抄送', - 'Class:ActionEmail/Attribute:cc+' => '抄送: 返回含有邮箱字段对象的OQL查询. -编辑时可点击放大镜图标获取参考示例', - 'Class:ActionEmail/Attribute:bcc' => '密抄', - 'Class:ActionEmail/Attribute:bcc+' => '密送: 返回含有邮箱字段对象的OQL查询. -编辑时可点击放大镜图标获取参考示例', - 'Class:ActionEmail/Attribute:subject' => '主题', - 'Class:ActionEmail/Attribute:subject+' => '邮件主题. 可包含类似$this->attribute_code$的占位符', - 'Class:ActionEmail/Attribute:body' => '正文', - 'Class:ActionEmail/Attribute:body+' => '邮件正文. 可包含以下占位符: + 'Class:ActionEmail/Attribute:subject' => '主题', + 'Class:ActionEmail/Attribute:subject+' => '邮件主题. 可包含类似$this->attribute_code$的占位符', + 'Class:ActionEmail/Attribute:body' => '正文', + 'Class:ActionEmail/Attribute:body+' => '邮件正文. 可包含以下占位符: - $this->attribute_code$ 触发通知的对象的任何属性, - $this->html(attribute_code)$ 内容同上但是使用html格式, - $this->hyperlink()$ 触发通知的对象的控制台链接, @@ -569,30 +631,30 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ - $this->head_html(case_log_attribute)$ 事例日志中的最新一条html格式的回复, - $this->attribute_external_key->attribute$ 任何远程属性的递归语法, - $current_contact->attribute$ 触发通知的用户的属性', - 'Class:ActionEmail/Attribute:importance' => '重要性', - 'Class:ActionEmail/Attribute:importance+' => '生成邮件的重要性标签设置', - 'Class:ActionEmail/Attribute:importance/Value:low' => '低', - 'Class:ActionEmail/Attribute:importance/Value:low+' => '', - 'Class:ActionEmail/Attribute:importance/Value:normal' => '普通', - 'Class:ActionEmail/Attribute:importance/Value:normal+' => '', - 'Class:ActionEmail/Attribute:importance/Value:high' => '高', - 'Class:ActionEmail/Attribute:importance/Value:high+' => '', - 'Class:ActionEmail/Attribute:language' => '语言', - 'Class:ActionEmail/Attribute:language+' => '在邮件中 (状态, 重要性, 优先级, 等等) 所使用的占位符 ($xxx$) 的语言', - 'Class:ActionEmail/Attribute:html_template' => 'HTML模板', - 'Class:ActionEmail/Attribute:html_template+' => '绑定在以下 \'正文\' 属性内容上的可选HTML模板, 用于定制邮件布局 (在模板中, \'正文\' 属性的内容将被占位符 $content$ 替换)', - 'Class:ActionEmail/Attribute:ignore_notify' => '忽略通知标记', - 'Class:ActionEmail/Attribute:ignore_notify+' => '如果设置为 \'是\' 则联系人的 \'通知\' 标记将不生效.', - 'Class:ActionEmail/Attribute:ignore_notify/Value:no' => '否', - 'Class:ActionEmail/Attribute:ignore_notify/Value:yes' => '是', - 'ActionEmail:main' => '消息', - 'ActionEmail:trigger' => '触发器', - 'ActionEmail:recipients' => '联系人', - 'ActionEmail:preview_tab' => '预览', - 'ActionEmail:preview_tab+' => '预览邮件模板', - 'ActionEmail:preview_warning' => '实际收到的邮件在客户端中可能与当前在浏览器中的预览有所不同.', - 'ActionEmail:preview_more_info' => '若需更多不同邮件客户端支持的CSS特性信息, 请参阅%1$s', - 'ActionEmail:content_placeholder_missing' => '标识符 "%1$s" 在HTML中不存在. 字段 "%2$s" 的内容将不会包含在生成的邮件中.', + 'Class:ActionEmail/Attribute:importance' => '重要性', + 'Class:ActionEmail/Attribute:importance+' => '生成邮件的重要性标签设置', + 'Class:ActionEmail/Attribute:importance/Value:low' => '低', + 'Class:ActionEmail/Attribute:importance/Value:low+' => '', + 'Class:ActionEmail/Attribute:importance/Value:normal' => '普通', + 'Class:ActionEmail/Attribute:importance/Value:normal+' => '', + 'Class:ActionEmail/Attribute:importance/Value:high' => '高', + 'Class:ActionEmail/Attribute:importance/Value:high+' => '', + 'Class:ActionEmail/Attribute:language' => '语言', + 'Class:ActionEmail/Attribute:language+' => '在邮件中 (状态, 重要性, 优先级, 等等) 所使用的占位符 ($xxx$) 的语言', + 'Class:ActionEmail/Attribute:html_template' => 'HTML模板', + 'Class:ActionEmail/Attribute:html_template+' => '绑定在以下 \'正文\' 属性内容上的可选HTML模板, 用于定制邮件布局 (在模板中, \'正文\' 属性的内容将被占位符 $content$ 替换)', + 'Class:ActionEmail/Attribute:ignore_notify' => '忽略通知标记', + 'Class:ActionEmail/Attribute:ignore_notify+' => '如果设置为 \'是\' 则联系人的 \'通知\' 标记将不生效.', + 'Class:ActionEmail/Attribute:ignore_notify/Value:no' => '否', + 'Class:ActionEmail/Attribute:ignore_notify/Value:yes' => '是', + 'ActionEmail:main' => '消息', + 'ActionEmail:trigger' => '触发器', + 'ActionEmail:recipients' => '联系人', + 'ActionEmail:preview_tab' => '预览', + 'ActionEmail:preview_tab+' => '预览邮件模板', + 'ActionEmail:preview_warning' => '实际收到的邮件在客户端中可能与当前在浏览器中的预览有所不同.', + 'ActionEmail:preview_more_info' => '若需更多不同邮件客户端支持的CSS特性信息, 请参阅%1$s', + 'ActionEmail:content_placeholder_missing' => '标识符 "%1$s" 在HTML中不存在. 字段 "%2$s" 的内容将不会包含在生成的邮件中.', ]); // @@ -603,7 +665,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'ActionNewsroom:trigger' => '触发器', 'ActionNewsroom:content' => '消息', 'ActionNewsroom:settings' => '设置', - 'Class:ActionNewsroom' => ITOP_APPLICATION_SHORT.'通知', + 'Class:ActionNewsroom' => '新闻室的通知', 'Class:ActionNewsroom+' => '', 'Class:ActionNewsroom/Attribute:title' => '标题', 'Class:ActionNewsroom/Attribute:title+' => 'Title of the news. Can contain placeholders like $this->attribute_code$~~', @@ -620,8 +682,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ - Otherwise, the application compact logo will be used~~', 'Class:ActionNewsroom/Attribute:priority' => '优先级', 'Class:ActionNewsroom/Attribute:priority+' => 'News will be ordered by decreasing priority, when displayed in the Newsroom popup', - 'Class:ActionNewsroom/Attribute:priority/Value:1' => '严峻', - 'Class:ActionNewsroom/Attribute:priority/Value:1+' => '严峻', + 'Class:ActionNewsroom/Attribute:priority/Value:1' => '严重', + 'Class:ActionNewsroom/Attribute:priority/Value:1+' => '严重', 'Class:ActionNewsroom/Attribute:priority/Value:2' => '紧急', 'Class:ActionNewsroom/Attribute:priority/Value:2+' => '紧急', 'Class:ActionNewsroom/Attribute:priority/Value:3' => '重要', @@ -641,21 +703,21 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:Trigger' => '触发器', - 'Class:Trigger+' => '自定义事件处理', - 'Class:Trigger/ComplementaryName' => '%1$s, %2$s', - 'Class:Trigger/Attribute:description' => '描述', - 'Class:Trigger/Attribute:description+' => '简短描述', - 'Class:Trigger/Attribute:action_list' => '触发的操作', - 'Class:Trigger/Attribute:action_list+' => '此触发器激活后要执行的操作', - 'Class:Trigger/Attribute:finalclass' => '触发器类型', - 'Class:Trigger/Attribute:finalclass+' => '根本属性的名称', - 'Class:Trigger/Attribute:context' => '上下文', - 'Class:Trigger/Attribute:context+' => '允许此触发器开启的上下文', - 'Class:Trigger/Attribute:complement' => '其它信息', - 'Class:Trigger/Attribute:complement+' => '此触发器提供的更多信息, 使用英文', - 'Class:Trigger/Attribute:subscription_policy' => 'Subscription policy~~', - 'Class:Trigger/Attribute:subscription_policy+' => 'Allows users to unsubscribe from the trigger~~', + 'Class:Trigger' => '触发器', + 'Class:Trigger+' => '自定义事件处理', + 'Class:Trigger/ComplementaryName' => '%1$s, %2$s', + 'Class:Trigger/Attribute:description' => '描述', + 'Class:Trigger/Attribute:description+' => '简短描述', + 'Class:Trigger/Attribute:action_list' => '触发的操作', + 'Class:Trigger/Attribute:action_list+' => '此触发器激活后要执行的操作', + 'Class:Trigger/Attribute:finalclass' => '触发器类型', + 'Class:Trigger/Attribute:finalclass+' => '根本属性的名称', + 'Class:Trigger/Attribute:context' => '上下文', + 'Class:Trigger/Attribute:context+' => '允许此触发器开启的上下文', + 'Class:Trigger/Attribute:complement' => '其它信息', + 'Class:Trigger/Attribute:complement+' => '此触发器提供的更多信息, 使用英文', + 'Class:Trigger/Attribute:subscription_policy' => 'Subscription policy~~', + 'Class:Trigger/Attribute:subscription_policy+' => 'Allows users to unsubscribe from the trigger~~', 'Class:Trigger/Attribute:subscription_policy/Value:allow_no_channel' => 'Allow complete unsubscription~~', 'Class:Trigger/Attribute:subscription_policy/Value:force_at_least_one_channel' => 'Force at least one channel (News or Email)~~', 'Class:Trigger/Attribute:subscription_policy/Value:force_all_channels' => 'Deny unsubscription~~', @@ -666,14 +728,14 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:TriggerOnObject' => '触发器 (类型依赖的)', - 'Class:TriggerOnObject+' => '在指定类型对象上的触发器', - 'Class:TriggerOnObject/Attribute:target_class' => '目标类型', - 'Class:TriggerOnObject/Attribute:target_class+' => '', - 'Class:TriggerOnObject/Attribute:filter' => '筛选器', - 'Class:TriggerOnObject/Attribute:filter+' => '限定将激活触发器的对象 (目标类型的)', - 'TriggerOnObject:WrongFilterQuery' => '错误的筛选查询: %1$s', - 'TriggerOnObject:WrongFilterClass' => '筛选查询返回的对象必须是类型 "%1$s"', + 'Class:TriggerOnObject' => '触发器 (类型依赖的)', + 'Class:TriggerOnObject+' => '在指定类型对象上的触发器', + 'Class:TriggerOnObject/Attribute:target_class' => '目标类型', + 'Class:TriggerOnObject/Attribute:target_class+' => 'Objects in this class will activate the trigger~~', + 'Class:TriggerOnObject/Attribute:filter' => '筛选器', + 'Class:TriggerOnObject/Attribute:filter+' => '限定将激活触发器的对象 (目标类型的)', + 'TriggerOnObject:WrongFilterQuery' => '错误的筛选查询: %1$s', + 'TriggerOnObject:WrongFilterClass' => '筛选查询返回的对象必须是类型 "%1$s"', ]); // @@ -681,7 +743,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:TriggerOnPortalUpdate' => '触发器 (工单更新时)', + 'Class:TriggerOnPortalUpdate' => '触发器 (工单被更新时)', 'Class:TriggerOnPortalUpdate+' => '终端用户更新工单时触发', ]); @@ -719,7 +781,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:TriggerOnObjectCreate' => '触发器 (对象创建时)', + 'Class:TriggerOnObjectCreate' => '触发器 (对象被创建时)', 'Class:TriggerOnObjectCreate+' => '对象创建时触发', ]); @@ -728,7 +790,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:TriggerOnObjectDelete' => '触发器 (对象删除时)', + 'Class:TriggerOnObjectDelete' => '触发器 (对象被删除时)', 'Class:TriggerOnObjectDelete+' => '指定类型或子类型对象删除时的触发器', ]); @@ -737,7 +799,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:TriggerOnObjectUpdate' => '触发器 (对象更新时)', + 'Class:TriggerOnObjectUpdate' => '触发器 (对象被更新时)', 'Class:TriggerOnObjectUpdate+' => '指定类型或子类型对象更新时的触发器', 'Class:TriggerOnObjectUpdate/Attribute:filter+' => 'This filter is computed after the object update in database. It restricts the objects which can trigger the actions~~', 'Class:TriggerOnObjectUpdate/Attribute:target_attcodes' => '目标字段', @@ -749,7 +811,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:TriggerOnObjectMention' => '触发器 (对象提及时)', + 'Class:TriggerOnObjectMention' => '触发器 (对象被提及时)', 'Class:TriggerOnObjectMention+' => '指定类型或子类型对象在属性日志中提及 (@xxx) 时的触发器', 'Class:TriggerOnObjectMention/Attribute:mentioned_filter' => '提及筛选', 'Class:TriggerOnObjectMention/Attribute:mentioned_filter+' => '限丁将激活此触发器的提及对象. 如果为空则任何类的提及对象将激活此触发器.', @@ -760,7 +822,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:TriggerOnAttributeBlobDownload' => '触发器 (对象文档下载时)', + 'Class:TriggerOnAttributeBlobDownload' => '触发器 (对象文档被下载时)', 'Class:TriggerOnAttributeBlobDownload+' => '指定类型或子类型对象的文档下载时的触发器', 'Class:TriggerOnAttributeBlobDownload/Attribute:target_attcodes' => '目标字段', 'Class:TriggerOnAttributeBlobDownload/Attribute:target_attcodes+' => '', @@ -784,8 +846,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkTriggerAction' => '操作/触发器', - 'Class:lnkTriggerAction+' => '关联触发器和操作', + 'Class:lnkTriggerAction' => '链接 操作/触发器', + 'Class:lnkTriggerAction+' => '链接触发器和操作', 'Class:lnkTriggerAction/Attribute:action_id' => '操作', 'Class:lnkTriggerAction/Attribute:action_id+' => '要执行的操作', 'Class:lnkTriggerAction/Attribute:action_name' => '操作', @@ -870,7 +932,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SynchroDataSource/Attribute:user_delete_policy/Value:administrators' => '仅管理员', 'Class:SynchroDataSource/Attribute:user_delete_policy/Value:everybody' => '所有允许删除此类型对象的用户', 'Class:SynchroDataSource/Attribute:user_delete_policy/Value:nobody' => '没有人', - 'SynchroDataSource:Description' => '描述', + + 'SynchroDataSource:Description' => '描述', 'SynchroDataSource:Reconciliation' => '搜索 & 使一致', 'SynchroDataSource:Deletion' => '删除规则', 'SynchroDataSource:Status' => '状态', @@ -996,7 +1059,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SynchroLog/Attribute:stats_nb_obj_new_unchanged' => '未更改对象数量', 'Class:SynchroLog/Attribute:last_error' => '最新错误', 'Class:SynchroLog/Attribute:traces' => '跟踪', - 'Class:SynchroReplica' => '同步复制', + 'Class:SynchroReplica' => '同步副本', 'Class:SynchroReplica/Attribute:sync_source_id' => '同步数据源', 'Class:SynchroReplica/Attribute:dest_id' => '目标对象 (编号)', 'Class:SynchroReplica/Attribute:dest_class' => '目标类型', @@ -1014,19 +1077,23 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SynchroReplica/Attribute:info_last_modified' => '最后修改日期', 'Class:appUserPreferences' => '用户偏好', 'Class:appUserPreferences/Attribute:userid' => '用户', - 'Class:appUserPreferences/Attribute:preferences' => '首选项', + 'Class:appUserPreferences/Attribute:preferences' => '偏好设置', 'Core:ExecProcess:Code1' => '命令错误或命令执行出错 (例如错误的脚本名称)', 'Core:ExecProcess:Code255' => 'PHP错误 (解析, 或运行时)', - 'Core:Duration_Seconds' => '%1$ds', - 'Core:EventNotificationNewsroom:ErrorNotificationNotSent' => 'Notification not sent~~', - 'Core:EventNotificationNewsroom:ErrorOnDBInsert' => 'An error occurred while saving the notification~~', + + // Attribute Duration + 'Core:Duration_Seconds' => '%1$ds', 'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds', - 'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds', - 'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds', - 'Core:ExplainWTC:ElapsedTime' => '耗时 (储存为 "%1$s")', + 'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds', + 'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds', + + // Explain working time computing + 'Core:ExplainWTC:ElapsedTime' => '耗时 (储存为 "%1$s")', 'Core:ExplainWTC:StopWatch-TimeSpent' => '在 "%1$s" 上消耗的时间', 'Core:ExplainWTC:StopWatch-Deadline' => '"%1$s" 的期限截止于%2$d%%', - 'Core:BulkExport:MissingParameter_Param' => '缺少参数 "%1$s"', + + // Bulk export + 'Core:BulkExport:MissingParameter_Param' => '缺少参数 "%1$s"', 'Core:BulkExport:InvalidParameter_Query' => '参数 "query" 的值无效. 在查询薄中没有找到对应编号: "%1$s" 的记录.', 'Core:BulkExport:ExportFormatPrompt' => '导出格式:', 'Core:BulkExportOf_Class' => '%1$s 导出', @@ -1072,7 +1139,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Core:BulkExportCanRunNonInteractive' => '点击这里运行非交互式导出.', 'Core:BulkExportLegacyExport' => '点击这里进入旧版导出.', 'Core:BulkExport:XLSXOptions' => 'Excel 选项', - 'Core:BulkExport:TextFormat' => '文本中包含一些HTML 标记', + 'Core:BulkExport:TextFormat' => '文本中包含一些 HTML 标签', 'Core:BulkExport:DateTimeFormat' => '日期和时间格式', 'Core:BulkExport:DateTimeFormatDefault_Example' => '默认格式 (%1$s), e.g. %2$s', 'Core:BulkExport:DateTimeFormatCustom_Format' => '自定义格式: %1$s', @@ -1095,7 +1162,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Core:Validator:Mandatory' => '这里必填', 'Core:Validator:MustBeInteger' => '必须是整数', 'Core:Validator:MustSelectOne' => '请选择', - 'INTERNAL:JQuery-DatePicker:LangCode' => 'zh-CN', ]); // @@ -1103,8 +1169,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:TagSetFieldData' => '类型%1$s的%2$s', - 'Class:TagSetFieldData+' => '~~', - 'Class:TagSetFieldData/Attribute:code' => '代码', + 'Class:TagSetFieldData+' => '', + + 'Class:TagSetFieldData/Attribute:code' => '代码', 'Class:TagSetFieldData/Attribute:code+' => '内部代码. 必须至少包含3个数字或字母', 'Class:TagSetFieldData/Attribute:label' => '标签', 'Class:TagSetFieldData/Attribute:label+' => '显示的标签', @@ -1113,7 +1180,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:TagSetFieldData/Attribute:finalclass' => '标签类型', 'Class:TagSetFieldData/Attribute:obj_class' => '对象类型', 'Class:TagSetFieldData/Attribute:obj_attcode' => '字段编码', - 'Core:TagSetFieldData:ErrorDeleteUsedTag' => '已使用的标签无法删除', + + 'Core:TagSetFieldData:ErrorDeleteUsedTag' => '已使用的标签无法删除', 'Core:TagSetFieldData:ErrorDuplicateTagCodeOrLabel' => '标签编码或名称必须唯一', 'Core:TagSetFieldData:ErrorTagCodeSyntax' => '标签编码必须介于3到%1$d个字符, 以字母开头.', 'Core:TagSetFieldData:ErrorTagCodeReservedWord' => '输入的标签编码为内部保留字', @@ -1130,7 +1198,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:DBProperty' => '数据库属性', - 'Class:DBProperty+' => '~~', + 'Class:DBProperty+' => '', 'Class:DBProperty/Attribute:name' => '名称', 'Class:DBProperty/Attribute:name+' => '', 'Class:DBProperty/Attribute:description' => '描述', @@ -1238,3 +1306,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ResourceSystemMenu' => '资源系统菜单', 'Class:ResourceSystemMenu+' => '', ]); + +// additional translation for zh_cn +Dict::Add('ZH CN', 'Chinese', '简体中文', [ + 'INTERNAL:JQuery-DatePicker:LangCode' => 'zh-CN', +]); diff --git a/dictionaries/zh_cn.dictionary.itop.ui.php b/dictionaries/zh_cn.dictionary.itop.ui.php index c3ba360bf5..8565913621 100644 --- a/dictionaries/zh_cn.dictionary.itop.ui.php +++ b/dictionaries/zh_cn.dictionary.itop.ui.php @@ -1,33 +1,42 @@ '审计类别', - 'Class:AuditCategory+' => '审计类别规定了需要被审计的对象的范围. + 'Class:AuditCategory' => '审计类别', + 'Class:AuditCategory+' => '审计类别规定了需要被审计的对象的范围. 其组合了用于此范围内对象的所有审计规则', - 'Class:AuditCategory/Attribute:name' => '类别', - 'Class:AuditCategory/Attribute:name+' => '类别简称', - 'Class:AuditCategory/Attribute:description' => '审计类别描述', - 'Class:AuditCategory/Attribute:description+' => '此审计类别的详细描述', - 'Class:AuditCategory/Attribute:definition_set' => '定义', - 'Class:AuditCategory/Attribute:definition_set+' => '定义用于审计的对象的OQL表达式', - 'Class:AuditCategory/Attribute:rules_list' => '审计规则', - 'Class:AuditCategory/Attribute:rules_list+' => '此类别的审计规则', - 'Class:AuditCategory/Attribute:ok_error_tolerance' => '报警阈值 (正常)', - 'Class:AuditCategory/Attribute:ok_error_tolerance+' => '结果仍然显示为正常 (绿色) 的可允许的最大无效对象比例', - 'Class:AuditCategory/Attribute:warning_error_tolerance' => '错误阈值 (警告)', + 'Class:AuditCategory/Attribute:name' => '类别', + 'Class:AuditCategory/Attribute:name+' => '类别简称', + 'Class:AuditCategory/Attribute:description' => '审计类别描述', + 'Class:AuditCategory/Attribute:description+' => '此审计类别的详细描述', + 'Class:AuditCategory/Attribute:definition_set' => '定义', + 'Class:AuditCategory/Attribute:definition_set+' => '定义用于审计的对象的OQL表达式', + 'Class:AuditCategory/Attribute:rules_list' => '审计规则', + 'Class:AuditCategory/Attribute:rules_list+' => '此类别的审计规则', + 'Class:AuditCategory/Attribute:ok_error_tolerance' => '报警阈值 (正常)', + 'Class:AuditCategory/Attribute:ok_error_tolerance+' => '结果仍然显示为正常 (绿色) 的可允许的最大无效对象比例', + 'Class:AuditCategory/Attribute:warning_error_tolerance' => '错误阈值 (警告)', 'Class:AuditCategory/Attribute:warning_error_tolerance+' => '结果仍然显示为警告 (橙色) 的可允许的最大无效对象比例', - 'Class:AuditCategory/Attribute:domains_list' => '审计域', - 'Class:AuditCategory/Attribute:domains_list+' => '关联此类别的审计域列表', + 'Class:AuditCategory/Attribute:domains_list' => '审计域', + 'Class:AuditCategory/Attribute:domains_list+' => '此类别相关的审计域列表', ]); // @@ -35,29 +44,31 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:AuditRule' => '审计规则', - 'Class:AuditRule+' => '一条审计规则对应审计类别中的一项检查. + 'Class:AuditRule' => '审计规则', + 'Class:AuditRule+' => '一条审计规则对应审计类别中的一项检查. 其应用于审计规则定义的对象范围', - 'Class:AuditRule/Attribute:name' => '名称', - 'Class:AuditRule/Attribute:name+' => '规则名称', - 'Class:AuditRule/Attribute:description' => '描述', - 'Class:AuditRule/Attribute:description+' => '检查什么? 如何修复? 谁去做? ...', - 'Class:AuditRule/Attribute:process' => 'Correction process~~', - 'Class:AuditRule/Attribute:process+' => 'How should it be fixed? Who should do it? ...~~', - 'Class:AuditRule/Attribute:query' => '要运行的查询', - 'Class:AuditRule/Attribute:query+' => '要运行的OQL表达式', - 'Class:AuditRule/Attribute:valid_flag' => '是否有效?', - 'Class:AuditRule/Attribute:valid_flag+' => '若规则返回有效对象则True, 否则False', - 'Class:AuditRule/Attribute:valid_flag/Value:true' => 'Valid objects~~', - 'Class:AuditRule/Attribute:valid_flag/Value:true+' => 'Then objects in error are those part of the scope and not in the list of valid objects~~', - 'Class:AuditRule/Attribute:valid_flag/Value:false' => 'Invalid objects~~', + 'Class:AuditRule/Attribute:name' => '名称', + 'Class:AuditRule/Attribute:name+' => '规则名称', + 'Class:AuditRule/Attribute:description' => '描述', + 'Class:AuditRule/Attribute:description+' => '检查什么? 如何修复? 谁去做? ...', + 'Class:AuditRule/Attribute:process' => 'Correction process~~', + 'Class:AuditRule/Attribute:process+' => 'How should it be fixed? Who should do it? ...~~', + 'Class:AuditRule/Attribute:query' => '要运行的查询', + 'Class:AuditRule/Attribute:query+' => '要运行的OQL表达式', + 'Class:AuditRule/Attribute:valid_flag' => '返回的对象:', + 'Class:AuditRule/Attribute:valid_flag+' => '查询返回的对象是否有效? +"有效对象": then objects in error are those part of the scope and not in the list of valid objects, +"无效对象": then objects in error are the invalid objects which are part of the scope~~', + 'Class:AuditRule/Attribute:valid_flag/Value:true' => '有效对象', + 'Class:AuditRule/Attribute:valid_flag/Value:true+' => 'Then objects in error are those part of the scope and not in the list of valid objects~~', + 'Class:AuditRule/Attribute:valid_flag/Value:false' => '无效对象', 'Class:AuditRule/Attribute:valid_flag/Value:false+' => 'Then objects in error are the invalid objects which are part of the scope~~', - 'Class:AuditRule/Attribute:category_id' => '类别', - 'Class:AuditRule/Attribute:category_id+' => '该规则对应的类别', - 'Class:AuditRule/Attribute:category_name' => '类别', - 'Class:AuditRule/Attribute:category_name+' => '该规则对应的类名称', - 'Class:AuditRule/Attribute:contact_id' => 'Owner~~', - 'Class:AuditRule/Attribute:contact_id+' => 'Team or person in charge of fixing the errors detected by this rule~~', + 'Class:AuditRule/Attribute:category_id' => '类别', + 'Class:AuditRule/Attribute:category_id+' => '该规则对应的类别', + 'Class:AuditRule/Attribute:category_name' => '类别名称', + 'Class:AuditRule/Attribute:category_name+' => '该规则对应的类名称', + 'Class:AuditRule/Attribute:contact_id' => 'Owner~~', + 'Class:AuditRule/Attribute:contact_id+' => 'Team or person in charge of fixing the errors detected by this rule~~', ]); // @@ -65,17 +76,17 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:AuditDomain' => '审计域', - 'Class:AuditDomain+' => '审计域用于分组审计规则. + 'Class:AuditDomain' => '审计域', + 'Class:AuditDomain+' => '审计域用于分组审计规则. 审计域通常对应负责检查和修复错误的人', - 'Class:AuditDomain/Attribute:name' => '名称', - 'Class:AuditDomain/Attribute:name+' => '用于细分负责修复错误或只对审计感兴趣的人员', - 'Class:AuditDomain/Attribute:description' => '描述', - 'Class:AuditDomain/Attribute:description+' => '', - 'Class:AuditDomain/Attribute:icon' => '图标', - 'Class:AuditDomain/Attribute:icon+' => '', - 'Class:AuditDomain/Attribute:categories_list' => '类别', - 'Class:AuditDomain/Attribute:categories_list+' => '关联的审计类别', + 'Class:AuditDomain/Attribute:name' => '名称', + 'Class:AuditDomain/Attribute:name+' => '用于细分负责修复错误或只对审计感兴趣的人', + 'Class:AuditDomain/Attribute:description' => '描述', + 'Class:AuditDomain/Attribute:description+' => '', + 'Class:AuditDomain/Attribute:icon' => '图标', + 'Class:AuditDomain/Attribute:icon+' => '', + 'Class:AuditDomain/Attribute:categories_list' => '类别', + 'Class:AuditDomain/Attribute:categories_list+' => '此类别相关的审计类别', ]); // @@ -83,7 +94,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:lnkAuditCategoryToAuditDomain' => '关联审计类别/审计域', + 'Class:lnkAuditCategoryToAuditDomain' => '链接 审计类别/审计域', 'Class:lnkAuditCategoryToAuditDomain+' => '', 'Class:lnkAuditCategoryToAuditDomain/Attribute:category_id' => '类别', 'Class:lnkAuditCategoryToAuditDomain/Attribute:category_id+' => '审计类别', @@ -100,32 +111,32 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:Query' => '查询', - 'Class:Query+' => '查询是一种动态的数据集', - 'Class:Query/Attribute:name' => '名称', - 'Class:Query/Attribute:name+' => '查询的名称', - 'Class:Query/Attribute:description' => '描述', - 'Class:Query/Attribute:description+' => '请描述此查询 (目的, 用法等等.)', - 'Class:Query/Attribute:is_template' => 'OQL模板', - 'Class:Query/Attribute:is_template+' => '可用于通知中的收件人OQL', - 'Class:Query/Attribute:is_template/Value:yes' => '是', - 'Class:Query/Attribute:is_template/Value:no' => '否', - 'Class:Query/Attribute:export_count' => '导出计数', - 'Class:Query/Attribute:export_count+' => '反应此查询执行的次数', - 'Class:Query/Attribute:export_last_date' => '最近导出', - 'Class:Query/Attribute:export_last_date+' => '最近一次导出执行的日期和时间', - 'Class:Query/Attribute:export_last_user_id' => '用户', - 'Class:Query/Attribute:export_last_user_id+' => '执行最近一次导出的用户', - 'Class:Query/Attribute:export_last_user_contact' => '联系人', + 'Class:Query' => '查询', + 'Class:Query+' => '查询是一种动态的数据集', + 'Class:Query/Attribute:name' => '名称', + 'Class:Query/Attribute:name+' => '查询的名称', + 'Class:Query/Attribute:description' => '描述', + 'Class:Query/Attribute:description+' => '请描述此查询 (目的, 用法等等.)', + 'Class:Query/Attribute:is_template' => 'OQL模板', + 'Class:Query/Attribute:is_template+' => '可用于通知中的收件人OQL', + 'Class:Query/Attribute:is_template/Value:yes' => '是', + 'Class:Query/Attribute:is_template/Value:no' => '否', + 'Class:Query/Attribute:export_count' => '导出计数', + 'Class:Query/Attribute:export_count+' => '反应此查询执行的次数', + 'Class:Query/Attribute:export_last_date' => '最近导出', + 'Class:Query/Attribute:export_last_date+' => '最近一次导出执行的日期和时间', + 'Class:Query/Attribute:export_last_user_id' => '用户', + 'Class:Query/Attribute:export_last_user_id+' => '执行最近一次导出的用户', + 'Class:Query/Attribute:export_last_user_contact' => '联系人', 'Class:Query/Attribute:export_last_user_contact+' => '执行最近一次导出的联系人', - 'Query:baseinfo' => '基本信息', - 'Query:exportInfo' => '导出信息', - 'Class:QueryOQL/Attribute:fields' => '字段', - 'Class:QueryOQL/Attribute:fields+' => '逗号分隔的待导出字段 (或者alias.attribute)', - 'Class:QueryOQL' => 'OQL查询', - 'Class:QueryOQL+' => '一种基于对象查询的语言', - 'Class:QueryOQL/Attribute:oql' => '表达式', - 'Class:QueryOQL/Attribute:oql+' => 'OQL表达式', + 'Query:baseinfo' => '基本信息', + 'Query:exportInfo' => '导出信息', + 'Class:QueryOQL/Attribute:fields' => '字段', + 'Class:QueryOQL/Attribute:fields+' => '逗号分隔的待导出字段 (或者alias.attribute)', + 'Class:QueryOQL' => 'OQL查询', + 'Class:QueryOQL+' => '一种基于对象查询的语言', + 'Class:QueryOQL/Attribute:oql' => '表达式', + 'Class:QueryOQL/Attribute:oql+' => 'OQL表达式', ]); ////////////////////////////////////////////////////////////////////// @@ -138,36 +149,37 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Class:User' => '用户', - 'Class:User+' => '用户登录名', - 'Class:User/Attribute:finalclass' => '账号类型', - 'Class:User/Attribute:finalclass+' => '根本属性的名称', - 'Class:User/Attribute:contactid' => '人员', - 'Class:User/Attribute:contactid+' => '来自业务数据的人员明细信息', - 'Class:User/Attribute:org_id' => '组织', - 'Class:User/Attribute:org_id+' => '用户所属的组织', - 'Class:User/Attribute:last_name' => '姓', - 'Class:User/Attribute:last_name+' => '对应联系人的姓氏', - 'Class:User/Attribute:first_name' => '名', - 'Class:User/Attribute:first_name+' => '对应联系人的名字', - 'Class:User/Attribute:email' => '邮箱', - 'Class:User/Attribute:email+' => '对应联系人的邮箱', - 'Class:User/Attribute:login' => '登录名', - 'Class:User/Attribute:login+' => '用户标识字符串', - 'Class:User/Attribute:language' => '语言', - 'Class:User/Attribute:language+' => '用户语言', - 'Class:User/Attribute:language/Value:EN US' => '英语', + 'Class:User' => '用户', + 'Class:User+' => '用户登录名', + 'Class:User/Attribute:finalclass' => '账号类型', + 'Class:User/Attribute:finalclass+' => '根本属性的名称', + 'Class:User/Attribute:contactid' => '个体', + 'Class:User/Attribute:contactid+' => '来自业务数据的个体明细信息', + 'Class:User/Attribute:org_id' => '组织', + 'Class:User/Attribute:org_id+' => '用户所属的组织', + 'Class:User/Attribute:last_name' => '姓', + 'Class:User/Attribute:last_name+' => '对应联系人的姓氏', + 'Class:User/Attribute:first_name' => '名', + 'Class:User/Attribute:first_name+' => '对应联系人的名字', + 'Class:User/Attribute:email' => '邮箱', + 'Class:User/Attribute:email+' => '对应联系人的邮箱', + 'Class:User/Attribute:login' => '登录名', + 'Class:User/Attribute:login+' => '用户标识字符串', + 'Class:User/Attribute:language' => '语言', + 'Class:User/Attribute:language+' => '用户语言', + 'Class:User/Attribute:language/Value:EN US' => '英语', 'Class:User/Attribute:language/Value:EN US+' => '英语 (U.S.)', - 'Class:User/Attribute:language/Value:FR FR' => '法语', + 'Class:User/Attribute:language/Value:FR FR' => '法语', 'Class:User/Attribute:language/Value:FR FR+' => '法语 (France)', - 'Class:User/Attribute:profile_list' => '角色', - 'Class:User/Attribute:profile_list+' => '授予此用户的角色', - 'Class:User/Attribute:allowed_org_list' => '可访问的组织', - 'Class:User/Attribute:allowed_org_list+' => '目标用户可以看到以下组织的数据. 如果没有指定,则无限制.', - 'Class:User/Attribute:status' => '状态', - 'Class:User/Attribute:status+' => '账号是否启用.', - 'Class:User/Attribute:status/Value:enabled' => '启用', + 'Class:User/Attribute:profile_list' => '角色', + 'Class:User/Attribute:profile_list+' => '授予此用户的角色', + 'Class:User/Attribute:allowed_org_list' => '可访问的组织', + 'Class:User/Attribute:allowed_org_list+' => '目标用户可以看到以下组织的数据. 如果没有指定,则无限制.', + 'Class:User/Attribute:status' => '状态', + 'Class:User/Attribute:status+' => '账号是否启用.', + 'Class:User/Attribute:status/Value:enabled' => '启用', 'Class:User/Attribute:status/Value:disabled' => '停用', + 'Class:User/Error:LoginMustBeUnique' => '登录名必须唯一 - "%1s" 已经被使用.', 'Class:User/Error:AtLeastOneProfileIsNeeded' => '必须指定至少一个角色给此用户.', 'Class:User/Error:PrivilegedUserMustHaveAccessToBackOffice' => 'Profile "%1$s" cannot be given to privileged Users (Administrators, SuperUsers and REST Services Users)~~', @@ -226,7 +238,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:URP_UserProfile+' => '用户的角色', 'Class:URP_UserProfile/Name' => '关联 %1$s 和 %2$s', 'Class:URP_UserProfile/Attribute:userid' => '用户', - 'Class:URP_UserProfile/Attribute:userid+' => '用户账号', + 'Class:URP_UserProfile/Attribute:userid+' => '账号', 'Class:URP_UserProfile/Attribute:userlogin' => '登录名', 'Class:URP_UserProfile/Attribute:userlogin+' => '用户的登录名', 'Class:URP_UserProfile/Attribute:profileid' => '角色', @@ -246,7 +258,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:URP_UserOrg+' => '可以访问的组织', 'Class:URP_UserOrg/Name' => '关联 %1$s 和 %2$s', 'Class:URP_UserOrg/Attribute:userid' => '用户', - 'Class:URP_UserOrg/Attribute:userid+' => '用户账号', + 'Class:URP_UserOrg/Attribute:userid+' => '账号', 'Class:URP_UserOrg/Attribute:userlogin' => '登录名', 'Class:URP_UserOrg/Attribute:userlogin+' => '用户的登录名', 'Class:URP_UserOrg/Attribute:allowed_org_id' => '组织', @@ -303,7 +315,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:URP_ActionGrant' => '操作权限', - 'Class:URP_ActionGrant+' => '类型权限', + 'Class:URP_ActionGrant+' => '基于类型的权限', 'Class:URP_ActionGrant/Attribute:profileid' => '角色', 'Class:URP_ActionGrant/Attribute:profileid+' => '使用角色', 'Class:URP_ActionGrant/Attribute:profile' => '角色', @@ -401,108 +413,114 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'BooleanLabel:yes' => '是', 'BooleanLabel:no' => '否', - 'UI:WelcomeMenu:Title' => '欢迎使用'.ITOP_APPLICATION_SHORT, 'UI:WelcomeMenu:AllOpenRequests' => '所有打开的需求: %1$d', - 'UI:WelcomeMenu:MyCalls' => '我办理的需求', - 'UI:WelcomeMenu:OpenIncidents' => '所有打开的事件: %1$d', - 'UI:WelcomeMenu:AllConfigItems' => '配置项: %1$d', - 'UI:WelcomeMenu:MyIncidents' => '分配给我的事件', - 'UI:AllOrganizations' => ' 所有组织 ', - 'UI:YourSearch' => '搜索', - 'UI:LoggedAsMessage' => '以 %1$s 身份登录 (%2$s)', - 'UI:LoggedAsMessage+Admin' => '以 %1$s 身份登录 (%2$s, 管理员)', - 'UI:Button:Logoff' => '注销', - 'UI:Button:GlobalSearch' => '搜索', - 'UI:Button:Search' => ' 搜索 ', - 'UI:Button:Clear' => ' 清空 ', - 'UI:Button:Confirm' => ' 确认 ', - 'UI:Button:SearchInHierarchy' => '递归搜索', - 'UI:Button:Query' => ' 查询 ', - 'UI:Button:Ok' => 'OK', - 'UI:Button:Save' => '保存', - 'UI:Button:SaveAnd' => '保存并%1$s', - 'UI:Button:Cancel' => '取消', - 'UI:Button:Close' => '关闭', - 'UI:Button:Apply' => '应用', - 'UI:Button:Send' => '发送', - 'UI:Button:SendAnd' => '发送并%1$s', - 'UI:Button:Back' => ' << 上一步 ', - 'UI:Button:Restart' => ' |<< 重来 ', - 'UI:Button:Next' => ' 下一步 >> ', - 'UI:Button:Finish' => ' 结束 ', - 'UI:Button:DoImport' => ' 执行导入 ! ', - 'UI:Button:Done' => ' 完成 ', - 'UI:Button:SimulateImport' => ' 模拟导入 ', - 'UI:Button:Test' => '测试!', - 'UI:Button:Evaluate' => ' 评估 ', - 'UI:Button:Evaluate:Title' => ' 评估 (Ctrl+Enter) ', - 'UI:Button:AddObject' => ' 添加... ', - 'UI:Button:BrowseObjects' => ' 浏览... ', - 'UI:Button:Add' => ' 添加 ', - 'UI:Button:AddToList' => ' << 添加 ', - 'UI:Button:RemoveFromList' => ' 移除 >> ', - 'UI:Button:FilterList' => ' 筛选... ', - 'UI:Button:Create' => ' 创建 ', - 'UI:Button:Delete' => ' 删除 ', - 'UI:Button:Rename' => ' 重命名... ', - 'UI:Button:ChangePassword' => ' 修改密码 ', - 'UI:Button:ResetPassword' => ' 重置密码 ', - 'UI:Button:Insert' => '插入', - 'UI:Button:More' => '更多', - 'UI:Button:Less' => '更少', - 'UI:Button:Wait' => '正在更新字段,请稍候', - 'UI:Treeview:CollapseAll' => '全部收起', - 'UI:Treeview:ExpandAll' => '全部展开', - 'UI:UserPref:DoNotShowAgain' => '不再显示', - 'UI:InputFile:NoFileSelected' => '未选择文件', - 'UI:InputFile:SelectFile' => '选择文件', - 'UI:SearchToggle' => '搜索', - 'UI:ClickToCreateNew' => '新建%1$s', - 'UI:SearchFor_Class' => '搜索%1$s ', - 'UI:NoObjectToDisplay' => '没有可显示的对象.', - 'UI:Error:SaveFailed' => '对象无法被保存:', - 'UI:Error:MandatoryTemplateParameter_object_id' => '当link_attr被指定时, 参数object_id是必须的. 请检查显示模板的定义.', - 'UI:Error:MandatoryTemplateParameter_target_attr' => '当link_attr被指定时, 参数target_attr是必须的. 请检查显示模板的定义.', - 'UI:Error:MandatoryTemplateParameter_group_by' => '参数group_by是必须的. 请检查显示模板的定义.', - 'UI:Error:InvalidGroupByFields' => '分组字段列表是无效的: "%1$s".', - 'UI:Error:UnsupportedStyleOfBlock' => '错误: 不被支持的block格式: "%1$s".', + 'UI:WelcomeMenu:Title' => '欢迎使用'.ITOP_APPLICATION_SHORT, + 'UI:WelcomeMenu:AllOpenRequests' => '所有打开的需求: %1$d', + 'UI:WelcomeMenu:MyCalls' => '我办理的需求', + 'UI:WelcomeMenu:OpenIncidents' => '所有打开的事件: %1$d', + 'UI:WelcomeMenu:AllConfigItems' => '配置项: %1$d', + 'UI:WelcomeMenu:MyIncidents' => '分配给我的事件', + 'UI:AllOrganizations' => ' 所有组织 ', + 'UI:YourSearch' => '搜索', + 'UI:LoggedAsMessage' => '以 %1$s 身份登录 (%2$s)', + 'UI:LoggedAsMessage+Admin' => '以 %1$s 身份登录 (%2$s, 管理员)', + 'UI:Button:Logoff' => '注销', + 'UI:Button:GlobalSearch' => '搜索', + 'UI:Button:Search' => ' 搜索 ', + 'UI:Button:Clear' => ' 清空 ', + 'UI:Button:Confirm' => ' 确认 ', + 'UI:Button:SearchInHierarchy' => '在架构中搜索', + 'UI:Button:Query' => ' 查询 ', + 'UI:Button:Ok' => 'OK', + 'UI:Button:Save' => '保存', + 'UI:Button:SaveAnd' => '保存并%1$s', + 'UI:Button:Cancel' => '取消', + 'UI:Button:Close' => '关闭', + 'UI:Button:Apply' => '应用', + 'UI:Button:Send' => '发送', + 'UI:Button:SendAnd' => '发送并%1$s', + 'UI:Button:Back' => ' << 上一步 ', + 'UI:Button:Restart' => ' |<< 重来 ', + 'UI:Button:Next' => ' 下一步 >> ', + 'UI:Button:Finish' => ' 结束 ', + 'UI:Button:DoImport' => ' 执行导入 ! ', + 'UI:Button:Done' => ' 完成 ', + 'UI:Button:SimulateImport' => ' 模拟导入 ', + 'UI:Button:Test' => '测试!', + 'UI:Button:Evaluate' => ' 评估 ', + 'UI:Button:Evaluate:Title' => ' 评估 (Ctrl+Enter) ', + 'UI:Button:AddObject' => ' 添加... ', + 'UI:Button:BrowseObjects' => ' 浏览... ', + 'UI:Button:Add' => ' 添加 ', + 'UI:Button:AddToList' => ' << 添加 ', + 'UI:Button:RemoveFromList' => ' 移除 >> ', + 'UI:Button:FilterList' => ' 筛选... ', + 'UI:Button:Create' => ' 创建 ', + 'UI:Button:Delete' => ' 删除 ', + 'UI:Button:Rename' => ' 重命名... ', + 'UI:Button:ChangePassword' => ' 修改密码 ', + 'UI:Button:ResetPassword' => ' 重置密码 ', + 'UI:Button:Insert' => '插入', + 'UI:Button:More' => '更多', + 'UI:Button:Less' => '更少', + 'UI:Button:Wait' => '正在更新字段,请稍候', + 'UI:Treeview:CollapseAll' => '全部收起', + 'UI:Treeview:ExpandAll' => '全部展开', + 'UI:UserPref:DoNotShowAgain' => '不再显示', + 'UI:InputFile:NoFileSelected' => '未选择文件', + 'UI:InputFile:SelectFile' => '请选择文件', + + 'UI:SearchToggle' => '搜索', + 'UI:ClickToCreateNew' => '新建%1$s', + 'UI:SearchFor_Class' => '搜索%1$s ', + 'UI:NoObjectToDisplay' => '没有可显示的对象.', + 'UI:Error:SaveFailed' => '对象无法被保存:', + 'UI:Error:MandatoryTemplateParameter_object_id' => '当link_attr被指定时, 参数object_id是必须的. 请检查显示模板的定义.', + 'UI:Error:MandatoryTemplateParameter_target_attr' => '当link_attr被指定时, 参数target_attr是必须的. 请检查显示模板的定义.', + 'UI:Error:MandatoryTemplateParameter_group_by' => '参数group_by是必须的. 请检查显示模板的定义.', + 'UI:Error:InvalidGroupByFields' => '分组字段列表是无效的: "%1$s".', + 'UI:Error:UnsupportedStyleOfBlock' => '错误: 不被支持的block格式: "%1$s".', 'UI:Error:IncorrectLinkDefinition_LinkedClass_Class' => '关联错误: 关联的对象: %1$s不是%2$s的外键', - 'UI:Error:Object_Class_Id_NotFound' => '对象: %1$s:%2$d找不到.', - 'UI:Error:WizardCircularReferenceInDependencies' => '错误: 字段之间的依赖性出现循环引用, 请检查数据模型.', - 'UI:Error:UploadedFileTooBig' => '上传文件太大. (允许的最大限制是 %1$s). 请检查 PHP 配置文件中的upload_max_filesize和post_max_size.', - 'UI:Error:UploadedFileTruncated.' => '上传的文件被截断 !', - 'UI:Error:NoTmpDir' => '未定义临时目录.', - 'UI:Error:CannotWriteToTmp_Dir' => '无法向硬盘写入临时文件. upload_tmp_dir = "%1$s".', - 'UI:Error:UploadStoppedByExtension_FileName' => '上传因为扩展名被停止. (原文件名 = "%1$s").', - 'UI:Error:UploadFailedUnknownCause_Code' => '文件上传失败, 原因未知. (错误代码 = "%1$s").', - 'UI:Error:1ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s.', - 'UI:Error:2ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s和%2$s.', - 'UI:Error:3ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s, %2$s和%3$s.', - 'UI:Error:4ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s, %2$s, %3$s和%4$s.', - 'UI:Error:IncorrectOQLQuery_Message' => '错误: 错误的OQL查询: %1$s', + 'UI:Error:Object_Class_Id_NotFound' => '对象: %1$s:%2$d找不到.', + 'UI:Error:WizardCircularReferenceInDependencies' => '错误: 字段之间的依赖性出现循环引用, 请检查数据模型.', + 'UI:Error:UploadedFileTooBig' => '上传文件太大. (允许的最大限制是 %1$s). 请检查 PHP 配置文件中的upload_max_filesize和post_max_size.', + 'UI:Error:UploadedFileTruncated.' => '上传的文件被截断 !', + 'UI:Error:NoTmpDir' => '未定义临时目录.', + 'UI:Error:CannotWriteToTmp_Dir' => '无法向硬盘写入临时文件. upload_tmp_dir = "%1$s".', + 'UI:Error:UploadStoppedByExtension_FileName' => '上传因为扩展名被停止. (原文件名 = "%1$s").', + 'UI:Error:UploadFailedUnknownCause_Code' => '文件上传失败, 原因未知. (错误代码 = "%1$s").', + + 'UI:Error:1ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s.', + 'UI:Error:2ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s和%2$s.', + 'UI:Error:3ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s, %2$s和%3$s.', + 'UI:Error:4ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s, %2$s, %3$s和%4$s.', + 'UI:Error:IncorrectOQLQuery_Message' => '错误: 错误的OQL查询: %1$s', 'UI:Error:AnErrorOccuredWhileRunningTheQuery_Message' => '运行此查询时发生了一个错误: %1$s', - 'UI:Error:ObjectAlreadyUpdated' => '错误: 此对象已更新.', - 'UI:Error:ObjectCannotBeUpdated' => '错误: 对象无法更新.', - 'UI:Error:ObjectsAlreadyDeleted' => '错误: 对象已被删除!', - 'UI:Error:BulkDeleteNotAllowedOn_Class' => '您无权进行%1$s类型对象的批量删除', - 'UI:Error:DeleteNotAllowedOn_Class' => '您无权删除%1$s类型的对象', - 'UI:Error:ReadNotAllowedOn_Class' => '您无权查看对象的%1$s', - 'UI:Error:BulkModifyNotAllowedOn_Class' => '您无权进行%1$s类型对象的批量更新', - 'UI:Error:ObjectAlreadyCloned' => '错误: 此对象已被克隆!', - 'UI:Error:ObjectAlreadyCreated' => '错误: 此对象已被创建!', - 'UI:Error:Invalid_Stimulus_On_Object_In_State' => '错误: 在对象%2$s的 "%3$s" 状态上的无效权限 "%1$s".', - 'UI:Error:InvalidDashboardFile' => '错误: 无效的仪表盘文件', - 'UI:Error:InvalidDashboard' => '错误: 无效的仪表盘', - 'UI:Error:MaintenanceMode' => '应用正处于维护中', - 'UI:Error:MaintenanceTitle' => '维护', - 'UI:Error:InvalidToken' => '错误: 所请求的操作已执行 (没有CSRF token)', - 'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP提供者%1$s不存在 (email_transport_smtp.oauth.provider)', - 'UI:GroupBy:Count' => '个数', - 'UI:GroupBy:Count+' => '项目数', - 'UI:CountOfObjects' => '%1$d个对象符合指定的条件.', - 'UI_CountOfObjectsShort' => '%1$d个对象.', - 'UI:NoObject_Class_ToDisplay' => '没有%1$s可以显示', + 'UI:Error:ObjectAlreadyUpdated' => '错误: 此对象已更新.', + 'UI:Error:ObjectCannotBeUpdated' => '错误: 对象无法更新.', + 'UI:Error:ObjectsAlreadyDeleted' => '错误: 对象已被删除!', + 'UI:Error:BulkDeleteNotAllowedOn_Class' => '您无权进行%1$s类型对象的批量删除', + 'UI:Error:DeleteNotAllowedOn_Class' => '您无权删除%1$s类型的对象', + 'UI:Error:ReadNotAllowedOn_Class' => '您无权查看对象的%1$s', + 'UI:Error:BulkModifyNotAllowedOn_Class' => '您无权进行%1$s类型对象的批量更新', + 'UI:Error:ObjectAlreadyCloned' => '错误: 此对象已被克隆!', + 'UI:Error:ObjectAlreadyCreated' => '错误: 此对象已被创建!', + 'UI:Error:Invalid_Stimulus_On_Object_In_State' => '错误: 在对象%2$s的 "%3$s" 状态上的无效权限 "%1$s".', + 'UI:Error:InvalidDashboardFile' => '错误: 无效的仪表盘文件', + 'UI:Error:InvalidDashboard' => '错误: 无效的仪表盘', + 'UI:Error:MaintenanceMode' => '应用正处于维护中', + 'UI:Error:MaintenanceTitle' => '维护', + 'UI:Error:InvalidToken' => '错误: 所请求的操作已执行 (没有CSRF token)', + 'UI:Error:TwigController' => 'Internal error in form controller~~', + + 'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP提供者%1$s不存在 (email_transport_smtp.oauth.provider)', + + 'UI:GroupBy:Count' => '个数', + 'UI:GroupBy:Count+' => '项目数', + 'UI:CountOfObjects' => '%1$d个对象符合指定的条件.', + 'UI_CountOfObjectsShort' => '%1$d个对象.', + 'UI:NoObject_Class_ToDisplay' => '没有%1$s可以显示', 'UI:History:LastModified_On_By' => '最后修改%1$s被%2$s.', - 'UI:HistoryTab' => '历史', + 'UI:HistoryTab' => '历史', 'UI:NotificationsTab' => '通知', 'UI:History:BulkImports' => '历史', 'UI:History:BulkImports+' => 'CSV导入列表 (最新导入在最)', @@ -583,49 +601,51 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:CSVImport:ObjectsWereModified' => '%1$d个对象已被修改.', 'UI:CSVImport:ObjectsWereAdded' => '%1$d个对象已被添加.', 'UI:CSVImport:ObjectsHadErrors' => '%1$d个对象发生错误.', - 'UI:Title:CSVImportStep2' => '第2步,共5步: CSV数据选项', - 'UI:Title:CSVImportStep3' => '第3步,共5步: 数据映射', - 'UI:Title:CSVImportStep4' => '第4步,共5步: 模拟导入', - 'UI:Title:CSVImportStep5' => '第5步,共5步: 完成导入', - 'UI:CSVImport:LinesNotImported' => '无法导入的行:', - 'UI:CSVImport:LinesNotImported+' => '以下行无法导入因为其中包含错误', - 'UI:CSVImport:SeparatorComma+' => ', (逗号)', - 'UI:CSVImport:SeparatorSemicolon+' => '; (分号)', - 'UI:CSVImport:SeparatorTab+' => '制表符', - 'UI:CSVImport:SeparatorOther' => '其它:', - 'UI:CSVImport:QualifierDoubleQuote+' => '" (双引号)', - 'UI:CSVImport:QualifierSimpleQuote+' => '\' (单引号)', - 'UI:CSVImport:QualifierOther' => '其它:', - 'UI:CSVImport:TreatFirstLineAsHeader' => '将第一行视作标题头 (列名)', + 'UI:Title:CSVImportStep2' => '第2步,共5步: CSV数据选项', + 'UI:Title:CSVImportStep3' => '第3步,共5步: 数据映射', + 'UI:Title:CSVImportStep4' => '第4步,共5步: 模拟导入', + 'UI:Title:CSVImportStep5' => '第5步,共5步: 完成导入', + 'UI:CSVImport:LinesNotImported' => '无法导入的行:', + 'UI:CSVImport:LinesNotImported+' => '以下行无法导入因为其中包含错误', + 'UI:CSVImport:SeparatorComma+' => ', (逗号)', + 'UI:CSVImport:SeparatorSemicolon+' => '; (分号)', + 'UI:CSVImport:SeparatorTab+' => '制表符', + 'UI:CSVImport:SeparatorOther' => '其它:', + 'UI:CSVImport:QualifierDoubleQuote+' => '" (双引号)', + 'UI:CSVImport:QualifierSimpleQuote+' => '\' (单引号)', + 'UI:CSVImport:QualifierOther' => '其它:', + 'UI:CSVImport:TreatFirstLineAsHeader' => '将第一行视作标题头 (列名)', 'UI:CSVImport:Skip_N_LinesAtTheBeginning' => '跳过文件的前%1$s行', - 'UI:CSVImport:CSVDataPreview' => 'CSV 数据预览', - 'UI:CSVImport:Column' => 'Column %1$s~~', - 'UI:CSVImport:SelectFile' => '请选择要导入的文件:', - 'UI:CSVImport:Tab:LoadFromFile' => '从文件导入', - 'UI:CSVImport:Tab:CopyPaste' => '复制和粘贴的数据', - 'UI:CSVImport:Tab:Templates' => '模板', - 'UI:CSVImport:PasteData' => '粘贴数据以导入:', - 'UI:CSVImport:PickClassForTemplate' => '选择模板: ', - 'UI:CSVImport:SeparatorCharacter' => '分隔符:', - 'UI:CSVImport:TextQualifierCharacter' => '文本修饰符', - 'UI:CSVImport:CommentsAndHeader' => '注释和头', - 'UI:CSVImport:SelectClass' => '选择要导入的类别:', - 'UI:CSVImport:AdvancedMode' => '高级模式', - 'UI:CSVImport:AdvancedMode+' => '在高级模式中, 对象的"id" (主键) 可以被用来修改和重命名对象.不管怎样, 列 "id" (如果存在) 只能被用做一个搜索条件, 不能与其它搜索条件混用.', - 'UI:CSVImport:SelectAClassFirst' => '要配置映射, 请先选择一个类型.', - 'UI:CSVImport:HeaderFields' => '字段', - 'UI:CSVImport:HeaderMappings' => '映射', - 'UI:CSVImport:HeaderSearch' => '搜索?', - 'UI:CSVImport:AlertIncompleteMapping' => '请为每个栏选择一个映射.', - 'UI:CSVImport:AlertMultipleMapping' => '请确保目标区域仅被映射一次.', - 'UI:CSVImport:AlertNoSearchCriteria' => '请选择至少一个搜索条件', - 'UI:CSVImport:Encoding' => '字符编码', - 'UI:CSVImport:ViewAllPossibleValues' => '查看所有可能值', - 'UI:CSVImport:ViewAllAmbiguousValues' => '查看所有模糊值', - 'UI:UniversalSearchTitle' => ITOP_APPLICATION_SHORT.' - 全局搜索', - 'UI:UniversalSearch:Error' => '错误: %1$s', - 'UI:UniversalSearch:LabelSelectTheClass' => '选择要搜索的类别: ', - 'UI:CSVReport-Value-Modified' => '已修改', + 'UI:CSVImport:CSVDataPreview' => 'CSV 数据预览', + 'UI:CSVImport:Column' => 'Column %1$s~~', + 'UI:CSVImport:SelectFile' => '请选择要导入的文件:', + 'UI:CSVImport:Tab:LoadFromFile' => '从文件导入', + 'UI:CSVImport:Tab:CopyPaste' => '复制和粘贴的数据', + 'UI:CSVImport:Tab:Templates' => '模板', + 'UI:CSVImport:PasteData' => '粘贴数据以导入:', + 'UI:CSVImport:PickClassForTemplate' => '请选择模板: ', + 'UI:CSVImport:SeparatorCharacter' => '分隔符:', + 'UI:CSVImport:TextQualifierCharacter' => '文本限定符', + 'UI:CSVImport:CommentsAndHeader' => '注释和头', + 'UI:CSVImport:SelectClass' => '请选择要导入的类别:', + 'UI:CSVImport:AdvancedMode' => '高级模式', + 'UI:CSVImport:AdvancedMode+' => '在高级模式中, 对象的"id" (主键) 可以被用来修改和重命名对象.'. + '不管怎样, 列 "id" (如果存在) 只能被用做一个搜索条件, 不能与其它搜索条件混用.', + 'UI:CSVImport:SelectAClassFirst' => '要配置映射, 请先选择一个类型.', + 'UI:CSVImport:HeaderFields' => '字段', + 'UI:CSVImport:HeaderMappings' => '映射', + 'UI:CSVImport:HeaderSearch' => '搜索?', + 'UI:CSVImport:AlertIncompleteMapping' => '请为每个栏选择一个映射.', + 'UI:CSVImport:AlertMultipleMapping' => '请确保目标区域仅被映射一次.', + 'UI:CSVImport:AlertNoSearchCriteria' => '请选择至少一个搜索条件', + 'UI:CSVImport:Encoding' => '字符编码', + 'UI:CSVImport:ViewAllPossibleValues' => '查看所有可能值', + 'UI:CSVImport:ViewAllAmbiguousValues' => '查看所有模糊值', + 'UI:UniversalSearchTitle' => ITOP_APPLICATION_SHORT.' - 全局搜索', + 'UI:UniversalSearch:Error' => '错误: %1$s', + 'UI:UniversalSearch:LabelSelectTheClass' => '选择要搜索的类别: ', + + 'UI:CSVReport-Value-Modified' => '已修改', 'UI:CSVReport-Value-SetIssue' => '属性值不合法', 'UI:CSVReport-Value-ChangeIssue' => '\'%1$s\' 值不合法', 'UI:CSVReport-Value-NoMatch' => '没有匹配到值 \'%1$s\'', @@ -633,122 +653,132 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:CSVReport-Value-NoMatch-NoObject' => '对象 \'%1$s\' 不存在', 'UI:CSVReport-Value-NoMatch-NoObject-ForCurrentUser' => '对象 \'%1$s\' 在您当前角色下没有找到', 'UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser' => '一些对象 \'%1$s\' 在您当前角色下不可见', - 'UI:CSVReport-Value-Missing' => '缺少必填项', - 'UI:CSVReport-Value-Ambiguous' => '模糊匹配: 找到 %1$s 个对象', - 'UI:CSVReport-Row-Unchanged' => '保持不变', - 'UI:CSVReport-Row-Created' => '新建', - 'UI:CSVReport-Row-Updated' => '已更新 %1$d 列', - 'UI:CSVReport-Row-Disappeared' => '已消失, %1$d 列发生变化', - 'UI:CSVReport-Row-Issue' => '问题: %1$s', - 'UI:CSVReport-Value-Issue-Null' => '不允许留空', - 'UI:CSVReport-Value-Issue-NotFound' => '对象找不到', - 'UI:CSVReport-Value-Issue-FoundMany' => '找到 %1$d 个匹配项', - 'UI:CSVReport-Value-Issue-Readonly' => '\'%1$s\' 的属性是只读的,不能修改 (当前值: %2$s, 建议值: %3$s)', - 'UI:CSVReport-Value-Issue-Format' => '输入处理失败: %1$s', - 'UI:CSVReport-Value-Issue-NoMatch' => '属性 \'%1$s\' 未知的值: 无法匹配, 请检查拼写', - 'UI:CSVReport-Value-Issue-AllowedValues' => '允许 \'%1$s\' 值: %2$s', - 'UI:CSVReport-Value-Issue-Unknown' => '属性 \'%1$s\' 未知的值: %2$s', - 'UI:CSVReport-Row-Issue-Inconsistent' => '属性不一致: %1$s', - 'UI:CSVReport-Row-Issue-Attribute' => '错误的属性', - 'UI:CSVReport-Row-Issue-MissingExtKey' => '创建失败, 因为缺少外键: %1$s', - 'UI:CSVReport-Row-Issue-DateFormat' => '日期格式错误', + + 'UI:CSVReport-Value-Missing' => '缺少必填项', + 'UI:CSVReport-Value-Ambiguous' => '模糊匹配: 找到 %1$s 个对象', + 'UI:CSVReport-Row-Unchanged' => '保持不变', + 'UI:CSVReport-Row-Created' => '新建', + 'UI:CSVReport-Row-Updated' => '已更新 %1$d 列', + 'UI:CSVReport-Row-Disappeared' => '已消失, %1$d 列发生变化', + 'UI:CSVReport-Row-Issue' => '问题: %1$s', + 'UI:CSVReport-Value-Issue-Null' => '不允许留空', + 'UI:CSVReport-Value-Issue-NotFound' => '对象找不到', + 'UI:CSVReport-Value-Issue-FoundMany' => '找到 %1$d 个匹配项', + 'UI:CSVReport-Value-Issue-Readonly' => '\'%1$s\' 的属性是只读的,不能修改 (当前值: %2$s, 建议值: %3$s)', + 'UI:CSVReport-Value-Issue-Format' => '输入处理失败: %1$s', + 'UI:CSVReport-Value-Issue-NoMatch' => '属性 \'%1$s\' 未知的值: 无法匹配, 请检查拼写', + 'UI:CSVReport-Value-Issue-AllowedValues' => '允许 \'%1$s\' 值: %2$s', + 'UI:CSVReport-Value-Issue-Unknown' => '属性 \'%1$s\' 未知的值: %2$s', + 'UI:CSVReport-Row-Issue-Inconsistent' => '属性不一致: %1$s', + 'UI:CSVReport-Row-Issue-Attribute' => '错误的属性', + 'UI:CSVReport-Row-Issue-MissingExtKey' => '创建失败, 因为缺少外键: %1$s', + 'UI:CSVReport-Row-Issue-DateFormat' => '日期格式错误', 'UI:CSVReport-Row-Issue-ExpectedDateFormat' => '预期格式: %1$s', - 'UI:CSVReport-Row-Issue-Reconciliation' => '无法保持一致', - 'UI:CSVReport-Row-Issue-Ambiguous' => '不明确的一致要求', - 'UI:CSVReport-Row-Issue-Internal' => '内部错误: %1$s, %2$s', - 'UI:CSVReport-Icon-Unchanged' => '保持不变', - 'UI:CSVReport-Icon-Modified' => '修改', - 'UI:CSVReport-Icon-Missing' => '丢失', + 'UI:CSVReport-Row-Issue-Reconciliation' => '无法保持一致', + 'UI:CSVReport-Row-Issue-Ambiguous' => '不明确的一致要求', + 'UI:CSVReport-Row-Issue-Internal' => '内部错误: %1$s, %2$s', + 'UI:CSVReport-Value-Issue-NoValue' => 'No value', + 'UI:CSVReport-Row-Issue-NbField' => 'Not the expected number of columns (found: %1$s, expected: %2$s)', + + 'UI:CSVReport-Icon-Unchanged' => '保持不变', + 'UI:CSVReport-Icon-Modified' => '修改', + 'UI:CSVReport-Icon-Missing' => '丢失', 'UI:CSVReport-Object-MissingToUpdate' => '丢失对象: 将被更新', - 'UI:CSVReport-Object-MissingUpdated' => '丢失对象: 已更新', - 'UI:CSVReport-Icon-Created' => '创建', - 'UI:CSVReport-Object-ToCreate' => '对象将被创建', - 'UI:CSVReport-Object-Created' => '对象已创建', - 'UI:CSVReport-Icon-Error' => '错误', - 'UI:CSVReport-Object-Error' => '错误: %1$s', - 'UI:CSVReport-Object-Ambiguous' => '模糊: %1$s', - 'UI:CSVReport-Stats-Errors' => '%1$.0f %% 已加载的对象包含错误, 它们将会被忽略.', - 'UI:CSVReport-Stats-Created' => '%1$.0f %% 已加载的对象将会被创建.', - 'UI:CSVReport-Stats-Modified' => '%1$.0f %% 已加载的对象将会被修改.', - 'UI:CSVExport:AdvancedMode' => '高级模式', + 'UI:CSVReport-Object-MissingUpdated' => '丢失对象: 已更新', + 'UI:CSVReport-Icon-Created' => '创建', + 'UI:CSVReport-Object-ToCreate' => '对象将被创建', + 'UI:CSVReport-Object-Created' => '对象已创建', + 'UI:CSVReport-Icon-Error' => '错误', + 'UI:CSVReport-Object-Error' => '错误: %1$s', + 'UI:CSVReport-Object-Ambiguous' => '模糊: %1$s', + 'UI:CSVReport-Stats-Errors' => '%1$.0f %% 已加载的对象包含错误, 它们将会被忽略.', + 'UI:CSVReport-Stats-Created' => '%1$.0f %% 已加载的对象将会被创建.', + 'UI:CSVReport-Stats-Modified' => '%1$.0f %% 已加载的对象将会被修改.', + + 'UI:CSVExport:AdvancedMode' => '高级模式', 'UI:CSVExport:AdvancedMode+' => '高级模式下, 导出将包含额外的列: 对象id, 外键id以及要使一致的属性.', 'UI:CSVExport:LostChars' => '编码问题', 'UI:CSVExport:LostChars+' => '已下载文件将使用编码 %1$s. 系统检测到一些字符与该格式不兼容. 这些字符将被替换为子集 (例如重读字符将丢失音调), 或者被丢弃. 您可以从浏览器中复制粘贴数据. 或者联系管理员更改字符编码 (参阅参数 \'csv_file_default_charset\').', - 'UI:Audit:Title' => ITOP_APPLICATION_SHORT.' - CMDB审计', - 'UI:Audit:Interactive:All:Title' => '审计结果', - 'UI:Audit:Interactive:All:SubTitle' => '全量审计: 包括所有规则, 所有类别, 所有审计域', - 'UI:Audit:Interactive:All:BreadCrumb' => '全量审计', - 'UI:Audit:Interactive:All:BreadCrumb+' => '所有规则的审计结果', - 'UI:Audit:Interactive:Categories:Title' => '所有类别的审计结果: %1$s', - 'UI:Audit:Interactive:Categories:SubTitle' => '所有属于类别%1$s的规则的审计结果', - 'UI:Audit:Interactive:Categories:BreadCrumb' => '类别', + + 'UI:Audit:Title' => ITOP_APPLICATION_SHORT.' - CMDB审计', + + 'UI:Audit:Interactive:All:Title' => '审计结果', + 'UI:Audit:Interactive:All:SubTitle' => '全量审计: 包括所有规则, 所有类别, 所有审计域', + 'UI:Audit:Interactive:All:BreadCrumb' => '全量审计', + 'UI:Audit:Interactive:All:BreadCrumb+' => '所有规则的审计结果', + 'UI:Audit:Interactive:Categories:Title' => '所有类别的审计结果: %1$s', + 'UI:Audit:Interactive:Categories:SubTitle' => '所有属于类别%1$s的规则的审计结果', + 'UI:Audit:Interactive:Categories:BreadCrumb' => '类别', 'UI:Audit:Interactive:Categories:BreadCrumb+' => '审计类别: %1$s', - 'UI:Audit:Interactive:Domain:Title' => '审计结果在域: %1$s', - 'UI:Audit:Interactive:Domain:SubTitle' => '所有关联所述域的类别内的审计规则的结果: %1$s', - 'UI:Audit:Interactive:Domain:BreadCrumb' => '全量审计', - 'UI:Audit:Interactive:Domain:BreadCrumb+' => '审计在域: %1$s', - 'UI:Audit:Interactive:Selection:Title' => '审计的选择', - 'UI:Audit:Interactive:Selection:SubTitle' => '选择一个域以将结果限定在该域内或者选择 "所有类别" 进行全量审计 (要审计的数据太大可能导致耗时较长甚至失败)', - 'UI:Audit:Interactive:Selection:BreadCrumb' => '选择', - 'UI:Audit:Interactive:Selection:BreadCrumb+' => '选择要运行的审计', - 'UI:Audit:Interactive:Selection:BadgeAll' => '所有类别', - 'UI:Audit:Interactive:Button:Back' => '返回审计', - 'UI:Audit:Interactive:Button:Configuration' => '审计配置', - 'UI:Audit:ViewRules' => '检查规则', - 'UI:Audit:HeaderAuditRule' => '审计规则', - 'UI:Audit:HeaderNbObjects' => '# 对象', - 'UI:Audit:HeaderNbErrors' => '# 错误', - 'UI:Audit:PercentageOk' => '% Ok', - 'UI:Audit:OqlError' => 'OQL 错误', - 'UI:Audit:Error:ValueNA' => 'n/a', - 'UI:Audit:ErrorIn_Rule' => '规则中有错误', - 'UI:Audit:ErrorIn_Rule_Reason' => 'OQL错误在规则%1$s中: %2$s.', - 'UI:Audit:ErrorIn_Category' => '类别中有错误', - 'UI:Audit:ErrorIn_Category_Reason' => 'OQL错误在类别%1$s中: %2$s.', - 'UI:Audit:AuditErrors' => '审计出错', - 'UI:Audit:Dashboard:ObjectsAudited' => '对象已审计', - 'UI:Audit:Dashboard:ObjectsInError' => '对象出错', - 'UI:Audit:Dashboard:ObjectsValidated' => '对象已验证', - 'UI:Audit:AuditCategory:Subtitle' => '%2$s个中有%1$s个错误 - %3$s%%', - 'UI:RunQuery:Title' => ITOP_APPLICATION_SHORT.' - OQL查询评估', - 'UI:RunQuery:QueryExamples' => '示例查询', - 'UI:RunQuery:QueryResults' => '查询结果', - 'UI:RunQuery:HeaderPurpose' => '目的', - 'UI:RunQuery:HeaderPurpose+' => '此查询的解释', - 'UI:RunQuery:HeaderOQLExpression' => 'OQL 表达式', - 'UI:RunQuery:HeaderOQLExpression+' => 'OQL 语法表示的查询', - 'UI:RunQuery:ExpressionToEvaluate' => '请输入表达式: ', - 'UI:RunQuery:QueryArguments' => '查询参数', - 'UI:RunQuery:MoreInfo' => '此查询的更多信息: ', - 'UI:RunQuery:DevelopedQuery' => '快捷查询表达式: ', - 'UI:RunQuery:SerializedFilter' => '序列化的过滤器: ', - 'UI:RunQuery:DevelopedOQL' => '优化的OQL', - 'UI:RunQuery:DevelopedOQLCount' => '开发的OQL计数', - 'UI:RunQuery:ResultSQLCount' => '结果SQL计数', - 'UI:RunQuery:ResultSQL' => '结果SQL', - 'UI:RunQuery:Error' => '运行此查询时发生了一个错误', - 'UI:Query:UrlForExcel' => '用于 MS-Excel 网络查询的 URL', - 'UI:Query:UrlV1' => '没有定义字段列表. 没有这个信息页面export-V2.php无法调用. 因此, 建议的以下 URL 指向传统页面: export.php. 该传统版本导出具有以下限制: 导出的字段列表很大程度依赖于导出格式和'.ITOP_APPLICATION_SHORT.'数据模型.
如果您需要确保导出的列保持长期稳定, 则必须为属性 "Fields" 指定值并使用页面export-V2.php.', - 'UI:Schema:Title' => ITOP_APPLICATION_SHORT.'对象模型', - 'UI:Schema:TitleForClass' => '%1$s 模式', - 'UI:Schema:NoClassSelected' => 'No class selected, please choose one~~', - 'UI:Schema:CategoryMenuItem' => '类别 %1$s', - 'UI:Schema:Relationships' => '关联', - 'UI:Schema:AbstractClass' => '抽象类型: 此类型不能实例化对象.', - 'UI:Schema:NonAbstractClass' => '非抽象类型: 此类型可以实例化对象.', - 'UI:Schema:ClassHierarchyTitle' => '类型层级', - 'UI:Schema:AllClasses' => '所有类型', - 'UI:Schema:ExternalKey_To' => '%1$s 的外键', - 'UI:Schema:Columns_Description' => '列: %1$s', - 'UI:Schema:Default_Description' => '缺省: "%1$s"', - 'UI:Schema:NullAllowed' => '允许留空', - 'UI:Schema:NullNotAllowed' => '不允许留空', - 'UI:Schema:Attributes' => '属性', - 'UI:Schema:AttributeCode' => '属性编码', - 'UI:Schema:AttributeCode+' => '属性的内部编码', - 'UI:Schema:Label' => '标签', - 'UI:Schema:Label+' => '属性标签', - 'UI:Schema:Type' => '类别', + 'UI:Audit:Interactive:Domain:Title' => '审计结果在域: %1$s', + 'UI:Audit:Interactive:Domain:SubTitle' => '所有关联所述域的类别内的审计规则的结果: %1$s', + 'UI:Audit:Interactive:Domain:BreadCrumb' => '全量审计', + 'UI:Audit:Interactive:Domain:BreadCrumb+' => '审计在域: %1$s', + 'UI:Audit:Interactive:Selection:Title' => '审计的选择', + 'UI:Audit:Interactive:Selection:SubTitle' => '请选择一个域以将结果限定在该域内或者选择 "所有类别" 进行全量审计 (要审计的数据太大可能导致耗时较长甚至失败)', + 'UI:Audit:Interactive:Selection:BreadCrumb' => '请选择', + 'UI:Audit:Interactive:Selection:BreadCrumb+' => '请选择要运行的审计', + 'UI:Audit:Interactive:Selection:BadgeAll' => '所有类别', + 'UI:Audit:Interactive:Button:Back' => '返回审计', + 'UI:Audit:Interactive:Button:Configuration' => '审计配置', + + 'UI:Audit:ViewRules' => '检查规则', + 'UI:Audit:HeaderAuditRule' => '审计规则', + 'UI:Audit:HeaderNbObjects' => '# 对象', + 'UI:Audit:HeaderNbErrors' => '# 错误', + 'UI:Audit:PercentageOk' => '% Ok', + 'UI:Audit:OqlError' => 'OQL 错误', + 'UI:Audit:Error:ValueNA' => 'n/a', + 'UI:Audit:ErrorIn_Rule' => '规则中有错误', + 'UI:Audit:ErrorIn_Rule_Reason' => 'OQL错误在规则%1$s中: %2$s.', + 'UI:Audit:ErrorIn_Category' => '类别中有错误', + 'UI:Audit:ErrorIn_Category_Reason' => 'OQL错误在类别%1$s中: %2$s.', + 'UI:Audit:AuditErrors' => '审计出错', + 'UI:Audit:Dashboard:ObjectsAudited' => '对象已审计', + 'UI:Audit:Dashboard:ObjectsInError' => '对象出错', + 'UI:Audit:Dashboard:ObjectsValidated' => '对象已验证', + 'UI:Audit:AuditCategory:Subtitle' => '%2$s个中有%1$s个错误 - %3$s%%', + + 'UI:RunQuery:Title' => ITOP_APPLICATION_SHORT.' - OQL查询评估', + 'UI:RunQuery:QueryExamples' => '示例查询', + 'UI:RunQuery:QueryResults' => '查询结果', + 'UI:RunQuery:HeaderPurpose' => '目的', + 'UI:RunQuery:HeaderPurpose+' => '此查询的解释', + 'UI:RunQuery:HeaderOQLExpression' => 'OQL 表达式', + 'UI:RunQuery:HeaderOQLExpression+' => 'OQL 语法表示的查询', + 'UI:RunQuery:ExpressionToEvaluate' => '请输入表达式: ', + 'UI:RunQuery:QueryArguments' => '查询参数', + 'UI:RunQuery:MoreInfo' => '此查询的更多信息: ', + 'UI:RunQuery:DevelopedQuery' => '快捷查询表达式: ', + 'UI:RunQuery:SerializedFilter' => '序列化的过滤器: ', + 'UI:RunQuery:DevelopedOQL' => '优化的OQL', + 'UI:RunQuery:DevelopedOQLCount' => '开发的OQL计数', + 'UI:RunQuery:ResultSQLCount' => '结果SQL计数', + 'UI:RunQuery:ResultSQL' => '结果SQL', + 'UI:RunQuery:Error' => '运行此查询时发生了一个错误', + 'UI:Query:UrlForExcel' => '用于 MS-Excel 网络查询的 URL', + 'UI:Query:UrlV1' => '没有定义字段列表. 没有这个信息页面export-V2.php无法调用. 因此, 建议的以下 URL 指向传统页面: export.php. 该传统版本导出具有以下限制: 导出的字段列表很大程度依赖于导出格式和'.ITOP_APPLICATION_SHORT.'数据模型.
如果您需要确保导出的列保持长期稳定, 则必须为属性 "Fields" 指定值并使用页面export-V2.php.', + 'UI:Schema:Title' => ITOP_APPLICATION_SHORT.'对象模型', + 'UI:Schema:TitleForClass' => '%1$s 模式', + 'UI:Schema:NoClassSelected' => 'No class selected, please choose one~~', + 'UI:Schema:CategoryMenuItem' => '类别 %1$s', + 'UI:Schema:Relationships' => '关联', + 'UI:Schema:AbstractClass' => '抽象类型: 此类型不能实例化对象.', + 'UI:Schema:NonAbstractClass' => '非抽象类型: 此类型可以实例化对象.', + 'UI:Schema:ClassHierarchyTitle' => '类型层级', + 'UI:Schema:AllClasses' => '所有类型', + 'UI:Schema:ExternalKey_To' => '%1$s 的外键', + 'UI:Schema:Columns_Description' => '列: %1$s', + 'UI:Schema:Default_Description' => '缺省: "%1$s"', + 'UI:Schema:NullAllowed' => '允许留空', + 'UI:Schema:NullNotAllowed' => '不允许留空', + 'UI:Schema:Attributes' => '属性', + 'UI:Schema:AttributeCode' => '属性编码', + 'UI:Schema:AttributeCode+' => '属性的内部编码', + 'UI:Schema:Label' => '标签', + 'UI:Schema:Label+' => '属性标签', + 'UI:Schema:Type' => '类别', + 'UI:Schema:Type+' => '属性的数据类型', 'UI:Schema:Origin' => '来自', 'UI:Schema:Origin+' => '此属性被定义的原始类型', @@ -806,19 +836,19 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Schema:Events:Listener' => '监听器', 'UI:Schema:Events:Rank' => '评分', 'UI:Schema:Events:Module' => '模块', - 'UI:LinksWidget:Autocomplete+' => '输入前3个字符...', + 'UI:LinksWidget:Autocomplete+' => '请输入前3个字符...', 'UI:Edit:SearchQuery' => '请选择预定义的查询', 'UI:Edit:TestQuery' => '测试查询', 'UI:Combo:SelectValue' => '--- 请选择 ---', - 'UI:Label:SelectedObjects' => '被选的对象: ', + 'UI:Label:SelectedObjects' => '已选的对象: ', 'UI:Label:AvailableObjects' => '可用的对象: ', 'UI:Link_Class_Attributes' => '%1$s 属性', - 'UI:SelectAllToggle+' => '全选/反选', + 'UI:SelectAllToggle+' => '全选/全不选', 'UI:AddObjectsOf_Class_LinkedWith_Class_Instance' => '添加 %1$s 个对象, 链接 %2$s: %3$s', 'UI:AddObjectsOf_Class_LinkedWith_Class' => ' %1$s ', 'UI:ManageObjectsOf_Class_LinkedWith_Class_Instance' => '管理 %1$s 个对象, 链接 %2$s: %3$s', 'UI:AddLinkedObjectsOf_Class' => '添加%1$s', - 'UI:RemoveLinkedObjectsOf_Class' => '移除对象', + 'UI:RemoveLinkedObjectsOf_Class' => '移除已选择的对象', 'UI:Message:EmptyList:UseAdd' => '列表为空, 请使用 "添加..." 按扭来添加元素.', 'UI:Message:EmptyList:UseSearchForm' => '使用上面的搜索表单, 以搜索要添加的对象.', 'UI:Wizard:FinalStepTitle' => '最后一步: 确认', @@ -858,8 +888,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Delect:Confirm_Count_ObjectsOf_Class' => '请确认要删除下列%2$s类型的%1$d个对象.', 'UI:Delete:Confirm_Object' => '请确认要删除%1$s.', 'UI:Delete:Confirm_Count_ObjectsOf_Class' => '请确认要删除下列%2$s类型的%1$d个对象.', - 'UI:WelcomeToITop' => '欢迎使用'.ITOP_APPLICATION, 'UI:DetailsPageTitle' => ITOP_APPLICATION_SHORT.' - %1$s - %2$s 详细内容', - 'UI:ErrorPageTitle' => ITOP_APPLICATION_SHORT.' - 错误', + 'UI:WelcomeToITop' => '欢迎使用'.ITOP_APPLICATION, + 'UI:DetailsPageTitle' => ITOP_APPLICATION_SHORT.' - %1$s - %2$s 详细内容', + 'UI:ErrorPageTitle' => ITOP_APPLICATION_SHORT.' - 错误', 'UI:ObjectDoesNotExist' => '抱歉, 此对象不存在 (或无权浏览此对象).', 'UI:ObjectArchived' => '对象已被归档. 请启用归档模式或联系管理员.', 'Tag:Archived' => '已归档', @@ -885,22 +916,21 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:CloneTitle_Class_Object' => '克隆 %1$s: %2$s', 'UI:CreationPageTitle_Class' => ITOP_APPLICATION_SHORT.' - 新建 %1$s', 'UI:CreationTitle_Class' => '新建 %1$s', - 'UI:SelectTheTypeOf_Class_ToCreate' => '选择 %1$s 的类型:', + 'UI:SelectTheTypeOf_Class_ToCreate' => '请选择 %1$s 的类型:', 'UI:Class_Object_NotUpdated' => '未发现变化, %1$s (%2$s) 没有被更新.', 'UI:Class_Object_Updated' => '%1$s (%2$s) 已更新.', 'UI:BulkDeletePageTitle' => ITOP_APPLICATION_SHORT.' - 批量删除', - 'UI:BulkDeleteTitle' => '选择您要删除的对象:', + 'UI:BulkDeleteTitle' => '请选择要删除的对象:', 'UI:PageTitle:ObjectCreated' => ITOP_APPLICATION_SHORT.' 对象已创建.', 'UI:Title:Object_Of_Class_Created' => '%1$s - %2$s已创建.', - 'UI:Toggle:CustomDashboard' => '自定义', - 'UI:Toggle:StandardDashboard' => '标准', 'UI:Apply_Stimulus_On_Object_In_State_ToTarget_State' => '应用 %1$s 在对象: %2$s上, 从 %3$s 状态到目标状态: %4$s.', 'UI:ObjectCouldNotBeWritten' => '对象不能写入: %1$s', 'UI:PageTitle:FatalError' => ITOP_APPLICATION_SHORT.' - 致命错误', 'UI:SystemIntrusion' => '访问被禁止. 您正尝试未被许可的操作.', 'UI:FatalErrorMessage' => '致命错误, '.ITOP_APPLICATION_SHORT.'无法继续.', 'UI:Error_Details' => '错误: %1$s.', - 'UI:PageTitle:ProfileProjections' => ITOP_APPLICATION_SHORT.'用户管理 - 角色映射', + + 'UI:PageTitle:ProfileProjections' => ITOP_APPLICATION_SHORT.'用户管理 - 角色映射', 'UI:UserManagement:Class' => '类型', 'UI:UserManagement:Class+' => '对象的类型', 'UI:UserManagement:ProjectedObject' => '对象', @@ -934,59 +964,69 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:UserManagement:NoLifeCycleApplicable' => '不适用', 'UI:UserManagement:NoLifeCycleApplicable+' => '此类型未定义生命周期', 'UI:UserManagement:GrantMatrix' => '授权矩阵', - 'Menu:SystemTools' => '系统', - 'UI:ChangeManagementMenu' => '变更管理', + + 'Menu:SystemTools' => '系统', + + 'UI:ChangeManagementMenu' => '变更管理', 'UI:ChangeManagementMenu+' => '变更管理', 'UI:ChangeManagementMenu:Title' => '变更概况', 'UI-ChangeManagementMenu-ChangesByType' => '按类别划分的变更', 'UI-ChangeManagementMenu-ChangesByStatus' => '按状态划分的变更', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => '尚未分配的变更', - 'UI:ConfigurationManagementMenu' => '配置管理', + + 'UI:ConfigurationManagementMenu' => '配置管理', 'UI:ConfigurationManagementMenu+' => '配置管理', 'UI:ConfigurationManagementMenu:Title' => '基础架构概况', 'UI-ConfigurationManagementMenu-InfraByType' => '按类别划分基础架构对象', 'UI-ConfigurationManagementMenu-InfraByStatus' => '按状态划分基础架构对象', - 'UI:ConfigMgmtMenuOverview:Title' => '配置管理仪表盘', + + 'UI:ConfigMgmtMenuOverview:Title' => '配置管理仪表盘', 'UI-ConfigMgmtMenuOverview-FunctionalCIbyStatus' => '按状态配置项目', 'UI-ConfigMgmtMenuOverview-FunctionalCIByType' => '按类别配置项目', - 'UI:RequestMgmtMenuOverview:Title' => '需求管理仪表盘', - 'UI-RequestManagementOverview-RequestByService' => '按服务划分用户需求', - 'UI-RequestManagementOverview-RequestByPriority' => '按优先级划分用户需求', - 'UI-RequestManagementOverview-RequestUnassigned' => '尚未分配给办理人的用户需求', - 'UI:IncidentMgmtMenuOverview:Title' => '事件管理仪表盘', + + 'UI:RequestMgmtMenuOverview:Title' => '需求管理仪表盘', + 'UI-RequestManagementOverview-RequestByService' => '按服务划分需求', + 'UI-RequestManagementOverview-RequestByPriority' => '按优先级划分需求', + 'UI-RequestManagementOverview-RequestUnassigned' => '尚未分配给办理人的需求', + + 'UI:IncidentMgmtMenuOverview:Title' => '事件管理仪表盘', 'UI-IncidentManagementOverview-IncidentByService' => '按服务级划分事件', 'UI-IncidentManagementOverview-IncidentByPriority' => '按优先级划分事件', 'UI-IncidentManagementOverview-IncidentUnassigned' => '尚未分配给办理人的事件', - 'UI:ChangeMgmtMenuOverview:Title' => '变更管理仪表盘', + + 'UI:ChangeMgmtMenuOverview:Title' => '变更管理仪表盘', 'UI-ChangeManagementOverview-ChangeByType' => '按类别划分变更', 'UI-ChangeManagementOverview-ChangeUnassigned' => '尚未分配给办理人的变更', 'UI-ChangeManagementOverview-ChangeWithOutage' => '变更引起的停机', - 'UI:ServiceMgmtMenuOverview:Title' => '服务管理仪表盘', + + 'UI:ServiceMgmtMenuOverview:Title' => '服务管理仪表盘', 'UI-ServiceManagementOverview-CustomerContractToRenew' => '客户合同需在30日内更新', 'UI-ServiceManagementOverview-ProviderContractToRenew' => '供应商合同需在30日内更新', - 'UI:ContactsMenu' => '联系人', + + 'UI:ContactsMenu' => '联系人', 'UI:ContactsMenu+' => '联系人', 'UI:ContactsMenu:Title' => '联系人概况', 'UI-ContactsMenu-ContactsByLocation' => '按地点划分联系人', 'UI-ContactsMenu-ContactsByType' => '按类别划分联系人', 'UI-ContactsMenu-ContactsByStatus' => '按状态划分联系人', - 'UI:NotificationsMenu:Title' => '配置通知', + + 'UI:NotificationsMenu:Title' => '配置通知', 'UI:NotificationsMenu:Help' => '帮助', - 'UI:NotificationsMenu:HelpContent' => '

在'.ITOP_APPLICATION_SHORT.'中, 通知可以被自定义. 它们是基于两个对象集: 触发器和操作.

+ 'UI:NotificationsMenu:HelpContent' => '

在'.ITOP_APPLICATION_SHORT.'中, 通知可以被自定义. 它们基于两个对象集: 触发器和操作.

触发器定义了什么时候发送通知. '.ITOP_APPLICATION_SHORT.'内核自带一些触发器, 另一些触发器可由扩展提供:

    -
  1. 一些触发器执行于指定类型的对象被创建后, 更新后删除后.
  2. -
  3. 一些触发器执行于指定类型的对象进入离开ve指定状态.
  4. -
  5. 一些触发器执行于TTO阈值或TTR阈值预设值达到时.
  6. +
  7. 有的触发器执行于指定类型的对象被创建后, 更新后删除后.
  8. +
  9. 有的触发器执行于指定类型的对象进入离开指定状态.
  10. +
  11. 有的触发器执行于TTO阈值或TTR阈值预设值达到时.

-操作定义触发器执行时要运行的动作. 目前仅支持2类动作: +操作定义了触发器执行时要运行的动作. 目前仅支持3类动作:

  1. 发送电子邮件: 此动作同时需要指定发送的邮件所使用的模板, 以及一些其他参数, 例如收件人, 优先级, 等.
    这里有一个专用的页面: email.test.php可用于测试和调试PHP的邮件配置.
  2. -
  3. Sending a newsroom message: Display a news to user directly in the application through the newsroom (bell icon on the left menu)
  4. -
  5. 外部webhook: 允许通过发送结构化数据到指定URL来与第三方应用集成.
  6. +
  7. 发送新闻消息: 在应用程序中通过新闻室直接向用户显示新闻消息(左侧菜单上的铃铛图标)
  8. +
  9. 调用外部webhook: 允许通过发送结构化数据到指定URL来与第三方应用集成.

若要执行, 操作必须和触发器相关联. @@ -1001,19 +1041,23 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:NotificationsMenu:Actions:ActionWebhook' => 'Webhook操作 (外部集成)', 'UI:NotificationsMenu:Actions:Action' => '其它操作', 'UI:NotificationsMenu:AvailableActions' => '有效的操作', - 'Menu:TagAdminMenu' => '标签配置', + + 'Menu:TagAdminMenu' => '标签配置', 'Menu:TagAdminMenu+' => '标签值管理', 'UI:TagAdminMenu:Title' => '标签配置', 'UI:TagAdminMenu:NoTags' => '未配置标签', 'UI:TagSetFieldData:Error' => '错误: %1$s', - 'UI:iTopVersion:Short' => '%1$s 版本 %2$s', + + 'UI:iTopVersion:Short' => '%1$s 版本 %2$s', 'UI:iTopVersion:Long' => '%1$s 版本 %2$s-%3$s 创建于 %4$s', 'UI:PropertiesTab' => '属性', - 'UI:OpenDocumentInNewWindow_' => '打开', + + 'UI:OpenDocumentInNewWindow_' => '打开', 'UI:DownloadDocument_' => '下载', 'UI:Document:NoPreview' => '此类型文档无法预览', 'UI:Download-CSV' => '下载 %1$s', - 'UI:DeadlineMissedBy_duration' => '超过 %1$s', + + 'UI:DeadlineMissedBy_duration' => '超过 %1$s', 'UI:Deadline_LessThan1Min' => '< 1分钟', 'UI:Deadline_Minutes' => '%1$d分钟', 'UI:Deadline_Hours_Minutes' => '%1$dh %2$dmin', @@ -1127,14 +1171,15 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Pagination:PageSize' => '每页%1$s个对象', 'UI:Pagination:PagesLabel' => '页:', 'UI:Pagination:All' => '全部', - 'UI:HierarchyOf_Class' => '%1$s层级', - 'UI:Preferences' => '首选项...', + 'UI:HierarchyOf_Class' => '%1$s 架构', + 'UI:Preferences' => '偏好设置...', 'UI:ArchiveModeOn' => '激活归档模式', 'UI:ArchiveModeOff' => '关闭归档模式', 'UI:ArchiveMode:Banner' => '归档模式', 'UI:ArchiveMode:Banner+' => '已归档的对象可见但不允许修改', 'UI:FavoriteOrganizations' => '快速访问', - 'UI:FavoriteOrganizations+' => '进入组织下的列表,可实现通过下拉菜单快速访问. 请注意,这并不是一个安全设置, 其它组织的对象依然可以通过选择 "所有组织" 下拉列表看到.', + 'UI:FavoriteOrganizations+' => '进入组织下的列表,可实现通过下拉菜单快速访问.'. + '请注意,这并不是一个安全设置, 其它组织的对象依然可以通过选择 "所有组织" 下拉列表看到.', 'UI:FavoriteLanguage' => '语言', 'UI:Favorites:SelectYourLanguage' => '选择语言', 'UI:FavoriteOtherSettings' => '其它设置', @@ -1157,7 +1202,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:PrintResolution:LetterLandscape' => '横向信纸', 'UI:Toggle:SwitchToStandardDashboard' => '切换到标准仪表盘', 'UI:Toggle:SwitchToCustomDashboard' => '切换到自定义仪表盘', - 'UI:ConfigureThisList' => '配置列表...', + + 'UI:ConfigureThisList' => '配置列表...', 'UI:ListConfigurationTitle' => '列表配置', 'UI:ColumnsAndSortOrder' => '列和排序顺序:', 'UI:UseDefaultSettings' => '使用默认配置', @@ -1172,12 +1218,13 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:ExtField_AsRemoteField' => '%1$s (%2$s)', 'UI:Button:MoveUp' => '上移', 'UI:Button:MoveDown' => '下移', - 'UI:OQL:UnknownClassAndFix' => '未知类型 "%1$s". 您可以试试 "%2$s".', + + 'UI:OQL:UnknownClassAndFix' => '未知类型 "%1$s". 您可以试试 "%2$s".', 'UI:OQL:UnknownClassNoFix' => '未知类型 "%1$s"', - 'UI:Dashboard:EditCustom' => '编辑自定义版本...', + + 'UI:Dashboard:EditCustom' => '编辑自定义版本...', 'UI:Dashboard:CreateCustom' => '新建自定义版本...', 'UI:Dashboard:DeleteCustom' => '删除自定义版本...', - 'UI:Dashboard:Edit' => '编辑这个页面...', 'UI:Dashboard:RevertConfirm' => '每个地方都会恢复到初始版本. 请确认您要这样做.', 'UI:ExportDashBoard' => '导出到文件', 'UI:ImportDashBoard' => '从文件导入...', @@ -1185,44 +1232,52 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:ImportDashboardText' => '选择要导入的仪表盘文件:', 'UI:Dashboard:Actions' => '仪表盘操作', 'UI:Dashboard:NotUpToDateUntilContainerSaved' => '此仪表盘显示的信息不包含后续的更新.', - 'UI:Dashboard:Revert' => '还原到初始版本...', + 'UI:DashletCreation:Title' => '新建组件', 'UI:DashletCreation:Dashboard' => '仪表盘', - 'UI:DashletCreation:DashletType' => '组件类型', + 'UI:DashletCreation:DashletType' => '仪表类型', 'UI:DashletCreation:EditNow' => '编辑仪表盘', - 'UI:DashboardEdit:Title' => '仪表盘编辑器', + + 'UI:DashboardEdit:Title' => '仪表盘编辑器', 'UI:DashboardEdit:DashboardTitle' => '标题', 'UI:DashboardEdit:AutoReload' => '自动刷新', 'UI:DashboardEdit:AutoReloadSec' => '自动刷新间隔 (秒)', 'UI:DashboardEdit:AutoReloadSec+' => '最小值是 %1$d 秒', 'UI:DashboardEdit:Revert' => '撤回', 'UI:DashboardEdit:Apply' => '应用', - 'UI:DashboardEdit:Layout' => '布局', + + 'UI:DashboardEdit:Layout' => '布局', 'UI:DashboardEdit:Properties' => '仪表盘属性', - 'UI:DashboardEdit:Dashlets' => '可用的组件', - 'UI:DashboardEdit:DashletProperties' => '组件属性', - 'UI:Form:Property' => '属性', + 'UI:DashboardEdit:Dashlets' => '可用的仪表', + 'UI:DashboardEdit:DashletProperties' => '仪表属性', + + 'UI:Form:Property' => '属性', 'UI:Form:Value' => '值', - 'UI:DashletUnknown:Label' => '未知', + + 'UI:DashletUnknown:Label' => '未知', 'UI:DashletUnknown:Description' => '未知仪表盘 (可能已卸载)', 'UI:DashletUnknown:RenderText:View' => '此仪表盘无法渲染.', 'UI:DashletUnknown:RenderText:Edit' => '此仪表盘无法渲染 (类型 "%1$s"). 请与管理员确认其可用.', 'UI:DashletUnknown:RenderNoDataText:Edit' => '此仪表盘无预览 (类型 "%1$s").', 'UI:DashletUnknown:Prop-XMLConfiguration' => '配置 (显示为纯 XML)', - 'UI:DashletProxy:Label' => '代理', - 'UI:DashletProxy:Description' => '代理仪表盘', + + 'UI:DashletProxy:Label' => '代理', + 'UI:DashletProxy:Description' => '代理仪表', 'UI:DashletProxy:RenderNoDataText:Edit' => '第三方组件无法预览 (类型 "%1$s").', 'UI:DashletProxy:Prop-XMLConfiguration' => '配置 (显示为纯 XML)', - 'UI:DashletPlainText:Label' => '文本', + + 'UI:DashletPlainText:Label' => '文本', 'UI:DashletPlainText:Description' => '纯文本 (无格式)', 'UI:DashletPlainText:Prop-Text' => '内容', 'UI:DashletPlainText:Prop-Text:Default' => '请在这里输入内容...', - 'UI:DashletObjectList:Label' => '对象列表', + + 'UI:DashletObjectList:Label' => '对象列表', 'UI:DashletObjectList:Description' => '对象列表仪表盘', 'UI:DashletObjectList:Prop-Title' => '标题', 'UI:DashletObjectList:Prop-Query' => '查询', 'UI:DashletObjectList:Prop-Menu' => '菜单', - 'UI:DashletGroupBy:Prop-Title' => '标题', + + 'UI:DashletGroupBy:Prop-Title' => '标题', 'UI:DashletGroupBy:Prop-Query' => '查询', 'UI:DashletGroupBy:Prop-Style' => '样式', 'UI:DashletGroupBy:Prop-GroupBy' => '分组...', @@ -1232,23 +1287,29 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:DashletGroupBy:Prop-GroupBy:DayOfMonth' => '%1$s月天数', 'UI:DashletGroupBy:Prop-GroupBy:Select-Hour' => '%1$s (小时)', 'UI:DashletGroupBy:Prop-GroupBy:Select-Month' => '%1$s (月)', - 'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek' => '%1$s (星期数)', + 'UI:DashletGroupBy:Prop-GroupBy:Select-Year' => '%1$s (年)', + 'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek' => '%1$s (星期数)', 'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth' => '%1$s (月天数)', 'UI:DashletGroupBy:MissingGroupBy' => '请选择对象的分组字段', - 'UI:DashletGroupByPie:Label' => '饼图', + + 'UI:DashletGroupByPie:Label' => '饼图', 'UI:DashletGroupByPie:Description' => '饼图', 'UI:DashletGroupByBars:Label' => '柱状图', 'UI:DashletGroupByBars:Description' => '柱状图', 'UI:DashletGroupByTable:Label' => '分组 (表)', 'UI:DashletGroupByTable:Description' => '列表 (以字段分组)', - 'UI:DashletGroupBy:Prop-Function' => '聚合函数', + + // New in 2.5 + 'UI:DashletGroupBy:Prop-Function' => '聚合函数', 'UI:DashletGroupBy:Prop-FunctionAttribute' => '函数属性', 'UI:DashletGroupBy:Prop-OrderDirection' => '方向', 'UI:DashletGroupBy:Prop-OrderField' => '排序', 'UI:DashletGroupBy:Prop-Limit' => '限制', - 'UI:DashletGroupBy:Order:asc' => '升序', + + 'UI:DashletGroupBy:Order:asc' => '升序', 'UI:DashletGroupBy:Order:desc' => '降序', - 'UI:GroupBy:count' => '个数', + + 'UI:GroupBy:count' => '个数', 'UI:GroupBy:count+' => '项目数', 'UI:GroupBy:sum' => '总数', 'UI:GroupBy:sum+' => '%1$s 之和', @@ -1258,13 +1319,16 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:GroupBy:min+' => '%1$s 最小值', 'UI:GroupBy:max' => '最大', 'UI:GroupBy:max+' => '%1$s 最大值', + // --- + 'UI:DashletHeaderStatic:Label' => '标头', 'UI:DashletHeaderStatic:Description' => '显示水平分隔符', 'UI:DashletHeaderStatic:Prop-Title' => '标题', 'UI:DashletHeaderStatic:Prop-Title:Default' => '联系人', 'UI:DashletHeaderStatic:Prop-Icon' => '图标', - 'UI:DashletIcon:None' => 'None~', - 'UI:DashletHeaderDynamic:Label' => '统计标头', + 'UI:DashletIcon:None' => '无', + + 'UI:DashletHeaderDynamic:Label' => '统计标头', 'UI:DashletHeaderDynamic:Description' => '统计标头 (分组...)', 'UI:DashletHeaderDynamic:Prop-Title' => '标题', 'UI:DashletHeaderDynamic:Prop-Title:Default' => '联系人', @@ -1274,75 +1338,80 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:DashletHeaderDynamic:Prop-Query' => '查询', 'UI:DashletHeaderDynamic:Prop-GroupBy' => '分组依据', 'UI:DashletHeaderDynamic:Prop-Values' => '值', - 'UI:DashletBadge:Label' => '证件', + + 'UI:DashletBadge:Label' => '证件', 'UI:DashletBadge:Description' => '新建/查询的对象图标', 'UI:DashletBadge:Prop-Class' => '类型', - 'DayOfWeek-Sunday' => '周日', - 'DayOfWeek-Monday' => '周一', - 'DayOfWeek-Tuesday' => '周二', - 'DayOfWeek-Wednesday' => '周三', - 'DayOfWeek-Thursday' => '周四', - 'DayOfWeek-Friday' => '周五', - 'DayOfWeek-Saturday' => '周六', - 'Month-01' => '一月', - 'Month-02' => '二月', - 'Month-03' => '三月', - 'Month-04' => '四月', - 'Month-05' => '五月', - 'Month-06' => '六月', - 'Month-07' => '七月', - 'Month-08' => '八月', - 'Month-09' => '九月', - 'Month-10' => '十月', - 'Month-11' => '十一月', - 'Month-12' => '十二月', - 'DayOfWeek-Sunday-Min' => '日', - 'DayOfWeek-Monday-Min' => '一', - 'DayOfWeek-Tuesday-Min' => '二', - 'DayOfWeek-Wednesday-Min' => '三', - 'DayOfWeek-Thursday-Min' => '四', - 'DayOfWeek-Friday-Min' => '五', - 'DayOfWeek-Saturday-Min' => '六', - 'Month-01-Short' => '1月', - 'Month-02-Short' => '2月', - 'Month-03-Short' => '3月', - 'Month-04-Short' => '4月', - 'Month-05-Short' => '5月', - 'Month-06-Short' => '6月', - 'Month-07-Short' => '7月', - 'Month-08-Short' => '8月', - 'Month-09-Short' => '9月', - 'Month-10-Short' => '10月', - 'Month-11-Short' => '11月', - 'Month-12-Short' => '12月', - 'Calendar-FirstDayOfWeek' => '0', - 'UI:Menu:ShortcutList' => '创建快捷方式...', - 'UI:Menu:FilterList' => '显示条件查询列表', - 'UI:ShortcutRenameDlg:Title' => '重命名快捷方式', - 'UI:ShortcutListDlg:Title' => '为此列表创建快捷方式', - 'UI:ShortcutDelete:Confirm' => '请确认是否删除该快捷方式.', - 'UI:ObjectShortcutInsert' => '插入对象的快捷方式', - 'Class:Shortcut' => '快捷方式', - 'Class:Shortcut+' => '', - 'Class:Shortcut/Attribute:name' => '名称', - 'Class:Shortcut/Attribute:name+' => '用于菜单和页面的标记', - 'Class:ShortcutOQL' => '搜索结果的快捷方式', - 'Class:ShortcutOQL+' => '', - 'Class:ShortcutOQL/Attribute:oql' => '查询', - 'Class:ShortcutOQL/Attribute:oql+' => '定义执行搜索的对象列表的OQL', - 'Class:TagSetFieldData/Attribute:finalclass' => '标签类型', - 'Class:TagSetFieldData/Attribute:obj_attcode' => '字段编码', - 'Class:TagSetFieldData/Attribute:obj_class' => '对象类型', - 'Class:ShortcutOQL/Attribute:auto_reload' => '自动刷新', - 'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => '禁用', + + 'DayOfWeek-Sunday' => '周日', + 'DayOfWeek-Monday' => '周一', + 'DayOfWeek-Tuesday' => '周二', + 'DayOfWeek-Wednesday' => '周三', + 'DayOfWeek-Thursday' => '周四', + 'DayOfWeek-Friday' => '周五', + 'DayOfWeek-Saturday' => '周六', + 'Month-01' => '一月', + 'Month-02' => '二月', + 'Month-03' => '三月', + 'Month-04' => '四月', + 'Month-05' => '五月', + 'Month-06' => '六月', + 'Month-07' => '七月', + 'Month-08' => '八月', + 'Month-09' => '九月', + 'Month-10' => '十月', + 'Month-11' => '十一月', + 'Month-12' => '十二月', + + // Short version for the DatePicker + 'DayOfWeek-Sunday-Min' => '日', + 'DayOfWeek-Monday-Min' => '一', + 'DayOfWeek-Tuesday-Min' => '二', + 'DayOfWeek-Wednesday-Min' => '三', + 'DayOfWeek-Thursday-Min' => '四', + 'DayOfWeek-Friday-Min' => '五', + 'DayOfWeek-Saturday-Min' => '六', + 'Month-01-Short' => '1月', + 'Month-02-Short' => '2月', + 'Month-03-Short' => '3月', + 'Month-04-Short' => '4月', + 'Month-05-Short' => '5月', + 'Month-06-Short' => '6月', + 'Month-07-Short' => '7月', + 'Month-08-Short' => '8月', + 'Month-09-Short' => '9月', + 'Month-10-Short' => '10月', + 'Month-11-Short' => '11月', + 'Month-12-Short' => '12月', + 'Calendar-FirstDayOfWeek' => '0', + // 0 = Sunday, 1 = Monday, etc... + + 'UI:Menu:ShortcutList' => '创建快捷方式...', + 'UI:Menu:FilterList' => '显示条件查询列表', + 'UI:ShortcutRenameDlg:Title' => '重命名快捷方式', + 'UI:ShortcutListDlg:Title' => '为此列表创建快捷方式', + 'UI:ShortcutDelete:Confirm' => '请确认是否删除该快捷方式.', + 'UI:ObjectShortcutInsert' => '插入对象的快捷方式', + 'Class:Shortcut' => '快捷方式', + 'Class:Shortcut+' => '', + 'Class:Shortcut/Attribute:name' => '名称', + 'Class:Shortcut/Attribute:name+' => '用于菜单和页面的标记', + 'Class:ShortcutOQL' => '搜索结果的快捷方式', + 'Class:ShortcutOQL+' => '', + 'Class:ShortcutOQL/Attribute:oql' => '查询', + 'Class:ShortcutOQL/Attribute:oql+' => '定义执行搜索的对象列表的OQL', + 'Class:ShortcutOQL/Attribute:auto_reload' => '自动刷新', + 'Class:ShortcutOQL/Attribute:auto_reload/Value:none' => '禁用', 'Class:ShortcutOQL/Attribute:auto_reload/Value:custom' => '自定义频率', - 'Class:ShortcutOQL/Attribute:auto_reload_sec' => '自动刷新间隔 (秒)', - 'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => '最小值是 %1$d 秒', - 'UI:FillAllMandatoryFields' => '请填写所有的必填项.', - 'UI:ValueMustBeSet' => '必填', + 'Class:ShortcutOQL/Attribute:auto_reload_sec' => '自动刷新间隔 (秒)', + 'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => '最小值是 %1$d 秒', + + 'UI:FillAllMandatoryFields' => '请填写所有的必填项.', + 'UI:ValueMustBeSet' => '必填', 'UI:ValueMustBeChanged' => '必须修改这个值', 'UI:ValueInvalidFormat' => '格式无效', - 'UI:CSVImportConfirmTitle' => '请确认本次操作', + + 'UI:CSVImportConfirmTitle' => '请确认本次操作', 'UI:CSVImportConfirmMessage' => '请确认是否继续?', 'UI:CSVImportError_items' => '错误: %1$d', 'UI:CSVImportCreated_items' => '创建: %1$d', @@ -1367,22 +1436,27 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ i分 (2位数, 比如 00..59) s秒 (2位数, 比如 00..59) ', + 'UI:Button:Remove' => '移除', 'UI:AddAnExisting_Class' => '添加 %1$s...', 'UI:SelectionOf_Class' => '选择 %1$s', - 'UI:AboutBox' => '关于'.ITOP_APPLICATION_SHORT.'...', + + 'UI:AboutBox' => '关于'.ITOP_APPLICATION_SHORT.'...', 'UI:About:Title' => '关于'.ITOP_APPLICATION_SHORT, 'UI:About:DataModel' => '数据模型', - 'UI:About:Support' => '支持信息', + 'UI:About:DataModel' => '数据模型', + 'UI:About:Support' => '支持信息', 'UI:About:Licenses' => '许可证', 'UI:About:InstallationOptions' => '已安装的模块', 'UI:About:ManualExtensionSource' => '扩展', 'UI:About:Extension_Version' => '版本: %1$s', 'UI:About:RemoteExtensionSource' => '数据', - 'UI:DisconnectedDlgMessage' => '您已断开. 要继续使用,需要重新验证您的用户名和密码.', + + 'UI:DisconnectedDlgMessage' => '您已断开. 要继续使用,需要重新验证您的用户名和密码.', 'UI:DisconnectedDlgTitle' => '警告!', 'UI:LoginAgain' => '再次登录', 'UI:StayOnThePage' => '保持在当前页面', - 'ExcelExporter:ExportMenu' => 'Excel 导出...', + + 'ExcelExporter:ExportMenu' => 'Excel 导出...', 'ExcelExporter:ExportDialogTitle' => 'Excel 导出', 'ExcelExporter:ExportButton' => '导出', 'ExcelExporter:DownloadButton' => '下载 %1$s', @@ -1393,41 +1467,54 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'ExcelExport:PreparingExport' => '正在准备导出...', 'ExcelExport:Statistics' => '状态', 'portal:legacy_portal' => '终端用户门户', - 'portal:backoffice' => ITOP_APPLICATION_SHORT.'后台用户界面', - 'UI:CurrentObjectIsLockedBy_User' => '对象被锁定, 因为正在修改%1$s.', + 'portal:backoffice' => ITOP_APPLICATION_SHORT.'后台管理界面', + + 'UI:CurrentObjectIsLockedBy_User' => '对象被锁定, 因为正在修改%1$s.', 'UI:CurrentObjectIsLockedBy_User_Explanation' => '此对象正在被%1$s修改. 您的修改无法提交以避免冲突.', 'UI:CurrentObjectIsSoftLockedBy_User' => '此对象当前正在被%1$s修改. 在其修改结束后您将能提交您的修改.', 'UI:CurrentObjectLockExpired' => '并发修改的锁定期已过.', 'UI:CurrentObjectLockExpired_Explanation' => '防止对象被并发修改的锁定已失效. 其他用户已允许修改当前对象因此您不能再提交您的修改.', 'UI:ConcurrentLockKilled' => '阻止并发修改当前对象的锁定已被删除.', 'UI:Menu:KillConcurrentLock' => '消除并发修改锁定!', - 'UI:Menu:ExportPDF' => '导出PDF...', - 'UI:Menu:PrintableVersion' => '打印', - 'UI:BrowseInlineImages' => '浏览图片...', + + 'UI:Menu:ExportPDF' => '导出PDF...', + 'UI:Menu:PrintableVersion' => '打印友好的版本', + + 'UI:BrowseInlineImages' => '浏览图片...', 'UI:UploadInlineImageLegend' => '上传新图片', 'UI:SelectInlineImageToUpload' => '选择要上传的图片', 'UI:AvailableInlineImagesLegend' => '可用的图片', 'UI:NoInlineImage' => '服务器上没有图片. 使用上面的 "浏览" 按钮, 从您的电脑上选择并上传到服务器.', 'UI:MissingInlineImage' => '缺少图片', - 'UI:ToggleFullScreen' => '切换最大化/最小化', + + 'UI:ToggleFullScreen' => '切换最大化/最小化', 'UI:Button:ResetImage' => '恢复之前的图片', 'UI:Button:RemoveImage' => '移除图片', 'UI:Button:UploadImage' => '从硬盘上传图像', 'UI:UploadNotSupportedInThisMode' => '本模式下不支持修改文件或图片.', - 'UI:Button:RemoveDocument' => '移除文档', - 'UI:Search:Toggle' => '折叠/展开', + + 'UI:Button:RemoveDocument' => '移除文档', + + // Search form + 'UI:Search:Toggle' => '折叠/展开', 'UI:Search:AutoSubmit:DisabledHint' => '此类别已禁用自动提交', 'UI:Search:Obsolescence:DisabledHint' => '根据您的设置, 废弃的数据会被隐藏', 'UI:Search:NoAutoSubmit:ExplainText' => '在搜索框中添加规则, 或者单击对象按钮查看对象.', 'UI:Search:Criterion:MoreMenu:AddCriteria' => '添加条件', - 'UI:Search:AddCriteria:List:RecentlyUsed:Title' => '最近使用', + // - Add new criteria button + 'UI:Search:AddCriteria:List:RecentlyUsed:Title' => '最近使用', 'UI:Search:AddCriteria:List:MostPopular:Title' => '最常用', 'UI:Search:AddCriteria:List:Others:Title' => '其它', 'UI:Search:AddCriteria:List:RecentlyUsed:Placeholder' => '还没有.', - 'UI:Search:Criteria:Toggle' => '折叠/展开', + + // - Criteria header actions + 'UI:Search:Criteria:Toggle' => '折叠/展开', 'UI:Search:Criteria:Remove' => '移除', 'UI:Search:Criteria:Locked' => '已锁定', - 'UI:Search:Criteria:Title:Default:Any' => '%1$s: 任何', + + // - Criteria titles + // - Default widget + 'UI:Search:Criteria:Title:Default:Any' => '%1$s: 任何', 'UI:Search:Criteria:Title:Default:Empty' => '%1$s 为空', 'UI:Search:Criteria:Title:Default:NotEmpty' => '%1$s 不为空', 'UI:Search:Criteria:Title:Default:Equals' => '%1$s 等于 %2$s', @@ -1448,46 +1535,63 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Search:Criteria:Title:Default:Between:All' => '%1$s: 任何', 'UI:Search:Criteria:Title:Default:Between:From' => '%1$s 从 %2$s', 'UI:Search:Criteria:Title:Default:Between:Until' => '%1$s 到 %2$s', - 'UI:Search:Criteria:Title:DateTime:Between' => '%2$s <= 1$s <= %3$s', - 'UI:Search:Criteria:Title:Enum:In' => '%1$s: %2$s', + // - Numeric widget + // None yet + // - DateTime widget + 'UI:Search:Criteria:Title:DateTime:Between' => '%2$s <= 1$s <= %3$s', + // - Enum widget + 'UI:Search:Criteria:Title:Enum:In' => '%1$s: %2$s', 'UI:Search:Criteria:Title:Enum:In:Many' => '%1$s: %2$s 和 %3$s 其他', 'UI:Search:Criteria:Title:Enum:In:All' => '%1$s: 任何', - 'UI:Search:Criteria:Title:TagSet:Matches' => '%1$s: %2$s', - 'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s 已定义', + // - TagSet widget + 'UI:Search:Criteria:Title:TagSet:Matches' => '%1$s: %2$s', + // - External key widget + 'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s 已定义', 'UI:Search:Criteria:Title:ExternalKey:NotEmpty' => '%1$s 未定义', 'UI:Search:Criteria:Title:ExternalKey:Equals' => '%1$s %2$s', 'UI:Search:Criteria:Title:ExternalKey:In' => '%1$s: %2$s', 'UI:Search:Criteria:Title:ExternalKey:In:Many' => '%1$s: %2$s 和 %3$s 其他', 'UI:Search:Criteria:Title:ExternalKey:In:All' => '%1$s: 任何', - 'UI:Search:Criteria:Title:HierarchicalKey:Empty' => '%1$s 已定义', + // - Hierarchical key widget + 'UI:Search:Criteria:Title:HierarchicalKey:Empty' => '%1$s 已定义', 'UI:Search:Criteria:Title:HierarchicalKey:NotEmpty' => '%1$s 未定义', 'UI:Search:Criteria:Title:HierarchicalKey:Equals' => '%1$s %2$s', 'UI:Search:Criteria:Title:HierarchicalKey:In' => '%1$s: %2$s', 'UI:Search:Criteria:Title:HierarchicalKey:In:Many' => '%1$s: %2$s 和 %3$s 其他', 'UI:Search:Criteria:Title:HierarchicalKey:In:All' => '%1$s: 任何', - 'UI:Search:Criteria:Operator:Default:Empty' => '为空', + + // - Criteria operators + // - Default widget + 'UI:Search:Criteria:Operator:Default:Empty' => '为空', 'UI:Search:Criteria:Operator:Default:NotEmpty' => '非空', 'UI:Search:Criteria:Operator:Default:Equals' => '等于', 'UI:Search:Criteria:Operator:Default:Between' => '之间', - 'UI:Search:Criteria:Operator:String:Contains' => '包含', + // - String widget + 'UI:Search:Criteria:Operator:String:Contains' => '包含', 'UI:Search:Criteria:Operator:String:StartsWith' => '起始为', 'UI:Search:Criteria:Operator:String:EndsWith' => '结尾是', 'UI:Search:Criteria:Operator:String:RegExp' => '正则表达式.', - 'UI:Search:Criteria:Operator:Numeric:Equals' => '等于', + // - Numeric widget + 'UI:Search:Criteria:Operator:Numeric:Equals' => '等于', 'UI:Search:Criteria:Operator:Numeric:GreaterThan' => '大于', 'UI:Search:Criteria:Operator:Numeric:GreaterThanOrEquals' => '大于/等于', 'UI:Search:Criteria:Operator:Numeric:LessThan' => '小于', 'UI:Search:Criteria:Operator:Numeric:LessThanOrEquals' => '小于/等于', 'UI:Search:Criteria:Operator:Numeric:Different' => '不同', - 'UI:Search:Criteria:Operator:TagSet:Matches' => '匹配', - 'UI:Search:Value:Filter:Placeholder' => '筛选器...', + // - Tag Set Widget + 'UI:Search:Criteria:Operator:TagSet:Matches' => '匹配', + + // - Other translations + 'UI:Search:Value:Filter:Placeholder' => '筛选器...', 'UI:Search:Value:Search:Placeholder' => '搜索...', 'UI:Search:Value:Autocomplete:StartTyping' => '开始输入值.', 'UI:Search:Value:Autocomplete:Wait' => '请稍候...', 'UI:Search:Value:Autocomplete:NoResult' => '没有找到结果.', - 'UI:Search:Value:Toggler:CheckAllNone' => '全选/不选', - 'UI:Search:Value:Toggler:CheckAllNoneFiltered' => '全选/不选', - 'UI:Search:Criteria:Numeric:From' => '从', + 'UI:Search:Value:Toggler:CheckAllNone' => '全选/全不选', + 'UI:Search:Value:Toggler:CheckAllNoneFiltered' => '全选/全不选', + + // - Widget other translations + 'UI:Search:Criteria:Numeric:From' => '从', 'UI:Search:Criteria:Numeric:Until' => '到', 'UI:Search:Criteria:Numeric:PlaceholderFrom' => '任何', 'UI:Search:Criteria:Numeric:PlaceholderUntil' => '任何', @@ -1500,9 +1604,13 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Search:Criteria:DateTime:PlaceholderUntil' => '任何日期', 'UI:Search:Criteria:DateTime:PlaceholderUntilTime' => '任何日期', 'UI:Search:Criteria:HierarchicalKey:ChildrenIncluded:Hint' => '将会包含选中对象的子集.', - 'UI:Search:Criteria:Raw:Filtered' => '已筛选', + + 'UI:Search:Criteria:Raw:Filtered' => '已筛选', 'UI:Search:Criteria:Raw:FilteredOn' => '基于%1$s筛选', + 'UI:StateChanged' => '状态已改变', + + 'UI:AddSubTree' => '添加条目', ]); // @@ -1512,14 +1620,17 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Expression:Operator:AND' => ' 与 ', 'Expression:Operator:OR' => ' 或 ', 'Expression:Operator:=' => ': ', - 'Expression:Unit:Short:DAY' => '日', + + 'Expression:Unit:Short:DAY' => '日', 'Expression:Unit:Short:WEEK' => '周', 'Expression:Unit:Short:MONTH' => '月', 'Expression:Unit:Short:YEAR' => '年', - 'Expression:Unit:Long:DAY' => '日', + + 'Expression:Unit:Long:DAY' => '日', 'Expression:Unit:Long:HOUR' => '小时', 'Expression:Unit:Long:MINUTE' => '分钟', - 'Expression:Verb:NOW' => '现在', + + 'Expression:Verb:NOW' => '现在', 'Expression:Verb:ISNULL' => ': 未定义', ]); @@ -1544,31 +1655,31 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ ]); Dict::Add('ZH CN', 'Chinese', '简体中文', [ - 'Menu:DataSources' => '同步数据源', - 'Menu:DataSources+' => '所有同步数据源', - 'Menu:AuditCategories' => '审计类别', - 'Menu:AuditCategories+' => '审计类别', - 'Menu:AuditCategories:Title' => '审计设置', - 'Menu:RunQueriesMenu' => '运行查询', - 'Menu:RunQueriesMenu+' => '运行任何查询', - 'Menu:QueryMenu' => '预定义查询', - 'Menu:QueryMenu+' => '可重复利用的查询语句', - 'Menu:UniversalSearchMenu' => '全局搜索', - 'Menu:UniversalSearchMenu+' => '搜索所有...', - 'Menu:UserManagementMenu' => '用户管理', - 'Menu:UserManagementMenu+' => '谁可以访问'.ITOP_APPLICATION_SHORT.'以及可以做什么?', - 'Menu:ProfilesMenu' => '角色', - 'Menu:ProfilesMenu+' => '查看已有的角色', - 'Menu:ProfilesMenu:Title' => '角色', - 'Menu:UserAccountsMenu' => '用户账号', - 'Menu:UserAccountsMenu+' => '处理 '.ITOP_APPLICATION_SHORT.' 用户及权限', - 'Menu:UserAccountsMenu:Title' => '用户账号', - 'Menu:UserManagement' => '用户管理', - 'Menu:Queries' => '查询', - 'Menu:ConfigurationTools' => '配置', - 'Menu:ConfigEditor' => '基本配置', - 'Menu:ConfigEditor+' => '配置文件编辑器', - 'Menu:Integrations' => '集成', - 'Menu:Integrations+' => '~~', + 'Menu:DataSources' => '同步数据源', + 'Menu:DataSources+' => '所有同步数据源', + 'Menu:AuditCategories' => '审计类别', + 'Menu:AuditCategories+' => '审计类别', + 'Menu:AuditCategories:Title' => '审计设置', + 'Menu:RunQueriesMenu' => '运行查询', + 'Menu:RunQueriesMenu+' => '运行任何查询', + 'Menu:QueryMenu' => '预定义查询', + 'Menu:QueryMenu+' => '可重复利用的查询语句', + 'Menu:UniversalSearchMenu' => '全局搜索', + 'Menu:UniversalSearchMenu+' => '搜索所有...', + 'Menu:UserManagementMenu' => '用户管理', + 'Menu:UserManagementMenu+' => '谁可以访问'.ITOP_APPLICATION_SHORT.'以及可以做什么?', + 'Menu:ProfilesMenu' => '角色', + 'Menu:ProfilesMenu+' => '查看已有的角色', + 'Menu:ProfilesMenu:Title' => '角色', + 'Menu:UserAccountsMenu' => '账号', + 'Menu:UserAccountsMenu+' => '处理 '.ITOP_APPLICATION_SHORT.' 用户及权限', + 'Menu:UserAccountsMenu:Title' => '账号', + 'Menu:UserManagement' => '用户管理', + 'Menu:Queries' => '查询', + 'Menu:ConfigurationTools' => '配置', + 'Menu:ConfigEditor' => '基本配置', + 'Menu:ConfigEditor+' => '配置文件编辑器', + 'Menu:Integrations' => '集成', + 'Menu:Integrations+' => '', ]); From 7c44c94de12ac741fd1e2a4fee5e368ef2e2d577 Mon Sep 17 00:00:00 2001 From: jf-cbd Date: Mon, 8 Jun 2026 13:50:14 +0200 Subject: [PATCH 03/36] Fix unit and CS fixer tests --- .../zh_cn.dict.itop-config-mgmt.php | 28 +-- .../zh_cn.dict.itop-virtualization-mgmt.php | 2 +- .../zh_cn.dictionary.itop.preferences.php | 2 +- dictionaries/zh_cn.dictionary.itop.core.php | 22 +- dictionaries/zh_cn.dictionary.itop.ui.php | 190 +++++++++--------- 5 files changed, 122 insertions(+), 122 deletions(-) diff --git a/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php b/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php index a3874dd015..2e7ad00f68 100644 --- a/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php +++ b/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php @@ -151,7 +151,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PhysicalDevice/Attribute:model_id+' => '', 'Class:PhysicalDevice/Attribute:model_end_of_support' => '型号过保日期', 'Class:PhysicalDevice/Attribute:model_end_of_support+' => '当硬件型号不再被制造商支持时,如果此信息在型号上有所记录.', - 'Class:PhysicalDevice/Attribute:model_name' => '型号名称', + 'Class:PhysicalDevice/Attribute:model_name' => '型号名称', 'Class:PhysicalDevice/Attribute:model_name+' => '', 'Class:PhysicalDevice/Attribute:asset_number' => '资产编号', 'Class:PhysicalDevice/Attribute:asset_number+' => '', @@ -323,7 +323,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Server/Attribute:osversion_name+' => '', 'Class:Server/Attribute:os_end_of_support' => 'OS 过保时间', 'Class:Server/Attribute:os_end_of_support+' => '厂商不再为该操作系统版本提供补丁的时间.', - 'Class:Server/Attribute:oslicence_id' => 'OS 许可证', + 'Class:Server/Attribute:oslicence_id' => 'OS 许可证', 'Class:Server/Attribute:oslicence_id+' => '', 'Class:Server/Attribute:oslicence_name' => 'OS 许可证名称', 'Class:Server/Attribute:oslicence_name+' => '', @@ -401,7 +401,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PC/Attribute:osversion_name+' => '', 'Class:PC/Attribute:os_end_of_support' => 'OS 过保时间', 'Class:PC/Attribute:os_end_of_support+' => '厂商不再为该操作系统版本提供补丁的时间.', - 'Class:PC/Attribute:cpu' => 'CPU', + 'Class:PC/Attribute:cpu' => 'CPU', 'Class:PC/Attribute:cpu+' => '', 'Class:PC/Attribute:ram' => '内存', 'Class:PC/Attribute:ram+' => '', @@ -593,7 +593,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SoftwareInstance/Attribute:software_name+' => '', 'Class:SoftwareInstance/Attribute:software_end_of_support' => '软件过保时间', 'Class:SoftwareInstance/Attribute:software_end_of_support+' => '厂商为此软件版本提供补丁的最后日期.', - 'Class:SoftwareInstance/Attribute:softwarelicence_id' => '软件许可证', + 'Class:SoftwareInstance/Attribute:softwarelicence_id' => '软件许可证', 'Class:SoftwareInstance/Attribute:softwarelicence_id+' => '', 'Class:SoftwareInstance/Attribute:softwarelicence_name' => '许可证名称', 'Class:SoftwareInstance/Attribute:softwarelicence_name+' => '', @@ -792,7 +792,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:VirtualMachine/Attribute:osversion_name+' => '', 'Class:VirtualMachine/Attribute:os_end_of_support' => 'OS 过保日期', 'Class:VirtualMachine/Attribute:os_end_of_support+' => '厂商不再支持该操作系统版本时的日期.', - 'Class:VirtualMachine/Attribute:oslicence_id' => 'OS 许可证', + 'Class:VirtualMachine/Attribute:oslicence_id' => 'OS 许可证', 'Class:VirtualMachine/Attribute:oslicence_id+' => '', 'Class:VirtualMachine/Attribute:oslicence_name' => '名称', 'Class:VirtualMachine/Attribute:oslicence_name+' => '', @@ -901,7 +901,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Tape' => '磁带', - 'Class:Tape+' => '磁带(或盒式磁带) 在 ' . ITOP_APPLICATION_SHORT . ' 中是磁带库的一部分,可移除的存储介质.', + 'Class:Tape+' => '磁带(或盒式磁带) 在 '.ITOP_APPLICATION_SHORT.' 中是磁带库的一部分,可移除的存储介质.', 'Class:Tape/Attribute:name' => '名称', 'Class:Tape/Attribute:name+' => '', 'Class:Tape/Attribute:description' => '描述', @@ -1088,8 +1088,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:OSVersion/Attribute:end_of_support+' => 'The date after which the editor ceases to provide patches for this OS version.~~', 'Class:OSVersion/Attribute:ospatches_list' => 'OS 补丁', 'Class:OSVersion/Attribute:ospatches_list+' => 'All the OS patches for this OS version~~', - 'Class:OSVersion/UniquenessRule:name_osfamily+' => 'OS 家族的名称必须唯一', - 'Class:OSVersion/UniquenessRule:name_osfamily' => '此 OS 版本已在 OS 家族中存在~~', + 'Class:OSVersion/UniquenessRule:name_osfamily+' => 'OS 家族的名称必须唯一', + 'Class:OSVersion/UniquenessRule:name_osfamily' => '此 OS 版本已在 OS 家族中存在', ]); // @@ -1101,7 +1101,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:OSFamily+' => '分类. 物理机、虚拟机、PC 的 "OS 家族" 属性可能的值列表.', 'Class:OSFamily/Attribute:osversions_list' => 'OS 版本', 'Class:OSFamily/Attribute:osversions_list+' => '此 OS 家族的所有 OS 版本', - 'Class:OSFamily/UniquenessRule:name+' => '名称必须唯一', + 'Class:OSFamily/UniquenessRule:name+' => '名称必须唯一', 'Class:OSFamily/UniquenessRule:name' => '此 OS 家族已存在', ]); @@ -1114,11 +1114,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Brand+' => '分类. 物理设备 "品牌" 的可能的值列表.', 'Class:Brand/Attribute:iosversions_list' => 'IOS 版本', 'Class:Brand/Attribute:iosversions_list+' => '此品牌的所有 IOS 版本', - 'Class:Brand/Attribute:logo' => 'Logo', + 'Class:Brand/Attribute:logo' => 'Logo', 'Class:Brand/Attribute:logo+' => '', 'Class:Brand/Attribute:models_list' => '型号', 'Class:Brand/Attribute:models_list+' => '此品牌的所有型号', - 'Class:Brand/Attribute:physicaldevices_list' => '物理设备', + 'Class:Brand/Attribute:physicaldevices_list' => '物理设备', 'Class:Brand/Attribute:physicaldevices_list+' => '此品牌的所有物理设备', 'Class:Brand/UniquenessRule:name+' => '名称必须唯一', 'Class:Brand/UniquenessRule:name' => '此品牌已存在', @@ -1142,7 +1142,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Model/Attribute:type+' => '', 'Class:Model/Attribute:end_of_support' => '过保日期', 'Class:Model/Attribute:end_of_support+' => '厂商提供补丁和支持的最后时间.', - 'Class:Model/Attribute:type/Value:PowerSource' => '电源', + 'Class:Model/Attribute:type/Value:PowerSource' => '电源', 'Class:Model/Attribute:type/Value:PowerSource+' => '电源', 'Class:Model/Attribute:type/Value:DiskArray' => '磁盘阵列', 'Class:Model/Attribute:type/Value:DiskArray+' => '磁盘阵列', @@ -1212,7 +1212,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:IOSVersion/Attribute:end_of_support+' => '厂商提供补丁的最后时间.', 'Class:IOSVersion/Attribute:networkdevices_list' => '网络设备', 'Class:IOSVersion/Attribute:networkdevices_list+' => '运行此 IOS 版本的所有网络设备', - 'Class:IOSVersion/UniquenessRule:name_brand+' => '名称在品牌中必须唯一', + 'Class:IOSVersion/UniquenessRule:name_brand+' => '名称在品牌中必须唯一', 'Class:IOSVersion/UniquenessRule:name_brand' => '此 IOS 版本已存在于此品牌', ]); @@ -1405,7 +1405,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:PhysicalInterface/Attribute:org_id+' => '', 'Class:PhysicalInterface/Attribute:location_id' => '位置', 'Class:PhysicalInterface/Attribute:location_id+' => '', - 'Class:PhysicalInterface/Attribute:vlans_list' => 'VLAN', + 'Class:PhysicalInterface/Attribute:vlans_list' => 'VLAN', 'Class:PhysicalInterface/Attribute:vlans_list+' => '', ]); diff --git a/datamodels/2.x/itop-virtualization-mgmt/dictionaries/zh_cn.dict.itop-virtualization-mgmt.php b/datamodels/2.x/itop-virtualization-mgmt/dictionaries/zh_cn.dict.itop-virtualization-mgmt.php index 864e8059a0..cb7a8621ad 100644 --- a/datamodels/2.x/itop-virtualization-mgmt/dictionaries/zh_cn.dict.itop-virtualization-mgmt.php +++ b/datamodels/2.x/itop-virtualization-mgmt/dictionaries/zh_cn.dict.itop-virtualization-mgmt.php @@ -43,7 +43,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:Cloud+' => '由云供应商运营的虚拟化主机. 它可以托管虚拟机和容器宿主机.', 'Class:Cloud/Attribute:provider_id' => '供应商', 'Class:Cloud/Attribute:logo' => 'Logo', - 'Class:Cloud/Attribute:logo+' => '在影响分析图中显示此云平台时用作对象图标~~', + 'Class:Cloud/Attribute:logo+' => '在影响分析图中显示此云平台时用作对象图标', 'Class:Cloud/Attribute:provider_id+' => '谁提供云平台', 'Class:Cloud/Attribute:location_id' => '位置', 'Class:Cloud/Attribute:location_id+' => '云平台的位置', diff --git a/dictionaries/ui/pages/preferences/zh_cn.dictionary.itop.preferences.php b/dictionaries/ui/pages/preferences/zh_cn.dictionary.itop.preferences.php index 25f880dc22..d707657078 100644 --- a/dictionaries/ui/pages/preferences/zh_cn.dictionary.itop.preferences.php +++ b/dictionaries/ui/pages/preferences/zh_cn.dictionary.itop.preferences.php @@ -54,6 +54,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Preferences:ChooseAPlaceholder+' => '请选择一个占位图片, 将在用户联系人没有设定头像图片时显示', 'UI:Preferences:ChooseAPlaceholder:Success:Message' => '您的占位图片已成功更新', 'UI:Preferences:Notifications' => '通知', - 'UI:Preferences:Notifications+' => '在 这里配置您想要收到的通知.', + 'UI:Preferences:Notifications+' => '在 这里配置您想要收到的通知.', ]); diff --git a/dictionaries/zh_cn.dictionary.itop.core.php b/dictionaries/zh_cn.dictionary.itop.core.php index dc46bfeaef..a42ce9a9f8 100644 --- a/dictionaries/zh_cn.dictionary.itop.core.php +++ b/dictionaries/zh_cn.dictionary.itop.core.php @@ -613,11 +613,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ActionEmail/Attribute:to+' => 'To: an OQL query returning objects having an email field. While editing, click on the magnifier to get pertinent examples. You can use in the OQL :this->attribute_code with an attribute code of the object which triggered the Notification. Then test your OQL syntax using the play icon.~~', - 'Class:ActionEmail/Attribute:cc' => '抄送', + 'Class:ActionEmail/Attribute:cc' => '抄送', 'Class:ActionEmail/Attribute:cc+' => 'Carbon Copy: an OQL query returning objects having an email field. While editing, click on the magnifier to get pertinent examples. You can use in the OQL :this->attribute_code with an attribute code of the object which triggered the Notification. Then test your OQL syntax using the play icon.~~', - 'Class:ActionEmail/Attribute:bcc' => '密抄', + 'Class:ActionEmail/Attribute:bcc' => '密抄', 'Class:ActionEmail/Attribute:bcc+' => '密送: 返回含有邮箱字段对象的OQL查询. 编辑时可点击放大镜图标获取参考示例', 'Class:ActionEmail/Attribute:subject' => '主题', @@ -933,7 +933,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SynchroDataSource/Attribute:user_delete_policy/Value:everybody' => '所有允许删除此类型对象的用户', 'Class:SynchroDataSource/Attribute:user_delete_policy/Value:nobody' => '没有人', - 'SynchroDataSource:Description' => '描述', + 'SynchroDataSource:Description' => '描述', 'SynchroDataSource:Reconciliation' => '搜索 & 使一致', 'SynchroDataSource:Deletion' => '删除规则', 'SynchroDataSource:Status' => '状态', @@ -1082,18 +1082,18 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Core:ExecProcess:Code255' => 'PHP错误 (解析, 或运行时)', // Attribute Duration - 'Core:Duration_Seconds' => '%1$ds', + 'Core:Duration_Seconds' => '%1$ds', 'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds', - 'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds', - 'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds', + 'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds', + 'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds', - // Explain working time computing - 'Core:ExplainWTC:ElapsedTime' => '耗时 (储存为 "%1$s")', + // Explain working time computing + 'Core:ExplainWTC:ElapsedTime' => '耗时 (储存为 "%1$s")', 'Core:ExplainWTC:StopWatch-TimeSpent' => '在 "%1$s" 上消耗的时间', 'Core:ExplainWTC:StopWatch-Deadline' => '"%1$s" 的期限截止于%2$d%%', // Bulk export - 'Core:BulkExport:MissingParameter_Param' => '缺少参数 "%1$s"', + 'Core:BulkExport:MissingParameter_Param' => '缺少参数 "%1$s"', 'Core:BulkExport:InvalidParameter_Query' => '参数 "query" 的值无效. 在查询薄中没有找到对应编号: "%1$s" 的记录.', 'Core:BulkExport:ExportFormatPrompt' => '导出格式:', 'Core:BulkExportOf_Class' => '%1$s 导出', @@ -1171,7 +1171,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:TagSetFieldData' => '类型%1$s的%2$s', 'Class:TagSetFieldData+' => '', - 'Class:TagSetFieldData/Attribute:code' => '代码', + 'Class:TagSetFieldData/Attribute:code' => '代码', 'Class:TagSetFieldData/Attribute:code+' => '内部代码. 必须至少包含3个数字或字母', 'Class:TagSetFieldData/Attribute:label' => '标签', 'Class:TagSetFieldData/Attribute:label+' => '显示的标签', @@ -1181,7 +1181,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:TagSetFieldData/Attribute:obj_class' => '对象类型', 'Class:TagSetFieldData/Attribute:obj_attcode' => '字段编码', - 'Core:TagSetFieldData:ErrorDeleteUsedTag' => '已使用的标签无法删除', + 'Core:TagSetFieldData:ErrorDeleteUsedTag' => '已使用的标签无法删除', 'Core:TagSetFieldData:ErrorDuplicateTagCodeOrLabel' => '标签编码或名称必须唯一', 'Core:TagSetFieldData:ErrorTagCodeSyntax' => '标签编码必须介于3到%1$d个字符, 以字母开头.', 'Core:TagSetFieldData:ErrorTagCodeReservedWord' => '输入的标签编码为内部保留字', diff --git a/dictionaries/zh_cn.dictionary.itop.ui.php b/dictionaries/zh_cn.dictionary.itop.ui.php index 8565913621..5edece91ed 100644 --- a/dictionaries/zh_cn.dictionary.itop.ui.php +++ b/dictionaries/zh_cn.dictionary.itop.ui.php @@ -56,10 +56,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:AuditRule/Attribute:query' => '要运行的查询', 'Class:AuditRule/Attribute:query+' => '要运行的OQL表达式', 'Class:AuditRule/Attribute:valid_flag' => '返回的对象:', - 'Class:AuditRule/Attribute:valid_flag+' => '查询返回的对象是否有效? -"有效对象": then objects in error are those part of the scope and not in the list of valid objects, -"无效对象": then objects in error are the invalid objects which are part of the scope~~', - 'Class:AuditRule/Attribute:valid_flag/Value:true' => '有效对象', + 'Class:AuditRule/Attribute:valid_flag+' => 'Does the query to run returns valid or invalid objects? +"Valid objects": then objects in error are those part of the scope and not in the list of valid objects, +"Invalid objects": then objects in error are the invalid objects which are part of the scope', + 'Class:AuditRule/Attribute:valid_flag/Value:true' => '有效对象', 'Class:AuditRule/Attribute:valid_flag/Value:true+' => 'Then objects in error are those part of the scope and not in the list of valid objects~~', 'Class:AuditRule/Attribute:valid_flag/Value:false' => '无效对象', 'Class:AuditRule/Attribute:valid_flag/Value:false+' => 'Then objects in error are the invalid objects which are part of the scope~~', @@ -415,7 +415,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'BooleanLabel:no' => '否', 'UI:WelcomeMenu:Title' => '欢迎使用'.ITOP_APPLICATION_SHORT, 'UI:WelcomeMenu:AllOpenRequests' => '所有打开的需求: %1$d', - 'UI:WelcomeMenu:MyCalls' => '我办理的需求', + 'UI:WelcomeMenu:MyCalls' => '我办理的需求', 'UI:WelcomeMenu:OpenIncidents' => '所有打开的事件: %1$d', 'UI:WelcomeMenu:AllConfigItems' => '配置项: %1$d', 'UI:WelcomeMenu:MyIncidents' => '分配给我的事件', @@ -469,7 +469,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:InputFile:NoFileSelected' => '未选择文件', 'UI:InputFile:SelectFile' => '请选择文件', - 'UI:SearchToggle' => '搜索', + 'UI:SearchToggle' => '搜索', 'UI:ClickToCreateNew' => '新建%1$s', 'UI:SearchFor_Class' => '搜索%1$s ', 'UI:NoObjectToDisplay' => '没有可显示的对象.', @@ -489,7 +489,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Error:UploadStoppedByExtension_FileName' => '上传因为扩展名被停止. (原文件名 = "%1$s").', 'UI:Error:UploadFailedUnknownCause_Code' => '文件上传失败, 原因未知. (错误代码 = "%1$s").', - 'UI:Error:1ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s.', + 'UI:Error:1ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s.', 'UI:Error:2ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s和%2$s.', 'UI:Error:3ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s, %2$s和%3$s.', 'UI:Error:4ParametersMissing' => '错误: 必须为此操作指定以下参数: %1$s, %2$s, %3$s和%4$s.', @@ -512,9 +512,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Error:InvalidToken' => '错误: 所请求的操作已执行 (没有CSRF token)', 'UI:Error:TwigController' => 'Internal error in form controller~~', - 'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP提供者%1$s不存在 (email_transport_smtp.oauth.provider)', + 'UI:Error:SMTP:UnknownVendor' => 'OAuth SMTP提供者%1$s不存在 (email_transport_smtp.oauth.provider)', - 'UI:GroupBy:Count' => '个数', + 'UI:GroupBy:Count' => '个数', 'UI:GroupBy:Count+' => '项目数', 'UI:CountOfObjects' => '%1$d个对象符合指定的条件.', 'UI_CountOfObjectsShort' => '%1$d个对象.', @@ -630,7 +630,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:CSVImport:SelectClass' => '请选择要导入的类别:', 'UI:CSVImport:AdvancedMode' => '高级模式', 'UI:CSVImport:AdvancedMode+' => '在高级模式中, 对象的"id" (主键) 可以被用来修改和重命名对象.'. - '不管怎样, 列 "id" (如果存在) 只能被用做一个搜索条件, 不能与其它搜索条件混用.', + '不管怎样, 列 "id" (如果存在) 只能被用做一个搜索条件, 不能与其它搜索条件混用.', 'UI:CSVImport:SelectAClassFirst' => '要配置映射, 请先选择一个类型.', 'UI:CSVImport:HeaderFields' => '字段', 'UI:CSVImport:HeaderMappings' => '映射', @@ -645,7 +645,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:UniversalSearch:Error' => '错误: %1$s', 'UI:UniversalSearch:LabelSelectTheClass' => '选择要搜索的类别: ', - 'UI:CSVReport-Value-Modified' => '已修改', + 'UI:CSVReport-Value-Modified' => '已修改', 'UI:CSVReport-Value-SetIssue' => '属性值不合法', 'UI:CSVReport-Value-ChangeIssue' => '\'%1$s\' 值不合法', 'UI:CSVReport-Value-NoMatch' => '没有匹配到值 \'%1$s\'', @@ -654,7 +654,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:CSVReport-Value-NoMatch-NoObject-ForCurrentUser' => '对象 \'%1$s\' 在您当前角色下没有找到', 'UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser' => '一些对象 \'%1$s\' 在您当前角色下不可见', - 'UI:CSVReport-Value-Missing' => '缺少必填项', + 'UI:CSVReport-Value-Missing' => '缺少必填项', 'UI:CSVReport-Value-Ambiguous' => '模糊匹配: 找到 %1$s 个对象', 'UI:CSVReport-Row-Unchanged' => '保持不变', 'UI:CSVReport-Row-Created' => '新建', @@ -680,7 +680,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:CSVReport-Value-Issue-NoValue' => 'No value', 'UI:CSVReport-Row-Issue-NbField' => 'Not the expected number of columns (found: %1$s, expected: %2$s)', - 'UI:CSVReport-Icon-Unchanged' => '保持不变', + 'UI:CSVReport-Icon-Unchanged' => '保持不变', 'UI:CSVReport-Icon-Modified' => '修改', 'UI:CSVReport-Icon-Missing' => '丢失', 'UI:CSVReport-Object-MissingToUpdate' => '丢失对象: 将被更新', @@ -695,14 +695,14 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:CSVReport-Stats-Created' => '%1$.0f %% 已加载的对象将会被创建.', 'UI:CSVReport-Stats-Modified' => '%1$.0f %% 已加载的对象将会被修改.', - 'UI:CSVExport:AdvancedMode' => '高级模式', + 'UI:CSVExport:AdvancedMode' => '高级模式', 'UI:CSVExport:AdvancedMode+' => '高级模式下, 导出将包含额外的列: 对象id, 外键id以及要使一致的属性.', 'UI:CSVExport:LostChars' => '编码问题', 'UI:CSVExport:LostChars+' => '已下载文件将使用编码 %1$s. 系统检测到一些字符与该格式不兼容. 这些字符将被替换为子集 (例如重读字符将丢失音调), 或者被丢弃. 您可以从浏览器中复制粘贴数据. 或者联系管理员更改字符编码 (参阅参数 \'csv_file_default_charset\').', - 'UI:Audit:Title' => ITOP_APPLICATION_SHORT.' - CMDB审计', + 'UI:Audit:Title' => ITOP_APPLICATION_SHORT.' - CMDB审计', - 'UI:Audit:Interactive:All:Title' => '审计结果', + 'UI:Audit:Interactive:All:Title' => '审计结果', 'UI:Audit:Interactive:All:SubTitle' => '全量审计: 包括所有规则, 所有类别, 所有审计域', 'UI:Audit:Interactive:All:BreadCrumb' => '全量审计', 'UI:Audit:Interactive:All:BreadCrumb+' => '所有规则的审计结果', @@ -722,7 +722,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Audit:Interactive:Button:Back' => '返回审计', 'UI:Audit:Interactive:Button:Configuration' => '审计配置', - 'UI:Audit:ViewRules' => '检查规则', + 'UI:Audit:ViewRules' => '检查规则', 'UI:Audit:HeaderAuditRule' => '审计规则', 'UI:Audit:HeaderNbObjects' => '# 对象', 'UI:Audit:HeaderNbErrors' => '# 错误', @@ -739,7 +739,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Audit:Dashboard:ObjectsValidated' => '对象已验证', 'UI:Audit:AuditCategory:Subtitle' => '%2$s个中有%1$s个错误 - %3$s%%', - 'UI:RunQuery:Title' => ITOP_APPLICATION_SHORT.' - OQL查询评估', + 'UI:RunQuery:Title' => ITOP_APPLICATION_SHORT.' - OQL查询评估', 'UI:RunQuery:QueryExamples' => '示例查询', 'UI:RunQuery:QueryResults' => '查询结果', 'UI:RunQuery:HeaderPurpose' => '目的', @@ -888,9 +888,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Delect:Confirm_Count_ObjectsOf_Class' => '请确认要删除下列%2$s类型的%1$d个对象.', 'UI:Delete:Confirm_Object' => '请确认要删除%1$s.', 'UI:Delete:Confirm_Count_ObjectsOf_Class' => '请确认要删除下列%2$s类型的%1$d个对象.', - 'UI:WelcomeToITop' => '欢迎使用'.ITOP_APPLICATION, - 'UI:DetailsPageTitle' => ITOP_APPLICATION_SHORT.' - %1$s - %2$s 详细内容', - 'UI:ErrorPageTitle' => ITOP_APPLICATION_SHORT.' - 错误', + 'UI:WelcomeToITop' => '欢迎使用'.ITOP_APPLICATION, + 'UI:DetailsPageTitle' => ITOP_APPLICATION_SHORT.' - %1$s - %2$s 详细内容', + 'UI:ErrorPageTitle' => ITOP_APPLICATION_SHORT.' - 错误', 'UI:ObjectDoesNotExist' => '抱歉, 此对象不存在 (或无权浏览此对象).', 'UI:ObjectArchived' => '对象已被归档. 请启用归档模式或联系管理员.', 'Tag:Archived' => '已归档', @@ -930,7 +930,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:FatalErrorMessage' => '致命错误, '.ITOP_APPLICATION_SHORT.'无法继续.', 'UI:Error_Details' => '错误: %1$s.', - 'UI:PageTitle:ProfileProjections' => ITOP_APPLICATION_SHORT.'用户管理 - 角色映射', + 'UI:PageTitle:ProfileProjections' => ITOP_APPLICATION_SHORT.'用户管理 - 角色映射', 'UI:UserManagement:Class' => '类型', 'UI:UserManagement:Class+' => '对象的类型', 'UI:UserManagement:ProjectedObject' => '对象', @@ -965,52 +965,52 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:UserManagement:NoLifeCycleApplicable+' => '此类型未定义生命周期', 'UI:UserManagement:GrantMatrix' => '授权矩阵', - 'Menu:SystemTools' => '系统', + 'Menu:SystemTools' => '系统', - 'UI:ChangeManagementMenu' => '变更管理', + 'UI:ChangeManagementMenu' => '变更管理', 'UI:ChangeManagementMenu+' => '变更管理', 'UI:ChangeManagementMenu:Title' => '变更概况', 'UI-ChangeManagementMenu-ChangesByType' => '按类别划分的变更', 'UI-ChangeManagementMenu-ChangesByStatus' => '按状态划分的变更', 'UI-ChangeManagementMenu-ChangesNotYetAssigned' => '尚未分配的变更', - 'UI:ConfigurationManagementMenu' => '配置管理', + 'UI:ConfigurationManagementMenu' => '配置管理', 'UI:ConfigurationManagementMenu+' => '配置管理', 'UI:ConfigurationManagementMenu:Title' => '基础架构概况', 'UI-ConfigurationManagementMenu-InfraByType' => '按类别划分基础架构对象', 'UI-ConfigurationManagementMenu-InfraByStatus' => '按状态划分基础架构对象', - 'UI:ConfigMgmtMenuOverview:Title' => '配置管理仪表盘', + 'UI:ConfigMgmtMenuOverview:Title' => '配置管理仪表盘', 'UI-ConfigMgmtMenuOverview-FunctionalCIbyStatus' => '按状态配置项目', 'UI-ConfigMgmtMenuOverview-FunctionalCIByType' => '按类别配置项目', - 'UI:RequestMgmtMenuOverview:Title' => '需求管理仪表盘', + 'UI:RequestMgmtMenuOverview:Title' => '需求管理仪表盘', 'UI-RequestManagementOverview-RequestByService' => '按服务划分需求', 'UI-RequestManagementOverview-RequestByPriority' => '按优先级划分需求', 'UI-RequestManagementOverview-RequestUnassigned' => '尚未分配给办理人的需求', - 'UI:IncidentMgmtMenuOverview:Title' => '事件管理仪表盘', + 'UI:IncidentMgmtMenuOverview:Title' => '事件管理仪表盘', 'UI-IncidentManagementOverview-IncidentByService' => '按服务级划分事件', 'UI-IncidentManagementOverview-IncidentByPriority' => '按优先级划分事件', 'UI-IncidentManagementOverview-IncidentUnassigned' => '尚未分配给办理人的事件', - 'UI:ChangeMgmtMenuOverview:Title' => '变更管理仪表盘', + 'UI:ChangeMgmtMenuOverview:Title' => '变更管理仪表盘', 'UI-ChangeManagementOverview-ChangeByType' => '按类别划分变更', 'UI-ChangeManagementOverview-ChangeUnassigned' => '尚未分配给办理人的变更', 'UI-ChangeManagementOverview-ChangeWithOutage' => '变更引起的停机', - 'UI:ServiceMgmtMenuOverview:Title' => '服务管理仪表盘', + 'UI:ServiceMgmtMenuOverview:Title' => '服务管理仪表盘', 'UI-ServiceManagementOverview-CustomerContractToRenew' => '客户合同需在30日内更新', 'UI-ServiceManagementOverview-ProviderContractToRenew' => '供应商合同需在30日内更新', - 'UI:ContactsMenu' => '联系人', + 'UI:ContactsMenu' => '联系人', 'UI:ContactsMenu+' => '联系人', 'UI:ContactsMenu:Title' => '联系人概况', 'UI-ContactsMenu-ContactsByLocation' => '按地点划分联系人', 'UI-ContactsMenu-ContactsByType' => '按类别划分联系人', 'UI-ContactsMenu-ContactsByStatus' => '按状态划分联系人', - 'UI:NotificationsMenu:Title' => '配置通知', + 'UI:NotificationsMenu:Title' => '配置通知', 'UI:NotificationsMenu:Help' => '帮助', 'UI:NotificationsMenu:HelpContent' => '

在'.ITOP_APPLICATION_SHORT.'中, 通知可以被自定义. 它们基于两个对象集: 触发器和操作.

触发器定义了什么时候发送通知. '.ITOP_APPLICATION_SHORT.'内核自带一些触发器, 另一些触发器可由扩展提供: @@ -1042,22 +1042,22 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:NotificationsMenu:Actions:Action' => '其它操作', 'UI:NotificationsMenu:AvailableActions' => '有效的操作', - 'Menu:TagAdminMenu' => '标签配置', + 'Menu:TagAdminMenu' => '标签配置', 'Menu:TagAdminMenu+' => '标签值管理', 'UI:TagAdminMenu:Title' => '标签配置', 'UI:TagAdminMenu:NoTags' => '未配置标签', 'UI:TagSetFieldData:Error' => '错误: %1$s', - 'UI:iTopVersion:Short' => '%1$s 版本 %2$s', + 'UI:iTopVersion:Short' => '%1$s 版本 %2$s', 'UI:iTopVersion:Long' => '%1$s 版本 %2$s-%3$s 创建于 %4$s', 'UI:PropertiesTab' => '属性', - 'UI:OpenDocumentInNewWindow_' => '打开', + 'UI:OpenDocumentInNewWindow_' => '打开', 'UI:DownloadDocument_' => '下载', 'UI:Document:NoPreview' => '此类型文档无法预览', 'UI:Download-CSV' => '下载 %1$s', - 'UI:DeadlineMissedBy_duration' => '超过 %1$s', + 'UI:DeadlineMissedBy_duration' => '超过 %1$s', 'UI:Deadline_LessThan1Min' => '< 1分钟', 'UI:Deadline_Minutes' => '%1$d分钟', 'UI:Deadline_Hours_Minutes' => '%1$dh %2$dmin', @@ -1179,7 +1179,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:ArchiveMode:Banner+' => '已归档的对象可见但不允许修改', 'UI:FavoriteOrganizations' => '快速访问', 'UI:FavoriteOrganizations+' => '进入组织下的列表,可实现通过下拉菜单快速访问.'. - '请注意,这并不是一个安全设置, 其它组织的对象依然可以通过选择 "所有组织" 下拉列表看到.', + '请注意,这并不是一个安全设置, 其它组织的对象依然可以通过选择 "所有组织" 下拉列表看到.', 'UI:FavoriteLanguage' => '语言', 'UI:Favorites:SelectYourLanguage' => '选择语言', 'UI:FavoriteOtherSettings' => '其它设置', @@ -1203,7 +1203,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Toggle:SwitchToStandardDashboard' => '切换到标准仪表盘', 'UI:Toggle:SwitchToCustomDashboard' => '切换到自定义仪表盘', - 'UI:ConfigureThisList' => '配置列表...', + 'UI:ConfigureThisList' => '配置列表...', 'UI:ListConfigurationTitle' => '列表配置', 'UI:ColumnsAndSortOrder' => '列和排序顺序:', 'UI:UseDefaultSettings' => '使用默认配置', @@ -1219,10 +1219,10 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Button:MoveUp' => '上移', 'UI:Button:MoveDown' => '下移', - 'UI:OQL:UnknownClassAndFix' => '未知类型 "%1$s". 您可以试试 "%2$s".', + 'UI:OQL:UnknownClassAndFix' => '未知类型 "%1$s". 您可以试试 "%2$s".', 'UI:OQL:UnknownClassNoFix' => '未知类型 "%1$s"', - 'UI:Dashboard:EditCustom' => '编辑自定义版本...', + 'UI:Dashboard:EditCustom' => '编辑自定义版本...', 'UI:Dashboard:CreateCustom' => '新建自定义版本...', 'UI:Dashboard:DeleteCustom' => '删除自定义版本...', 'UI:Dashboard:RevertConfirm' => '每个地方都会恢复到初始版本. 请确认您要这样做.', @@ -1238,7 +1238,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:DashletCreation:DashletType' => '仪表类型', 'UI:DashletCreation:EditNow' => '编辑仪表盘', - 'UI:DashboardEdit:Title' => '仪表盘编辑器', + 'UI:DashboardEdit:Title' => '仪表盘编辑器', 'UI:DashboardEdit:DashboardTitle' => '标题', 'UI:DashboardEdit:AutoReload' => '自动刷新', 'UI:DashboardEdit:AutoReloadSec' => '自动刷新间隔 (秒)', @@ -1246,38 +1246,38 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:DashboardEdit:Revert' => '撤回', 'UI:DashboardEdit:Apply' => '应用', - 'UI:DashboardEdit:Layout' => '布局', + 'UI:DashboardEdit:Layout' => '布局', 'UI:DashboardEdit:Properties' => '仪表盘属性', 'UI:DashboardEdit:Dashlets' => '可用的仪表', 'UI:DashboardEdit:DashletProperties' => '仪表属性', - 'UI:Form:Property' => '属性', + 'UI:Form:Property' => '属性', 'UI:Form:Value' => '值', - 'UI:DashletUnknown:Label' => '未知', + 'UI:DashletUnknown:Label' => '未知', 'UI:DashletUnknown:Description' => '未知仪表盘 (可能已卸载)', 'UI:DashletUnknown:RenderText:View' => '此仪表盘无法渲染.', 'UI:DashletUnknown:RenderText:Edit' => '此仪表盘无法渲染 (类型 "%1$s"). 请与管理员确认其可用.', 'UI:DashletUnknown:RenderNoDataText:Edit' => '此仪表盘无预览 (类型 "%1$s").', 'UI:DashletUnknown:Prop-XMLConfiguration' => '配置 (显示为纯 XML)', - 'UI:DashletProxy:Label' => '代理', + 'UI:DashletProxy:Label' => '代理', 'UI:DashletProxy:Description' => '代理仪表', 'UI:DashletProxy:RenderNoDataText:Edit' => '第三方组件无法预览 (类型 "%1$s").', 'UI:DashletProxy:Prop-XMLConfiguration' => '配置 (显示为纯 XML)', - 'UI:DashletPlainText:Label' => '文本', + 'UI:DashletPlainText:Label' => '文本', 'UI:DashletPlainText:Description' => '纯文本 (无格式)', 'UI:DashletPlainText:Prop-Text' => '内容', 'UI:DashletPlainText:Prop-Text:Default' => '请在这里输入内容...', - 'UI:DashletObjectList:Label' => '对象列表', + 'UI:DashletObjectList:Label' => '对象列表', 'UI:DashletObjectList:Description' => '对象列表仪表盘', 'UI:DashletObjectList:Prop-Title' => '标题', 'UI:DashletObjectList:Prop-Query' => '查询', 'UI:DashletObjectList:Prop-Menu' => '菜单', - 'UI:DashletGroupBy:Prop-Title' => '标题', + 'UI:DashletGroupBy:Prop-Title' => '标题', 'UI:DashletGroupBy:Prop-Query' => '查询', 'UI:DashletGroupBy:Prop-Style' => '样式', 'UI:DashletGroupBy:Prop-GroupBy' => '分组...', @@ -1288,28 +1288,28 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:DashletGroupBy:Prop-GroupBy:Select-Hour' => '%1$s (小时)', 'UI:DashletGroupBy:Prop-GroupBy:Select-Month' => '%1$s (月)', 'UI:DashletGroupBy:Prop-GroupBy:Select-Year' => '%1$s (年)', - 'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek' => '%1$s (星期数)', + 'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek' => '%1$s (星期数)', 'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth' => '%1$s (月天数)', 'UI:DashletGroupBy:MissingGroupBy' => '请选择对象的分组字段', - 'UI:DashletGroupByPie:Label' => '饼图', + 'UI:DashletGroupByPie:Label' => '饼图', 'UI:DashletGroupByPie:Description' => '饼图', 'UI:DashletGroupByBars:Label' => '柱状图', 'UI:DashletGroupByBars:Description' => '柱状图', 'UI:DashletGroupByTable:Label' => '分组 (表)', 'UI:DashletGroupByTable:Description' => '列表 (以字段分组)', - // New in 2.5 - 'UI:DashletGroupBy:Prop-Function' => '聚合函数', + // New in 2.5 + 'UI:DashletGroupBy:Prop-Function' => '聚合函数', 'UI:DashletGroupBy:Prop-FunctionAttribute' => '函数属性', 'UI:DashletGroupBy:Prop-OrderDirection' => '方向', 'UI:DashletGroupBy:Prop-OrderField' => '排序', 'UI:DashletGroupBy:Prop-Limit' => '限制', - 'UI:DashletGroupBy:Order:asc' => '升序', + 'UI:DashletGroupBy:Order:asc' => '升序', 'UI:DashletGroupBy:Order:desc' => '降序', - 'UI:GroupBy:count' => '个数', + 'UI:GroupBy:count' => '个数', 'UI:GroupBy:count+' => '项目数', 'UI:GroupBy:sum' => '总数', 'UI:GroupBy:sum+' => '%1$s 之和', @@ -1328,7 +1328,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:DashletHeaderStatic:Prop-Icon' => '图标', 'UI:DashletIcon:None' => '无', - 'UI:DashletHeaderDynamic:Label' => '统计标头', + 'UI:DashletHeaderDynamic:Label' => '统计标头', 'UI:DashletHeaderDynamic:Description' => '统计标头 (分组...)', 'UI:DashletHeaderDynamic:Prop-Title' => '标题', 'UI:DashletHeaderDynamic:Prop-Title:Default' => '联系人', @@ -1339,11 +1339,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:DashletHeaderDynamic:Prop-GroupBy' => '分组依据', 'UI:DashletHeaderDynamic:Prop-Values' => '值', - 'UI:DashletBadge:Label' => '证件', + 'UI:DashletBadge:Label' => '证件', 'UI:DashletBadge:Description' => '新建/查询的对象图标', 'UI:DashletBadge:Prop-Class' => '类型', - 'DayOfWeek-Sunday' => '周日', + 'DayOfWeek-Sunday' => '周日', 'DayOfWeek-Monday' => '周一', 'DayOfWeek-Tuesday' => '周二', 'DayOfWeek-Wednesday' => '周三', @@ -1363,8 +1363,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Month-11' => '十一月', 'Month-12' => '十二月', - // Short version for the DatePicker - 'DayOfWeek-Sunday-Min' => '日', + // Short version for the DatePicker + 'DayOfWeek-Sunday-Min' => '日', 'DayOfWeek-Monday-Min' => '一', 'DayOfWeek-Tuesday-Min' => '二', 'DayOfWeek-Wednesday-Min' => '三', @@ -1386,7 +1386,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Calendar-FirstDayOfWeek' => '0', // 0 = Sunday, 1 = Monday, etc... - 'UI:Menu:ShortcutList' => '创建快捷方式...', + 'UI:Menu:ShortcutList' => '创建快捷方式...', 'UI:Menu:FilterList' => '显示条件查询列表', 'UI:ShortcutRenameDlg:Title' => '重命名快捷方式', 'UI:ShortcutListDlg:Title' => '为此列表创建快捷方式', @@ -1406,12 +1406,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:ShortcutOQL/Attribute:auto_reload_sec' => '自动刷新间隔 (秒)', 'Class:ShortcutOQL/Attribute:auto_reload_sec/tip' => '最小值是 %1$d 秒', - 'UI:FillAllMandatoryFields' => '请填写所有的必填项.', + 'UI:FillAllMandatoryFields' => '请填写所有的必填项.', 'UI:ValueMustBeSet' => '必填', 'UI:ValueMustBeChanged' => '必须修改这个值', 'UI:ValueInvalidFormat' => '格式无效', - 'UI:CSVImportConfirmTitle' => '请确认本次操作', + 'UI:CSVImportConfirmTitle' => '请确认本次操作', 'UI:CSVImportConfirmMessage' => '请确认是否继续?', 'UI:CSVImportError_items' => '错误: %1$d', 'UI:CSVImportCreated_items' => '创建: %1$d', @@ -1441,22 +1441,22 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:AddAnExisting_Class' => '添加 %1$s...', 'UI:SelectionOf_Class' => '选择 %1$s', - 'UI:AboutBox' => '关于'.ITOP_APPLICATION_SHORT.'...', + 'UI:AboutBox' => '关于'.ITOP_APPLICATION_SHORT.'...', 'UI:About:Title' => '关于'.ITOP_APPLICATION_SHORT, 'UI:About:DataModel' => '数据模型', 'UI:About:DataModel' => '数据模型', - 'UI:About:Support' => '支持信息', + 'UI:About:Support' => '支持信息', 'UI:About:Licenses' => '许可证', 'UI:About:InstallationOptions' => '已安装的模块', 'UI:About:ManualExtensionSource' => '扩展', 'UI:About:Extension_Version' => '版本: %1$s', 'UI:About:RemoteExtensionSource' => '数据', - 'UI:DisconnectedDlgMessage' => '您已断开. 要继续使用,需要重新验证您的用户名和密码.', + 'UI:DisconnectedDlgMessage' => '您已断开. 要继续使用,需要重新验证您的用户名和密码.', 'UI:DisconnectedDlgTitle' => '警告!', 'UI:LoginAgain' => '再次登录', 'UI:StayOnThePage' => '保持在当前页面', - 'ExcelExporter:ExportMenu' => 'Excel 导出...', + 'ExcelExporter:ExportMenu' => 'Excel 导出...', 'ExcelExporter:ExportDialogTitle' => 'Excel 导出', 'ExcelExporter:ExportButton' => '导出', 'ExcelExporter:DownloadButton' => '下载 %1$s', @@ -1469,7 +1469,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'portal:legacy_portal' => '终端用户门户', 'portal:backoffice' => ITOP_APPLICATION_SHORT.'后台管理界面', - 'UI:CurrentObjectIsLockedBy_User' => '对象被锁定, 因为正在修改%1$s.', + 'UI:CurrentObjectIsLockedBy_User' => '对象被锁定, 因为正在修改%1$s.', 'UI:CurrentObjectIsLockedBy_User_Explanation' => '此对象正在被%1$s修改. 您的修改无法提交以避免冲突.', 'UI:CurrentObjectIsSoftLockedBy_User' => '此对象当前正在被%1$s修改. 在其修改结束后您将能提交您的修改.', 'UI:CurrentObjectLockExpired' => '并发修改的锁定期已过.', @@ -1477,44 +1477,44 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:ConcurrentLockKilled' => '阻止并发修改当前对象的锁定已被删除.', 'UI:Menu:KillConcurrentLock' => '消除并发修改锁定!', - 'UI:Menu:ExportPDF' => '导出PDF...', + 'UI:Menu:ExportPDF' => '导出PDF...', 'UI:Menu:PrintableVersion' => '打印友好的版本', - 'UI:BrowseInlineImages' => '浏览图片...', + 'UI:BrowseInlineImages' => '浏览图片...', 'UI:UploadInlineImageLegend' => '上传新图片', 'UI:SelectInlineImageToUpload' => '选择要上传的图片', 'UI:AvailableInlineImagesLegend' => '可用的图片', 'UI:NoInlineImage' => '服务器上没有图片. 使用上面的 "浏览" 按钮, 从您的电脑上选择并上传到服务器.', 'UI:MissingInlineImage' => '缺少图片', - 'UI:ToggleFullScreen' => '切换最大化/最小化', + 'UI:ToggleFullScreen' => '切换最大化/最小化', 'UI:Button:ResetImage' => '恢复之前的图片', 'UI:Button:RemoveImage' => '移除图片', 'UI:Button:UploadImage' => '从硬盘上传图像', 'UI:UploadNotSupportedInThisMode' => '本模式下不支持修改文件或图片.', - 'UI:Button:RemoveDocument' => '移除文档', + 'UI:Button:RemoveDocument' => '移除文档', - // Search form - 'UI:Search:Toggle' => '折叠/展开', + // Search form + 'UI:Search:Toggle' => '折叠/展开', 'UI:Search:AutoSubmit:DisabledHint' => '此类别已禁用自动提交', 'UI:Search:Obsolescence:DisabledHint' => '根据您的设置, 废弃的数据会被隐藏', 'UI:Search:NoAutoSubmit:ExplainText' => '在搜索框中添加规则, 或者单击对象按钮查看对象.', 'UI:Search:Criterion:MoreMenu:AddCriteria' => '添加条件', // - Add new criteria button - 'UI:Search:AddCriteria:List:RecentlyUsed:Title' => '最近使用', + 'UI:Search:AddCriteria:List:RecentlyUsed:Title' => '最近使用', 'UI:Search:AddCriteria:List:MostPopular:Title' => '最常用', 'UI:Search:AddCriteria:List:Others:Title' => '其它', 'UI:Search:AddCriteria:List:RecentlyUsed:Placeholder' => '还没有.', - // - Criteria header actions - 'UI:Search:Criteria:Toggle' => '折叠/展开', + // - Criteria header actions + 'UI:Search:Criteria:Toggle' => '折叠/展开', 'UI:Search:Criteria:Remove' => '移除', 'UI:Search:Criteria:Locked' => '已锁定', // - Criteria titles // - Default widget - 'UI:Search:Criteria:Title:Default:Any' => '%1$s: 任何', + 'UI:Search:Criteria:Title:Default:Any' => '%1$s: 任何', 'UI:Search:Criteria:Title:Default:Empty' => '%1$s 为空', 'UI:Search:Criteria:Title:Default:NotEmpty' => '%1$s 不为空', 'UI:Search:Criteria:Title:Default:Equals' => '%1$s 等于 %2$s', @@ -1538,22 +1538,22 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ // - Numeric widget // None yet // - DateTime widget - 'UI:Search:Criteria:Title:DateTime:Between' => '%2$s <= 1$s <= %3$s', + 'UI:Search:Criteria:Title:DateTime:Between' => '%2$s <= 1$s <= %3$s', // - Enum widget - 'UI:Search:Criteria:Title:Enum:In' => '%1$s: %2$s', + 'UI:Search:Criteria:Title:Enum:In' => '%1$s: %2$s', 'UI:Search:Criteria:Title:Enum:In:Many' => '%1$s: %2$s 和 %3$s 其他', 'UI:Search:Criteria:Title:Enum:In:All' => '%1$s: 任何', // - TagSet widget - 'UI:Search:Criteria:Title:TagSet:Matches' => '%1$s: %2$s', + 'UI:Search:Criteria:Title:TagSet:Matches' => '%1$s: %2$s', // - External key widget - 'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s 已定义', + 'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s 已定义', 'UI:Search:Criteria:Title:ExternalKey:NotEmpty' => '%1$s 未定义', 'UI:Search:Criteria:Title:ExternalKey:Equals' => '%1$s %2$s', 'UI:Search:Criteria:Title:ExternalKey:In' => '%1$s: %2$s', 'UI:Search:Criteria:Title:ExternalKey:In:Many' => '%1$s: %2$s 和 %3$s 其他', 'UI:Search:Criteria:Title:ExternalKey:In:All' => '%1$s: 任何', // - Hierarchical key widget - 'UI:Search:Criteria:Title:HierarchicalKey:Empty' => '%1$s 已定义', + 'UI:Search:Criteria:Title:HierarchicalKey:Empty' => '%1$s 已定义', 'UI:Search:Criteria:Title:HierarchicalKey:NotEmpty' => '%1$s 未定义', 'UI:Search:Criteria:Title:HierarchicalKey:Equals' => '%1$s %2$s', 'UI:Search:Criteria:Title:HierarchicalKey:In' => '%1$s: %2$s', @@ -1561,28 +1561,28 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Search:Criteria:Title:HierarchicalKey:In:All' => '%1$s: 任何', // - Criteria operators - // - Default widget - 'UI:Search:Criteria:Operator:Default:Empty' => '为空', + // - Default widget + 'UI:Search:Criteria:Operator:Default:Empty' => '为空', 'UI:Search:Criteria:Operator:Default:NotEmpty' => '非空', 'UI:Search:Criteria:Operator:Default:Equals' => '等于', 'UI:Search:Criteria:Operator:Default:Between' => '之间', // - String widget - 'UI:Search:Criteria:Operator:String:Contains' => '包含', + 'UI:Search:Criteria:Operator:String:Contains' => '包含', 'UI:Search:Criteria:Operator:String:StartsWith' => '起始为', 'UI:Search:Criteria:Operator:String:EndsWith' => '结尾是', 'UI:Search:Criteria:Operator:String:RegExp' => '正则表达式.', // - Numeric widget - 'UI:Search:Criteria:Operator:Numeric:Equals' => '等于', + 'UI:Search:Criteria:Operator:Numeric:Equals' => '等于', 'UI:Search:Criteria:Operator:Numeric:GreaterThan' => '大于', 'UI:Search:Criteria:Operator:Numeric:GreaterThanOrEquals' => '大于/等于', 'UI:Search:Criteria:Operator:Numeric:LessThan' => '小于', 'UI:Search:Criteria:Operator:Numeric:LessThanOrEquals' => '小于/等于', 'UI:Search:Criteria:Operator:Numeric:Different' => '不同', // - Tag Set Widget - 'UI:Search:Criteria:Operator:TagSet:Matches' => '匹配', + 'UI:Search:Criteria:Operator:TagSet:Matches' => '匹配', - // - Other translations - 'UI:Search:Value:Filter:Placeholder' => '筛选器...', + // - Other translations + 'UI:Search:Value:Filter:Placeholder' => '筛选器...', 'UI:Search:Value:Search:Placeholder' => '搜索...', 'UI:Search:Value:Autocomplete:StartTyping' => '开始输入值.', 'UI:Search:Value:Autocomplete:Wait' => '请稍候...', @@ -1591,7 +1591,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Search:Value:Toggler:CheckAllNoneFiltered' => '全选/全不选', // - Widget other translations - 'UI:Search:Criteria:Numeric:From' => '从', + 'UI:Search:Criteria:Numeric:From' => '从', 'UI:Search:Criteria:Numeric:Until' => '到', 'UI:Search:Criteria:Numeric:PlaceholderFrom' => '任何', 'UI:Search:Criteria:Numeric:PlaceholderUntil' => '任何', @@ -1605,12 +1605,12 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Search:Criteria:DateTime:PlaceholderUntilTime' => '任何日期', 'UI:Search:Criteria:HierarchicalKey:ChildrenIncluded:Hint' => '将会包含选中对象的子集.', - 'UI:Search:Criteria:Raw:Filtered' => '已筛选', + 'UI:Search:Criteria:Raw:Filtered' => '已筛选', 'UI:Search:Criteria:Raw:FilteredOn' => '基于%1$s筛选', 'UI:StateChanged' => '状态已改变', - 'UI:AddSubTree' => '添加条目', + 'UI:AddSubTree' => '添加条目', ]); // @@ -1621,16 +1621,16 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Expression:Operator:OR' => ' 或 ', 'Expression:Operator:=' => ': ', - 'Expression:Unit:Short:DAY' => '日', + 'Expression:Unit:Short:DAY' => '日', 'Expression:Unit:Short:WEEK' => '周', 'Expression:Unit:Short:MONTH' => '月', 'Expression:Unit:Short:YEAR' => '年', - 'Expression:Unit:Long:DAY' => '日', + 'Expression:Unit:Long:DAY' => '日', 'Expression:Unit:Long:HOUR' => '小时', 'Expression:Unit:Long:MINUTE' => '分钟', - 'Expression:Verb:NOW' => '现在', + 'Expression:Verb:NOW' => '现在', 'Expression:Verb:ISNULL' => ': 未定义', ]); From 3426b927689b6c2a2982e165c99e757d0b7e7f01 Mon Sep 17 00:00:00 2001 From: jf-cbd Date: Mon, 8 Jun 2026 14:04:01 +0200 Subject: [PATCH 04/36] Fix unit test --- .../dictionaries/zh_cn.dict.itop-config-mgmt.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php b/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php index 2e7ad00f68..df2908ba82 100644 --- a/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php +++ b/datamodels/2.x/itop-config-mgmt/dictionaries/zh_cn.dict.itop-config-mgmt.php @@ -1415,7 +1415,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:lnkPhysicalInterfaceToVLAN' => '链接 物理网卡/VLAN', - 'Class:lnkPhysicalInterfaceToVLAN+' => 'This link indicates when a network interface is part of a VLAN (虚拟局域网).~~', + 'Class:lnkPhysicalInterfaceToVLAN+' => 'This link indicates when a network interface is part of a VLAN (虚拟局域网).', 'Class:lnkPhysicalInterfaceToVLAN/Name' => '%1$s %2$s / %3$s', 'Class:lnkPhysicalInterfaceToVLAN/Attribute:physicalinterface_id' => '物理网卡', 'Class:lnkPhysicalInterfaceToVLAN/Attribute:physicalinterface_id+' => '', From a419914236af29e74a9c66b207ad62093338819d Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 8 Jun 2026 17:13:49 +0200 Subject: [PATCH 05/36] =?UTF-8?q?N=C2=B09412=20-=20fix=20uninstall=20optio?= =?UTF-8?q?n=20and=20others=20when=20going=20to=20setup=20from=20extension?= =?UTF-8?q?=20mgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataFeatureRemovalController.php | 15 ++++++-- setup/extensionsmap.class.inc.php | 1 - .../xml_setup/fresh-install.xml | 14 ++++---- setup/wizardsteps/WizStepDataAudit.php | 2 ++ .../wizardsteps/WizStepLandingBeforeAudit.php | 3 -- .../DataFeatureControllerTest.php | 35 +++++++++++++++++++ .../simulate-audit-from-setup.php | 8 +++-- 7 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/DataFeatureControllerTest.php diff --git a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php index bac4fb5362..77cfabeb82 100644 --- a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php +++ b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php @@ -25,6 +25,7 @@ use CoreException; use Dict; use Exception; use MetaModel; +use MFCompiler; use RunTimeEnvironment; use SecurityException; use SetupUtils; @@ -102,6 +103,8 @@ class DataFeatureRemovalController extends Controller 'removed_extensions' => '[]', 'extensions_not_uninstallable' => '[]', 'copy_setup_files' => 1, + 'force-uninstall' => "", + 'use_symbolic_links' => MFCompiler::UseSymbolicLinks() ? 'on' : '', 'return_button_label' => '', ]; @@ -122,6 +125,7 @@ class DataFeatureRemovalController extends Controller //it does not come from setup // we get extensions from 1st screen uiblocks $this->ReadExtensionsDiff(); + $aHiddenInputs['force-uninstall'] = $this->bForcedUninstallation ? 'on' : ''; $aAddedExtensions = $this->aExtensionsToCheck['to_be_installed']; $aHiddenInputs['added_extensions'] = $this->ConvertIntoSetupFormat($aAddedExtensions); @@ -158,7 +162,7 @@ class DataFeatureRemovalController extends Controller if ("[]" === $aHiddenInputs['selected_modules']) { //to make setup redirection work, we need to pass complex data structures to setup wizards (ie extension/module lists) $oConfig = MetaModel::GetConfig(); - $aSelectedExtensions = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap()->GetSelectedExtensions($oConfig, $aAddedExtensions, $aRemovedExtensions); + $aSelectedExtensions = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap()->GetSelectedExtensions($oConfig, array_keys($aAddedExtensions), array_keys($aRemovedExtensions)); $aHiddenInputs['selected_extensions'] = $this->ConvertIntoSetupFormat($aSelectedExtensions); $oRunTimeEnvironment = $this->GetRuntimeEnvironment($aRemovedExtensions); @@ -200,7 +204,12 @@ class DataFeatureRemovalController extends Controller private function ConvertIntoSetupFormat(array $aData): string { - return json_encode($aData); + $aNewData = []; + foreach ($aData as $k => $sVal) { + $aNewData[] = sprintf('"%s":"%s"', $k, $sVal); + } + + return "{".implode(',', $aNewData)."}"; } /** @@ -426,7 +435,7 @@ class DataFeatureRemovalController extends Controller $aExtensionData['extra_flags']['selected'] = false; $sLabel = $aAvailableExtensions[$sCode]['label']; $this->aExtensionsToCheck['to_be_removed'][$sCode] = $sLabel; - if (!$aExtensionData['extra_flags']['uninstallable'] || $aExtensionData['extra_flags']['remote']) { + if (! $this->bForcedUninstallation && $aExtensionData['extra_flags']['uninstallable']) { $this->bForcedUninstallation = true; } } elseif (!$aExtensionData['installed'] && $aSelectedExtensionsFromUI[$sCode] === 'on') { diff --git a/setup/extensionsmap.class.inc.php b/setup/extensionsmap.class.inc.php index f8388ead9a..9ab8410986 100644 --- a/setup/extensionsmap.class.inc.php +++ b/setup/extensionsmap.class.inc.php @@ -736,7 +736,6 @@ class iTopExtensionsMap public function GetSelectedExtensions(Config $oConfig, array $aAddedExtensions, array $aRemovedExtensions): array { $aDbChoices = self::GetChoicesFromDatabase($oConfig); - foreach ($aDbChoices as $i => $sChoice) { if (in_array($sChoice, $aRemovedExtensions)) { unset($aDbChoices[$i]); diff --git a/setup/unattended-install/xml_setup/fresh-install.xml b/setup/unattended-install/xml_setup/fresh-install.xml index f44b7cdb80..97146edbc2 100644 --- a/setup/unattended-install/xml_setup/fresh-install.xml +++ b/setup/unattended-install/xml_setup/fresh-install.xml @@ -13,10 +13,10 @@ production - - - - + localhost + iTop + blob99 + configcomments @@ -24,9 +24,9 @@ /usr/bin/dot - - - + admin + admin + FR FR diff --git a/setup/wizardsteps/WizStepDataAudit.php b/setup/wizardsteps/WizStepDataAudit.php index fe8e2bfe51..71c92f823b 100644 --- a/setup/wizardsteps/WizStepDataAudit.php +++ b/setup/wizardsteps/WizStepDataAudit.php @@ -113,6 +113,8 @@ JS); 'extensions_not_uninstallable' => '[]', 'copy_setup_files' => 1, 'return_button_label' => '', + 'force-uninstall' => "", + 'use_symbolic_links' => "", ]; $aHiddenInputs = ''; foreach ($aParams as $sParamName => $defaultValue) { diff --git a/setup/wizardsteps/WizStepLandingBeforeAudit.php b/setup/wizardsteps/WizStepLandingBeforeAudit.php index d4bb4aa10e..1c5317ea8a 100644 --- a/setup/wizardsteps/WizStepLandingBeforeAudit.php +++ b/setup/wizardsteps/WizStepLandingBeforeAudit.php @@ -21,9 +21,6 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice $oWizard->SetParameter('datamodel_version', ITOP_CORE_VERSION); $oWizard->SetParameter('upgrade_type', 'use-compatible'); - $oWizard->SaveParameter('use_symbolic_links', MFCompiler::UseSymbolicLinks()); - $oWizard->SaveParameter('force-uninstall', ''); - // should be done at the end parent::__construct($oWizard, $sCurrentState, false); } diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/DataFeatureControllerTest.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/DataFeatureControllerTest.php new file mode 100644 index 0000000000..2c1de6c9db --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/DataFeatureControllerTest.php @@ -0,0 +1,35 @@ +RequireOnceItopFile('env-production/combodo-data-feature-removal/vendor/autoload.php'); + } + + public function testConvertIntoSetupFormat() + { + $oController = new DataFeatureRemovalController(); + + $aExtensions = [ + 'itop-container-mgmt', + 'combodo-monitoring', + ]; + $expected = '{"0":"itop-container-mgmt","1":"combodo-monitoring"}'; + $this->assertEquals($expected, $this->InvokeNonPublicMethod(DataFeatureRemovalController::class, 'ConvertIntoSetupFormat', $oController, [ $aExtensions])); + } +} diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php index 34d0c7e181..8ed3a18ef4 100644 --- a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php @@ -34,7 +34,9 @@ function GetLastestInstallFile(): ?string return $sLastFilePath; } -$aRemovedExtensions = ['itop-container-mgmt' => 'Containerization']; +$aRemovedExtensions = [ + 'itop-container-mgmt' => 'Containerization', +]; $sPath = GetLastestInstallFile(); if (is_null($sPath)) { @@ -56,10 +58,12 @@ $aPostParams = [ "auth_pwd" => 'admin', 'login_mode' => 'form', 'operation' => 'AnalysisResult', - 'setup_token' => $sToken, + 'authent' => $sToken, 'selected_modules' => utils::HtmlEntities(json_encode($aSelectedModules)), 'selected_extensions' => utils::HtmlEntities(json_encode($aSelectedExtensions)), 'removed_extensions' => utils::HtmlEntities(json_encode($aRemovedExtensions)), + 'force-uninstall' => "on", + 'use_symbolic_links' => "", ]; $sHiddenPostedInput = ""; From 1a57a427c05af10f8f2ee72c3c2c98e2f0c1e679 Mon Sep 17 00:00:00 2001 From: Lenaick Date: Tue, 9 Jun 2026 14:29:06 +0200 Subject: [PATCH 06/36] =?UTF-8?q?N=C2=B09454=20-=20Make=20MTT=20compatible?= =?UTF-8?q?=20with=20the=20dry=20run=20(#931)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * N°9454 - Make MTT compatible with the dry run * N°9454 - Make MTT compatible with the dry run * N°9454 - Cleaning up configuration handling * N°9454 - Cleanup code * N°9454 - Remove writable directories * Update setup/wizardsteps/WizStepLandingBeforeAudit.php Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update setup/runtimeenv.class.inc.php Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * N°9454 - Secure target environment wizard step done form submission --------- Co-authored-by: Eric Espie Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- core/metamodel.class.php | 10 +++-- .../DataFeatureRemovalController.php | 3 +- setup/compiler.class.inc.php | 34 ++++---------- .../DryRemovalRuntimeEnvironment.php | 2 +- setup/runtimeenv.class.inc.php | 33 +++++++------- setup/sequencers/StepSequencer.php | 7 +-- setup/setuputils.class.inc.php | 15 +++++-- .../InstallationFileService.php | 5 ++- setup/wizardsteps/AbstractWizStepInstall.php | 2 +- setup/wizardsteps/WizStepDataAudit.php | 7 +-- setup/wizardsteps/WizStepDone.php | 6 ++- .../wizardsteps/WizStepLandingBeforeAudit.php | 45 ++++++++++++------- setup/wizardsteps/WizStepModulesChoice.php | 8 ++-- 13 files changed, 94 insertions(+), 83 deletions(-) diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 9fe20e3c6f..540ae3d8e7 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -22,7 +22,6 @@ use Combodo\iTop\Application\EventRegister\ApplicationEvents; use Combodo\iTop\Core\MetaModel\FriendlyNameType; use Combodo\iTop\Service\Events\EventData; use Combodo\iTop\Service\Events\EventService; -use Combodo\iTop\Setup\ModuleDependency\Module; use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader; require_once APPROOT.'core/modulehandler.class.inc.php'; @@ -4660,12 +4659,15 @@ abstract class MetaModel * @throws \CoreException * @throws \MySQLException */ - public static function DBExists($bMustBeComplete = true) + public static function DBExists($bMustBeComplete = true, ?string $sDBName = null) { - if (!CMDBSource::IsDB(self::$m_sDBName)) { + if (is_null($sDBName)) { + $sDBName = self::$m_sDBName; + } + if (!CMDBSource::IsDB($sDBName)) { return false; } - CMDBSource::SelectDB(self::$m_sDBName); + CMDBSource::SelectDB($sDBName); $aFound = []; $aMissing = []; diff --git a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php index 77cfabeb82..c1880c8e8c 100644 --- a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php +++ b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php @@ -98,7 +98,7 @@ class DataFeatureRemovalController extends Controller $aHiddenInputNames = [ 'selected_extensions' => '[]', 'selected_modules' => '[]', - 'display_choices' => '[]', + 'display_choices' => '', 'added_extensions' => '[]', 'removed_extensions' => '[]', 'extensions_not_uninstallable' => '[]', @@ -106,6 +106,7 @@ class DataFeatureRemovalController extends Controller 'force-uninstall' => "", 'use_symbolic_links' => MFCompiler::UseSymbolicLinks() ? 'on' : '', 'return_button_label' => '', + 'target_env' => ITOP_DEFAULT_ENV, ]; $aHiddenInputs = []; diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index a51cd4862d..16a8d0992c 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -20,7 +20,6 @@ use Combodo\iTop\Application\Branding; use Combodo\iTop\Application\WebPage\iTopWebPage; -use Combodo\iTop\Application\WebPage\Page; use Combodo\iTop\DesignDocument; use Combodo\iTop\DesignElement; use Combodo\iTop\PropertyType\PropertyTypeDesign; @@ -264,14 +263,13 @@ class MFCompiler * Compile the data model into PHP files and data structures * * @param string $sTargetDir The target directory where to put the resulting files - * @param Page $oP For some output... * @param bool $bUseSymbolicLinks * @param bool $bSkipTempDir * * @return void * @throws Exception */ - public function Compile($sTargetDir, $oP = null, $bUseSymbolicLinks = null, $bSkipTempDir = false, $bEnterMaintenanceMode = true) + public function Compile($sTargetDir, $bUseSymbolicLinks = null, $bSkipTempDir = false) { if (is_null($bUseSymbolicLinks)) { $bUseSymbolicLinks = false; @@ -283,16 +281,7 @@ class MFCompiler } $sFinalTargetDir = $sTargetDir; - $bIsAlreadyInMaintenanceMode = SetupUtils::IsInMaintenanceMode(); - $sConfigFilePath = utils::GetConfigFilePath($this->sEnvironment); - if (is_file($sConfigFilePath)) { - $oConfig = new Config($sConfigFilePath); - } else { - $oConfig = null; - } - if (($this->sEnvironment == ITOP_DEFAULT_ENV) && !$bIsAlreadyInMaintenanceMode && $bEnterMaintenanceMode) { - SetupUtils::EnterMaintenanceMode($oConfig); - } + if ($bUseSymbolicLinks || $bSkipTempDir) { // Skip the creation of a temporary dictionary, not compatible with symbolic links $sTempTargetDir = $sFinalTargetDir; @@ -307,15 +296,13 @@ class MFCompiler } try { - $this->DoCompile($sTempTargetDir, $sFinalTargetDir, $oP = null, $bUseSymbolicLinks); + $this->DoCompile($sTempTargetDir, $sFinalTargetDir, $bUseSymbolicLinks); } catch (Exception $e) { if ($sTempTargetDir != $sFinalTargetDir) { // Cleanup the temporary directory SetupUtils::rrmdir($sTempTargetDir); } - if (($this->sEnvironment == ITOP_DEFAULT_ENV) && !$bIsAlreadyInMaintenanceMode && $bEnterMaintenanceMode) { - SetupUtils::ExitMaintenanceMode(); - } + throw $e; } @@ -323,9 +310,6 @@ class MFCompiler // Move the results to the target directory SetupUtils::movedir($sTempTargetDir, $sFinalTargetDir); } - if (($this->sEnvironment == ITOP_DEFAULT_ENV) && !$bIsAlreadyInMaintenanceMode && $bEnterMaintenanceMode) { - SetupUtils::ExitMaintenanceMode(); - } // Reset the opcache since otherwise the PHP "model" files may still be cached !! // In case of bad luck (this happens **sometimes** - see N. 550), we may analyze the database structure @@ -343,13 +327,14 @@ class MFCompiler /** * Perform the actual "Compilation" of all modules + * * @param string $sTempTargetDir * @param string $sFinalTargetDir - * @param Page $oP * @param bool $bUseSymbolicLinks + * * @throws Exception */ - protected function DoCompile($sTempTargetDir, $sFinalTargetDir, $oP = null, $bUseSymbolicLinks = false) + protected function DoCompile($sTempTargetDir, $sFinalTargetDir, $bUseSymbolicLinks = false) { $aAllClasses = []; // flat list of classes $aModulesInfo = []; // Hash array of module_name => array('version' => string, 'root_dir' => string) @@ -538,7 +523,7 @@ EOF; } try { /** @var iTopWebPage $oP */ - $aMenuLines = $this->CompileMenu($oMenuNode, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir, $oP); + $aMenuLines = $this->CompileMenu($oMenuNode, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir); } catch (DOMFormatException $e) { throw new Exception("Failed to process menu '$sMenuId', from '$sModuleRootDir': ".$e->getMessage()); } @@ -2581,13 +2566,12 @@ CSS; * @param string $sTempTargetDir * @param string $sFinalTargetDir * @param string $sModuleRelativeDir - * @param iTopWebPage $oP * * @return array * @throws \DOMException * @throws \DOMFormatException */ - protected function CompileMenu($oMenu, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir, $oP) + protected function CompileMenu($oMenu, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir) { $this->CompileFiles($oMenu, $sTempTargetDir.'/'.$sModuleRelativeDir, $sFinalTargetDir.'/'.$sModuleRelativeDir, $sModuleRelativeDir); diff --git a/setup/feature_removal/DryRemovalRuntimeEnvironment.php b/setup/feature_removal/DryRemovalRuntimeEnvironment.php index 4cb8d1d42f..6d1835e82e 100644 --- a/setup/feature_removal/DryRemovalRuntimeEnvironment.php +++ b/setup/feature_removal/DryRemovalRuntimeEnvironment.php @@ -15,7 +15,7 @@ class DryRemovalRuntimeEnvironment extends RunTimeEnvironment * * @param string $sSourceEnv: environment from which setup is inspired to simulate extension removal and usee CompileFrom... */ - public function __construct($sSourceEnv = 'production', array $aExtensionCodesToRemove = []) + public function __construct($sSourceEnv = ITOP_DEFAULT_ENV, array $aExtensionCodesToRemove = []) { parent::__construct($sSourceEnv, false); $this->aExtensionsToRemoveByCode = $aExtensionCodesToRemove; diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index ec28de9b56..937631e52a 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -396,9 +396,11 @@ class RunTimeEnvironment $sBuildConfigFile = APPCONF.$this->sBuildEnv.'/'.ITOP_CONFIG_FILE; // Write the config file - @chmod($sBuildConfigFile, 0770); // In case it exists: RWX for owner and group, nothing for others + if (is_file($sBuildConfigFile)) { + chmod($sBuildConfigFile, 0770); // In case it exists: RWX for owner and group, nothing for others + } $oConfig->WriteToFile($sBuildConfigFile); - @chmod($sBuildConfigFile, 0440); // Read-only for owner and group, nothing for others + chmod($sBuildConfigFile, 0440); // Read-only for owner and group, nothing for others } /** @@ -1331,11 +1333,12 @@ class RunTimeEnvironment public function DataToCleanupAudit() { - $oSetupAudit = new SetupAudit(ITOP_DEFAULT_ENV, $this->sBuildEnv); + $oSetupAudit = new SetupAudit($this->GetFinalEnv(), $this->GetBuildEnv()); //Make sure the MetaModel is started before analysing for issues - $sConfFile = utils::GetConfigFilePath(ITOP_DEFAULT_ENV); - MetaModel::Startup($sConfFile, false, false); // Start on production environment + $sFinalEnv = $this->GetFinalEnv(); + $sConfFile = utils::GetConfigFilePath($sFinalEnv); + MetaModel::Startup($sConfFile, false, false, false, $sFinalEnv); // Start on environment $oSetupAudit->RunDataAudit(true); $iCount = $oSetupAudit->GetDataToCleanupCount(); @@ -1350,15 +1353,11 @@ class RunTimeEnvironment $sDestinationEnv = $this->sBuildEnv; if ($sDestinationEnv != $sSourceEnv) { + if (file_exists(utils::GetDataPath().$sDestinationEnv.'.delta.xml')) { + unlink(utils::GetDataPath().$sDestinationEnv.'.delta.xml'); + } SetupUtils::CopyFile(utils::GetDataPath().$sSourceEnv.'.delta.xml', utils::GetDataPath().$sDestinationEnv.'.delta.xml'); SetupUtils::copydir(utils::GetDataPath().$sSourceEnv.'-modules/', utils::GetDataPath().$sDestinationEnv.'-modules/'); - - // Copy the config file - // - $sFinalConfig = APPCONF.$sDestinationEnv.'/config-itop.php'; - if (is_file($sFinalConfig)) { - chmod($sFinalConfig, 0770); // In case it exists: RWX for owner and group, nothing for others - } SetupUtils::copydir(APPCONF.$sSourceEnv, APPCONF.$sDestinationEnv); MetaModel::ResetAllCaches($sDestinationEnv); } @@ -1409,7 +1408,7 @@ class RunTimeEnvironment self::MakeDirSafe($sBuildDir); $bSkipTempDir = ($this->sFinalEnv != $this->sBuildEnv); // No need for a temporary directory if sBuildEnv is already a temporary directory $oMFCompiler = new MFCompiler($oFactory, $this->sFinalEnv); - $oMFCompiler->Compile($sBuildDir, null, $bUseSymLinks, $bSkipTempDir); + $oMFCompiler->Compile($sBuildDir, $bUseSymLinks, $bSkipTempDir); MetaModel::ResetAllCaches($this->sBuildEnv); @@ -1471,7 +1470,7 @@ class RunTimeEnvironment SetupUtils::tidydir($sBuildPath); } - $oExtensionsMap = new iTopExtensionsMap(ITOP_DEFAULT_ENV, $aDirsToScan); + $oExtensionsMap = new iTopExtensionsMap($this->GetFinalEnv(), $aDirsToScan); // Removed modules are stored as static for FindModules() $oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes); @@ -1522,7 +1521,7 @@ class RunTimeEnvironment } $oMFCompiler = new MFCompiler($oFactory, $sEnvironment); - $oMFCompiler->Compile($sBuildPath, null, $bUseSymbolicLinks, false, false); + $oMFCompiler->Compile($sBuildPath, $bUseSymbolicLinks, false); SetupLog::Info("Data model successfully compiled to '$sBuildPath'."); $sCacheDir = APPROOT.'/data/cache-'.$sEnvironment.'/'; @@ -1572,7 +1571,7 @@ class RunTimeEnvironment public function EnterReadOnlyMode(Config $oConfig) { - if ($this->GetFinalEnv() != 'production') { + if ($this->GetFinalEnv() != ITOP_DEFAULT_ENV) { return; } @@ -1592,7 +1591,7 @@ class RunTimeEnvironment public function ExitReadOnlyMode() { - if ($this->GetFinalEnv() != 'production') { + if ($this->GetFinalEnv() != ITOP_DEFAULT_ENV) { return; } diff --git a/setup/sequencers/StepSequencer.php b/setup/sequencers/StepSequencer.php index 455c9aaea7..58e9583e6b 100644 --- a/setup/sequencers/StepSequencer.php +++ b/setup/sequencers/StepSequencer.php @@ -42,7 +42,7 @@ abstract class StepSequencer public function __construct(Parameters $oParams, ?RunTimeEnvironment $oRunTimeEnvironment = null, string $sSourceDesc = 'Setup') { if (is_null($oRunTimeEnvironment)) { - $sEnvironment = $oParams->Get('target_env', 'production'); + $sEnvironment = $oParams->Get('target_env', ITOP_DEFAULT_ENV); $this->oRunTimeEnvironment = new RunTimeEnvironment($sEnvironment, false); } else { $this->oRunTimeEnvironment = $oRunTimeEnvironment; @@ -171,12 +171,13 @@ abstract class StepSequencer protected function GetConfig() { if (! is_null($this->oTestConfig)) { + // For unit tests return $this->oTestConfig; } // Caching config here is a bad idea, the first config loaded does not contain module settings - $sTargetEnvironment = $this->oRunTimeEnvironment->GetBuildEnv(); - $sConfigFile = APPCONF.$sTargetEnvironment.'/'.ITOP_CONFIG_FILE; + $sBuildEnvironment = $this->oRunTimeEnvironment->GetBuildEnv(); + $sConfigFile = APPCONF.$sBuildEnvironment.'/'.ITOP_CONFIG_FILE; try { if (file_exists($sConfigFile)) { $oConfig = new Config($sConfigFile); diff --git a/setup/setuputils.class.inc.php b/setup/setuputils.class.inc.php index 776861168d..91df4621fa 100644 --- a/setup/setuputils.class.inc.php +++ b/setup/setuputils.class.inc.php @@ -527,11 +527,15 @@ class SetupUtils * * @since 3.0.0 N°2214 replace SetupLog::Log calls by CheckResult::TRACE */ - public static function CheckBackupPrerequisites($sDBBackupPath, $sMySQLBinDir = null) + public static function CheckBackupPrerequisites($sDBBackupPath, $sMySQLBinDir = null, string $sEnvironment = ITOP_DEFAULT_ENV) { $aResult = []; $aResult[] = new CheckResult(CheckResult::TRACE, 'Info - CheckBackupPrerequisites'); + if ($sEnvironment !== ITOP_DEFAULT_ENV) { + $aResult[] = new CheckResult(CheckResult::ERROR, "Can only backup ".ITOP_DEFAULT_ENV." environment"); + } + // zip extension // if (!extension_loaded('phar')) { @@ -1592,13 +1596,13 @@ JS if (is_dir($oWizard->GetParameter('copy_extensions_from'))) { $aDirsToScan[] = $oWizard->GetParameter('copy_extensions_from'); } - $sExtraDir = utils::GetDataPath().'production-modules/'; + $sExtraDir = utils::GetDataPath().$oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV).'-modules/'; if (is_dir($sExtraDir)) { $aDirsToScan[] = $sExtraDir; } - $oProductionEnv = new RunTimeEnvironment(); + $oProductionEnv = new RunTimeEnvironment($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV)); $aRemovedExtensionCodes = json_decode($oWizard->GetParameter('removed_extensions'), true) ?? []; - $oExtensionsMap = new iTopExtensionsMap(ITOP_DEFAULT_ENV, $aDirsToScan); + $oExtensionsMap = new iTopExtensionsMap($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV), $aDirsToScan); $oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes); $aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan, $bAbortOnMissingDependency, $aModulesToLoad); @@ -1615,9 +1619,12 @@ JS } /** + * Get version of production application + * * @param WizardController $oWizard * * @return array|bool + * @throws \CoreException */ public static function GetApplicationVersion($oWizard) { diff --git a/setup/unattended-install/InstallationFileService.php b/setup/unattended-install/InstallationFileService.php index 1b05151cfe..655fc56355 100644 --- a/setup/unattended-install/InstallationFileService.php +++ b/setup/unattended-install/InstallationFileService.php @@ -56,7 +56,7 @@ class InstallationFileService public function GetProductionEnv(): RunTimeEnvironment { if (is_null($this->oProductionEnv)) { - $this->oProductionEnv = new RunTimeEnvironment(); + $this->oProductionEnv = new RunTimeEnvironment($this->sTargetEnvironment); } return $this->oProductionEnv; } @@ -258,8 +258,9 @@ class InstallationFileService public function ProcessDefaultModules(): void { $sProductionModuleDir = APPROOT.'data/'.$this->sTargetEnvironment.'-modules/'; + $oConfig = new Config(APPCONF.$this->sTargetEnvironment.'/'.ITOP_CONFIG_FILE); - $aAvailableModules = $this->GetProductionEnv()->AnalyzeInstallation(MetaModel::GetConfig(), $this->GetExtraDirs()); + $aAvailableModules = $this->GetProductionEnv()->AnalyzeInstallation($oConfig, $this->GetExtraDirs()); $this->aAutoSelectModules = []; foreach ($aAvailableModules as $sModuleId => $aModule) { diff --git a/setup/wizardsteps/AbstractWizStepInstall.php b/setup/wizardsteps/AbstractWizStepInstall.php index 60f39a6c1d..c5dece5c95 100644 --- a/setup/wizardsteps/AbstractWizStepInstall.php +++ b/setup/wizardsteps/AbstractWizStepInstall.php @@ -73,7 +73,7 @@ abstract class AbstractWizStepInstall extends WizardStep 'datamodel_version' => $this->oWizard->GetParameter('datamodel_version'), //TODO: let the installer compute this automatically... 'previous_configuration_file' => $sPreviousConfigurationFile, 'extensions_dir' => $this->oWizard->GetParameter('extensions_dir', 'extensions'), - 'target_env' => 'production', + 'target_env' => $this->oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV), 'workspace_dir' => '', 'database' => [ 'server' => $this->oWizard->GetParameter('db_server'), diff --git a/setup/wizardsteps/WizStepDataAudit.php b/setup/wizardsteps/WizStepDataAudit.php index 71c92f823b..90ef5633da 100644 --- a/setup/wizardsteps/WizStepDataAudit.php +++ b/setup/wizardsteps/WizStepDataAudit.php @@ -102,17 +102,19 @@ JS); */ public function PostFormDisplay(SetupPage $oPage) { - $sApplicationUrl = utils::GetAbsoluteUrlModulePage('combodo-data-feature-removal', 'index.php'); + $sEnvironment = $this->oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV); + $sApplicationUrl = utils::GetAbsoluteUrlModulePage('combodo-data-feature-removal', 'index.php', ['switch_env' => $sEnvironment], $sEnvironment); $aParams = [ 'selected_modules' => '[]', 'selected_extensions' => '[]', - 'display_choices' => '[]', + 'display_choices' => '', 'added_extensions' => '[]', 'removed_extensions' => '[]', 'extensions_not_uninstallable' => '[]', 'copy_setup_files' => 1, 'return_button_label' => '', + 'target_env' => ITOP_DEFAULT_ENV, 'force-uninstall' => "", 'use_symbolic_links' => "", ]; @@ -124,7 +126,6 @@ JS); INPUT; } - $sUID = Session::Get('setup_token'); $oPage->add( <<oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV)); $oProductionEnv->InitDataModel($oConfig, true); $sIframeUrl = $oConfig->GetModuleSetting('itop-hub-connector', 'setup_url', ''); @@ -118,7 +119,8 @@ class WizStepDone extends WizardStep "); } - $sForm = '

'; + $sTargetEnv = utils::HtmlEntities($this->oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV)); + $sForm = '
'; $sForm .= ''; $sForm .= ''; $sForm .= "
"; diff --git a/setup/wizardsteps/WizStepLandingBeforeAudit.php b/setup/wizardsteps/WizStepLandingBeforeAudit.php index 1c5317ea8a..d318860a9a 100644 --- a/setup/wizardsteps/WizStepLandingBeforeAudit.php +++ b/setup/wizardsteps/WizStepLandingBeforeAudit.php @@ -7,10 +7,12 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice { + private RunTimeEnvironment $oRuntimeEnv; + public function __construct(WizardController $oWizard, $sCurrentState) { - $oProductionEnv = new RunTimeEnvironment(); - $sBuildConfigFile = APPCONF.$oProductionEnv->GetBuildEnv().'/'.ITOP_CONFIG_FILE; + $this->oRuntimeEnv = new RunTimeEnvironment($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV)); + $sBuildConfigFile = APPCONF.$this->oRuntimeEnv->GetBuildEnv().'/'.ITOP_CONFIG_FILE; $this->oConfig = new Config($sBuildConfigFile); $oWizard->SetParameter('previous_version_dir', APPROOT); @@ -21,6 +23,19 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice $oWizard->SetParameter('datamodel_version', ITOP_CORE_VERSION); $oWizard->SetParameter('upgrade_type', 'use-compatible'); + $oWizard->SetParameter('db_server', $this->oConfig->Get('db_host')); + $oWizard->SetParameter('db_user', $this->oConfig->Get('db_user')); + $oWizard->SetParameter('db_pwd', $this->oConfig->Get('db_pwd')); + $oWizard->SetParameter('db_name', $this->oConfig->Get('db_name')); + $oWizard->SetParameter('db_prefix', $this->oConfig->Get('db_subname')); + $oWizard->SetParameter('db_tls_enabled', $this->oConfig->Get('db_tls.enabled')); + $oWizard->SetParameter('db_tls_ca', $this->oConfig->Get('db_tls.ca') ?? ''); + $oWizard->SetParameter('display_choices', ''); + $oWizard->SetParameter('extensions_not_uninstallable', '[]'); + + $oWizard->SaveParameter('use_symbolic_links', MFCompiler::UseSymbolicLinks()); + $oWizard->SaveParameter('force-uninstall', ''); + // should be done at the end parent::__construct($oWizard, $sCurrentState, false); } @@ -37,22 +52,10 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice */ public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState { - $oProductionEnv = new RunTimeEnvironment(); - $sBuildConfigFile = APPCONF.$oProductionEnv->GetBuildEnv().'/'.ITOP_CONFIG_FILE; + // Change the rights to production config file ! + $sBuildConfigFile = APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE; @chmod($sBuildConfigFile, 0770); // In case it exists: RWX for owner and group, nothing for others - $oConfig = new Config($sBuildConfigFile); - $this->oWizard->SetParameter('db_server', $oConfig->Get('db_host')); - $this->oWizard->SetParameter('db_user', $oConfig->Get('db_user')); - $this->oWizard->SetParameter('db_pwd', $oConfig->Get('db_pwd')); - $this->oWizard->SetParameter('db_name', $oConfig->Get('db_name')); - $this->oWizard->SetParameter('db_prefix', $oConfig->Get('db_subname')); - $this->oWizard->SetParameter('db_tls_enabled', $oConfig->Get('db_tls.enabled')); - $this->oWizard->SetParameter('db_tls_ca', $oConfig->Get('db_tls.ca') ?? ''); - - $this->oWizard->SetParameter('display_choices', '[]'); - $this->oWizard->SetParameter('extensions_not_uninstallable', '[]'); - $aWizardSteps = $this->GetWizardSteps(); $this->oWizard->SetWizardSteps($aWizardSteps); $this->sCurrentState = count($aWizardSteps) - 1; @@ -60,6 +63,16 @@ class WizStepLandingBeforeAudit extends WizStepModulesChoice $aSelectedComponents = $this->GetSelectedComponents($this->aSteps, $this->oWizard->GetParameter('selected_extensions')); $this->oWizard->SetParameter('selected_components', json_encode($aSelectedComponents)); + // Save the choices for the summary step + $sDisplayChoices = '
    '; + $i = 0; + foreach ($this->aSteps as $aStepInfo) { + $sDisplayChoices .= $this->GetSelectedModules($aStepInfo, $aSelectedComponents[$i], $aModules, '', '', $aExtensions); + $i++; + } + $sDisplayChoices .= '
'; + $this->oWizard->SetParameter('display_choices', $sDisplayChoices); + return new WizardState(WizStepDataAudit::class); } diff --git a/setup/wizardsteps/WizStepModulesChoice.php b/setup/wizardsteps/WizStepModulesChoice.php index 28a2051bce..8914e4bed2 100644 --- a/setup/wizardsteps/WizStepModulesChoice.php +++ b/setup/wizardsteps/WizStepModulesChoice.php @@ -58,10 +58,10 @@ class WizStepModulesChoice extends AbstractWizStepInstall $this->oExtensionsMap = new iTopExtensionsMap(); $sPreviousSourceDir = $this->oWizard->GetParameter('previous_version_dir', ''); $sConfigPath = null; - if (($sPreviousSourceDir !== '') && is_readable($sPreviousSourceDir.'/conf/production/config-itop.php')) { - $sConfigPath = $sPreviousSourceDir.'/conf/production/config-itop.php'; - } elseif (is_readable(utils::GetConfigFilePath(ITOP_DEFAULT_ENV))) { - $sConfigPath = utils::GetConfigFilePath(ITOP_DEFAULT_ENV); + if (($sPreviousSourceDir !== '') && is_readable($sPreviousSourceDir.'/conf/'.ITOP_DEFAULT_ENV.'/config-itop.php')) { + $sConfigPath = $sPreviousSourceDir.'/conf/'.ITOP_DEFAULT_ENV.'/config-itop.php'; + } elseif (is_readable(utils::GetConfigFilePath($this->oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV)))) { + $sConfigPath = utils::GetConfigFilePath($this->oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV)); } // only called if the config file exists : we are updating a previous installation ! From f48efbbd03342816f2728099c9cc0f4a2c58376d Mon Sep 17 00:00:00 2001 From: Lenaick Date: Tue, 9 Jun 2026 16:49:47 +0200 Subject: [PATCH 07/36] =?UTF-8?q?N=C2=B09597=20-=20Switch=20environment=20?= =?UTF-8?q?to=20a=20build=20environment=20must=20not=20be=20available=20(#?= =?UTF-8?q?925)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * N°9597 - Switch environment to a build environment must not be available * N°9597 - Switch environment to a build environment must not be available --- application/startup.inc.php | 22 +------ lib/composer/autoload_classmap.php | 1 + lib/composer/autoload_static.php | 1 + sources/Service/Startup/StartupService.php | 58 +++++++++++++++++++ .../Service/Startup/StartupServiceTest.php | 50 ++++++++++++++++ 5 files changed, 112 insertions(+), 20 deletions(-) create mode 100644 sources/Service/Startup/StartupService.php create mode 100644 tests/php-unit-tests/unitary-tests/sources/Service/Startup/StartupServiceTest.php diff --git a/application/startup.inc.php b/application/startup.inc.php index 73460293b5..2eecc35eae 100644 --- a/application/startup.inc.php +++ b/application/startup.inc.php @@ -17,6 +17,7 @@ // You should have received a copy of the GNU Affero General Public License // along with iTop. If not, see use Combodo\iTop\Application\Helper\Session; +use Combodo\iTop\Service\Startup\StartupService; require_once(APPROOT.'core/cmdbobject.class.inc.php'); require_once(APPROOT.'application/utils.inc.php'); @@ -69,26 +70,7 @@ $oKPI->ComputeAndReport("Session Start"); $sSwitchEnv = utils::ReadParam('switch_env', null); $bAllowCache = true; -if (($sSwitchEnv != null) && file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE) && (Session::Get('itop_env') !== $sSwitchEnv)) { - Session::Set('itop_env', $sSwitchEnv); - $sEnv = $sSwitchEnv; - $bAllowCache = false; - // Reset the opcache since otherwise the PHP "model" files may still be cached !! - if (function_exists('opcache_reset')) { - // Zend opcode cache - opcache_reset(); - } - if (function_exists('apc_clear_cache')) { - // APC(u) cache - apc_clear_cache(); - } - // TODO: reset the credentials as well ?? -} elseif (Session::IsSet('itop_env')) { - $sEnv = Session::Get('itop_env'); -} else { - $sEnv = ITOP_DEFAULT_ENV; - Session::Set('itop_env', ITOP_DEFAULT_ENV); -} +$sEnv = (new StartupService())->SetItopEnvironment($sSwitchEnv, $bAllowCache); $sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE; try { MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv); diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 50b73aabc9..626b2db633 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -638,6 +638,7 @@ return array( 'Combodo\\iTop\\Service\\Router\\Exception\\RouteNotFoundException' => $baseDir . '/sources/Service/Router/Exception/RouteNotFoundException.php', 'Combodo\\iTop\\Service\\Router\\Exception\\RouterException' => $baseDir . '/sources/Service/Router/Exception/RouterException.php', 'Combodo\\iTop\\Service\\Router\\Router' => $baseDir . '/sources/Service/Router/Router.php', + 'Combodo\\iTop\\Service\\Startup\\StartupService' => $baseDir . '/sources/Service/Startup/StartupService.php', 'Combodo\\iTop\\Service\\SummaryCard\\SummaryCardService' => $baseDir . '/sources/Service/SummaryCard/SummaryCardService.php', 'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectConfig' => $baseDir . '/sources/Service/TemporaryObjects/TemporaryObjectConfig.php', 'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectGC' => $baseDir . '/sources/Service/TemporaryObjects/TemporaryObjectGC.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 2c315e4f0e..fe840689d7 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -1039,6 +1039,7 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685 'Combodo\\iTop\\Service\\Router\\Exception\\RouteNotFoundException' => __DIR__ . '/../..' . '/sources/Service/Router/Exception/RouteNotFoundException.php', 'Combodo\\iTop\\Service\\Router\\Exception\\RouterException' => __DIR__ . '/../..' . '/sources/Service/Router/Exception/RouterException.php', 'Combodo\\iTop\\Service\\Router\\Router' => __DIR__ . '/../..' . '/sources/Service/Router/Router.php', + 'Combodo\\iTop\\Service\\Startup\\StartupService' => __DIR__ . '/../..' . '/sources/Service/Startup/StartupService.php', 'Combodo\\iTop\\Service\\SummaryCard\\SummaryCardService' => __DIR__ . '/../..' . '/sources/Service/SummaryCard/SummaryCardService.php', 'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectConfig' => __DIR__ . '/../..' . '/sources/Service/TemporaryObjects/TemporaryObjectConfig.php', 'Combodo\\iTop\\Service\\TemporaryObjects\\TemporaryObjectGC' => __DIR__ . '/../..' . '/sources/Service/TemporaryObjects/TemporaryObjectGC.php', diff --git a/sources/Service/Startup/StartupService.php b/sources/Service/Startup/StartupService.php new file mode 100644 index 0000000000..7b81c6a6ca --- /dev/null +++ b/sources/Service/Startup/StartupService.php @@ -0,0 +1,58 @@ +IsBuildEnvironment($sSwitchEnv)) { + $oException = new CoreException("Switching to environment '$sSwitchEnv' is not allowed since it is a build environment"); + IssueLog::Exception("Trying to switch to environment '$sSwitchEnv' is not allowed since it is a build environment", $oException); + throw $oException; + } + + if ( + ($sSwitchEnv != null) + && file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE) + && (Session::Get('itop_env') !== $sSwitchEnv) + ) { + Session::Set('itop_env', $sSwitchEnv); + $sEnv = $sSwitchEnv; + $bAllowCache = false; + // Reset the opcache since otherwise the PHP "model" files may still be cached !! + if (function_exists('opcache_reset')) { + // Zend opcode cache + opcache_reset(); + } + if (function_exists('apc_clear_cache')) { + // APC(u) cache + apc_clear_cache(); + } + // TODO: reset the credentials as well ?? + } elseif (Session::IsSet('itop_env')) { + $sEnv = Session::Get('itop_env'); + } else { + $sEnv = ITOP_DEFAULT_ENV; + Session::Set('itop_env', ITOP_DEFAULT_ENV); + } + + return $sEnv; + } + + public function IsBuildEnvironment(?string $sEnv): bool + { + return $sEnv != null && str_ends_with($sEnv, '-build'); + } +} diff --git a/tests/php-unit-tests/unitary-tests/sources/Service/Startup/StartupServiceTest.php b/tests/php-unit-tests/unitary-tests/sources/Service/Startup/StartupServiceTest.php new file mode 100644 index 0000000000..25f01f10ba --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/sources/Service/Startup/StartupServiceTest.php @@ -0,0 +1,50 @@ +RequireOnceItopFile('application/utils.inc.php'); + $this->oStartupService = new StartupService(); + } + + public function testSetItopEnvironmentUsesDefaultWhenEnvironmentIsNull(): void + { + $bAllowCache = true; + $sEnv = $this->oStartupService->SetItopEnvironment(null, $bAllowCache); + $this->assertEquals(ITOP_DEFAULT_ENV, $sEnv); + $this->assertTrue($bAllowCache); + } + + public function testSetItopEnvironmentWithValidEnvironment(): void + { + $bAllowCache = true; + $sEnv = $this->oStartupService->SetItopEnvironment('test', $bAllowCache); + $this->assertEquals('test', $sEnv); + $this->assertFalse($bAllowCache); + } + + public function testSetItopEnvironmentThrowsForBuildEnvironment() + { + $bAllowCache = true; + $this->expectException(CoreException::class); + $this->expectExceptionMessage("Switching to environment 'test-build' is not allowed since it is a build environment"); + $this->oStartupService->SetItopEnvironment('test-build', $bAllowCache); + } + + public function testIsBuildEnvironment() + { + $this->assertTrue($this->oStartupService->IsBuildEnvironment('test-build')); + $this->assertFalse($this->oStartupService->IsBuildEnvironment('test')); + $this->assertFalse($this->oStartupService->IsBuildEnvironment(null)); + } +} From 733eea8a786d9705d8323a0f59ca0dafa295db46 Mon Sep 17 00:00:00 2001 From: Lenaick Date: Tue, 9 Jun 2026 16:56:58 +0200 Subject: [PATCH 08/36] =?UTF-8?q?N=C2=B09654=20-=20Reduce=20surface=20atta?= =?UTF-8?q?ck=20on=20config=20file=20rights=20during=20setup=20(#932)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataFeatureRemovalController.php | 12 +++---- setup/ajax.dataloader.php | 15 ++++----- setup/setup.js | 3 +- setup/wizard.php | 31 +++++++++++++++++-- setup/wizardcontroller.class.inc.php | 24 -------------- setup/wizardsteps/WizStepDBParams.php | 3 +- setup/wizardsteps/WizStepDataAudit.php | 7 ----- setup/wizardsteps/WizStepDone.php | 15 ++------- setup/wizardsteps/WizStepInstall.php | 3 -- .../wizardsteps/WizStepInstallMiscParams.php | 2 -- setup/wizardsteps/WizStepInstallOrUpgrade.php | 3 +- .../wizardsteps/WizStepLandingBeforeAudit.php | 4 --- setup/wizardsteps/WizStepSummary.php | 3 -- .../wizardsteps/WizStepUpgradeMiscParams.php | 2 -- setup/wizardsteps/WizStepWelcome.php | 2 -- 15 files changed, 46 insertions(+), 83 deletions(-) diff --git a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php index c1880c8e8c..944b5e88a8 100644 --- a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php +++ b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php @@ -85,11 +85,8 @@ class DataFeatureRemovalController extends Controller { $aParams = []; - try { - //from setup wizard/mtp - SetupUtils::CheckSetupToken(); - SetupUtils::EraseSetupToken(); - } catch (SecurityException $e) { + //from setup wizard/mtp + if (!SetupUtils::IsSessionSetupTokenValid()) { //from same module $this->ValidateTransactionId(); } @@ -184,7 +181,6 @@ class DataFeatureRemovalController extends Controller $aParams['aSetupParams'] = [ "_class" => "WizStepLandingBeforeAudit", "operation" => "next", - "_params[authent]" => SetupUtils::CreateSetupToken(), ]; foreach ($aHiddenInputs as $sInputName => $sInputValue) { @@ -200,6 +196,10 @@ class DataFeatureRemovalController extends Controller $aParams['bDeletionNeeded'] = ($aParams['iQueryCount'] > 0); Session::Set('aDeletionExecutionSummary', serialize($this->aDeletionExecutionSummary)); + if (!$aParams['bHasDeletionNeeded']) { + SetupUtils::CreateSetupToken(); + } + $this->DisplayPage($aParams, 'AnalysisResult'); } diff --git a/setup/ajax.dataloader.php b/setup/ajax.dataloader.php index 53c672faa2..1d5370643b 100644 --- a/setup/ajax.dataloader.php +++ b/setup/ajax.dataloader.php @@ -35,6 +35,7 @@ * 'percent': integer 0..100 the percentage of completion once the file has been loaded */ +use Combodo\iTop\Application\Helper\Session; use Combodo\iTop\Application\WebPage\AjaxPage; $bBypassMaintenance = true; // Reset maintenance mode in case of problem @@ -129,7 +130,10 @@ header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past */ $sOperation = utils::ReadParam('operation', ''); try { - SetupUtils::CheckSetupToken(); + Session::Start(); + if (!SetupUtils::IsSessionSetupTokenValid()) { + throw new SecurityException("Invalid session token"); + } switch ($sOperation) { case 'async_action': @@ -150,14 +154,7 @@ try { /** @var WizardStep $oStep */ $oStep = new $sClass($oDummyController, $sState); $sConfigFile = utils::GetConfigFilePath(ITOP_DEFAULT_ENV); - if (file_exists($sConfigFile) && !is_writable($sConfigFile) && $oStep->RequiresWritableConfig()) { - $sRelativePath = utils::GetConfigFilePathRelative(ITOP_DEFAULT_ENV); - $sErrorMsg = "Error: the configuration file '".$sRelativePath."' already exists and cannot be overwritten."; - $sErrorMsg .= "The wizard cannot modify the configuration file for you. If you want to upgrade ".ITOP_APPLICATION.", make sure that the file '".$sRelativePath."' can be modified by the web server."; - throw new Exception($sErrorMsg); - } else { - $oStep->AsyncAction($oPage, $sActionCode, $aParams); - } + $oStep->AsyncAction($oPage, $sActionCode, $aParams); } $oPage->output(); break; diff --git a/setup/setup.js b/setup/setup.js index 26026dddd0..f385f08b00 100644 --- a/setup/setup.js +++ b/setup/setup.js @@ -2,9 +2,8 @@ function WizardAsyncAction(sActionCode, oParams, OnErrorFunction) { var sStepClass = $('#_class').val(); var sStepState = $('#_state').val(); - var sAuthent = $('#authent_token').val(); - var oMap = { operation: 'async_action', step_class: sStepClass, step_state: sStepState, code: sActionCode, authent : sAuthent, params: oParams }; + var oMap = { operation: 'async_action', step_class: sStepClass, step_state: sStepState, code: sActionCode, params: oParams }; var ErrorFn = OnErrorFunction; $(document).ajaxError(function(event, request, settings) { diff --git a/setup/wizard.php b/setup/wizard.php index f48fda0c29..f7367dd1d0 100644 --- a/setup/wizard.php +++ b/setup/wizard.php @@ -67,7 +67,34 @@ if (SetupUtils::IsSessionSetupTokenValid()) { $oWizard->Run(); } else { SetupUtils::ExitMaintenanceMode(false); - // Force initializing the setup - $oWizard->Start(); + + $sConfigFile = utils::GetConfigFilePath(ITOP_DEFAULT_ENV); + if (file_exists($sConfigFile)) { + // The configuration file already exists + if (!is_writable($sConfigFile)) { + SetupUtils::ExitReadOnlyMode(false); // Reset readonly mode in case of problem + SetupUtils::EraseSetupToken(); + $sRelativePath = utils::GetConfigFilePathRelative(ITOP_DEFAULT_ENV); + $oP = new SetupPage('Installation Cannot Continue'); + $oP->add("

Fatal error

\n"); + $oP->error("Error: the configuration file '".$sRelativePath."' already exists and cannot be overwritten."); + $oP->p("The wizard cannot modify the configuration file for you. If you want to upgrade ".ITOP_APPLICATION.", make sure that the file '".$sRelativePath."' can be modified by the web server."); + + $sButtonsHtml = <<Reload +HTML; + $oP->p($sButtonsHtml); + + $oP->output(); + // Prevent token creation + exit; + } else { + chmod($sConfigFile, 0440); + } + } + SetupUtils::CreateSetupToken(); + + // Start the setup + $oWizard->Start(); } diff --git a/setup/wizardcontroller.class.inc.php b/setup/wizardcontroller.class.inc.php index 97cd4a4255..860f7f666b 100644 --- a/setup/wizardcontroller.class.inc.php +++ b/setup/wizardcontroller.class.inc.php @@ -195,30 +195,6 @@ class WizardController { SetupLog::Info("=== Setup screen: ".$oStep->GetTitle().' ('.get_class($oStep).')'); $oPage = new SetupPage($oStep->GetTitle()); - if ($oStep->RequiresWritableConfig()) { - $sConfigFile = utils::GetConfigFilePath(ITOP_DEFAULT_ENV); - if (file_exists($sConfigFile)) { - // The configuration file already exists - if (!is_writable($sConfigFile)) { - SetupUtils::ExitReadOnlyMode(false); // Reset readonly mode in case of problem - SetupUtils::EraseSetupToken(); - $sRelativePath = utils::GetConfigFilePathRelative(ITOP_DEFAULT_ENV); - $oP = new SetupPage('Installation Cannot Continue'); - $oP->add("

Fatal error

\n"); - $oP->error("Error: the configuration file '".$sRelativePath."' already exists and cannot be overwritten."); - $oP->p("The wizard cannot modify the configuration file for you. If you want to upgrade ".ITOP_APPLICATION.", make sure that the file '".$sRelativePath."' can be modified by the web server."); - - $sButtonsHtml = <<Reload -HTML; - $oP->p($sButtonsHtml); - - $oP->output(); - // Prevent token creation - exit; - } - } - } $oPage->LinkScriptFromAppRoot('setup/setup.js'); $oPage->add(''); $oPage->add('
'); diff --git a/setup/wizardsteps/WizStepDBParams.php b/setup/wizardsteps/WizStepDBParams.php index f8d870680e..5726652b86 100644 --- a/setup/wizardsteps/WizStepDBParams.php +++ b/setup/wizardsteps/WizStepDBParams.php @@ -21,6 +21,7 @@ /** * Database Connection parameters screen */ + use Combodo\iTop\Application\WebPage\WebPage; class WizStepDBParams extends WizardStep @@ -76,8 +77,6 @@ class WizStepDBParams extends WizardStep $sTlsCA, $sNewDBName ); - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); $oPage->add(''); $sCreateDB = $this->oWizard->GetParameter('create_db', 'yes'); if ($sCreateDB == 'no') { diff --git a/setup/wizardsteps/WizStepDataAudit.php b/setup/wizardsteps/WizStepDataAudit.php index 90ef5633da..1bc480e9c3 100644 --- a/setup/wizardsteps/WizStepDataAudit.php +++ b/setup/wizardsteps/WizStepDataAudit.php @@ -18,8 +18,6 @@ * You should have received a copy of the GNU Affero General Public License */ -use Combodo\iTop\Application\Helper\Session; - require_once(APPROOT.'setup/sequencers/DataAuditSequencer.php'); /** @@ -77,9 +75,6 @@ class WizStepDataAudit extends WizStepInstall $sJSONData = json_encode($aInstallParams); $oPage->add(''); - - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); if (!$this->CheckDependencies()) { $oPage->error($this->sDependencyIssue); $oPage->add_ready_script(<< INPUT; } - $sUID = Session::Get('setup_token'); $oPage->add( << - $aHiddenInputs HTML diff --git a/setup/wizardsteps/WizStepDone.php b/setup/wizardsteps/WizStepDone.php index e964fda383..9353bf2212 100644 --- a/setup/wizardsteps/WizStepDone.php +++ b/setup/wizardsteps/WizStepDone.php @@ -69,13 +69,13 @@ class WizStepDone extends WizardStep } $bHasBackup = false; - if (($this->oWizard->GetParameter('mode', '') == 'upgrade') && $this->oWizard->GetParameter('db_backup', false) && $this->oWizard->GetParameter('authent', false)) { + if (($this->oWizard->GetParameter('mode', '') == 'upgrade') && $this->oWizard->GetParameter('db_backup', false)) { $sBackupDestination = $this->oWizard->GetParameter('db_backup_path', ''); if (file_exists($sBackupDestination.'.tar.gz')) { $bHasBackup = true; // To mitigate security risks: pass only the filename without the extension, the download will add the extension itself $oPage->p('Your backup is ready'); - $oPage->p(' Download '.basename($sBackupDestination).''); + $oPage->p(' Download '.basename($sBackupDestination).''); } else { $oPage->p(' Warning: Backup creation failed !'); } @@ -121,8 +121,6 @@ class WizStepDone extends WizardStep $sTargetEnv = utils::HtmlEntities($this->oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV)); $sForm = '
'; - $sForm .= ''; - $sForm .= ''; $sForm .= "
"; $sForm .= ''; @@ -151,15 +149,6 @@ class WizStepDone extends WizardStep return 'return false;'; } - /** - * Tells whether this step of the wizard requires that the configuration file be writable - * @return bool True if the wizard will possibly need to modify the configuration at some point - */ - public function RequiresWritableConfig() - { - return false; //This step executes once the config was written and secured - } - public function AsyncAction(WebPage $oPage, $sCode, $aParameters) { SetupUtils::EraseSetupToken(); diff --git a/setup/wizardsteps/WizStepInstall.php b/setup/wizardsteps/WizStepInstall.php index 648e1f2e7f..83907077a8 100644 --- a/setup/wizardsteps/WizStepInstall.php +++ b/setup/wizardsteps/WizStepInstall.php @@ -98,9 +98,6 @@ EOF $sJSONData = json_encode($aInstallParams); $oPage->add(''); - - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); if (!$this->CheckDependencies()) { $oPage->error($this->sDependencyIssue); $oPage->add_ready_script(<<p('
'); - $oPage->add(''); //$oPage->add(''); $oPage->add_ready_script( <<GetWizardSteps(); $this->oWizard->SetWizardSteps($aWizardSteps); $this->sCurrentState = count($aWizardSteps) - 1; diff --git a/setup/wizardsteps/WizStepSummary.php b/setup/wizardsteps/WizStepSummary.php index 1f91b4536b..0678b913bc 100644 --- a/setup/wizardsteps/WizStepSummary.php +++ b/setup/wizardsteps/WizStepSummary.php @@ -238,9 +238,6 @@ class WizStepSummary extends AbstractWizStepInstall } - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); - $oPage->add_ready_script( <<add(''); $oPage->add(''); $oPage->add(''); - $sAuthentToken = $this->oWizard->GetParameter('authent', ''); - $oPage->add(''); $oPage->add_ready_script( <<oWizard->SetParameter('authent', $sUID); return new WizardState(WizStepInstallOrUpgrade::class); } From 6e536e7dbe12d2f2023b3322118f9684a594ff1d Mon Sep 17 00:00:00 2001 From: "lenaick.moreira" Date: Wed, 10 Jun 2026 10:53:16 +0200 Subject: [PATCH 09/36] =?UTF-8?q?:white=5Fcheck=5Fmark:=20N=C2=B09597=20-?= =?UTF-8?q?=20Fix=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sources/Service/Startup/StartupServiceTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/php-unit-tests/unitary-tests/sources/Service/Startup/StartupServiceTest.php b/tests/php-unit-tests/unitary-tests/sources/Service/Startup/StartupServiceTest.php index 25f01f10ba..0eb2555582 100644 --- a/tests/php-unit-tests/unitary-tests/sources/Service/Startup/StartupServiceTest.php +++ b/tests/php-unit-tests/unitary-tests/sources/Service/Startup/StartupServiceTest.php @@ -15,6 +15,19 @@ class StartupServiceTest extends ItopTestCase parent::setUp(); $this->RequireOnceItopFile('application/utils.inc.php'); $this->oStartupService = new StartupService(); + + if (!file_exists(APPCONF.'test/'.ITOP_CONFIG_FILE)) { + if (!is_dir(APPCONF.'test/')) { + mkdir(APPCONF.'test/'); + } + copy(APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE, APPCONF.'test/'.ITOP_CONFIG_FILE); + } + } + + protected function tearDown(): void + { + parent::tearDown(); + unlink(APPCONF.'test/'.ITOP_CONFIG_FILE); } public function testSetItopEnvironmentUsesDefaultWhenEnvironmentIsNull(): void From a95125e8bf4447eb23a891de8ab201a157189451 Mon Sep 17 00:00:00 2001 From: Lenaick Date: Wed, 10 Jun 2026 16:03:09 +0200 Subject: [PATCH 10/36] =?UTF-8?q?N=C2=B09638=20-=20Add=20setup=20ContextTa?= =?UTF-8?q?g=20for=20async=20operation=20(#934)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup/ajax.dataloader.php | 2 ++ sources/Application/WebPage/WebPage.php | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/ajax.dataloader.php b/setup/ajax.dataloader.php index 1d5370643b..f2e9be4669 100644 --- a/setup/ajax.dataloader.php +++ b/setup/ajax.dataloader.php @@ -135,6 +135,8 @@ try { throw new SecurityException("Invalid session token"); } + $oContextTag = new ContextTag(ContextTag::TAG_SETUP); + switch ($sOperation) { case 'async_action': ini_set('max_execution_time', max(240, ini_get('max_execution_time'))); diff --git a/sources/Application/WebPage/WebPage.php b/sources/Application/WebPage/WebPage.php index cc47249ebc..bd2dcaa0e6 100644 --- a/sources/Application/WebPage/WebPage.php +++ b/sources/Application/WebPage/WebPage.php @@ -1794,11 +1794,9 @@ JS; } /** - * @param bool $bReturnOutput - * * @throws \Exception */ - protected function output_dict_entries($bReturnOutput = false) + protected function output_dict_entries() { if ($this->sContentType != 'text/plain' && $this->sContentType != 'application/json' && $this->sContentType != 'application/javascript') { /** @var \iBackofficeDictEntriesExtension $oExtensionInstance */ From b36b0de93c5d5b91fff001b58bd9f2a8e8716c8f Mon Sep 17 00:00:00 2001 From: "lenaick.moreira" Date: Wed, 10 Jun 2026 16:45:28 +0200 Subject: [PATCH 11/36] =?UTF-8?q?N=C2=B09654=20-=20fix=20typo=20in=20param?= =?UTF-8?q?=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Controller/DataFeatureRemovalController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php index 944b5e88a8..a1915a9a61 100644 --- a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php +++ b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php @@ -27,7 +27,6 @@ use Exception; use MetaModel; use MFCompiler; use RunTimeEnvironment; -use SecurityException; use SetupUtils; use utils; @@ -196,7 +195,7 @@ class DataFeatureRemovalController extends Controller $aParams['bDeletionNeeded'] = ($aParams['iQueryCount'] > 0); Session::Set('aDeletionExecutionSummary', serialize($this->aDeletionExecutionSummary)); - if (!$aParams['bHasDeletionNeeded']) { + if (!$aParams['bDeletionNeeded']) { SetupUtils::CreateSetupToken(); } From cf76c23ac6f72b623114164d725358413f7073e8 Mon Sep 17 00:00:00 2001 From: odain-cbd <56586767+odain-cbd@users.noreply.github.com> Date: Thu, 11 Jun 2026 07:09:44 +0200 Subject: [PATCH 12/36] =?UTF-8?q?=20N=C2=B09670=20-=20Extension=20manageme?= =?UTF-8?q?nt=20-=20even=20if=20not=20selected,=20an=20extension=20is=20an?= =?UTF-8?q?alysed=20during=20setup=20check=20compatibility=20(#933)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * N°9670 - Extension management - even if not selected, an extension is analysed during setup check compatibility * N°9670 - enhance log msg * N°9670 - PR feedback --- setup/runtimeenv.class.inc.php | 34 +++++++++++++++--- setup/sequencers/DataAuditSequencer.php | 2 ++ .../setup/RunTimeEnvironmentTest.php | 35 +++++++++++++++---- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index 937631e52a..f7331c610e 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -1416,6 +1416,7 @@ class RunTimeEnvironment } /** + * @param array $aSelectedExtensionCodes * @param array $aRemovedExtensionCodes * @param array $aSelectedModules * @param string $sSourceDir @@ -1427,7 +1428,7 @@ class RunTimeEnvironment * @throws \CoreException * */ - public function DoCompile(array $aRemovedExtensionCodes, array $aSelectedModules, string $sSourceDir, string $sExtensionDir, bool $bUseSymbolicLinks = false): void + public function DoCompile(array $aSelectedExtensionCodes, array $aRemovedExtensionCodes, array $aSelectedModules, string $sSourceDir, string $sExtensionDir, bool $bUseSymbolicLinks = false): void { SetupLog::Info('Compiling data model.'); @@ -1475,13 +1476,38 @@ class RunTimeEnvironment $oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes); // Check that all the extensions have a code + $aNoCodeExtensionSourceDirs = []; + $bSetupFailure = false; + $aNoCodeExtensionLabelsThatBreakSetup = []; foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) { + if (in_array($oExtension->sCode, $aSelectedExtensionCodes)) { + $oExtension->bMarkedAsChosen = true; + } + if (empty($oExtension->sCode)) { - $sExtensionLabel = !empty($oExtension->sLabel) ? $oExtension->sLabel : $oExtension->sSourceDir; - $sErrorMessage = sprintf('Extension "%s" cannot be installed: Missing extension code', $sExtensionLabel); + if (empty($oExtension->sLabel)) { + $sExtensionLabel = $oExtension->sSourceDir; + $aNoCodeExtensionSourceDirs [] = $oExtension->sSourceDir; + } else { + $sExtensionLabel = $oExtension->sLabel; + $aNoCodeExtensionSourceDirs [$sExtensionLabel] = $oExtension->sSourceDir; + } + + if ($oExtension->bMarkedAsChosen) { + $aNoCodeExtensionLabelsThatBreakSetup[] = $sExtensionLabel; + $bSetupFailure = true; + } + } + } + + if (count($aNoCodeExtensionSourceDirs) > 0) { + if ($bSetupFailure) { + $sErrorMessage = sprintf('Selected extension(s) cannot be installed: Missing extension code (%s)', implode(',', $aNoCodeExtensionLabelsThatBreakSetup)); $e = new CoreException($sErrorMessage); - IssueLog::Exception($sErrorMessage, $e); + SetupLog::Exception($sErrorMessage, $e, null, $aNoCodeExtensionSourceDirs); throw $e; + } else { + SetupLog::Warning("Non selected extension(s) cannot be installed: Missing extension code", null, $aNoCodeExtensionSourceDirs); } } diff --git a/setup/sequencers/DataAuditSequencer.php b/setup/sequencers/DataAuditSequencer.php index c6172d4c2e..f8df7fdf4d 100644 --- a/setup/sequencers/DataAuditSequencer.php +++ b/setup/sequencers/DataAuditSequencer.php @@ -67,9 +67,11 @@ class DataAuditSequencer extends StepSequencer $sSourceDir = $this->oParams->Get('source_dir', 'datamodels/latest'); $sExtensionDir = $this->oParams->Get('extensions_dir', 'extensions'); $aRemovedExtensionCodes = $this->oParams->Get('removed_extensions', []); + $aSelectedExtensionCodes = $this->oParams->Get('selected_extensions', []); $bUseSymbolicLinks = $this->oParams->Get('use_symbolic_links', null) === 'on'; MetaModel::ResetAllCaches($this->oRunTimeEnvironment->GetBuildEnv()); $this->oRunTimeEnvironment->DoCompile( + $aSelectedExtensionCodes, $aRemovedExtensionCodes, $aSelectedModules, $sSourceDir, diff --git a/tests/php-unit-tests/unitary-tests/setup/RunTimeEnvironmentTest.php b/tests/php-unit-tests/unitary-tests/setup/RunTimeEnvironmentTest.php index b753907c51..a1ccbd70ef 100644 --- a/tests/php-unit-tests/unitary-tests/setup/RunTimeEnvironmentTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/RunTimeEnvironmentTest.php @@ -3,6 +3,7 @@ namespace Combodo\iTop\Test\UnitTest\Setup; use Combodo\iTop\Test\UnitTest\ItopTestCase; +use DOMFormatException; use Exception; use RunTimeEnvironment; @@ -29,7 +30,29 @@ class RunTimeEnvironmentTest extends ItopTestCase parent::tearDown(); } - public function testDoCompileThrowsWhenExtensionCodeIsMissing(): void + public function testDoCompileDoesNotThrowWhenUnselectedExtensionCodeIsMissing(): void + { + [$sToken, $sSourceDirRelative, $sExtensionsDirRelative] = $this->CreateFixtureContext('runtimeenv-missing-code-'); + + $sInvalidExtensionXml = << + + + Test extension without code + 1.0.0 + false + + +XML; + file_put_contents(APPROOT.$sExtensionsDirRelative.'/extension.xml', $sInvalidExtensionXml); + $oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sToken); + + //early DOMFormatException to avoid any real compilation + $this->expectException(DOMFormatException::class); + $oRunTimeEnvironment->DoCompile([], [], [], $sSourceDirRelative, $sExtensionsDirRelative); + } + + public function testDoCompileThrowsWhenSelectedExtensionCodeIsMissing(): void { [$sToken, $sSourceDirRelative, $sExtensionsDirRelative] = $this->CreateFixtureContext('runtimeenv-missing-code-'); @@ -47,12 +70,12 @@ XML; $oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sToken); $this->expectException(Exception::class); - $this->expectExceptionMessage('Extension "Broken extension" cannot be installed: Missing extension code'); + $this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code (Broken extension)"); - $oRunTimeEnvironment->DoCompile([], [], $sSourceDirRelative, $sExtensionsDirRelative, false); + $oRunTimeEnvironment->DoCompile([""], [], [], $sSourceDirRelative, $sExtensionsDirRelative); } - public function testDoCompileThrowsWhenExtensionCodeAndLabelAreMissing(): void + public function testDoCompileThrowsWhenSelectedExtensionCodeAndLabelAreMissing(): void { [$sToken, $sSourceDirRelative, $sExtensionsDirRelative] = $this->CreateFixtureContext('runtimeenv-missing-label-'); @@ -70,9 +93,9 @@ XML; $oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sToken); $this->expectException(Exception::class); - $this->expectExceptionMessage(sprintf('Extension "%s" cannot be installed: Missing extension code', $sExtensionsDirAbsolute)); + $this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code ($sExtensionsDirAbsolute)"); - $oRunTimeEnvironment->DoCompile([], [], $sSourceDirRelative, $sExtensionsDirRelative, false); + $oRunTimeEnvironment->DoCompile([""], [], [], $sSourceDirRelative, $sExtensionsDirRelative, false); } private function CreateFixtureContext(string $sTokenPrefix): array From db7fd6b3c0ff3e0ca163d3d6782ca818eca59644 Mon Sep 17 00:00:00 2001 From: "lenaick.moreira" Date: Thu, 11 Jun 2026 09:41:45 +0200 Subject: [PATCH 13/36] =?UTF-8?q?:rewind:=20N=C2=B09638=20-=20Rollback=20u?= =?UTF-8?q?ndesired=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sources/Application/WebPage/WebPage.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sources/Application/WebPage/WebPage.php b/sources/Application/WebPage/WebPage.php index bd2dcaa0e6..cc47249ebc 100644 --- a/sources/Application/WebPage/WebPage.php +++ b/sources/Application/WebPage/WebPage.php @@ -1794,9 +1794,11 @@ JS; } /** + * @param bool $bReturnOutput + * * @throws \Exception */ - protected function output_dict_entries() + protected function output_dict_entries($bReturnOutput = false) { if ($this->sContentType != 'text/plain' && $this->sContentType != 'application/json' && $this->sContentType != 'application/javascript') { /** @var \iBackofficeDictEntriesExtension $oExtensionInstance */ From 28a09068a0fd59b4374a10829cd61bc703257b8f Mon Sep 17 00:00:00 2001 From: Timmy38 Date: Thu, 11 Jun 2026 17:14:48 +0200 Subject: [PATCH 14/36] =?UTF-8?q?N=C2=B09683=20Add=20markdown=20in=20UIBlo?= =?UTF-8?q?ck=20for=20behat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cs.dictionary.itop.extensions-details.php | 1 + .../da.dictionary.itop.extensions-details.php | 1 + .../de.dictionary.itop.extensions-details.php | 1 + .../en.dictionary.itop.extensions-details.php | 1 + .../es_cr.dictionary.itop.extensions-details.php | 1 + .../fr.dictionary.itop.extensions-details.php | 1 + .../hu.dictionary.itop.extensions-details.php | 1 + .../it.dictionary.itop.extensions-details.php | 1 + .../ja.dictionary.itop.extensions-details.php | 1 + .../nl.dictionary.itop.extensions-details.php | 1 + .../pl.dictionary.itop.extensions-details.php | 1 + .../pt_br.dictionary.itop.extensions-details.php | 1 + .../ru.dictionary.itop.extensions-details.php | 1 + .../sk.dictionary.itop.extensions-details.php | 1 + .../tr.dictionary.itop.extensions-details.php | 1 + .../zh_cn.dictionary.itop.extensions-details.php | 1 + .../Application/UI/Base/Component/Input/Toggler.php | 13 ++++++++++++- .../UI/Base/Layout/Extension/ExtensionDetails.php | 3 ++- .../base/components/input/input-toggler.html.twig | 2 +- 19 files changed, 31 insertions(+), 3 deletions(-) diff --git a/dictionaries/ui/layouts/extensions-details/cs.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/cs.dictionary.itop.extensions-details.php index 2f38ac0a26..04e71d6511 100644 --- a/dictionaries/ui/layouts/extensions-details/cs.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/cs.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/da.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/da.dictionary.itop.extensions-details.php index ad743ccc36..73e4f4efe4 100644 --- a/dictionaries/ui/layouts/extensions-details/da.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/da.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/de.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/de.dictionary.itop.extensions-details.php index 505ce5e34e..816dd69e78 100644 --- a/dictionaries/ui/layouts/extensions-details/de.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/de.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('DE DE', 'German', 'Deutsch', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/en.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/en.dictionary.itop.extensions-details.php index b8d1415ced..4e01932f53 100644 --- a/dictionaries/ui/layouts/extensions-details/en.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/en.dictionary.itop.extensions-details.php @@ -19,4 +19,5 @@ Dict::Add('EN US', 'English', 'English', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s', ]); diff --git a/dictionaries/ui/layouts/extensions-details/es_cr.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/es_cr.dictionary.itop.extensions-details.php index ae18bb850c..0a128e19e8 100644 --- a/dictionaries/ui/layouts/extensions-details/es_cr.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/es_cr.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/fr.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/fr.dictionary.itop.extensions-details.php index 5114c91c77..4fb6090335 100644 --- a/dictionaries/ui/layouts/extensions-details/fr.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/fr.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('FR FR', 'French', 'Français', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'Plus d\'informations', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Forcer la désinstallation', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Plus d\'actions', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/hu.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/hu.dictionary.itop.extensions-details.php index cded3a4641..5f81e69874 100644 --- a/dictionaries/ui/layouts/extensions-details/hu.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/hu.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/it.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/it.dictionary.itop.extensions-details.php index 424a0b13f9..7c44bc63ef 100644 --- a/dictionaries/ui/layouts/extensions-details/it.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/it.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/ja.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/ja.dictionary.itop.extensions-details.php index 96703e5832..a3b527c0bb 100644 --- a/dictionaries/ui/layouts/extensions-details/ja.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/ja.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('JA JP', 'Japanese', '日本語', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/nl.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/nl.dictionary.itop.extensions-details.php index 1774a4dca1..53fcb5b4a8 100644 --- a/dictionaries/ui/layouts/extensions-details/nl.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/nl.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/pl.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/pl.dictionary.itop.extensions-details.php index 48628bd08e..8234013376 100644 --- a/dictionaries/ui/layouts/extensions-details/pl.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/pl.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('PL PL', 'Polish', 'Polski', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/pt_br.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/pt_br.dictionary.itop.extensions-details.php index f469ddfdfc..4683a2b433 100644 --- a/dictionaries/ui/layouts/extensions-details/pt_br.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/pt_br.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/ru.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/ru.dictionary.itop.extensions-details.php index 9b5967a04d..ef233f68b0 100644 --- a/dictionaries/ui/layouts/extensions-details/ru.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/ru.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('RU RU', 'Russian', 'Русский', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/sk.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/sk.dictionary.itop.extensions-details.php index 3aab38ee98..f4db3fabc9 100644 --- a/dictionaries/ui/layouts/extensions-details/sk.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/sk.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/tr.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/tr.dictionary.itop.extensions-details.php index 45ca6000a3..0e66dc741e 100644 --- a/dictionaries/ui/layouts/extensions-details/tr.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/tr.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/dictionaries/ui/layouts/extensions-details/zh_cn.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/zh_cn.dictionary.itop.extensions-details.php index afce37f0ac..802d156699 100644 --- a/dictionaries/ui/layouts/extensions-details/zh_cn.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/zh_cn.dictionary.itop.extensions-details.php @@ -21,4 +21,5 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', 'UI:Layout:ExtensionsDetails:MoreActions' => 'Show more actions~~', + 'UI:Layout:ExtensionsDetails:TogglerTooltip' => 'Toggle %1$s~~', ]); diff --git a/sources/Application/UI/Base/Component/Input/Toggler.php b/sources/Application/UI/Base/Component/Input/Toggler.php index 1b11129bb5..be8b0e5cdf 100644 --- a/sources/Application/UI/Base/Component/Input/Toggler.php +++ b/sources/Application/UI/Base/Component/Input/Toggler.php @@ -18,10 +18,12 @@ class Toggler extends Input public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/input/input-toggler'; public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'base/components/input/input-toggler'; - public function __construct(?string $sId = null) + protected string $sTooltip = ''; + public function __construct(?string $sId = null, string $sTooltip = '') { parent::__construct($sId); $this->SetType('checkbox'); + $this->SetTooltip($sTooltip); } public function SetIsToggled(bool $bIsToggled): static @@ -33,4 +35,13 @@ class Toggler extends Input { return $this->IsChecked(); } + + public function GetTooltip(): string + { + return $this->sTooltip; + } + public function SetTooltip($sTooltip): void + { + $this->sTooltip = $sTooltip; + } } diff --git a/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php b/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php index bf53a364c9..cb69d48ac7 100644 --- a/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php +++ b/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php @@ -182,13 +182,14 @@ class ExtensionDetails extends UIContentBlock $sName = 'aSelectedExtensions['.$this->GetCode().']'; $this->oToggler = new Toggler(); $this->oToggler->SetName($sName); + $this->oToggler->SetTooltip(Dict::Format('UI:Layout:ExtensionsDetails:TogglerTooltip' ,$this->GetLabel())); $this->oToggler->AddCSSClass('toggler-install'); } protected function InitializePopoverMenu() { $this->oPopoverMenu = new PopoverMenu(); - $oPopoverOpenButton = ButtonUIBlockFactory::MakeIconAction('fas fa-ellipsis-v', Dict::S('UI:Layout:ExtensionsDetails:MoreActions')); + $oPopoverOpenButton = ButtonUIBlockFactory::MakeIconAction('fas fa-ellipsis-v', Dict::S('UI:Layout:ExtensionsDetails:MoreActions'), 'dropdown-menu-'.$this->GetCode()); $this->oPopoverMenu->SetTogglerFromBlock($oPopoverOpenButton); $this->oMoreActions = new UIContentBlock(); $this->oMoreActions->AddSubBlock($this->oPopoverMenu); diff --git a/templates/base/components/input/input-toggler.html.twig b/templates/base/components/input/input-toggler.html.twig index 1674d06cb9..4b1d922b8d 100644 --- a/templates/base/components/input/input-toggler.html.twig +++ b/templates/base/components/input/input-toggler.html.twig @@ -2,7 +2,7 @@ {% block iboInput %} {{ parent() }} - + {% endblock %} From 0bf773a23cc152a2ed5e499dcf525e72a0c2765f Mon Sep 17 00:00:00 2001 From: odain Date: Thu, 11 Jun 2026 18:47:48 +0200 Subject: [PATCH 15/36] =?UTF-8?q?N=C2=B09454=20-=20fix=20unattended=20regr?= =?UTF-8?q?ession=20when=20fresh=20install=20and=20no=20conf=20yet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup/unattended-install/InstallationFileService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/unattended-install/InstallationFileService.php b/setup/unattended-install/InstallationFileService.php index 655fc56355..fe4959bff1 100644 --- a/setup/unattended-install/InstallationFileService.php +++ b/setup/unattended-install/InstallationFileService.php @@ -258,7 +258,7 @@ class InstallationFileService public function ProcessDefaultModules(): void { $sProductionModuleDir = APPROOT.'data/'.$this->sTargetEnvironment.'-modules/'; - $oConfig = new Config(APPCONF.$this->sTargetEnvironment.'/'.ITOP_CONFIG_FILE); + $oConfig = new Config(APPCONF.$this->sTargetEnvironment.'/'.ITOP_CONFIG_FILE, false); $aAvailableModules = $this->GetProductionEnv()->AnalyzeInstallation($oConfig, $this->GetExtraDirs()); From 3805a322c10ed668b78d947f39663a9cc6050901 Mon Sep 17 00:00:00 2001 From: Vincent Dumas <42336698+v-dumas@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:09:02 +0200 Subject: [PATCH 16/36] =?UTF-8?q?N=C2=B09160=20Copy=20log=20entry=20from?= =?UTF-8?q?=20parent=20ticket=20(#927)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * N°9160 - Refactor UpdateChildxxxxxLog and add test --- .../datamodel.itop-incident-mgmt-itil.xml | 100 +++-------- .../en.dict.itop-incident-mgmt-itil.php | 6 + .../fr.dict.itop-incident-mgmt-itil.php | 10 +- .../datamodel.itop-request-mgmt-itil.xml | 48 ++--- .../en.dict.itop-request-mgmt-itil.php | 2 + .../fr.dict.itop-request-mgmt-itil.php | 2 + .../datamodel.itop-request-mgmt.xml | 40 +---- .../en.dict.itop-request-mgmt.php | 2 + .../fr.dict.itop-request-mgmt.php | 2 + .../itop-tickets/datamodel.itop-tickets.xml | 57 ++++++ .../itop-tickets/UpdateChildTicketLogTest.php | 166 ++++++++++++++++++ 11 files changed, 281 insertions(+), 154 deletions(-) create mode 100644 tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php diff --git a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml index 3be94a74b2..663ba74ff9 100755 --- a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml +++ b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml @@ -460,6 +460,18 @@ parent_incident_id ref + + id AND status NOT IN ('rejected','resolved','closed')]]> + + parent_request_id + UserRequest + true + DEL_MANUAL + + + parent_request_id + ref + parent_problem_id Problem @@ -987,6 +999,9 @@ + + + @@ -1301,90 +1316,20 @@ LifecycleAction Get('public_log'); - $sLogPublic = $oLog->GetModifiedEntry('html'); - if ($sLogPublic != '') - { - $sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('public_log',$sLogPublic); - $oRequest->DBUpdate(); - } - - } - $oLog = $this->Get('private_log'); - $sLogPrivate = $oLog->GetModifiedEntry('html'); - if ($sLogPrivate != '') - { - $sOQL = "SELECT UserRequest WHERE parent_incident_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('private_log',$sLogPrivate); - $oRequest->DBUpdate(); - } - } - return true; - + if (MetaModel::IsValidClass('UserRequest')) { + return $this->UpdateChildTicketLog('UserRequest', 'parent_incident_id', ['public_log' => 'public_log', 'private_log' => 'private_log'] ); + } + return true; }]]> + false public LifecycleAction Get('public_log'); - $sLogPublic = $oLog->GetModifiedEntry('html'); - if ($sLogPublic != '') - { - $sOQL = "SELECT Incident WHERE parent_incident_id=:ticket"; - $oChildIncidentSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oIncident = $oChildIncidentSet->Fetch()) - { - $oIncident->set('public_log',$sLogPublic); - $oIncident->DBUpdate(); - } - - } - $oLog = $this->Get('private_log'); - $sLogPrivate = $oLog->GetModifiedEntry('html'); - if ($sLogPrivate != '') - { - $sOQL = "SELECT Incident WHERE parent_incident_id=:ticket"; - $oChildIncidentSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oIncident = $oChildIncidentSet->Fetch()) - { - $oIncident->set('private_log',$sLogPrivate); - $oIncident->DBUpdate(); - } - } - return true; - + return $this->UpdateChildTicketLog('Incident', 'parent_incident_id', ['public_log' => 'public_log', 'private_log' => 'private_log']); }]]> @@ -1558,6 +1503,9 @@ 10 + + 15 + 20 diff --git a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php index 2966031a09..a5383abb92 100644 --- a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php +++ b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/en.dict.itop-incident-mgmt-itil.php @@ -44,6 +44,8 @@ Dict::Add('EN US', 'English', 'English', [ 'UI-IncidentManagementOverview-OpenIncidentByStatus' => 'Open incidents by status', 'UI-IncidentManagementOverview-OpenIncidentByAgent' => 'Open incidents by agent', 'UI-IncidentManagementOverview-OpenIncidentByCustomer' => 'Open incidents by customer', + 'Class:Incident/Method:UpdateChildTicketWith:public_log' => 'Public log entry from parent Incident %2$s:

', + 'Class:Incident/Method:UpdateChildTicketWith:private_log' => 'Private log entry from parent Incident [[Incident:%1$s]]:

', ]); // Dictionnay conventions @@ -193,6 +195,10 @@ Dict::Add('EN US', 'English', 'English', [ 'Class:Incident/Attribute:parent_incident_id+' => '', 'Class:Incident/Attribute:parent_incident_ref' => 'Parent incident ref', 'Class:Incident/Attribute:parent_incident_ref+' => '', + 'Class:Incident/Attribute:parent_request_id' => 'Parent request', + 'Class:Incident/Attribute:parent_request_id+' => '', + 'Class:Incident/Attribute:parent_request_ref' => 'Parent request ref', + 'Class:Incident/Attribute:parent_request_ref+' => '', 'Class:Incident/Attribute:parent_change_id' => 'Parent change', 'Class:Incident/Attribute:parent_change_id+' => '', 'Class:Incident/Attribute:parent_change_ref' => 'Parent change ref', diff --git a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/fr.dict.itop-incident-mgmt-itil.php b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/fr.dict.itop-incident-mgmt-itil.php index c7672c7027..7b329d4a2b 100644 --- a/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/fr.dict.itop-incident-mgmt-itil.php +++ b/datamodels/2.x/itop-incident-mgmt-itil/dictionaries/fr.dict.itop-incident-mgmt-itil.php @@ -179,15 +179,19 @@ Dict::Add('FR FR', 'French', 'Français', [ 'Class:Incident/Attribute:pending_reason+' => '', 'Class:Incident/Attribute:parent_incident_id' => 'Incident parent', 'Class:Incident/Attribute:parent_incident_id+' => '', - 'Class:Incident/Attribute:parent_incident_ref' => 'Référence incident parent', + 'Class:Incident/Attribute:parent_incident_ref' => 'Réf. incident parent', 'Class:Incident/Attribute:parent_incident_ref+' => '', + 'Class:Incident/Attribute:parent_request_id' => 'Demande parente', + 'Class:Incident/Attribute:parent_request_id+' => '', + 'Class:Incident/Attribute:parent_request_ref' => 'Réf. demande parente', + 'Class:Incident/Attribute:parent_request_ref+' => '', 'Class:Incident/Attribute:parent_change_id' => 'Changement parent', 'Class:Incident/Attribute:parent_change_id+' => '', - 'Class:Incident/Attribute:parent_change_ref' => 'Ref Changement parent', + 'Class:Incident/Attribute:parent_change_ref' => 'Réf. changement parent', 'Class:Incident/Attribute:parent_change_ref+' => '', 'Class:Incident/Attribute:parent_problem_id' => 'Problème lié', 'Class:Incident/Attribute:parent_problem_id+' => '', - 'Class:Incident/Attribute:parent_problem_ref' => 'Référence problème lié', + 'Class:Incident/Attribute:parent_problem_ref' => 'Réf. problème lié', 'Class:Incident/Attribute:parent_problem_ref+' => '', 'Class:Incident/Attribute:related_request_list' => 'Requêtes filles', 'Class:Incident/Attribute:related_request_list+' => '', diff --git a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml index 766272cbec..9169fe93eb 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml +++ b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml @@ -1451,44 +1451,17 @@ public LifecycleAction UpdateChildTicketLog('UserRequest', 'parent_request_id', ['public_log' => 'public_log', 'private_log' => 'private_log' ]); +}]]> +
+ + false + public + LifecycleAction + Get('public_log'); - $sLogPublic = $oLog->GetModifiedEntry('html'); - if ($sLogPublic != '') - { - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('public_log',$sLogPublic); - $oRequest->DBUpdate(); - } - - } - $oLog = $this->Get('private_log'); - $sLogPrivate = $oLog->GetModifiedEntry('html'); - if ($sLogPrivate != '') - { - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('private_log',$sLogPrivate); - $oRequest->DBUpdate(); - } - } - return true; - + return $this->UpdateChildTicketLog('Incident', 'parent_request_id', ['public_log' => 'public_log', 'private_log' => 'private_log']); }]]> @@ -1526,6 +1499,7 @@ parent::OnUpdate(); $this->Set('last_update', time()); $this->UpdateChildRequestLog(); + $this->UpdateChildIncidentLog(); }]]> diff --git a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php index a2d2fe1861..4c7c504fd0 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/en.dict.itop-request-mgmt-itil.php @@ -37,6 +37,8 @@ Dict::Add('EN US', 'English', 'English', [ 'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Open requests by customer', 'Class:UserRequest:KnownErrorList' => 'Known Errors', 'Class:UserRequest:KnownErrorList+' => 'Known Errors related to Functional CI linked to the current ticket', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log automatic copy from parent User Request %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log automatic copy from parent User Request [[UserRequest:%1$s]]:

', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/fr.dict.itop-request-mgmt-itil.php b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/fr.dict.itop-request-mgmt-itil.php index b61397a8fd..ec07842c85 100644 --- a/datamodels/2.x/itop-request-mgmt-itil/dictionaries/fr.dict.itop-request-mgmt-itil.php +++ b/datamodels/2.x/itop-request-mgmt-itil/dictionaries/fr.dict.itop-request-mgmt-itil.php @@ -42,6 +42,8 @@ Dict::Add('FR FR', 'French', 'Français', [ 'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Requêtes ouvertes par client', 'Class:UserRequest:KnownErrorList' => 'Erreurs connues', 'Class:UserRequest:KnownErrorList+' => 'Erreurs connues liées à des éléments de configuration impactés par ce ticket', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Copie automatique du log public de la demande parente %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Copie automatique du log privé de la demande parente [[UserRequest:%1$s]]:

', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml index db1edcb30d..97025b5810 100755 --- a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml +++ b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml @@ -1486,44 +1486,8 @@ public LifecycleAction Get('public_log'); - $sLogPublic = $oLog->GetModifiedEntry('html'); - if ($sLogPublic != '') - { - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('public_log',$sLogPublic); - $oRequest->DBUpdate(); - } - - } - $oLog = $this->Get('private_log'); - $sLogPrivate = $oLog->GetModifiedEntry('html'); - if ($sLogPrivate != '') - { - $sOQL = "SELECT UserRequest WHERE parent_request_id=:ticket"; - $oChildRequestSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), - array(), - array( - 'ticket' => $this->GetKey(), - ) - ); - while($oRequest = $oChildRequestSet->Fetch()) - { - $oRequest->set('private_log',$sLogPrivate); - $oRequest->DBUpdate(); - } - } - return true; - +{ + return $this->UpdateChildTicketLog('UserRequest', 'parent_request_id', ['public_log' => 'public_log', 'private_log' => 'private_log'] ); }]]> diff --git a/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php index 1aeaac0830..5e8f40bacb 100644 --- a/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/dictionaries/en.dict.itop-request-mgmt.php @@ -41,6 +41,8 @@ Dict::Add('EN US', 'English', 'English', [ 'Menu:UserRequest:MyWorkOrders+' => 'All work orders assigned to me', 'Class:Problem:KnownProblemList' => 'Known problems', 'Tickets:Related:OpenIncidents' => 'Open incidents', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Public log automatic copy from parent User Request %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Private log automatic copy from parent User Request [[UserRequest:%1$s]]:

', ]); // Dictionnay conventions diff --git a/datamodels/2.x/itop-request-mgmt/dictionaries/fr.dict.itop-request-mgmt.php b/datamodels/2.x/itop-request-mgmt/dictionaries/fr.dict.itop-request-mgmt.php index 802d264f02..5e9bc7a2ef 100644 --- a/datamodels/2.x/itop-request-mgmt/dictionaries/fr.dict.itop-request-mgmt.php +++ b/datamodels/2.x/itop-request-mgmt/dictionaries/fr.dict.itop-request-mgmt.php @@ -42,6 +42,8 @@ Dict::Add('FR FR', 'French', 'Français', [ 'UI-RequestManagementOverview-OpenRequestByCustomer' => 'Requêtes ouvertes par organisation', 'Class:UserRequest:KnownErrorList' => 'Erreurs connues', 'Class:UserRequest:KnownErrorList+' => 'Erreurs connues liées à des éléments de configuration impactés par ce ticket', + 'Class:UserRequest/Method:UpdateChildTicketWith:public_log' => 'Copie automatique du log public de la demande parente %2$s:

', + 'Class:UserRequest/Method:UpdateChildTicketWith:private_log' => 'Copie automatique du log privé de la demande parente [[UserRequest:%1$s]]:

', 'Menu:UserRequest:MyWorkOrders' => 'Tâches qui me sont assignées', 'Menu:UserRequest:MyWorkOrders+' => '', 'Class:Problem:KnownProblemList' => 'Problèmes connus', diff --git a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml index f9a5ec37f6..99ee4c78e8 100755 --- a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml +++ b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml @@ -351,6 +351,63 @@ }]]>
+ + 'private_log']) + * So in the example parent.public_log will be copied into each child.private_log + * with a prefix using a dictionary entry like 'Class:/Method:UpdateChildTicketWith:' with one placeholder %1$s for the parent ticket ref + * resulting in an entry like "Copy of public log entry from parent Incident I-000123: " in the child private_log + * + */]]> + + false + public + LifecycleAction + GetTargetClass() !== get_class($this))) { + ErrorLog::Debug("Attribute $sChildParentAttCode should be an external key of class $sChildClass, pointing to class ".get_class($this),"DataModel"); + return true; // Do nothing + } + + $sParentClass = get_class($this); + $aChildEntries = []; + foreach ($aLogAttCodes as $sParentAttCode => $sChildAttCode) { + if (MetaModel::IsValidAttCode($sParentClass, $sParentAttCode) && MetaModel::GetAttributeDef($sParentClass, $sParentAttCode) instanceof AttributeCaseLog + && MetaModel::IsValidAttCode($sChildClass, $sChildAttCode) && MetaModel::GetAttributeDef($sChildClass, $sChildAttCode) instanceof AttributeCaseLog + && (!utils::IsNullOrEmptyString($this->Get($sParentAttCode)->GetModifiedEntry('html')))) { + $aChildEntries[$sChildAttCode] = Dict::Format('Class:'.$sParentClass.'/Method:UpdateChildTicketWith:'.$sParentAttCode, $this->GetKey(), $this->Get('ref')).$this->Get($sParentAttCode)->GetModifiedEntry('html'); + } + } + if ($aChildEntries == []) { + return true; // nothing to update + } + + $sOQL = "SELECT $sChildClass WHERE $sChildParentAttCode = :ticket_id"; + $oChildSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOQL), [], ['ticket_id' => $this->GetKey()]); + while ($oChild = $oChildSet->Fetch()) { + if (is_object($oChild)) { // Seems that empty set, maybe in case of OQL syntax error, can return a single empty object instead of no object at all + foreach ($aChildEntries as $sAttCode => $sEntry) { + $oChild->Set($sAttCode, $sEntry); + } + $oChild->DBUpdate(); + } + } + return true; + + }]]> +
diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php new file mode 100644 index 0000000000..5781d0711b --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-tickets/UpdateChildTicketLogTest.php @@ -0,0 +1,166 @@ + +// + +namespace Combodo\iTop\Test\UnitTest\Module\iTopTickets; + +use Combodo\iTop\Test\UnitTest\ItopDataTestCase; +use ormCaseLog; +use MetaModel; + +class UpdateChildTicketLogTest extends ItopDataTestCase +{ + public function testUpdateChildTicketLog_PublicLogOnTwoChild(): void + { + //Given a parent ticket with two child ticket + list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(2); + $this->assertCount(2, $aChildrenTree[$iParentTicket], 'The test setup should create exactly two child tickets.'); + $sParentPublicLogEntry = 'This is a public log entry for the parent ticket.'; + $oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket); + + // When I enter a public_log entry for the parent ticket + $oParentTicket->Set('public_log', $sParentPublicLogEntry); + $oParentTicket->DBUpdate(); + + // Then the log should be copied to all descendants and contain parent references recursively + $this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry); + } + + public function testUpdateChildTicketLog_PrivateAndPublicLog(): void + { + //Given a parent ticket with two child ticket + list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(3); + $sParentPublicLogEntry = 'This is a public log entry for the parent ticket.'; + $sParentPrivateLogEntry = 'This is a private log entry for the parent ticket.'; + + // When I enter both a public_log and a private_log entry for the parent ticket + $oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket); + $oParentTicket->Set('public_log', $sParentPublicLogEntry); + $oParentTicket->Set('private_log', $sParentPrivateLogEntry); + $oParentTicket->DBUpdate(); + + // Then both logs should be copied to all descendants and keep ancestor references recursively + $this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'public_log', $sParentPublicLogEntry); + $this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry); + } + + public function testUpdateChildTicketLog_PrivateLogOnMultipleLevels(): void + { + //Given a parent ticket with two child ticket + list($iParentTicket, $aChildrenTree) = $this->GivenUserRequests(1, 4); + $sParentPrivateLogEntry = 'This is a private log entry for the parent ticket.'; + + // When I enter both a public_log and a private_log entry for the parent ticket + $oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket); + $oParentTicket->Set('private_log', $sParentPrivateLogEntry); + $oParentTicket->DBUpdate(); + + // Then the private log should be copied on each level with parent + grand-parent +... references + $this->AssertLogContainsParentsRefOrKeyRecursively($iParentTicket, $aChildrenTree[$iParentTicket], 'private_log', $sParentPrivateLogEntry); + } + + private function AssertLogContainsParentsRefOrKeyRecursively(int $iParentTicket, array $aChildrenTree, string $sLogAttCode, string $sExpectedEntry, array $aAncestors = []): void + { + $oParentTicket = MetaModel::GetObject('UserRequest', $iParentTicket); + $aAncestorsToFind = array_merge($aAncestors, [[ + 'id' => (string) $iParentTicket, + 'ref' => (string) $oParentTicket->Get('ref'), + ]]); + + foreach ($aChildrenTree as $iChildOrIndex => $aChildNodeOrId) { + if (is_array($aChildNodeOrId)) { + $iChildTicket = (int) $iChildOrIndex; + $aGrandChildrenTree = $aChildNodeOrId; + } else { + $iChildTicket = (int) $aChildNodeOrId; + $aGrandChildrenTree = []; + } + + $oChildTicket = MetaModel::GetObject('UserRequest', $iChildTicket); + $sLastLogEntry = $oChildTicket->Get($sLogAttCode)->GetLatestEntry(); + $this->assertNotEmpty($sLastLogEntry, "The $sLogAttCode entry was not copied to child ticket #$iChildTicket."); + $this->assertStringContainsString($sExpectedEntry, $sLastLogEntry, "The $sLogAttCode entry on child ticket #$iChildTicket does not contain the original parent entry."); + foreach ($aAncestorsToFind as $aAncestor) { + $bContainsAncestorRef = strpos($sLastLogEntry, $aAncestor['ref']) !== false; + $bContainsAncestorId = strpos($sLastLogEntry, $aAncestor['id']) !== false; + $this->assertTrue( + $bContainsAncestorRef || $bContainsAncestorId, + "The $sLogAttCode entry on child ticket #$iChildTicket does not contain ancestor ref '{$aAncestor['ref']}' nor ancestor id '{$aAncestor['id']}'." + ); + } + + if ($aGrandChildrenTree !== []) { + $this->AssertLogContainsParentsRefOrKeyRecursively($iChildTicket, $aGrandChildrenTree, $sLogAttCode, $sExpectedEntry, $aAncestorsToFind); + } + } + } + /** + * @return array + * @throws \Exception + */ + private function GivenUserRequests(int $iCount, int $iLevel = 2): array + { + $iOrg = $this->GivenObjectInDB('Organization', [ + 'name' => 'Test Organization for Log Update', + ]); + // Given a parent ticket + $iParentTicket = $this->GivenObjectInDB('UserRequest', [ + 'title' => 'Parent Ticket for Log Update Test', + 'description' => 'This is the parent ticket for testing log updates.', + 'org_id' => $iOrg, + 'ref' => 'R-00001', + ]); + + $iRemainingLevels = max(0, $iLevel - 1); + $iRefCounter = 1; + $aChildrenTree = [ + $iParentTicket => $this->GivenChildTicketsRecursive($iParentTicket, $iCount, $iRemainingLevels, $iOrg, $iRefCounter), + ]; + + return [$iParentTicket, $aChildrenTree]; + } + + private function GivenChildTicketsRecursive(int $iParentTicket, int $iCount, int $iRemainingLevels, int $iOrg, int &$iRefCounter): array + { + if ($iRemainingLevels <= 0) { + return []; + } + + $aChildren = []; + for ($i = 1; $i <= $iCount; $i++) { + $iRefCounter++; + $sRef = sprintf('R-%05d', $iRefCounter); + $iChildTicket = $this->GivenObjectInDB('UserRequest', [ + 'title' => "Child Ticket $sRef for Log Update Test", + 'description' => "This is child ticket $sRef for testing log updates.", + 'org_id' => $iOrg, + 'parent_request_id' => $iParentTicket, + 'ref' => $sRef, + ]); + + if ($iRemainingLevels > 1) { + $aChildren[$iChildTicket] = $this->GivenChildTicketsRecursive($iChildTicket, $iCount, $iRemainingLevels - 1, $iOrg, $iRefCounter); + } else { + $aChildren[] = $iChildTicket; + } + } + + return $aChildren; + } +} From 6a20e36434fc99c9cfa32f1eb4327a77460613f7 Mon Sep 17 00:00:00 2001 From: Timmy38 Date: Fri, 12 Jun 2026 11:23:16 +0200 Subject: [PATCH 17/36] =?UTF-8?q?N=C2=B09683=20Fix=20Code=20Style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/UI/Base/Layout/Extension/ExtensionDetails.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php b/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php index cb69d48ac7..ef3578142b 100644 --- a/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php +++ b/sources/Application/UI/Base/Layout/Extension/ExtensionDetails.php @@ -182,7 +182,7 @@ class ExtensionDetails extends UIContentBlock $sName = 'aSelectedExtensions['.$this->GetCode().']'; $this->oToggler = new Toggler(); $this->oToggler->SetName($sName); - $this->oToggler->SetTooltip(Dict::Format('UI:Layout:ExtensionsDetails:TogglerTooltip' ,$this->GetLabel())); + $this->oToggler->SetTooltip(Dict::Format('UI:Layout:ExtensionsDetails:TogglerTooltip', $this->GetLabel())); $this->oToggler->AddCSSClass('toggler-install'); } From 65b58ec4e2fc886b6c85e8ed73adff79327ee9fc Mon Sep 17 00:00:00 2001 From: Timmy38 Date: Mon, 15 Jun 2026 11:49:52 +0200 Subject: [PATCH 18/36] =?UTF-8?q?N=C2=B09690=20Removed=20and=20added=20mod?= =?UTF-8?q?ules/extensions=20are=20now=20read=20from=20GET=20parameters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../simulate-audit-from-setup.php | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php index 8ed3a18ef4..3620055f43 100644 --- a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php @@ -6,12 +6,6 @@ require_once(dirname(__DIR__, 6).'/approot.inc.php'); require_once(APPROOT.'application/startup.inc.php'); require_once(APPROOT.'setup/setuputils.class.inc.php'); -$aParams = [ - "exec_module" => "combodo-data-feature-removal", - "exec_page" => "index.php", - 'exec_env' => 'production', -]; - new ContextTag(ContextTag::TAG_SETUP); $sToken = SetupUtils::CreateSetupToken(); @@ -34,24 +28,45 @@ function GetLastestInstallFile(): ?string return $sLastFilePath; } -$aRemovedExtensions = [ - 'itop-container-mgmt' => 'Containerization', -]; - $sPath = GetLastestInstallFile(); if (is_null($sPath)) { - throw new Exception("$sPath no installation XM. Launch a setup...."); + throw new Exception("$sPath no installation XML. Launch a setup...."); } $aParams = new XMLParameters($sPath); -$aSelectedModules = array_filter($aParams->Get('selected_modules', []), static function ($element) { - global $aRemovedExtensions; - return ! array_key_exists($element, $aRemovedExtensions); -}); +$aSelectedModules = $aParams->Get('selected_modules', []); +$aSelectedExtensions = $aParams->Get('selected_extensions', []); -$aSelectedExtensions = array_filter($aParams->Get('selected_extensions', []), static function ($element) { - global $aRemovedExtensions; - return ! array_key_exists($element, $aRemovedExtensions); -}); +$sAddedExtensions = utils::ReadParam('added_extensions', ''); +$aAddedExtensions = []; +if (mb_strlen($sAddedExtensions) > 0) { + $aAddedExtensions = explode(',', $sAddedExtensions); +} +$oExtensionMap = new iTopExtensionsMap(); +foreach ($aAddedExtensions as $sExtensionCode) { + if (mb_strlen($sExtensionCode) <= 0) { + continue; + } + $oExtension = $oExtensionMap->GetFromExtensionCode($sExtensionCode); + $aSelectedExtensions[] = $oExtension->sCode; + foreach ($oExtension->aModules as $sModuleCode) { + if (!in_array($sModuleCode, $aSelectedModules)) { + $aSelectedModules[] = $sModuleCode; + } + } +} + +$sRemovedExtensions = utils::ReadParam('removed_modules', '',false, 'raw'); +$aRemovedExtensionsAndModules = []; +if (mb_strlen($sRemovedExtensions) > 0) { + $aRemovedExtensionsAndModules = explode(',', $sRemovedExtensions); +} + +$aSelectedModules = array_filter($aSelectedModules, fn($element) => !in_array($element, $aRemovedExtensionsAndModules)); +$aSelectedExtensions = array_filter($aSelectedExtensions, fn($element) => !in_array($element, $aRemovedExtensionsAndModules)); +$aRemovedExtensionsAndModules = array_filter($aRemovedExtensionsAndModules, fn($element) => !is_null($oExtensionMap->GetFromExtensionCode($element))); + +$aRemovedExtensions = array_combine($aRemovedExtensionsAndModules, $aRemovedExtensionsAndModules); +$aAddedExtensions = array_combine($aAddedExtensions, $aAddedExtensions); $aPostParams = [ "auth_user" => 'admin', @@ -62,8 +77,9 @@ $aPostParams = [ 'selected_modules' => utils::HtmlEntities(json_encode($aSelectedModules)), 'selected_extensions' => utils::HtmlEntities(json_encode($aSelectedExtensions)), 'removed_extensions' => utils::HtmlEntities(json_encode($aRemovedExtensions)), - 'force-uninstall' => "on", - 'use_symbolic_links' => "", + 'added_extensions' => utils::HtmlEntities(json_encode($aAddedExtensions)), + 'force-uninstall' => 'on', + 'use_symbolic_links' => '', ]; $sHiddenPostedInput = ""; From 3e38e349e5537e05ca01b91db031fd00078e9e05 Mon Sep 17 00:00:00 2001 From: Timmy38 Date: Mon, 15 Jun 2026 11:53:14 +0200 Subject: [PATCH 19/36] =?UTF-8?q?N=C2=B09690=20Fix=20code=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../simulate-audit-from-setup.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php index 3620055f43..eec10f1c46 100644 --- a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php @@ -55,15 +55,15 @@ foreach ($aAddedExtensions as $sExtensionCode) { } } -$sRemovedExtensions = utils::ReadParam('removed_modules', '',false, 'raw'); +$sRemovedExtensions = utils::ReadParam('removed_modules', '', false, 'raw'); $aRemovedExtensionsAndModules = []; if (mb_strlen($sRemovedExtensions) > 0) { $aRemovedExtensionsAndModules = explode(',', $sRemovedExtensions); } -$aSelectedModules = array_filter($aSelectedModules, fn($element) => !in_array($element, $aRemovedExtensionsAndModules)); -$aSelectedExtensions = array_filter($aSelectedExtensions, fn($element) => !in_array($element, $aRemovedExtensionsAndModules)); -$aRemovedExtensionsAndModules = array_filter($aRemovedExtensionsAndModules, fn($element) => !is_null($oExtensionMap->GetFromExtensionCode($element))); +$aSelectedModules = array_filter($aSelectedModules, fn ($element) => !in_array($element, $aRemovedExtensionsAndModules)); +$aSelectedExtensions = array_filter($aSelectedExtensions, fn ($element) => !in_array($element, $aRemovedExtensionsAndModules)); +$aRemovedExtensionsAndModules = array_filter($aRemovedExtensionsAndModules, fn ($element) => !is_null($oExtensionMap->GetFromExtensionCode($element))); $aRemovedExtensions = array_combine($aRemovedExtensionsAndModules, $aRemovedExtensionsAndModules); $aAddedExtensions = array_combine($aAddedExtensions, $aAddedExtensions); From dd6b03cccc74b0f99fa9ab4200466d8d2e3889fd Mon Sep 17 00:00:00 2001 From: Anne-Catherine <57360138+accognet@users.noreply.github.com> Date: Mon, 15 Jun 2026 15:58:36 +0200 Subject: [PATCH 20/36] =?UTF-8?q?N=C2=B03789=20-=20Bulk=20actions=20on=20r?= =?UTF-8?q?eplicas:=20(Resync,=20Force=20Update,=20Delete,...)=20(#546)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/cmdbabstract.class.inc.php | 6 + application/displayblock.class.inc.php | 16 +- css/backoffice/pages/_data-synchro.scss | 10 + dictionaries/cs.dictionary.itop.core.php | 33 +++ dictionaries/da.dictionary.itop.core.php | 33 +++ dictionaries/de.dictionary.itop.core.php | 33 +++ dictionaries/en.dictionary.itop.core.php | 138 +++++++----- dictionaries/en_gb.dictionary.itop.core.php | 33 +++ dictionaries/es_cr.dictionary.itop.core.php | 33 +++ dictionaries/fr.dictionary.itop.core.php | 34 +++ dictionaries/hu.dictionary.itop.core.php | 33 +++ dictionaries/it.dictionary.itop.core.php | 33 +++ dictionaries/ja.dictionary.itop.core.php | 33 +++ dictionaries/nl.dictionary.itop.core.php | 33 +++ dictionaries/pl.dictionary.itop.core.php | 33 +++ dictionaries/pt_br.dictionary.itop.core.php | 33 +++ dictionaries/ru.dictionary.itop.core.php | 34 +++ dictionaries/sk.dictionary.itop.core.php | 33 +++ dictionaries/tr.dictionary.itop.core.php | 33 +++ dictionaries/zh_cn.dictionary.itop.core.php | 34 +++ lib/autoload.php | 5 +- lib/composer/autoload_classmap.php | 2 + lib/composer/autoload_static.php | 158 ++++++------- lib/composer/platform_check.php | 5 +- pages/UI.php | 124 ++--------- sources/Application/Helper/BulkHelper.php | 178 +++++++++++++++ .../Helper/SynchroReplicaHelper.php | 115 ++++++++++ .../AttributeObjectKey.php | 4 + synchro/replica.php | 99 ++++++++- synchro/synchrodatasource.class.inc.php | 210 +++++++++++++++++- 30 files changed, 1352 insertions(+), 249 deletions(-) create mode 100644 sources/Application/Helper/BulkHelper.php create mode 100644 sources/Application/Helper/SynchroReplicaHelper.php diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 8c0441e84c..9fdfc7714f 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -524,6 +524,12 @@ JS $sLabel = Dict::S('Tag:Synchronized'); $sSynchroTagId = 'synchro_icon-'.$this->GetKey(); $aTags[$sSynchroTagId] = ['title' => $sTip, 'css_classes' => 'ibo-object-details--tag--synchronized', 'decoration_classes' => 'fas fa-lock', 'label' => $sLabel]; + if (UserRights::IsActionAllowed(SynchroReplica::class, UR_ACTION_READ)) { + $oDBSearch = DBObjectSearch::FromOQL('SELECT SynchroReplica WHERE dest_class=:sClass AND dest_id=:id'); + $sFilter = rawurlencode($oDBSearch->serialize(false,['sClass'=>get_class($this),'id'=>$this->GetKey()])); + $sUrlSearchReplica = 'UI.php?operation=search&filter='.$sFilter; + $oPage->add_ready_script("$('#$sSynchroTagId').on('click',function() {window.location = '$sUrlSearchReplica' });"); + } } } diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index ca2fe6857a..26a8fe7a8d 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -1947,7 +1947,7 @@ class MenuBlock extends DisplayBlock $sSelectedClassName = MetaModel::GetName($sSelectedClass); // Check rights on class - $bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject')); + $bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && (($oReflectionClass->IsSubclassOf(cmdbAbstractObject::class) || $sSelectedClass === SynchroReplica::class)); $bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE); // Refine filter on selected class so bullk actions occur on the right class @@ -1958,7 +1958,15 @@ class MenuBlock extends DisplayBlock // Action label dict code has a specific suffix for "Link" / "Remote" aliases to allow dedicated labels in linksets. $sActionLabelCodeSuffix = in_array($sSelectedAlias, ['Link', 'Remote']) ? $sSelectedAlias : 'Class'; if ($bIsBulkModifyAllowed) { - $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName)); + if($sSelectedClass === SynchroReplica::class){ + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnlinkAll:', Dict::S('Class:SynchroReplica/Action:unlink_all'),'unlink'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnLinkSynchroAll:', Dict::S('Class:SynchroReplica/Action:unlinksynchro_all'),'unlinksynchro'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:SynchroAll:', Dict::S('Class:SynchroReplica/Action:synchro_all'),'synchro'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:AllowDeleteAll:', Dict::S('Class:SynchroReplica/Action:allowdelete_all'),'allowdelete'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:DenyDeleteAll:', Dict::S('Class:SynchroReplica/Action:denydelete_all'),'denydelete'); + } else { + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName)); + } } if ($bIsBulkDeleteAllowed) { $this->AddBulkDeleteObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:BulkDelete:'.$sSelectedAlias, Dict::Format('UI:Menu:BulkDelete_'.$sActionLabelCodeSuffix, $sSelectedClassName)); @@ -2478,11 +2486,11 @@ class MenuBlock extends DisplayBlock * @since 3.1.0 * @internal */ - protected function AddBulkModifyObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:ModifyAll', $sActionLabel = 'UI:Menu:ModifyAll'): void + protected function AddBulkModifyObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:ModifyAll', $sActionLabel = 'UI:Menu:ModifyAll', $sOperationName = 'modify'): void { $aActions[$sActionIdentifier] = [ 'label' => Dict::S($sActionLabel), - 'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=select_for_modify_all&class=$sClass&filter=".urlencode($sFilter)), + 'url' => $this->PrepareUrlForStandardMenuAction($sClass, 'operation=select_for_'.$sOperationName.'_all&class='.$sClass.'&filter='.urlencode($sFilter)), ] + $this->GetDefaultParamsForMenuAction(); } diff --git a/css/backoffice/pages/_data-synchro.scss b/css/backoffice/pages/_data-synchro.scss index 956329ce76..fb16024513 100644 --- a/css/backoffice/pages/_data-synchro.scss +++ b/css/backoffice/pages/_data-synchro.scss @@ -80,4 +80,14 @@ $ibo-data-synchro-source--synoptics--cell--arrow--border: 2px solid $ibo-data-sy } .ibo-data-synchro-source--replicas-status--warning{ margin: $ibo-data-synchro-source--replicas-status--warning--margin; +} + +.ibo-page-header--replica-title{ + display: table; + width: 100%; + + >.ibo-toolbar--button{ + display: table-cell; + vertical-align: middle; + } } \ No newline at end of file diff --git a/dictionaries/cs.dictionary.itop.core.php b/dictionaries/cs.dictionary.itop.core.php index 32f49e460c..45898cc0d0 100755 --- a/dictionaries/cs.dictionary.itop.core.php +++ b/dictionaries/cs.dictionary.itop.core.php @@ -976,6 +976,39 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Poslední varování', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Datum vytvoření', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Datum poslední úpravy', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Uživatelské předvolby', 'Class:appUserPreferences/Attribute:userid' => 'Uživatel', 'Class:appUserPreferences/Attribute:preferences' => 'Předvolby', diff --git a/dictionaries/da.dictionary.itop.core.php b/dictionaries/da.dictionary.itop.core.php index fef6af27c1..4f50299508 100644 --- a/dictionaries/da.dictionary.itop.core.php +++ b/dictionaries/da.dictionary.itop.core.php @@ -975,6 +975,39 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Advarsler', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Oprettelses Dato', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Sidste Ændrings Dato', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Bruger Indstillinger', 'Class:appUserPreferences/Attribute:userid' => 'Bruger', 'Class:appUserPreferences/Attribute:preferences' => 'Inst.', diff --git a/dictionaries/de.dictionary.itop.core.php b/dictionaries/de.dictionary.itop.core.php index dacc17ab66..8aa2fe4a51 100644 --- a/dictionaries/de.dictionary.itop.core.php +++ b/dictionaries/de.dictionary.itop.core.php @@ -972,6 +972,39 @@ Dict::Add('DE DE', 'German', 'Deutsch', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Letzte Warnung', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Erzeugungs-Datum', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Datum der letzten Modifikation', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Benutzer-Voreinstellungen', 'Class:appUserPreferences/Attribute:userid' => 'Benutzer', 'Class:appUserPreferences/Attribute:preferences' => 'Voreinstellungen', diff --git a/dictionaries/en.dictionary.itop.core.php b/dictionaries/en.dictionary.itop.core.php index 8b26efc141..2ab22beee6 100644 --- a/dictionaries/en.dictionary.itop.core.php +++ b/dictionaries/en.dictionary.itop.core.php @@ -982,7 +982,7 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of 'Core:SynchroReplica:PublicData' => 'Public Data', 'Core:SynchroReplica:PrivateDetails' => 'Private Details', 'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s', - 'Core:SynchroReplica:ListOfReplicas' => 'List of Replica', + 'Core:SynchroReplica:ListOfReplicas' => 'Replicas of the data source: %1$s', 'Core:SynchroAttExtKey:ReconciliationById' => 'id (Primary Key)', 'Core:SynchroAtt:attcode' => 'Attribute', 'Core:SynchroAtt:attcode+' => 'Field of the object', @@ -1036,65 +1036,99 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of 'Class:SynchroAttLinkSet/Attribute:row_separator' => 'Rows separator', 'Class:SynchroAttLinkSet/Attribute:attribute_separator' => 'Attributes separator', 'Class:SynchroLog' => 'Synchro Log', - 'Class:SynchroLog/Attribute:sync_source_id' => 'Synchro Data Source', - 'Class:SynchroLog/Attribute:start_date' => 'Start Date', - 'Class:SynchroLog/Attribute:end_date' => 'End Date', - 'Class:SynchroLog/Attribute:status' => 'Status', - 'Class:SynchroLog/Attribute:status/Value:completed' => 'Completed', - 'Class:SynchroLog/Attribute:status/Value:error' => 'Error', - 'Class:SynchroLog/Attribute:status/Value:running' => 'Still Running', - 'Class:SynchroLog/Attribute:stats_nb_replica_seen' => 'Nb replica seen', - 'Class:SynchroLog/Attribute:stats_nb_replica_total' => 'Nb replica total', - 'Class:SynchroLog/Attribute:stats_nb_obj_deleted' => 'Nb objects deleted', - 'Class:SynchroLog/Attribute:stats_nb_obj_deleted_errors' => 'Nb of errors while deleting', - 'Class:SynchroLog/Attribute:stats_nb_obj_obsoleted' => 'Nb objects obsoleted', - 'Class:SynchroLog/Attribute:stats_nb_obj_obsoleted_errors' => 'Nb of errors while obsoleting', - 'Class:SynchroLog/Attribute:stats_nb_obj_created' => 'Nb objects created', - 'Class:SynchroLog/Attribute:stats_nb_obj_created_errors' => 'Nb or errors while creating', - 'Class:SynchroLog/Attribute:stats_nb_obj_updated' => 'Nb objects updated', - 'Class:SynchroLog/Attribute:stats_nb_obj_updated_errors' => 'Nb errors while updating', - 'Class:SynchroLog/Attribute:stats_nb_replica_reconciled_errors' => 'Nb of errors during reconciliation', + 'Class:SynchroLog/Attribute:sync_source_id' => 'Synchro Data Source', + 'Class:SynchroLog/Attribute:start_date' => 'Start Date', + 'Class:SynchroLog/Attribute:end_date' => 'End Date', + 'Class:SynchroLog/Attribute:status' => 'Status', + 'Class:SynchroLog/Attribute:status/Value:completed' => 'Completed', + 'Class:SynchroLog/Attribute:status/Value:error' => 'Error', + 'Class:SynchroLog/Attribute:status/Value:running' => 'Still Running', + 'Class:SynchroLog/Attribute:stats_nb_replica_seen' => 'Nb replica seen', + 'Class:SynchroLog/Attribute:stats_nb_replica_total' => 'Nb replica total', + 'Class:SynchroLog/Attribute:stats_nb_obj_deleted' => 'Nb objects deleted', + 'Class:SynchroLog/Attribute:stats_nb_obj_deleted_errors' => 'Nb of errors while deleting', + 'Class:SynchroLog/Attribute:stats_nb_obj_obsoleted' => 'Nb objects obsoleted', + 'Class:SynchroLog/Attribute:stats_nb_obj_obsoleted_errors' => 'Nb of errors while obsoleting', + 'Class:SynchroLog/Attribute:stats_nb_obj_created' => 'Nb objects created', + 'Class:SynchroLog/Attribute:stats_nb_obj_created_errors' => 'Nb or errors while creating', + 'Class:SynchroLog/Attribute:stats_nb_obj_updated' => 'Nb objects updated', + 'Class:SynchroLog/Attribute:stats_nb_obj_updated_errors' => 'Nb errors while updating', + 'Class:SynchroLog/Attribute:stats_nb_replica_reconciled_errors' => 'Nb of errors during reconciliation', 'Class:SynchroLog/Attribute:stats_nb_replica_disappeared_no_action' => 'Nb replica disappeared', - 'Class:SynchroLog/Attribute:stats_nb_obj_new_updated' => 'Nb objects updated', - 'Class:SynchroLog/Attribute:stats_nb_obj_new_unchanged' => 'Nb objects unchanged', - 'Class:SynchroLog/Attribute:last_error' => 'Last error', - 'Class:SynchroLog/Attribute:traces' => 'Traces', - 'Class:SynchroReplica' => 'Synchro Replica', - 'Class:SynchroReplica/Attribute:sync_source_id' => 'Synchro Data Source', - 'Class:SynchroReplica/Attribute:dest_id' => 'Destination object (ID)', - 'Class:SynchroReplica/Attribute:dest_class' => 'Destination type', - 'Class:SynchroReplica/Attribute:status_last_seen' => 'Last seen', - 'Class:SynchroReplica/Attribute:status' => 'Status', - 'Class:SynchroReplica/Attribute:status/Value:modified' => 'Modified', - 'Class:SynchroReplica/Attribute:status/Value:new' => 'New', - 'Class:SynchroReplica/Attribute:status/Value:obsolete' => 'Obsolete', - 'Class:SynchroReplica/Attribute:status/Value:orphan' => 'Orphan', - 'Class:SynchroReplica/Attribute:status/Value:synchronized' => 'Synchronized', - 'Class:SynchroReplica/Attribute:status_dest_creator' => 'Object Created ?', - 'Class:SynchroReplica/Attribute:status_last_error' => 'Last Error', - 'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings', - 'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date', - 'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date', - 'Class:appUserPreferences' => 'User Preferences', - 'Class:appUserPreferences/Attribute:userid' => 'User', + 'Class:SynchroLog/Attribute:stats_nb_obj_new_updated' => 'Nb objects updated', + 'Class:SynchroLog/Attribute:stats_nb_obj_new_unchanged' => 'Nb objects unchanged', + 'Class:SynchroLog/Attribute:last_error' => 'Last error', + 'Class:SynchroLog/Attribute:traces' => 'Traces', + 'Class:SynchroReplica' => 'Synchro Replica', + 'Class:SynchroReplica/Attribute:sync_source_id' => 'Synchro Data Source', + 'Class:SynchroReplica/Attribute:dest_id' => 'Destination object (ID)', + 'Class:SynchroReplica/Attribute:dest_class' => 'Destination type', + 'Class:SynchroReplica/Attribute:status_last_seen' => 'Last seen', + 'Class:SynchroReplica/Attribute:status' => 'Status', + 'Class:SynchroReplica/Attribute:status/Value:modified' => 'Modified', + 'Class:SynchroReplica/Attribute:status/Value:new' => 'New', + 'Class:SynchroReplica/Attribute:status/Value:obsolete' => 'Obsolete', + 'Class:SynchroReplica/Attribute:status/Value:orphan' => 'Orphan', + 'Class:SynchroReplica/Attribute:status/Value:synchronized' => 'Synchronized', + 'Class:SynchroReplica/Attribute:status_dest_creator' => 'Object Created ?', + 'Class:SynchroReplica/Attribute:status_last_error' => 'Last Error', + 'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings', + 'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date', + 'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date', + 'Class:SynchroReplica/Action:delete+' => 'delete replica', + 'Class:SynchroReplica/Action:unlink' => 'Unlink', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica', + 'Class:SynchroReplica/Action:synchro' => 'Synchro', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica', + + 'Class:appUserPreferences' => 'User Preferences', + 'Class:appUserPreferences/Attribute:userid' => 'User', 'Class:appUserPreferences/Attribute:preferences' => 'Prefs', - 'Core:ExecProcess:Code1' => 'Wrong command or command finished with errors (e.g. wrong script name)', - 'Core:ExecProcess:Code255' => 'PHP Error (parsing, or runtime)', + 'Core:ExecProcess:Code1' => 'Wrong command or command finished with errors (e.g. wrong script name)', + 'Core:ExecProcess:Code255' => 'PHP Error (parsing, or runtime)', // Attribute Duration - 'Core:Duration_Seconds' => '%1$ds', - 'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds', - 'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds', - 'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds', + 'Core:Duration_Seconds' => '%1$ds', + 'Core:Duration_Minutes_Seconds' => '%1$dmin %2$ds', + 'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds', + 'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds', // Explain working time computing - 'Core:ExplainWTC:ElapsedTime' => 'Time elapsed (stored as "%1$s")', - 'Core:ExplainWTC:StopWatch-TimeSpent' => 'Time spent for "%1$s"', - 'Core:ExplainWTC:StopWatch-Deadline' => 'Deadline for "%1$s" at %2$d%%', + 'Core:ExplainWTC:ElapsedTime' => 'Time elapsed (stored as "%1$s")', + 'Core:ExplainWTC:StopWatch-TimeSpent' => 'Time spent for "%1$s"', + 'Core:ExplainWTC:StopWatch-Deadline' => 'Deadline for "%1$s" at %2$d%%', // Bulk export - 'Core:BulkExport:MissingParameter_Param' => 'Missing parameter "%1$s"', - 'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter "query". There is no Query Phrasebook corresponding to the id: "%1$s".', + 'Core:BulkExport:MissingParameter_Param' => 'Missing parameter "%1$s"', + 'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter "query". There is no Query Phrasebook corresponding to the id: "%1$s".', 'Core:BulkExport:ExportFormatPrompt' => 'Export format:', 'Core:BulkExportOf_Class' => '%1$s Export', 'Core:BulkExport:ClickHereToDownload_FileName' => 'Click here to download %1$s', diff --git a/dictionaries/en_gb.dictionary.itop.core.php b/dictionaries/en_gb.dictionary.itop.core.php index dc47b2aed8..dd3a6c6067 100644 --- a/dictionaries/en_gb.dictionary.itop.core.php +++ b/dictionaries/en_gb.dictionary.itop.core.php @@ -1058,6 +1058,39 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of 'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'User Preferences', 'Class:appUserPreferences/Attribute:userid' => 'User', 'Class:appUserPreferences/Attribute:preferences' => 'Prefs', diff --git a/dictionaries/es_cr.dictionary.itop.core.php b/dictionaries/es_cr.dictionary.itop.core.php index ca397d5f1f..ee3aff6af1 100644 --- a/dictionaries/es_cr.dictionary.itop.core.php +++ b/dictionaries/es_cr.dictionary.itop.core.php @@ -963,6 +963,39 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Advertencias', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Fecha de Creación', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Fecha Última Modificación', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Preferencias de Usuario', 'Class:appUserPreferences/Attribute:userid' => 'Usuario', 'Class:appUserPreferences/Attribute:preferences' => 'Preferencias', diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php index 25e31bdb7a..e155817e60 100644 --- a/dictionaries/fr.dictionary.itop.core.php +++ b/dictionaries/fr.dictionary.itop.core.php @@ -1021,6 +1021,40 @@ Plusieurs champs peuvent ainsi être modifiés lors d\'une "mise à jour" en app 'Class:SynchroReplica/Attribute:status_last_warning' => 'Avertissements', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Date de création', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Date de dernière modification', + 'Class:SynchroReplica/Action:delete+' => 'Supprimer le réplica', + 'Class:SynchroReplica/Action:unlink' => 'Délier', + 'Class:SynchroReplica/Action:unlink+' => 'Séparer le réplica de son objet lié', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Délier & Synchroniser', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Délier le réplica avec son objet cible et executer la synchronisation sur ce réplica', + 'Class:SynchroReplica/Action:synchro' => 'Synchroniser', + 'Class:SynchroReplica/Action:synchro+' => 'Exécuter la synchronisation sur ce réplica', + 'Class:SynchroReplica/Action:allowdelete' => 'Permettre la suppression de l\'objet lié à ce réplica', + 'Class:SynchroReplica/Action:allowdelete+' => 'L\'objet lié à un réplica supprimé est supprimé', + 'Class:SynchroReplica/Action:denydelete' => 'Interdire la suppression de l\objet lié à ce réplica', + 'Class:SynchroReplica/Action:denydelete+' => 'L\'objet lié à un réplica supprimé n\'est pas supprimé', + + 'Class:SynchroReplica/Action:unlink_all' => 'Délier les réplicas et les objets', + 'Class:SynchroReplica/Action:unlink_all+' => 'Séparer les réplicas de leurs objets liés', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Délier & Synchroniser les réplicas et les objets', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Séparer les réplicas le leurs objets cilbles et exécuter la synchronisation sur les réplicas', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchroniser les objets Réplica', + 'Class:SynchroReplica/Action:synchro_all+' => 'Exécuter la synchronisation avec ces réplicas', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Autoriser la suppression des objets liés aux Réplicas', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'L\'objet lié à un réplica supprimé est supprimé', + 'Class:SynchroReplica/Action:denydelete_all' => 'Interdire la suppression des objets liés aux Réplicas', + 'Class:SynchroReplica/Action:denydelete_all+' => 'L\'objet lié à un réplica supprimé n\'est pas supprimé', + + 'UI:UnlinkAllTabTitle' => 'Délier les réplicas des objets', + 'UI:UnlinkAllPageTitle' => 'Séparer les réplicas de leurs objets liés', + 'UI:UnlinkSynchroAllTabTitle' => 'Délier & synchroniser les réplicas', + 'UI:UnlinkSynchroAllPageTitle' => 'Délier & synchroniser les réplicas ', + 'UI:SynchroAllTabTitle' => 'Synchroniser les réplica', + 'UI:SynchroAllPageTitle' => 'Synchroniser les réplica', + 'UI:AllowDeleteAllTabTitle' => 'Autoriser la suppression des objets liés aux réplicas', + 'UI:AllowDeleteAllPageTitle' => 'Autoriser la suppression des objets liés aux réplicas', + 'UI:DenyDeleteAllTabTitle' => 'Interdire la suppression des objets liés aux réplicas', + 'UI:DenyDeleteAllPageTitle' => 'Interdire la suppression des objets liés aux réplicas', + 'Class:appUserPreferences' => 'Préférences utilisateur', 'Class:appUserPreferences/Attribute:userid' => 'Utilisateur', 'Class:appUserPreferences/Attribute:preferences' => 'Préférences', diff --git a/dictionaries/hu.dictionary.itop.core.php b/dictionaries/hu.dictionary.itop.core.php index e6b8421c69..359a414d29 100755 --- a/dictionaries/hu.dictionary.itop.core.php +++ b/dictionaries/hu.dictionary.itop.core.php @@ -970,6 +970,39 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Figyelmeztetések', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Létrehozás dátuma', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Utolsó módosítás dátuma', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Felhasználói beállítások', 'Class:appUserPreferences/Attribute:userid' => 'Felhasználónév', 'Class:appUserPreferences/Attribute:preferences' => 'Beállítások', diff --git a/dictionaries/it.dictionary.itop.core.php b/dictionaries/it.dictionary.itop.core.php index efdd1698a0..bfe3bcfb5e 100644 --- a/dictionaries/it.dictionary.itop.core.php +++ b/dictionaries/it.dictionary.itop.core.php @@ -970,6 +970,39 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Avvisi', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Data di creazione', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Data di ultima modifica', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Preferenze utente', 'Class:appUserPreferences/Attribute:userid' => 'Utente', 'Class:appUserPreferences/Attribute:preferences' => 'Prefs', diff --git a/dictionaries/ja.dictionary.itop.core.php b/dictionaries/ja.dictionary.itop.core.php index 838640226a..cf82b2359f 100644 --- a/dictionaries/ja.dictionary.itop.core.php +++ b/dictionaries/ja.dictionary.itop.core.php @@ -974,6 +974,39 @@ Dict::Add('JA JP', 'Japanese', '日本語', [ 'Class:SynchroReplica/Attribute:status_last_warning' => '警告', 'Class:SynchroReplica/Attribute:info_creation_date' => '作成日', 'Class:SynchroReplica/Attribute:info_last_modified' => '最終修正日', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'ユーザプリファレンス', 'Class:appUserPreferences/Attribute:userid' => 'ユーザ', 'Class:appUserPreferences/Attribute:preferences' => 'プリファレンス', diff --git a/dictionaries/nl.dictionary.itop.core.php b/dictionaries/nl.dictionary.itop.core.php index 32f76aec57..b9f74d8a97 100644 --- a/dictionaries/nl.dictionary.itop.core.php +++ b/dictionaries/nl.dictionary.itop.core.php @@ -972,6 +972,39 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Waarschuwingen', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Datum van aanmaken', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Datum van de laatste aanpassing', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Gebruikersvoorkeuren', 'Class:appUserPreferences/Attribute:userid' => 'Gebruiker', 'Class:appUserPreferences/Attribute:preferences' => 'Voorkeuren', diff --git a/dictionaries/pl.dictionary.itop.core.php b/dictionaries/pl.dictionary.itop.core.php index be7450a3d3..925628470e 100644 --- a/dictionaries/pl.dictionary.itop.core.php +++ b/dictionaries/pl.dictionary.itop.core.php @@ -969,6 +969,39 @@ Dict::Add('PL PL', 'Polish', 'Polski', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Ostrzeżenia', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Data utworzenia', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Data ostatniej zmiany', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Preferencje użytkownika', 'Class:appUserPreferences/Attribute:userid' => 'Użytkownik', 'Class:appUserPreferences/Attribute:preferences' => 'Preferencje', diff --git a/dictionaries/pt_br.dictionary.itop.core.php b/dictionaries/pt_br.dictionary.itop.core.php index e919b141fd..d13bb6a2aa 100644 --- a/dictionaries/pt_br.dictionary.itop.core.php +++ b/dictionaries/pt_br.dictionary.itop.core.php @@ -970,6 +970,39 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Alertas', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Data de criação', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Última data modificação', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Preferências de usuário', 'Class:appUserPreferences/Attribute:userid' => 'Usuário', 'Class:appUserPreferences/Attribute:preferences' => 'Preferências', diff --git a/dictionaries/ru.dictionary.itop.core.php b/dictionaries/ru.dictionary.itop.core.php index 3de6a260d3..59a1dda46e 100644 --- a/dictionaries/ru.dictionary.itop.core.php +++ b/dictionaries/ru.dictionary.itop.core.php @@ -975,6 +975,40 @@ Dict::Add('RU RU', 'Russian', 'Русский', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Предупреждения', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Дата создания', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Дата последнего изменения', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:appUserPreferences' => 'Предпочтения пользователя', 'Class:appUserPreferences/Attribute:userid' => 'Пользователь', 'Class:appUserPreferences/Attribute:preferences' => 'Предпочтения', diff --git a/dictionaries/sk.dictionary.itop.core.php b/dictionaries/sk.dictionary.itop.core.php index 5c5b033fbb..2111bf3b34 100644 --- a/dictionaries/sk.dictionary.itop.core.php +++ b/dictionaries/sk.dictionary.itop.core.php @@ -993,6 +993,39 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of 'Class:SynchroReplica/Attribute:status_last_warning' => 'Upozornenia', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Dátum vytvorenia', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Dátum poslednej úpravy', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Užívateľské preferencie', 'Class:appUserPreferences/Attribute:userid' => 'Užívateľ', 'Class:appUserPreferences/Attribute:preferences' => 'Preferencie', diff --git a/dictionaries/tr.dictionary.itop.core.php b/dictionaries/tr.dictionary.itop.core.php index e8ad498944..72992b5d2e 100644 --- a/dictionaries/tr.dictionary.itop.core.php +++ b/dictionaries/tr.dictionary.itop.core.php @@ -975,6 +975,39 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [ 'Class:SynchroReplica/Attribute:status_last_warning' => 'Uyarılar', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Yaratılış Tarihi', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Son değiştirilme tarih', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', 'Class:appUserPreferences' => 'Kullanıcı Tercihleri', 'Class:appUserPreferences/Attribute:userid' => 'Kullanıcı', 'Class:appUserPreferences/Attribute:preferences' => 'Tercihler', diff --git a/dictionaries/zh_cn.dictionary.itop.core.php b/dictionaries/zh_cn.dictionary.itop.core.php index a42ce9a9f8..44a90a902c 100644 --- a/dictionaries/zh_cn.dictionary.itop.core.php +++ b/dictionaries/zh_cn.dictionary.itop.core.php @@ -1075,6 +1075,40 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'Class:SynchroReplica/Attribute:status_last_warning' => '告警', 'Class:SynchroReplica/Attribute:info_creation_date' => '创建日期', 'Class:SynchroReplica/Attribute:info_last_modified' => '最后修改日期', + 'Class:SynchroReplica/Action:delete+' => 'delete replica~~', + 'Class:SynchroReplica/Action:unlink' => 'Unlink~~', + 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro~~', + 'Class:SynchroReplica/Action:unlinksynchro+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro' => 'Synchro~~', + 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete' => 'Allow delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:allowdelete+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete' => 'Deny delete of object linked to this synchro replica~~', + 'Class:SynchroReplica/Action:denydelete+' => 'Object linked to a deleted replica is not deleted~~', + + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object~~', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects~~', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica~~', + 'Class:SynchroReplica/Action:allowdelete_all' => 'Allow delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:allowdelete_all+' => 'Object linked to a deleted replica is deleted~~', + 'Class:SynchroReplica/Action:denydelete_all' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:SynchroReplica/Action:denydelete_all+' => 'Object linked to a deleted replica is not deleted~~', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects~~', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects~~', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects~~', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects~~', + 'UI:AllowDeleteAllTabTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:AllowDeleteAllPageTitle' => 'Allow delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllTabTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'UI:DenyDeleteAllPageTitle' => 'Deny delete of objects linked to Synchro Replica~~', + 'Class:appUserPreferences' => '用户偏好', 'Class:appUserPreferences/Attribute:userid' => '用户', 'Class:appUserPreferences/Attribute:preferences' => '偏好设置', diff --git a/lib/autoload.php b/lib/autoload.php index 9861c4c24c..1b6f5ac129 100644 --- a/lib/autoload.php +++ b/lib/autoload.php @@ -14,7 +14,10 @@ if (PHP_VERSION_ID < 50600) { echo $err; } } - throw new RuntimeException($err); + trigger_error( + $err, + E_USER_ERROR + ); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 626b2db633..a51c3ba362 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -131,12 +131,14 @@ return array( 'Collator' => $vendorDir . '/symfony/polyfill-intl-icu/Resources/stubs/Collator.php', 'Combodo\\iTop\\Application\\Branding' => $baseDir . '/sources/Application/Branding.php', 'Combodo\\iTop\\Application\\EventRegister\\ApplicationEvents' => $baseDir . '/sources/Application/EventRegister/ApplicationEvents.php', + 'Combodo\\iTop\\Application\\Helper\\BulkHelper' => $baseDir . '/sources/Application/Helper/BulkHelper.php', 'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => $baseDir . '/sources/Application/Helper/CKEditorHelper.php', 'Combodo\\iTop\\Application\\Helper\\ExportHelper' => $baseDir . '/sources/Application/Helper/ExportHelper.php', 'Combodo\\iTop\\Application\\Helper\\FormHelper' => $baseDir . '/sources/Application/Helper/FormHelper.php', 'Combodo\\iTop\\Application\\Helper\\ImportHelper' => $baseDir . '/sources/Application/Helper/ImportHelper.php', 'Combodo\\iTop\\Application\\Helper\\SearchHelper' => $baseDir . '/sources/Application/Helper/SearchHelper.php', 'Combodo\\iTop\\Application\\Helper\\Session' => $baseDir . '/sources/Application/Helper/Session.php', + 'Combodo\\iTop\\Application\\Helper\\SynchroReplicaHelper' => $baseDir . '/sources/Application/Helper/SynchroReplicaHelper.php', 'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => $baseDir . '/sources/Application/Helper/WebResourcesHelper.php', 'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => $baseDir . '/sources/Application/Newsroom/iTopNewsroomProvider.php', 'Combodo\\iTop\\Application\\Search\\AjaxSearchException' => $baseDir . '/sources/Application/Search/ajaxsearchexception.class.inc.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index fe840689d7..db32c807e5 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -28,12 +28,12 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685 ); public static $prefixLengthsPsr4 = array ( - 'T' => + 'T' => array ( 'Twig\\' => 5, 'TheNetworg\\OAuth2\\Client\\' => 25, ), - 'S' => + 'S' => array ( 'Symfony\\Runtime\\Symfony\\Component\\' => 34, 'Symfony\\Polyfill\\Php83\\' => 23, @@ -85,7 +85,7 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685 'ScssPhp\\ScssPhp\\' => 16, 'Sabberworm\\CSS\\' => 15, ), - 'P' => + 'P' => array ( 'Psr\\Log\\' => 8, 'Psr\\Http\\Message\\' => 17, @@ -96,310 +96,310 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685 'PhpParser\\' => 10, 'Pelago\\Emogrifier\\' => 18, ), - 'L' => + 'L' => array ( 'League\\OAuth2\\Client\\' => 21, ), - 'G' => + 'G' => array ( 'GuzzleHttp\\Psr7\\' => 16, 'GuzzleHttp\\Promise\\' => 19, 'GuzzleHttp\\' => 11, ), - 'F' => + 'F' => array ( 'Firebase\\JWT\\' => 13, ), - 'E' => + 'E' => array ( 'Egulias\\EmailValidator\\' => 23, ), - 'D' => + 'D' => array ( 'Doctrine\\Common\\Lexer\\' => 22, ), ); public static $prefixDirsPsr4 = array ( - 'Twig\\' => + 'Twig\\' => array ( 0 => __DIR__ . '/..' . '/twig/twig/src', ), - 'TheNetworg\\OAuth2\\Client\\' => + 'TheNetworg\\OAuth2\\Client\\' => array ( 0 => __DIR__ . '/..' . '/thenetworg/oauth2-azure/src', ), - 'Symfony\\Runtime\\Symfony\\Component\\' => + 'Symfony\\Runtime\\Symfony\\Component\\' => array ( 0 => __DIR__ . '/..' . '/symfony/runtime/Internal', ), - 'Symfony\\Polyfill\\Php83\\' => + 'Symfony\\Polyfill\\Php83\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php83', ), - 'Symfony\\Polyfill\\Mbstring\\' => + 'Symfony\\Polyfill\\Mbstring\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', ), - 'Symfony\\Polyfill\\Intl\\Normalizer\\' => + 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer', ), - 'Symfony\\Polyfill\\Intl\\Idn\\' => + 'Symfony\\Polyfill\\Intl\\Idn\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn', ), - 'Symfony\\Polyfill\\Intl\\Icu\\' => + 'Symfony\\Polyfill\\Intl\\Icu\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-icu', ), - 'Symfony\\Polyfill\\Intl\\Grapheme\\' => + 'Symfony\\Polyfill\\Intl\\Grapheme\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme', ), - 'Symfony\\Polyfill\\Ctype\\' => + 'Symfony\\Polyfill\\Ctype\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', ), - 'Symfony\\Contracts\\Translation\\' => + 'Symfony\\Contracts\\Translation\\' => array ( 0 => __DIR__ . '/..' . '/symfony/translation-contracts', ), - 'Symfony\\Contracts\\Service\\' => + 'Symfony\\Contracts\\Service\\' => array ( 0 => __DIR__ . '/..' . '/symfony/service-contracts', ), - 'Symfony\\Contracts\\EventDispatcher\\' => + 'Symfony\\Contracts\\EventDispatcher\\' => array ( 0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts', ), - 'Symfony\\Contracts\\Cache\\' => + 'Symfony\\Contracts\\Cache\\' => array ( 0 => __DIR__ . '/..' . '/symfony/cache-contracts', ), - 'Symfony\\Component\\Yaml\\' => + 'Symfony\\Component\\Yaml\\' => array ( 0 => __DIR__ . '/..' . '/symfony/yaml', ), - 'Symfony\\Component\\VarExporter\\' => + 'Symfony\\Component\\VarExporter\\' => array ( 0 => __DIR__ . '/..' . '/symfony/var-exporter', ), - 'Symfony\\Component\\VarDumper\\' => + 'Symfony\\Component\\VarDumper\\' => array ( 0 => __DIR__ . '/..' . '/symfony/var-dumper', ), - 'Symfony\\Component\\Validator\\' => + 'Symfony\\Component\\Validator\\' => array ( 0 => __DIR__ . '/..' . '/symfony/validator', ), - 'Symfony\\Component\\String\\' => + 'Symfony\\Component\\String\\' => array ( 0 => __DIR__ . '/..' . '/symfony/string', ), - 'Symfony\\Component\\Stopwatch\\' => + 'Symfony\\Component\\Stopwatch\\' => array ( 0 => __DIR__ . '/..' . '/symfony/stopwatch', ), - 'Symfony\\Component\\Security\\Csrf\\' => + 'Symfony\\Component\\Security\\Csrf\\' => array ( 0 => __DIR__ . '/..' . '/symfony/security-csrf', ), - 'Symfony\\Component\\Security\\Core\\' => + 'Symfony\\Component\\Security\\Core\\' => array ( 0 => __DIR__ . '/..' . '/symfony/security-core', ), - 'Symfony\\Component\\Runtime\\' => + 'Symfony\\Component\\Runtime\\' => array ( 0 => __DIR__ . '/..' . '/symfony/runtime', ), - 'Symfony\\Component\\Routing\\' => + 'Symfony\\Component\\Routing\\' => array ( 0 => __DIR__ . '/..' . '/symfony/routing', ), - 'Symfony\\Component\\PropertyInfo\\' => + 'Symfony\\Component\\PropertyInfo\\' => array ( 0 => __DIR__ . '/..' . '/symfony/property-info', ), - 'Symfony\\Component\\PropertyAccess\\' => + 'Symfony\\Component\\PropertyAccess\\' => array ( 0 => __DIR__ . '/..' . '/symfony/property-access', ), - 'Symfony\\Component\\PasswordHasher\\' => + 'Symfony\\Component\\PasswordHasher\\' => array ( 0 => __DIR__ . '/..' . '/symfony/password-hasher', ), - 'Symfony\\Component\\OptionsResolver\\' => + 'Symfony\\Component\\OptionsResolver\\' => array ( 0 => __DIR__ . '/..' . '/symfony/options-resolver', ), - 'Symfony\\Component\\Mime\\' => + 'Symfony\\Component\\Mime\\' => array ( 0 => __DIR__ . '/..' . '/symfony/mime', ), - 'Symfony\\Component\\Mailer\\' => + 'Symfony\\Component\\Mailer\\' => array ( 0 => __DIR__ . '/..' . '/symfony/mailer', ), - 'Symfony\\Component\\HttpKernel\\' => + 'Symfony\\Component\\HttpKernel\\' => array ( 0 => __DIR__ . '/..' . '/symfony/http-kernel', ), - 'Symfony\\Component\\HttpFoundation\\' => + 'Symfony\\Component\\HttpFoundation\\' => array ( 0 => __DIR__ . '/..' . '/symfony/http-foundation', ), - 'Symfony\\Component\\Form\\' => + 'Symfony\\Component\\Form\\' => array ( 0 => __DIR__ . '/..' . '/symfony/form', ), - 'Symfony\\Component\\Finder\\' => + 'Symfony\\Component\\Finder\\' => array ( 0 => __DIR__ . '/..' . '/symfony/finder', ), - 'Symfony\\Component\\Filesystem\\' => + 'Symfony\\Component\\Filesystem\\' => array ( 0 => __DIR__ . '/..' . '/symfony/filesystem', ), - 'Symfony\\Component\\EventDispatcher\\' => + 'Symfony\\Component\\EventDispatcher\\' => array ( 0 => __DIR__ . '/..' . '/symfony/event-dispatcher', ), - 'Symfony\\Component\\ErrorHandler\\' => + 'Symfony\\Component\\ErrorHandler\\' => array ( 0 => __DIR__ . '/..' . '/symfony/error-handler', ), - 'Symfony\\Component\\Dotenv\\' => + 'Symfony\\Component\\Dotenv\\' => array ( 0 => __DIR__ . '/..' . '/symfony/dotenv', ), - 'Symfony\\Component\\DependencyInjection\\' => + 'Symfony\\Component\\DependencyInjection\\' => array ( 0 => __DIR__ . '/..' . '/symfony/dependency-injection', ), - 'Symfony\\Component\\CssSelector\\' => + 'Symfony\\Component\\CssSelector\\' => array ( 0 => __DIR__ . '/..' . '/symfony/css-selector', ), - 'Symfony\\Component\\Console\\' => + 'Symfony\\Component\\Console\\' => array ( 0 => __DIR__ . '/..' . '/symfony/console', ), - 'Symfony\\Component\\Config\\' => + 'Symfony\\Component\\Config\\' => array ( 0 => __DIR__ . '/..' . '/symfony/config', ), - 'Symfony\\Component\\Cache\\' => + 'Symfony\\Component\\Cache\\' => array ( 0 => __DIR__ . '/..' . '/symfony/cache', ), - 'Symfony\\Bundle\\WebProfilerBundle\\' => + 'Symfony\\Bundle\\WebProfilerBundle\\' => array ( 0 => __DIR__ . '/..' . '/symfony/web-profiler-bundle', ), - 'Symfony\\Bundle\\TwigBundle\\' => + 'Symfony\\Bundle\\TwigBundle\\' => array ( 0 => __DIR__ . '/..' . '/symfony/twig-bundle', ), - 'Symfony\\Bundle\\FrameworkBundle\\' => + 'Symfony\\Bundle\\FrameworkBundle\\' => array ( 0 => __DIR__ . '/..' . '/symfony/framework-bundle', ), - 'Symfony\\Bundle\\DebugBundle\\' => + 'Symfony\\Bundle\\DebugBundle\\' => array ( 0 => __DIR__ . '/..' . '/symfony/debug-bundle', ), - 'Symfony\\Bridge\\Twig\\' => + 'Symfony\\Bridge\\Twig\\' => array ( 0 => __DIR__ . '/..' . '/symfony/twig-bridge', ), - 'Soundasleep\\' => + 'Soundasleep\\' => array ( 0 => __DIR__ . '/..' . '/soundasleep/html2text/src', ), - 'ScssPhp\\ScssPhp\\' => + 'ScssPhp\\ScssPhp\\' => array ( 0 => __DIR__ . '/..' . '/scssphp/scssphp/src', ), - 'Sabberworm\\CSS\\' => + 'Sabberworm\\CSS\\' => array ( 0 => __DIR__ . '/..' . '/sabberworm/php-css-parser/src', ), - 'Psr\\Log\\' => + 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/src', ), - 'Psr\\Http\\Message\\' => + 'Psr\\Http\\Message\\' => array ( 0 => __DIR__ . '/..' . '/psr/http-factory/src', 1 => __DIR__ . '/..' . '/psr/http-message/src', ), - 'Psr\\Http\\Client\\' => + 'Psr\\Http\\Client\\' => array ( 0 => __DIR__ . '/..' . '/psr/http-client/src', ), - 'Psr\\EventDispatcher\\' => + 'Psr\\EventDispatcher\\' => array ( 0 => __DIR__ . '/..' . '/psr/event-dispatcher/src', ), - 'Psr\\Container\\' => + 'Psr\\Container\\' => array ( 0 => __DIR__ . '/..' . '/psr/container/src', ), - 'Psr\\Cache\\' => + 'Psr\\Cache\\' => array ( 0 => __DIR__ . '/..' . '/psr/cache/src', ), - 'PhpParser\\' => + 'PhpParser\\' => array ( 0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser', ), - 'Pelago\\Emogrifier\\' => + 'Pelago\\Emogrifier\\' => array ( 0 => __DIR__ . '/..' . '/pelago/emogrifier/src', ), - 'League\\OAuth2\\Client\\' => + 'League\\OAuth2\\Client\\' => array ( 0 => __DIR__ . '/..' . '/league/oauth2-google/src', 1 => __DIR__ . '/..' . '/league/oauth2-client/src', ), - 'GuzzleHttp\\Psr7\\' => + 'GuzzleHttp\\Psr7\\' => array ( 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src', ), - 'GuzzleHttp\\Promise\\' => + 'GuzzleHttp\\Promise\\' => array ( 0 => __DIR__ . '/..' . '/guzzlehttp/promises/src', ), - 'GuzzleHttp\\' => + 'GuzzleHttp\\' => array ( 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src', ), - 'Firebase\\JWT\\' => + 'Firebase\\JWT\\' => array ( 0 => __DIR__ . '/..' . '/firebase/php-jwt/src', ), - 'Egulias\\EmailValidator\\' => + 'Egulias\\EmailValidator\\' => array ( 0 => __DIR__ . '/..' . '/egulias/email-validator/src', ), - 'Doctrine\\Common\\Lexer\\' => + 'Doctrine\\Common\\Lexer\\' => array ( 0 => __DIR__ . '/..' . '/doctrine/lexer/src', ), ); public static $prefixesPsr0 = array ( - 'C' => + 'C' => array ( - 'Console' => + 'Console' => array ( 0 => __DIR__ . '/..' . '/pear/console_getopt', ), ), - 'A' => + 'A' => array ( - 'Archive_Tar' => + 'Archive_Tar' => array ( 0 => __DIR__ . '/..' . '/pear/archive_tar', ), @@ -532,12 +532,14 @@ class ComposerStaticInitfc0e9e9dea11dcbb6272414776c30685 'Collator' => __DIR__ . '/..' . '/symfony/polyfill-intl-icu/Resources/stubs/Collator.php', 'Combodo\\iTop\\Application\\Branding' => __DIR__ . '/../..' . '/sources/Application/Branding.php', 'Combodo\\iTop\\Application\\EventRegister\\ApplicationEvents' => __DIR__ . '/../..' . '/sources/Application/EventRegister/ApplicationEvents.php', + 'Combodo\\iTop\\Application\\Helper\\BulkHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/BulkHelper.php', 'Combodo\\iTop\\Application\\Helper\\CKEditorHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/CKEditorHelper.php', 'Combodo\\iTop\\Application\\Helper\\ExportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ExportHelper.php', 'Combodo\\iTop\\Application\\Helper\\FormHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/FormHelper.php', 'Combodo\\iTop\\Application\\Helper\\ImportHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/ImportHelper.php', 'Combodo\\iTop\\Application\\Helper\\SearchHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SearchHelper.php', 'Combodo\\iTop\\Application\\Helper\\Session' => __DIR__ . '/../..' . '/sources/Application/Helper/Session.php', + 'Combodo\\iTop\\Application\\Helper\\SynchroReplicaHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/SynchroReplicaHelper.php', 'Combodo\\iTop\\Application\\Helper\\WebResourcesHelper' => __DIR__ . '/../..' . '/sources/Application/Helper/WebResourcesHelper.php', 'Combodo\\iTop\\Application\\Newsroom\\iTopNewsroomProvider' => __DIR__ . '/../..' . '/sources/Application/Newsroom/iTopNewsroomProvider.php', 'Combodo\\iTop\\Application\\Search\\AjaxSearchException' => __DIR__ . '/../..' . '/sources/Application/Search/ajaxsearchexception.class.inc.php', diff --git a/lib/composer/platform_check.php b/lib/composer/platform_check.php index f6cf0ea27c..bb733000d3 100644 --- a/lib/composer/platform_check.php +++ b/lib/composer/platform_check.php @@ -36,7 +36,8 @@ if ($issues) { echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; } } - throw new \RuntimeException( - 'Composer detected issues in your platform: ' . implode(' ', $issues) + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR ); } diff --git a/pages/UI.php b/pages/UI.php index 2761915084..b3dda33ba5 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -5,12 +5,13 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ +use Combodo\iTop\Application\Helper\BulkHelper; use Combodo\iTop\Application\Helper\SearchHelper; use Combodo\iTop\Application\Helper\Session; +use Combodo\iTop\Application\Helper\SynchroReplicaHelper; use Combodo\iTop\Application\TwigBase\Twig\TwigHelper; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory; -use Combodo\iTop\Application\UI\Base\Component\Form\Form; use Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearchHelper; use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory; @@ -577,18 +578,35 @@ try { /////////////////////////////////////////////////////////////////////////////////////////// case 'select_for_modify_all': // Select the list of objects to be modified (bulk modify) - UI::OperationSelectForModifyAll($oP); + BulkHelper::OperationSelectForModifyAll($oP); break; /////////////////////////////////////////////////////////////////////////////////////////// case 'form_for_modify_all': // Form to modify multiple objects (bulk modify) - UI::OperationFormForModifyAll($oP, $oAppContext); + BulkHelper::OperationFormForModifyAll($oP, $oAppContext); + break; + + case 'form_for_unlink_all': // Form to modify multiple objects (bulk modify) + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'unlink'); + break; + case 'form_for_unlinksynchro_all': // Form to modify multiple objects (bulk modify) + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'unlinksynchro'); + break; + case 'form_for_synchro_all': // Form to modify multiple objects (bulk modify) + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'synchro'); + break; + + case 'form_for_allowdelete_all': // Form to modify multiple objects (bulk modify) + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'allowdelete'); + break; + case 'form_for_denydelete_all': // Form to modify multiple objects (bulk modify) + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'denydelete'); break; /////////////////////////////////////////////////////////////////////////////////////////// case 'preview_or_modify_all': // Preview or apply bulk modify - UI::OperationPreviewOrModifyAll($oP, $oAppContext); + BulkHelper::OperationPreviewOrModifyAll($oP, $oAppContext); break; /////////////////////////////////////////////////////////////////////////////////////////// @@ -627,7 +645,7 @@ try { 'title' => Dict::S('UI:BulkDeleteTitle'), ]; $oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_DELETE); - DisplayMultipleSelectionForm($oP, $oFilter, 'bulk_delete', $oChecker, [], $aDisplayParams); + BulkHelper::DisplayMultipleSelectionForm($oP, $oFilter, 'bulk_delete', $oChecker, [], $aDisplayParams); break; /////////////////////////////////////////////////////////////////////////////////////////// @@ -1338,99 +1356,3 @@ try { IssueLog::Debug('UI.php operation='.$sOperationToLog.', error='.$e->getMessage()."\n".$sErrorStackTrace, LogChannels::CONSOLE); } -class UI -{ - /** - * Operation select_for_modify_all - * - * @param iTopWebPage $oP - * - * @throws \ApplicationException - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \OQLException - */ - public static function OperationSelectForModifyAll(iTopWebPage $oP): void - { - $oP->DisableBreadCrumb(); - $oP->set_title(Dict::S('UI:ModifyAllPageTitle')); - $sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); - if (empty($sFilter)) { - throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'filter')); - } - $oFilter = DBObjectSearch::unserialize($sFilter); //TODO : check that the filter is valid - // Add user filter - $oFilter->UpdateContextFromUser(); - $oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_MODIFY); - $sClass = $oFilter->GetClass(); - $sClassName = MetaModel::GetName($sClass); - - $aDisplayParams = [ - 'icon' => MetaModel::GetClassIcon($sClass, false), - 'title' => Dict::Format('UI:Modify_ObjectsOf_Class', $sClassName), - ]; - DisplayMultipleSelectionForm($oP, $oFilter, 'form_for_modify_all', $oChecker, [], $aDisplayParams); - } - - /** - * Operation form_for_modify_all - * - * @param iTopWebPage $oP - * @param \ApplicationContext $oAppContext - * - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \CoreUnexpectedValue - * @throws \MySQLException - * @throws \OQLException - */ - public static function OperationFormForModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void - { - $oP->DisableBreadCrumb(); - $sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); - $sClass = utils::ReadParam('class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS); - $oFullSetFilter = DBObjectSearch::unserialize($sFilter); - // Add user filter - $oFullSetFilter->UpdateContextFromUser(); - $aSelectedObj = utils::ReadMultipleSelection($oFullSetFilter); - $sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter).$oAppContext->GetForLink(true); - $aContext = ['filter' => utils::EscapeHtml($sFilter)]; - cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, [], $aContext); - } - - /** - * Operation preview_or_modify_all - * - * @param iTopWebPage $oP - * @param \ApplicationContext $oAppContext - * - * @throws \ApplicationException - * @throws \ArchivedObjectException - * @throws \CoreCannotSaveObjectException - * @throws \CoreException - * @throws \DictExceptionMissingString - * @throws \OQLException - */ - public static function OperationPreviewOrModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void - { - $oP->DisableBreadCrumb(); - $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); - $oFilter = DBObjectSearch::unserialize($sFilter); // TO DO : check that the filter is valid - // Add user filter - $oFilter->UpdateContextFromUser(); - - $sClass = utils::ReadParam('class', '', false, 'class'); - $bPreview = utils::ReadParam('preview_mode', ''); - $sSelectedObj = utils::ReadParam('selectObj', '', false, 'raw_data'); - if (empty($sClass) || empty($sSelectedObj)) { // TO DO: check that the class name is valid ! - throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj')); - } - $aSelectedObj = explode(',', $sSelectedObj); - $sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter).$oAppContext->GetForLink(true); - $aContext = [ - 'filter' => utils::EscapeHtml($sFilter), - 'selectObj' => $sSelectedObj, - ]; - cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext); - } -} diff --git a/sources/Application/Helper/BulkHelper.php b/sources/Application/Helper/BulkHelper.php new file mode 100644 index 0000000000..dac8c3a79e --- /dev/null +++ b/sources/Application/Helper/BulkHelper.php @@ -0,0 +1,178 @@ +DisableBreadCrumb(); + $oP->set_title(Dict::S($sTitleTab)); + $sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); + if (empty($sFilter)) { + throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'filter')); + } + $oFilter = DBObjectSearch::unserialize($sFilter); //TODO : check that the filter is valid + // Add user filter + $oFilter->UpdateContextFromUser(); + $oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_MODIFY); + $sClass = $oFilter->GetClass(); + $sClassName = MetaModel::GetName($sClass); + + $aDisplayParams = [ + 'icon' => MetaModel::GetClassIcon($sClass, false), + 'title' => Dict::Format($sTitleCode, $sClassName), + ]; + self::DisplayMultipleSelectionForm($oP, $oFilter, $sNextOperation, $oChecker, [], $aDisplayParams); + } + + /** + * Operation form_for_modify_all + * + * @param iTopWebPage $oP + * @param \ApplicationContext $oAppContext + * + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \MySQLException + * @throws \OQLException + */ + public static function OperationFormForModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void + { + $oP->DisableBreadCrumb(); + $sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); + $sClass = utils::ReadParam('class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS); + $oFullSetFilter = DBObjectSearch::unserialize($sFilter); + // Add user filter + $oFullSetFilter->UpdateContextFromUser(); + $aSelectedObj = utils::ReadMultipleSelection($oFullSetFilter); + $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter=' . urlencode($sFilter) . '&' . $oAppContext->GetForLink(); + $aContext = array('filter' => utils::EscapeHtml($sFilter)); + cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, array(), $aContext); + } + + /** + * Operation preview_or_modify_all + * + * @param iTopWebPage $oP + * @param \ApplicationContext $oAppContext + * + * @throws \ApplicationException + * @throws \ArchivedObjectException + * @throws \CoreCannotSaveObjectException + * @throws \CoreException + * @throws \DictExceptionMissingString + * @throws \OQLException + */ + public static function OperationPreviewOrModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void + { + $oP->DisableBreadCrumb(); + $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); + $oFilter = DBObjectSearch::unserialize($sFilter); // TO DO : check that the filter is valid + // Add user filter + $oFilter->UpdateContextFromUser(); + + $sClass = utils::ReadParam('class', '', false, 'class'); + $bPreview = utils::ReadParam('preview_mode', ''); + $sSelectedObj = utils::ReadParam('selectObj', '', false, 'raw_data'); + if (empty($sClass) || empty($sSelectedObj)) // TO DO: check that the class name is valid ! + { + throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj')); + } + $aSelectedObj = explode(',', $sSelectedObj); + $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter=' . urlencode($sFilter) . '&' . $oAppContext->GetForLink(); + $aContext = array( + 'filter' => utils::EscapeHtml($sFilter), + 'selectObj' => $sSelectedObj, + ); + cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext); + }/** + * Displays a form (checkboxes) to select the objects for which to apply a given action + * Only the objects for which the action is valid can be checked. By default all valid objects are checked + * + * @param WebPage $oP WebPage The page for output + * @param \DBSearch $oFilter DBSearch The filter that defines the list of objects + * @param string $sNextOperation string The next operation (code) to be executed when the form is submitted + * @param ActionChecker $oChecker ActionChecker The helper class/instance used to check for which object the action is valid + * @param array $aExtraFormParams + * @param array $aDisplayParams + * + * @throws \ApplicationException + * @throws \ArchivedObjectException + * @throws \CoreException + *@since 3.0.0 $aDisplayParams parameter + * + */ + public static function DisplayMultipleSelectionForm(WebPage $oP, DBSearch $oFilter, string $sNextOperation, ActionChecker $oChecker, array $aExtraFormParams = [], array $aDisplayParams = []) + { + $oAppContext = new ApplicationContext(); + $iBulkActionAllowed = $oChecker->IsAllowed(); + $aExtraParams = ['selection_type' => 'multiple', 'selection_mode' => true, 'display_limit' => false, 'menu' => false]; + if ($iBulkActionAllowed == UR_ALLOWED_DEPENDS) { + $aExtraParams['selection_enabled'] = $oChecker->GetAllowedIDs(); + } else { + if (UR_ALLOWED_NO) { + throw new ApplicationException(Dict::Format('UI:ActionNotAllowed')); + } + } + + $oForm = new Form(); + $oForm->SetAction( utils::GetAbsoluteUrlAppRoot().'pages/UI.php'); + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sNextOperation)); + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $oFilter->GetClass())); + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('filter', utils::HtmlEntities($oFilter->Serialize()))); + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId())); + foreach ($aExtraFormParams as $sName => $sValue) { + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden($sName, $sValue)); + } + $oForm->AddSubBlock($oAppContext->GetForFormBlock()); + $oDisplayBlock = new DisplayBlock($oFilter, 'list', false); + //by default all the elements are selected + $aExtraParams['selectionMode'] = 'negative'; + if (array_key_exists('icon', $aDisplayParams) || array_key_exists('title', $aDisplayParams)) { + $aExtraParams['surround_with_panel'] = true; + if (array_key_exists('icon', $aDisplayParams)) { + $aExtraParams['panel_icon'] = $aDisplayParams['icon']; + } + if (array_key_exists('title', $aDisplayParams)) { + $aExtraParams['panel_title'] = $aDisplayParams['title']; + } + } + $oForm->AddSubBlock($oDisplayBlock->GetDisplay($oP, 1, $aExtraParams)); + $oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null); + $oToolbarButtons->AddCSSClass('ibo-toolbar--button'); + $oForm->AddSubBlock($oToolbarButtons); + $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel')->SetOnClickJsCode('window.history.back()')); + $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Next'), 'next', Dict::S('UI:Button:Next'), true)); + + $oP->AddUiBlock($oForm); + } +} \ No newline at end of file diff --git a/sources/Application/Helper/SynchroReplicaHelper.php b/sources/Application/Helper/SynchroReplicaHelper.php new file mode 100644 index 0000000000..b37defbc32 --- /dev/null +++ b/sources/Application/Helper/SynchroReplicaHelper.php @@ -0,0 +1,115 @@ +DisableBreadCrumb(); + $sClass = utils::ReadParam('class', '', false, 'class'); + $sFilter = utils::ReadPostedParam('filter', '', 'raw_data'); + $oFullSetFilter = DBObjectSearch::unserialize($sFilter); + // Add user filter + $oFullSetFilter->UpdateContextFromUser(); + $aSelectObject = utils::ReadMultipleSelection($oFullSetFilter); + if ( empty($sClass) || empty($aSelectObject)) // TO DO: check that the class name is valid ! + { + throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObject[]')); + } + $sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink(); + + $aHeaders = array( + 'object' => array('label' => MetaModel::GetName($sClass), 'description' => Dict::S('UI:ModifiedObject')), + 'status' => array( + 'label' => Dict::S('UI:BulkModifyStatus'), + 'description' => Dict::S('UI:BulkModifyStatus+'), + ), + 'errors' => array( + 'label' => Dict::S('UI:BulkModifyErrors'), + 'description' => Dict::S('UI:BulkModifyErrors+'), + ), + ); + $aRows = array(); + + $sHeaderTitle = Dict::Format('UI:Modify_N_ObjectsOf_Class', count($aSelectObject), MetaModel::GetName($sClass)); + $sClassIcon = MetaModel::GetClassIcon($sClass, false); + + // Not in preview mode, do the update for real + $sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id'); + if (!utils::IsTransactionValid($sTransactionId, false)) { + throw new Exception(Dict::S('UI:Error:ObjectAlreadyUpdated')); + } + utils::RemoveTransaction($sTransactionId); + + // Avoid too many events + $iPreviousTimeLimit = ini_get('max_execution_time'); + $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); + $aErrors = []; + foreach ($aSelectObject as $iId) { + set_time_limit(intval($iLoopTimeLimit)); + /** @var \cmdbAbstractObject $oObj */ + $oReplica = MetaModel::GetObject(SynchroReplica::class, $iId); + $bResult = true; + try { + if (in_array($sOperation, ['unlink', 'unlinksynchro'])) { + $oReplica->UnLink(); + } + if (in_array($sOperation, ['synchro', 'unlinksynchro'])) { + $oStatLog = $oReplica->ReSynchro(); + $aErrors = $oStatLog->GetTraces(); + } + if ($sOperation == 'allowdelete') { + $oReplica->Set('status_dest_creator', 1); + $oReplica->DBUpdate(); + } + if ($sOperation == 'denydelete') { + $oReplica->Set('status_dest_creator', 0); + $oReplica->DBUpdate(); + } + } + catch (Exception $e) { + $bResult = false; + $aErrors[] = $e->getMessage(); + } + + $sStatus = $bResult ? Dict::S('UI:BulkModifyStatusModified') : Dict::S('UI:BulkModifyStatusSkipped'); + + $aErrorsToDisplay = array_map(function ($sError) { + return utils::HtmlEntities($sError); + }, $aErrors); + $aRows[] = array( + 'object' => $oReplica->GetHyperlink(), + 'status' => $sStatus, + 'errors' => '

'.($bResult ? '' : implode('

', $aErrorsToDisplay)).'

', + ); + } + + set_time_limit(intval($iPreviousTimeLimit)); + $oTable = DataTableUIBlockFactory::MakeForForm('BulkModify', $aHeaders, $aRows); + $oTable->AddOption("bFullscreen", true); + + $oPanel = PanelUIBlockFactory::MakeForClass($sClass, ''); + $oPanel->SetIcon($sClassIcon); + $oPanel->SetTitle($sHeaderTitle); + $oPanel->AddCSSClass('ibo-datatable-panel'); + $oPanel->AddSubBlock($oTable); + + $oP->AddUiBlock($oPanel); + $oP->AddSubBlock(ButtonUIBlockFactory::MakeForSecondaryAction(Dict::S('UI:Button:Done')))->SetOnClickJsCode("window.location.href='$sCancelUrl'")->AddCSSClass('mt-5'); + } +} \ No newline at end of file diff --git a/sources/Core/AttributeDefinition/AttributeObjectKey.php b/sources/Core/AttributeDefinition/AttributeObjectKey.php index 122c489093..1d62a61718 100644 --- a/sources/Core/AttributeDefinition/AttributeObjectKey.php +++ b/sources/Core/AttributeDefinition/AttributeObjectKey.php @@ -114,4 +114,8 @@ class AttributeObjectKey extends AttributeDBFieldVoid return (int)$proposedValue; } + public function GetTargetClass($iType = EXTKEY_RELATIVE) + { + return ''; + } } diff --git a/synchro/replica.php b/synchro/replica.php index 0c9c16726d..21dd073e73 100644 --- a/synchro/replica.php +++ b/synchro/replica.php @@ -18,7 +18,10 @@ * You should have received a copy of the GNU Affero General Public License */ +use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory; use Combodo\iTop\Application\WebPage\iTopWebPage; +use Combodo\iTop\Application\Helper\BulkHelper; require_once('../approot.inc.php'); require_once(APPROOT.'/application/application.inc.php'); @@ -34,6 +37,7 @@ $oP = new iTopWebPage("iTop - Synchro Replicas"); // Main program $sOperation = utils::ReadParam('operation', 'details'); + try { switch ($sOperation) { case 'details': @@ -41,26 +45,26 @@ try { if ($iId == null) { throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id')); } - $oReplica = MetaModel::GetObject('SynchroReplica', $iId); + $oReplica = MetaModel::GetObject(SynchroReplica::class, $iId); $oReplica->DisplayDetails($oP); break; case 'oql': + $iSourceId = utils::ReadParam('datasource', null); + if ($iSourceId != null) { + $oSource = MetaModel::GetObject(SynchroDataSource::class, $iSourceId); + $oBackButton = ButtonUIBlockFactory::MakeLinkNeutral( ApplicationContext::MakeObjectUrl(SynchroDataSource::class, $iSourceId), Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetName()), 'fas fa-chevron-left'); + $oP->AddUiBlock($oBackButton); + $oP->AddUiBlock(TitleUIBlockFactory::MakeForPage(Dict::Format('Core:SynchroReplica:ListOfReplicas', $oSource->GetName()))); + } + $sOQL = utils::ReadParam('oql', null, false, 'raw_data'); if ($sOQL == null) { throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'oql')); } $oFilter = DBObjectSearch::FromOQL($sOQL); - $oBlock1 = new DisplayBlock($oFilter, 'search', false, ['menu' => false, 'table_id' => '1']); + $oBlock1 = new DisplayBlock($oFilter, 'search', false, ['menu' => true, 'table_id' => '1']); $oBlock1->Display($oP, 0); - $oP->add(''); - $iSourceId = utils::ReadParam('datasource', null); - if ($iSourceId != null) { - $oSource = MetaModel::GetObject('SynchroDataSource', $iSourceId); - $oP->p(Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetHyperlink()).''); - } - $oBlock = new DisplayBlock($oFilter, 'list', false, ['menu' => false]); - $oBlock->Display($oP, 1); break; case 'delete': @@ -69,6 +73,81 @@ try { $sDelete = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?'.$_SERVER['QUERY_STRING']; header("Location: $sDelete"); break; + + case 'unlinksynchro': + $iId = utils::ReadParam('id', null); + if ($iId == null) { + throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id')); + } + $oReplica = MetaModel::GetObject(SynchroReplica::class, $iId); + $oReplica->UnLink(); + + $oStatLog = $oReplica->ReSynchro(); + $oP->add(implode('
', $oStatLog->GetTraces())); + + $oReplica->DisplayDetails($oP); + break; + + case 'unlink': + $iId = utils::ReadParam('id', null); + if ($iId == null) { + throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id')); + } + $oReplica = MetaModel::GetObject(SynchroReplica::class, $iId); + $oReplica->UnLink(); + + $oReplica->DisplayDetails($oP); + break; + + case 'synchro': + $iId = utils::ReadParam('id', null); + if ($iId == null) { + throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id')); + } + $oReplica = MetaModel::GetObject(SynchroReplica::class, $iId); + $oStatLog = $oReplica->ReSynchro(); + $oReplica->DisplayDetails($oP); + break; + + case 'allowdelete': + $iId = utils::ReadParam('id', null); + if ($iId == null) { + throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id')); + } + $oReplica = MetaModel::GetObject(SynchroReplica::class, $iId); + $oStatLog = $oReplica->Set('status_dest_creator',1); + $oReplica->DisplayDetails($oP); + break; + + case 'denydelete': // Select the list of objects to be modified (bulk modify) + $iId = utils::ReadParam('id', null); + if ($iId == null) { + throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id')); + } + $oReplica = MetaModel::GetObject(SynchroReplica::class, $iId); + $oStatLog = $oReplica->Set('status_dest_creator', 0); + $oReplica->DisplayDetails($oP); + break; + + case 'select_for_unlink_all': // Select the list of objects to be modified (bulk modify) + BulkHelper::OperationSelectForModifyAll($oP,'UI:UnlinkAllTabTitle', 'UI:UnlinkAllPageTitle', 'form_for_unlink_all'); + break; + + case 'select_for_unlinksynchro_all': // Select the list of objects to be modified (bulk modify) + BulkHelper::OperationSelectForModifyAll($oP,'UI:UnlinkSynchroAllTabTitle', 'UI:UnlinkSynchroAllPageTitle', 'form_for_unlinksynchro_all'); + break; + + case 'select_for_synchro_all': // Select the list of objects to be modified (bulk modify) + BulkHelper::OperationSelectForModifyAll($oP,'UI:SynchroAllTabTitle', 'UI:SynchroAllPageTitle','form_for_synchro_all'); + break; + + case 'select_for_allowdelete_all': // Select the list of objects to be modified (bulk modify) + BulkHelper::OperationSelectForModifyAll($oP,'UI:AllowDeleteAllTabTitle', 'UI:AllowDeleteAllPageTitle','form_for_allowdelete_all'); + break; + + case 'select_for_denydelete_all': // Select the list of objects to be modified (bulk modify) + BulkHelper::OperationSelectForModifyAll($oP,'UI:DenyDeleteAllTabTitle', 'UI:DenyDeleteAllPageTitle','form_for_denydelete_all'); + break; } } catch (CoreException $e) { $oP->p('An error occured while running the query:'); diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index 46ad018f42..b390b6d5c6 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -6,6 +6,12 @@ */ use Combodo\iTop\Application\WebPage\WebPage; +use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu; +use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory; +use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin; class SynchroDataSource extends cmdbAbstractObject { @@ -2108,6 +2114,12 @@ class SynchroReplica extends DBObject implements iDisplay // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } + public function InitExtendedData($oSource) + { + $sSQLTable = $oSource->GetDataTable(); + $this->m_aExtendedData = $this->LoadExtendedDataFromTable($sSQLTable); + } + public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null) { parent::__construct($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec); @@ -2189,6 +2201,16 @@ class SynchroReplica extends DBObject implements iDisplay $this->Set('status_last_error', $sText); } + /* + * Disassociate the replica from the destination object and set the status to "new" to be synchronized with the next operation + */ + public function UnLink(){ + $this->Set('dest_id', ''); + $this->Set('dest_class', ''); + $this->Set('status', 'new'); + $this->DBWrite(); + } + public function Synchro($oDataSource, $aReconciliationKeys, $aAttributes, $oChange, &$oStatLog) { $oStatLog->AddTrace(">>> Beginning of SynchroReplica::Synchro, replica status is '".$this->Get('status')."'.", $this); @@ -2365,6 +2387,89 @@ class SynchroReplica extends DBObject implements iDisplay $oStatLog->AddTrace('<<< End of SynchroReplica::Synchro.', $this); } + + /** + * + * @return \SynchroLog + * @throws \ArchivedObjectException + * @throws \CoreCannotSaveObjectException + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \CoreWarning + * @throws \MySQLException + * @throws \OQLException + * @throws \SynchroExceptionNotStarted + */ + public function ReSynchro(): SynchroLog + { + $oDataSource = MetaModel::GetObject(SynchroDataSource::class, $this->Get('sync_source_id')); + + $oStatLog = new SynchroLog(); + $oStatLog->Set('sync_source_id', $oDataSource->GetKey()); + $oStatLog->Set('start_date', time()); + $oStatLog->Set('status', 'running'); + $oStatLog->AddTrace('Manual synchro'); + + // Get the list of SQL columns + $aAttCodesExpected = array(); + $aAttCodesToReconcile = array(); + $aAttCodesToUpdate = array(); + $sSelectAtt = 'SELECT SynchroAttribute WHERE sync_source_id = :source_id AND (update = 1 OR reconcile = 1)'; + $oSetAtt = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAtt), array() /* order by*/, array('source_id' => $oDataSource->GetKey()) /* aArgs */); + while ($oSyncAtt = $oSetAtt->Fetch()) { + if ($oSyncAtt->Get('update')) { + $aAttCodesToUpdate[$oSyncAtt->Get('attcode')] = $oSyncAtt; + } + if ($oSyncAtt->Get('reconcile')) { + $aAttCodesToReconcile[$oSyncAtt->Get('attcode')] = $oSyncAtt; + } + $aAttCodesExpected[$oSyncAtt->Get('attcode')] = $oSyncAtt; + } + + // Get the list of attributes, determine reconciliation keys and update targets + // + if ($oDataSource->Get('reconciliation_policy') == 'use_attributes') { + $aReconciliationKeys = $aAttCodesToReconcile; + } elseif ($oDataSource->Get('reconciliation_policy') == 'use_primary_key') { + // Override the settings made at the attribute level ! + $aReconciliationKeys = array('primary_key' => null); + } + + if (count($aAttCodesToUpdate) == 0) { + $oStatLog->AddTrace('No attribute to update'); + throw new SynchroExceptionNotStarted('There is no attribute to update'); + } + if (count($aReconciliationKeys) == 0) { + $oStatLog->AddTrace('No attribute for reconciliation'); + throw new SynchroExceptionNotStarted('No attribute for reconciliation'); + } + + + $aAttributesToUpdate = array(); + foreach ($aAttCodesToUpdate as $sAttCode => $oSyncAtt) { + $oAttDef = MetaModel::GetAttributeDef($oDataSource->GetTargetClass(), $sAttCode); + if ($oAttDef->IsWritable()) { + $aAttributesToUpdate[$sAttCode] = $oSyncAtt; + } + } + // Create a change used for logging all the modifications/creations happening during the synchro + $oChange = MetaModel::NewObject('CMDBChange'); + $oChange->Set('date', time()); + $sUserString = CMDBChange::GetCurrentUserName(); + $oChange->Set('userinfo', $sUserString.' '.Dict::S('Core:SyncDataExchangeComment')); + $oChange->Set('origin', CMDBChangeOrigin::SYNCHRO_DATA_SOURCE); + $oChange->DBInsert(); + CMDBObject::SetCurrentChange($oChange); + + $this->InitExtendedData($oDataSource); + + $this->Synchro($oDataSource, $aReconciliationKeys, $aAttributesToUpdate, $oChange, $oStatLog); + $this->DBUpdate(); + + return $oStatLog; + } + + /** * Updates the destination object with the Extended data found in the synchro_data_XXXX table * @@ -2681,12 +2786,115 @@ class SynchroReplica extends DBObject implements iDisplay public function DisplayDetails(WebPage $oPage, $bEditMode = false) { // Object's details - //$this->DisplayBareHeader($oPage, $bEditMode); + $this->DisplayBareHeader($oPage, $bEditMode); + $oPage->AddTabContainer(OBJECT_PROPERTIES_TAB); $oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB); $oPage->SetCurrentTab('UI:PropertiesTab'); $this->DisplayBareProperties($oPage, $bEditMode); } + public function DisplayBareHeader(WebPage $oPage, $bEditMode = false) + { + $oBlock = UIContentBlockUIBlockFactory::MakeStandard('title-for-replica', ['ibo-page-header--replica-title']); + $oPage->AddSubBlock($oBlock); + + $sId = $this->GetKey(); + $oTitle = TitleUIBlockFactory::MakeNeutral(Dict::S('Class:SynchroReplica')); + $oBlock->AddSubBlock($oTitle); + $oActionsToolbar = ToolbarUIBlockFactory::MakeForButton(MenuBlock::ACTIONS_TOOLBAR_ID_PREFIX.$sId); + $oActionsToolbar->AddCSSClass('ibo-panel--toolbar'); + $oBlock->AddSubBlock($oActionsToolbar); + + $sClass = get_class($this); + $sRootUrl = utils::GetAbsoluteUrlAppRoot(); + $sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass); + $oAppContext = new ApplicationContext(); + $sContext = $oAppContext->GetForLink(); + if (utils::IsNotNullOrEmptyString($sContext)) { + $sContext = '&'.$sContext; + } + + $aActions = []; + //Delete + if (UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE)) { + $aActions['UI:Menu:Delete'] = array( + 'label' => Dict::S('UI:Menu:Delete'), + 'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$sId{$sContext}", + 'tooltip' => Dict::S('Class:SynchroReplica/Action:delete+'), + ); + } + + if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) { + if (count($aActions) > 0) { + $sSeparator = ''; + $aActions['sep_0'] = array('label' => $sSeparator, 'url' => ''); + } + $sUrl = "{$sRootUrl}synchro/replica.php?operation=unlink&class=$sClass&id=$sId{$sContext}"; + $aActions['Class:SynchroReplica/Action:unlink'] = [ + 'label' => Dict::S('Class:SynchroReplica/Action:unlink'), + 'url' => $sUrl, + 'tooltip' => Dict::S('Class:SynchroReplica/Action:unlink+'), + ]; + + $sUrl = "{$sRootUrl}synchro/replica.php?operation=unlinksynchro&class=$sClass&id=$sId{$sContext}"; + $aActions['Class:SynchroReplica/Action:unlinksynchro'] = [ + 'label' => Dict::S('Class:SynchroReplica/Action:unlinksynchro'), + 'url' => $sUrl, + 'tooltip' => Dict::S('Class:SynchroReplica/Action:unlinksynchro+'), + ]; + + $sUrl = "{$sRootUrl}synchro/replica.php?operation=synchro&class=$sClass&id=$sId{$sContext}"; + $aActions['Class:SynchroReplica/Action:synchro'] = [ + 'label' => Dict::S('Class:SynchroReplica/Action:synchro'), + 'url' => $sUrl, + 'tooltip' => Dict::S('Class:SynchroReplica/Action:synchro+'), + ]; + + if ($this->Get('status_dest_creator') == 1) { + $sUrl = "{$sRootUrl}synchro/replica.php?operation=denydelete&class=$sClass&id=$sId{$sContext}"; + $aActions['Class:SynchroReplica/Action:denydelete'] = [ + 'label' => Dict::S('Class:SynchroReplica/Action:denydelete'), + 'url' => $sUrl, + 'tooltip' => Dict::S('Class:SynchroReplica/Action:denydelete+'), + ]; + } else { + $sUrl = "{$sRootUrl}synchro/replica.php?operation=allowdelete&class=$sClass&id=$sId{$sContext}"; + $aActions['Class:SynchroReplica/Action:allowdelete'] = [ + 'label' => Dict::S('Class:SynchroReplica/Action:allowdelete'), + 'url' => $sUrl, + 'tooltip' => Dict::S('Class:SynchroReplica/Action:allowdelete+'), + ]; + } + } + if (count($aActions) > 0) { + $sRegularActionsMenuTogglerId = "ibo-regular-actions-menu-toggler-{$sId}"; + $sRegularActionsPopoverMenuId = "ibo-regular-actions-popover-{$sId}"; + + $oActionButton = ButtonUIBlockFactory::MakeIconAction('fas fa-ellipsis-v', Dict::S('UI:Menu:Actions'), 'UI:Menu:Actions', '', false, $sRegularActionsMenuTogglerId) + ->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']); + + $oRegularActionsMenu = $oPage->GetPopoverMenu($sRegularActionsPopoverMenuId, $aActions) + ->SetTogglerJSSelector("#$sRegularActionsMenuTogglerId") + ->SetContainer(PopoverMenu::ENUM_CONTAINER_BODY); + + $oActionsToolbar->AddSubBlock($oActionButton) + ->AddSubBlock($oRegularActionsMenu); + + $oActionButton = ButtonUIBlockFactory::MakeIconLink('fas fa-search', Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), "{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}", '', 'UI:SearchFor_Class'); + $oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']); + $oActionsToolbar->AddSubBlock($oActionButton); + } + + $sUrl = "{$sRootUrl}pages/$sUIPage?operation=display&class=$sClass&id=$sId{$sContext}"; + $oActionButton = ButtonUIBlockFactory::MakeAlternativeNeutral('', 'UI:Button:Refresh'); + $oActionButton->SetIconClass('fas fa-sync-alt') + ->SetOnClickJsCode('window.location.href=\''.$sUrl.'\'') + ->SetTooltip(Dict::S('UI:Button:Refresh')) + ->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']); + $oActionsToolbar->AddSubBlock($oActionButton); + + return $oBlock; + } public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = []) { From ed1cfc3b0884578d3322851ee34521ac60aa97eb Mon Sep 17 00:00:00 2001 From: Anne-Catherine <57360138+accognet@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:01:06 +0200 Subject: [PATCH 21/36] =?UTF-8?q?N=C2=B07289=20-=20Read-only=20attribute,?= =?UTF-8?q?=20dynamically=20read-write,=20entry=20silently=20ignored=20on?= =?UTF-8?q?=20submission=20(#724)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/cmdbabstract.class.inc.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 9fdfc7714f..1169c3a279 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -175,6 +175,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay */ protected $sDisplayMode; protected $aFieldsMap; + /** + * @var array Store posted values in order to be used in GetAttributeFlags + * @since 3.3.0 + */ + protected $aPostedValues = []; /** * If true, bypass IsActionAllowedOnAttribute when writing this object @@ -3800,7 +3805,7 @@ HTML; return $aWriteableAttList; } - /** + /** * Compute the attribute flags depending on the object state */ public function GetFormAttributeFlags($sAttCode) @@ -3992,6 +3997,7 @@ HTML; $aErrors = []; $aFinalValues = []; + $this->aPostedValues = $aValues; // Store the values for later use (e.g. in GetAttributeFlags) foreach ($this->GetWriteableAttList(array_keys($aValues), $aErrors, $aAttFlags) as $sAttCode => $oAttDef) { $aFinalValues[$sAttCode] = $aValues[$sAttCode]; } From caa1b8652d5d6901adfbf375a342172cca391996 Mon Sep 17 00:00:00 2001 From: Anne-Cath Date: Mon, 15 Jun 2026 16:51:02 +0200 Subject: [PATCH 22/36] FIxes unit tests --- application/cmdbabstract.class.inc.php | 2 +- application/displayblock.class.inc.php | 19 ++-- dictionaries/en_gb.dictionary.itop.core.php | 2 +- dictionaries/fr.dictionary.itop.core.php | 2 +- dictionaries/ru.dictionary.itop.core.php | 2 +- pages/UI.php | 10 +- sources/Application/Helper/BulkHelper.php | 92 ++++++++++--------- .../Helper/SynchroReplicaHelper.php | 4 +- synchro/replica.php | 17 ++-- synchro/synchrodatasource.class.inc.php | 6 +- 10 files changed, 79 insertions(+), 77 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 1169c3a279..da11de9e8a 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -3805,7 +3805,7 @@ HTML; return $aWriteableAttList; } - /** + /** * Compute the attribute flags depending on the object state */ public function GetFormAttributeFlags($sAttCode) diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 26a8fe7a8d..df6dc1e1e6 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -1078,7 +1078,6 @@ JS if ($aCounts[$sStateValue] == 0) { $aCounts[$sStateValue] = ['link' => '-', 'label' => $aCounts[$sStateValue]]; - ; } else { $oSingleGroupByValueFilter = $this->m_oFilter->DeepClone(); $oSingleGroupByValueFilter->AddCondition($sStateAttrCode, $sStateValue, '='); @@ -1948,7 +1947,7 @@ class MenuBlock extends DisplayBlock // Check rights on class $bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && (($oReflectionClass->IsSubclassOf(cmdbAbstractObject::class) || $sSelectedClass === SynchroReplica::class)); - $bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE); + $bIsBulkDeleteAllowed = (bool)UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE); // Refine filter on selected class so bullk actions occur on the right class $oSelectedClassFilter = $this->GetFilter()->DeepClone(); @@ -1958,12 +1957,12 @@ class MenuBlock extends DisplayBlock // Action label dict code has a specific suffix for "Link" / "Remote" aliases to allow dedicated labels in linksets. $sActionLabelCodeSuffix = in_array($sSelectedAlias, ['Link', 'Remote']) ? $sSelectedAlias : 'Class'; if ($bIsBulkModifyAllowed) { - if($sSelectedClass === SynchroReplica::class){ - $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnlinkAll:', Dict::S('Class:SynchroReplica/Action:unlink_all'),'unlink'); - $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnLinkSynchroAll:', Dict::S('Class:SynchroReplica/Action:unlinksynchro_all'),'unlinksynchro'); - $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:SynchroAll:', Dict::S('Class:SynchroReplica/Action:synchro_all'),'synchro'); - $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:AllowDeleteAll:', Dict::S('Class:SynchroReplica/Action:allowdelete_all'),'allowdelete'); - $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:DenyDeleteAll:', Dict::S('Class:SynchroReplica/Action:denydelete_all'),'denydelete'); + if ($sSelectedClass === SynchroReplica::class) { + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnlinkAll:', Dict::S('Class:SynchroReplica/Action:unlink_all'), 'unlink'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnLinkSynchroAll:', Dict::S('Class:SynchroReplica/Action:unlinksynchro_all'), 'unlinksynchro'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:SynchroAll:', Dict::S('Class:SynchroReplica/Action:synchro_all'), 'synchro'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:AllowDeleteAll:', Dict::S('Class:SynchroReplica/Action:allowdelete_all'), 'allowdelete'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:DenyDeleteAll:', Dict::S('Class:SynchroReplica/Action:denydelete_all'), 'denydelete'); } else { $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName)); } @@ -2489,8 +2488,8 @@ class MenuBlock extends DisplayBlock protected function AddBulkModifyObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:ModifyAll', $sActionLabel = 'UI:Menu:ModifyAll', $sOperationName = 'modify'): void { $aActions[$sActionIdentifier] = [ - 'label' => Dict::S($sActionLabel), - 'url' => $this->PrepareUrlForStandardMenuAction($sClass, 'operation=select_for_'.$sOperationName.'_all&class='.$sClass.'&filter='.urlencode($sFilter)), + 'label' => Dict::S($sActionLabel), + 'url' => $this->PrepareUrlForStandardMenuAction($sClass, 'operation=select_for_'.$sOperationName.'_all&class='.$sClass.'&filter='.urlencode($sFilter)), ] + $this->GetDefaultParamsForMenuAction(); } diff --git a/dictionaries/en_gb.dictionary.itop.core.php b/dictionaries/en_gb.dictionary.itop.core.php index dd3a6c6067..60b1a99036 100644 --- a/dictionaries/en_gb.dictionary.itop.core.php +++ b/dictionaries/en_gb.dictionary.itop.core.php @@ -965,7 +965,7 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of 'Core:SynchroReplica:PublicData' => 'Public Data', 'Core:SynchroReplica:PrivateDetails' => 'Private Details', 'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s', - 'Core:SynchroReplica:ListOfReplicas' => 'List of Replica', + 'Core:SynchroReplica:ListOfReplicas' => 'Replicas of the data source: %1$s', 'Core:SynchroAttExtKey:ReconciliationById' => 'id (Primary Key)', 'Core:SynchroAtt:attcode' => 'Attribute', 'Core:SynchroAtt:attcode+' => 'Field of the object', diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php index e155817e60..f4803cedfb 100644 --- a/dictionaries/fr.dictionary.itop.core.php +++ b/dictionaries/fr.dictionary.itop.core.php @@ -928,7 +928,7 @@ Plusieurs champs peuvent ainsi être modifiés lors d\'une "mise à jour" en app 'Core:SynchroReplica:PublicData' => 'Données synchronisées', 'Core:SynchroReplica:PrivateDetails' => 'Informations internes', 'Core:SynchroReplica:BackToDataSource' => 'Retourner aux détails de la source de données: %1$s', - 'Core:SynchroReplica:ListOfReplicas' => 'Liste des réplica', + 'Core:SynchroReplica:ListOfReplicas' => 'Liste des réplicas de %1$s', 'Core:SynchroAttExtKey:ReconciliationById' => 'id (clé primaire)', 'Core:SynchroAtt:attcode' => 'Champ', 'Core:SynchroAtt:attcode+' => '', diff --git a/dictionaries/ru.dictionary.itop.core.php b/dictionaries/ru.dictionary.itop.core.php index 59a1dda46e..f867f114cd 100644 --- a/dictionaries/ru.dictionary.itop.core.php +++ b/dictionaries/ru.dictionary.itop.core.php @@ -882,7 +882,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', [ 'Core:SynchroReplica:PublicData' => 'Public Data~~', 'Core:SynchroReplica:PrivateDetails' => 'Private Details~~', 'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s~~', - 'Core:SynchroReplica:ListOfReplicas' => 'List of Replica~~', + 'Core:SynchroReplica:ListOfReplicas' => 'Replicas of the data source: %1$s~~', 'Core:SynchroAttExtKey:ReconciliationById' => 'id (Primary Key)~~', 'Core:SynchroAtt:attcode' => 'Атрибут', 'Core:SynchroAtt:attcode+' => 'Поле объекта', diff --git a/pages/UI.php b/pages/UI.php index b3dda33ba5..625739f32f 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -587,20 +587,20 @@ try { break; case 'form_for_unlink_all': // Form to modify multiple objects (bulk modify) - SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'unlink'); + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'unlink'); break; case 'form_for_unlinksynchro_all': // Form to modify multiple objects (bulk modify) - SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'unlinksynchro'); + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'unlinksynchro'); break; case 'form_for_synchro_all': // Form to modify multiple objects (bulk modify) - SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'synchro'); + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'synchro'); break; case 'form_for_allowdelete_all': // Form to modify multiple objects (bulk modify) - SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'allowdelete'); + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'allowdelete'); break; case 'form_for_denydelete_all': // Form to modify multiple objects (bulk modify) - SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext,'denydelete'); + SynchroReplicaHelper::OperationUnlinkAll($oP, $oAppContext, 'denydelete'); break; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/sources/Application/Helper/BulkHelper.php b/sources/Application/Helper/BulkHelper.php index dac8c3a79e..0aad8f430b 100644 --- a/sources/Application/Helper/BulkHelper.php +++ b/sources/Application/Helper/BulkHelper.php @@ -31,7 +31,7 @@ class BulkHelper * @throws \CoreException * @throws \OQLException */ - public static function OperationSelectForModifyAll(iTopWebPage $oP, $sTitleTab = 'UI:ModifyAllPageTitle', $sTitleCode = 'UI:Modify_ObjectsOf_Class', $sNextOperation = 'form_for_modify_all'): void + public static function OperationSelectForModifyAll(iTopWebPage $oP, $sTitleTab = 'UI:ModifyAllPageTitle', $sTitleCode = 'UI:Modify_ObjectsOf_Class', $sNextOperation = 'form_for_modify_all'): void { $oP->DisableBreadCrumb(); $oP->set_title(Dict::S($sTitleTab)); @@ -47,10 +47,10 @@ class BulkHelper $sClassName = MetaModel::GetName($sClass); $aDisplayParams = [ - 'icon' => MetaModel::GetClassIcon($sClass, false), - 'title' => Dict::Format($sTitleCode, $sClassName), + 'icon' => MetaModel::GetClassIcon($sClass, false), + 'title' => Dict::Format($sTitleCode, $sClassName), ]; - self::DisplayMultipleSelectionForm($oP, $oFilter, $sNextOperation, $oChecker, [], $aDisplayParams); + self::DisplayMultipleSelectionForm($oP, $oFilter, $sNextOperation, $oChecker, [], $aDisplayParams); } /** @@ -74,47 +74,49 @@ class BulkHelper // Add user filter $oFullSetFilter->UpdateContextFromUser(); $aSelectedObj = utils::ReadMultipleSelection($oFullSetFilter); - $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter=' . urlencode($sFilter) . '&' . $oAppContext->GetForLink(); - $aContext = array('filter' => utils::EscapeHtml($sFilter)); - cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, array(), $aContext); + $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter='.urlencode($sFilter).'&'.$oAppContext->GetForLink(); + $aContext = array('filter' => utils::EscapeHtml($sFilter)); + cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, array(), $aContext); } - /** - * Operation preview_or_modify_all - * - * @param iTopWebPage $oP - * @param \ApplicationContext $oAppContext - * - * @throws \ApplicationException - * @throws \ArchivedObjectException - * @throws \CoreCannotSaveObjectException - * @throws \CoreException - * @throws \DictExceptionMissingString - * @throws \OQLException - */ - public static function OperationPreviewOrModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void - { - $oP->DisableBreadCrumb(); - $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); - $oFilter = DBObjectSearch::unserialize($sFilter); // TO DO : check that the filter is valid - // Add user filter - $oFilter->UpdateContextFromUser(); + /** + * Operation preview_or_modify_all + * + * @param iTopWebPage $oP + * @param \ApplicationContext $oAppContext + * + * @throws \ApplicationException + * @throws \ArchivedObjectException + * @throws \CoreCannotSaveObjectException + * @throws \CoreException + * @throws \DictExceptionMissingString + * @throws \OQLException + */ + public static function OperationPreviewOrModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void + { + $oP->DisableBreadCrumb(); + $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); + $oFilter = DBObjectSearch::unserialize($sFilter); // TO DO : check that the filter is valid + // Add user filter + $oFilter->UpdateContextFromUser(); - $sClass = utils::ReadParam('class', '', false, 'class'); - $bPreview = utils::ReadParam('preview_mode', ''); - $sSelectedObj = utils::ReadParam('selectObj', '', false, 'raw_data'); - if (empty($sClass) || empty($sSelectedObj)) // TO DO: check that the class name is valid ! - { - throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj')); - } - $aSelectedObj = explode(',', $sSelectedObj); - $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter=' . urlencode($sFilter) . '&' . $oAppContext->GetForLink(); - $aContext = array( - 'filter' => utils::EscapeHtml($sFilter), - 'selectObj' => $sSelectedObj, - ); - cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext); - }/** + $sClass = utils::ReadParam('class', '', false, 'class'); + $bPreview = utils::ReadParam('preview_mode', ''); + $sSelectedObj = utils::ReadParam('selectObj', '', false, 'raw_data'); + if (empty($sClass) || empty($sSelectedObj)) // TO DO: check that the class name is valid ! + { + throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj')); + } + $aSelectedObj = explode(',', $sSelectedObj); + $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter='.urlencode($sFilter).'&'.$oAppContext->GetForLink(); + $aContext = array( + 'filter' => utils::EscapeHtml($sFilter), + 'selectObj' => $sSelectedObj, + ); + cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext); + } + + /** * Displays a form (checkboxes) to select the objects for which to apply a given action * Only the objects for which the action is valid can be checked. By default all valid objects are checked * @@ -128,8 +130,8 @@ class BulkHelper * @throws \ApplicationException * @throws \ArchivedObjectException * @throws \CoreException - *@since 3.0.0 $aDisplayParams parameter - * + * @since 3.0.0 $aDisplayParams parameter + * */ public static function DisplayMultipleSelectionForm(WebPage $oP, DBSearch $oFilter, string $sNextOperation, ActionChecker $oChecker, array $aExtraFormParams = [], array $aDisplayParams = []) { @@ -145,7 +147,7 @@ class BulkHelper } $oForm = new Form(); - $oForm->SetAction( utils::GetAbsoluteUrlAppRoot().'pages/UI.php'); + $oForm->SetAction(utils::GetAbsoluteUrlAppRoot().'pages/UI.php'); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sNextOperation)); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $oFilter->GetClass())); $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('filter', utils::HtmlEntities($oFilter->Serialize()))); diff --git a/sources/Application/Helper/SynchroReplicaHelper.php b/sources/Application/Helper/SynchroReplicaHelper.php index b37defbc32..32e9707a39 100644 --- a/sources/Application/Helper/SynchroReplicaHelper.php +++ b/sources/Application/Helper/SynchroReplicaHelper.php @@ -27,7 +27,7 @@ class SynchroReplicaHelper // Add user filter $oFullSetFilter->UpdateContextFromUser(); $aSelectObject = utils::ReadMultipleSelection($oFullSetFilter); - if ( empty($sClass) || empty($aSelectObject)) // TO DO: check that the class name is valid ! + if (empty($sClass) || empty($aSelectObject)) // TO DO: check that the class name is valid ! { throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObject[]')); } @@ -67,7 +67,7 @@ class SynchroReplicaHelper $bResult = true; try { if (in_array($sOperation, ['unlink', 'unlinksynchro'])) { - $oReplica->UnLink(); + $oReplica->UnLink(); } if (in_array($sOperation, ['synchro', 'unlinksynchro'])) { $oStatLog = $oReplica->ReSynchro(); diff --git a/synchro/replica.php b/synchro/replica.php index 21dd073e73..292a886719 100644 --- a/synchro/replica.php +++ b/synchro/replica.php @@ -18,10 +18,10 @@ * You should have received a copy of the GNU Affero General Public License */ +use Combodo\iTop\Application\Helper\BulkHelper; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory; use Combodo\iTop\Application\WebPage\iTopWebPage; -use Combodo\iTop\Application\Helper\BulkHelper; require_once('../approot.inc.php'); require_once(APPROOT.'/application/application.inc.php'); @@ -37,7 +37,6 @@ $oP = new iTopWebPage("iTop - Synchro Replicas"); // Main program $sOperation = utils::ReadParam('operation', 'details'); - try { switch ($sOperation) { case 'details': @@ -53,7 +52,7 @@ try { $iSourceId = utils::ReadParam('datasource', null); if ($iSourceId != null) { $oSource = MetaModel::GetObject(SynchroDataSource::class, $iSourceId); - $oBackButton = ButtonUIBlockFactory::MakeLinkNeutral( ApplicationContext::MakeObjectUrl(SynchroDataSource::class, $iSourceId), Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetName()), 'fas fa-chevron-left'); + $oBackButton = ButtonUIBlockFactory::MakeLinkNeutral(ApplicationContext::MakeObjectUrl(SynchroDataSource::class, $iSourceId), Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetName()), 'fas fa-chevron-left'); $oP->AddUiBlock($oBackButton); $oP->AddUiBlock(TitleUIBlockFactory::MakeForPage(Dict::Format('Core:SynchroReplica:ListOfReplicas', $oSource->GetName()))); } @@ -115,7 +114,7 @@ try { throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id')); } $oReplica = MetaModel::GetObject(SynchroReplica::class, $iId); - $oStatLog = $oReplica->Set('status_dest_creator',1); + $oStatLog = $oReplica->Set('status_dest_creator', 1); $oReplica->DisplayDetails($oP); break; @@ -130,23 +129,23 @@ try { break; case 'select_for_unlink_all': // Select the list of objects to be modified (bulk modify) - BulkHelper::OperationSelectForModifyAll($oP,'UI:UnlinkAllTabTitle', 'UI:UnlinkAllPageTitle', 'form_for_unlink_all'); + BulkHelper::OperationSelectForModifyAll($oP, 'UI:UnlinkAllTabTitle', 'UI:UnlinkAllPageTitle', 'form_for_unlink_all'); break; case 'select_for_unlinksynchro_all': // Select the list of objects to be modified (bulk modify) - BulkHelper::OperationSelectForModifyAll($oP,'UI:UnlinkSynchroAllTabTitle', 'UI:UnlinkSynchroAllPageTitle', 'form_for_unlinksynchro_all'); + BulkHelper::OperationSelectForModifyAll($oP, 'UI:UnlinkSynchroAllTabTitle', 'UI:UnlinkSynchroAllPageTitle', 'form_for_unlinksynchro_all'); break; case 'select_for_synchro_all': // Select the list of objects to be modified (bulk modify) - BulkHelper::OperationSelectForModifyAll($oP,'UI:SynchroAllTabTitle', 'UI:SynchroAllPageTitle','form_for_synchro_all'); + BulkHelper::OperationSelectForModifyAll($oP, 'UI:SynchroAllTabTitle', 'UI:SynchroAllPageTitle', 'form_for_synchro_all'); break; case 'select_for_allowdelete_all': // Select the list of objects to be modified (bulk modify) - BulkHelper::OperationSelectForModifyAll($oP,'UI:AllowDeleteAllTabTitle', 'UI:AllowDeleteAllPageTitle','form_for_allowdelete_all'); + BulkHelper::OperationSelectForModifyAll($oP, 'UI:AllowDeleteAllTabTitle', 'UI:AllowDeleteAllPageTitle', 'form_for_allowdelete_all'); break; case 'select_for_denydelete_all': // Select the list of objects to be modified (bulk modify) - BulkHelper::OperationSelectForModifyAll($oP,'UI:DenyDeleteAllTabTitle', 'UI:DenyDeleteAllPageTitle','form_for_denydelete_all'); + BulkHelper::OperationSelectForModifyAll($oP, 'UI:DenyDeleteAllTabTitle', 'UI:DenyDeleteAllPageTitle', 'form_for_denydelete_all'); break; } } catch (CoreException $e) { diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index b390b6d5c6..39d342f7e3 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -5,12 +5,12 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ -use Combodo\iTop\Application\WebPage\WebPage; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu; use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory; +use Combodo\iTop\Application\WebPage\WebPage; use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin; class SynchroDataSource extends cmdbAbstractObject @@ -2204,7 +2204,8 @@ class SynchroReplica extends DBObject implements iDisplay /* * Disassociate the replica from the destination object and set the status to "new" to be synchronized with the next operation */ - public function UnLink(){ + public function UnLink() + { $this->Set('dest_id', ''); $this->Set('dest_class', ''); $this->Set('status', 'new'); @@ -2793,6 +2794,7 @@ class SynchroReplica extends DBObject implements iDisplay $oPage->SetCurrentTab('UI:PropertiesTab'); $this->DisplayBareProperties($oPage, $bEditMode); } + public function DisplayBareHeader(WebPage $oPage, $bEditMode = false) { $oBlock = UIContentBlockUIBlockFactory::MakeStandard('title-for-replica', ['ibo-page-header--replica-title']); From 8ccf832bdd6d38aec8883750396bb55c3bde1d76 Mon Sep 17 00:00:00 2001 From: Molkobain Date: Mon, 15 Jun 2026 19:10:25 +0200 Subject: [PATCH 23/36] :green_heart: Fix PHP code styles --- application/cmdbabstract.class.inc.php | 12 ++++---- sources/Application/Helper/BulkHelper.php | 13 ++++----- .../Helper/SynchroReplicaHelper.php | 29 +++++++++---------- synchro/synchrodatasource.class.inc.php | 21 ++++++-------- 4 files changed, 34 insertions(+), 41 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index da11de9e8a..364b2a4d2f 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -175,11 +175,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay */ protected $sDisplayMode; protected $aFieldsMap; - /** - * @var array Store posted values in order to be used in GetAttributeFlags - * @since 3.3.0 - */ - protected $aPostedValues = []; + /** + * @var array Store posted values in order to be used in GetAttributeFlags + * @since 3.3.0 + */ + protected $aPostedValues = []; /** * If true, bypass IsActionAllowedOnAttribute when writing this object @@ -531,7 +531,7 @@ JS $aTags[$sSynchroTagId] = ['title' => $sTip, 'css_classes' => 'ibo-object-details--tag--synchronized', 'decoration_classes' => 'fas fa-lock', 'label' => $sLabel]; if (UserRights::IsActionAllowed(SynchroReplica::class, UR_ACTION_READ)) { $oDBSearch = DBObjectSearch::FromOQL('SELECT SynchroReplica WHERE dest_class=:sClass AND dest_id=:id'); - $sFilter = rawurlencode($oDBSearch->serialize(false,['sClass'=>get_class($this),'id'=>$this->GetKey()])); + $sFilter = rawurlencode($oDBSearch->serialize(false, ['sClass' => get_class($this),'id' => $this->GetKey()])); $sUrlSearchReplica = 'UI.php?operation=search&filter='.$sFilter; $oPage->add_ready_script("$('#$sSynchroTagId').on('click',function() {window.location = '$sUrlSearchReplica' });"); } diff --git a/sources/Application/Helper/BulkHelper.php b/sources/Application/Helper/BulkHelper.php index 0aad8f430b..b2593b6707 100644 --- a/sources/Application/Helper/BulkHelper.php +++ b/sources/Application/Helper/BulkHelper.php @@ -75,8 +75,8 @@ class BulkHelper $oFullSetFilter->UpdateContextFromUser(); $aSelectedObj = utils::ReadMultipleSelection($oFullSetFilter); $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter='.urlencode($sFilter).'&'.$oAppContext->GetForLink(); - $aContext = array('filter' => utils::EscapeHtml($sFilter)); - cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, array(), $aContext); + $aContext = ['filter' => utils::EscapeHtml($sFilter)]; + cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, [], $aContext); } /** @@ -103,16 +103,15 @@ class BulkHelper $sClass = utils::ReadParam('class', '', false, 'class'); $bPreview = utils::ReadParam('preview_mode', ''); $sSelectedObj = utils::ReadParam('selectObj', '', false, 'raw_data'); - if (empty($sClass) || empty($sSelectedObj)) // TO DO: check that the class name is valid ! - { + if (empty($sClass) || empty($sSelectedObj)) { // TO DO: check that the class name is valid ! throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj')); } $aSelectedObj = explode(',', $sSelectedObj); $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter='.urlencode($sFilter).'&'.$oAppContext->GetForLink(); - $aContext = array( + $aContext = [ 'filter' => utils::EscapeHtml($sFilter), 'selectObj' => $sSelectedObj, - ); + ]; cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext); } @@ -177,4 +176,4 @@ class BulkHelper $oP->AddUiBlock($oForm); } -} \ No newline at end of file +} diff --git a/sources/Application/Helper/SynchroReplicaHelper.php b/sources/Application/Helper/SynchroReplicaHelper.php index 32e9707a39..012fdd30d9 100644 --- a/sources/Application/Helper/SynchroReplicaHelper.php +++ b/sources/Application/Helper/SynchroReplicaHelper.php @@ -17,7 +17,6 @@ use utils; class SynchroReplicaHelper { - public static function OperationUnlinkAll(iTopWebPage $oP, ApplicationContext $oAppContext, $sOperation = 'unlink'): void { $oP->DisableBreadCrumb(); @@ -27,24 +26,23 @@ class SynchroReplicaHelper // Add user filter $oFullSetFilter->UpdateContextFromUser(); $aSelectObject = utils::ReadMultipleSelection($oFullSetFilter); - if (empty($sClass) || empty($aSelectObject)) // TO DO: check that the class name is valid ! - { + if (empty($sClass) || empty($aSelectObject)) { // TO DO: check that the class name is valid ! throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObject[]')); } $sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink(); - $aHeaders = array( - 'object' => array('label' => MetaModel::GetName($sClass), 'description' => Dict::S('UI:ModifiedObject')), - 'status' => array( + $aHeaders = [ + 'object' => ['label' => MetaModel::GetName($sClass), 'description' => Dict::S('UI:ModifiedObject')], + 'status' => [ 'label' => Dict::S('UI:BulkModifyStatus'), 'description' => Dict::S('UI:BulkModifyStatus+'), - ), - 'errors' => array( + ], + 'errors' => [ 'label' => Dict::S('UI:BulkModifyErrors'), 'description' => Dict::S('UI:BulkModifyErrors+'), - ), - ); - $aRows = array(); + ], + ]; + $aRows = []; $sHeaderTitle = Dict::Format('UI:Modify_N_ObjectsOf_Class', count($aSelectObject), MetaModel::GetName($sClass)); $sClassIcon = MetaModel::GetClassIcon($sClass, false); @@ -81,8 +79,7 @@ class SynchroReplicaHelper $oReplica->Set('status_dest_creator', 0); $oReplica->DBUpdate(); } - } - catch (Exception $e) { + } catch (Exception $e) { $bResult = false; $aErrors[] = $e->getMessage(); } @@ -92,11 +89,11 @@ class SynchroReplicaHelper $aErrorsToDisplay = array_map(function ($sError) { return utils::HtmlEntities($sError); }, $aErrors); - $aRows[] = array( + $aRows[] = [ 'object' => $oReplica->GetHyperlink(), 'status' => $sStatus, 'errors' => '

'.($bResult ? '' : implode('

', $aErrorsToDisplay)).'

', - ); + ]; } set_time_limit(intval($iPreviousTimeLimit)); @@ -112,4 +109,4 @@ class SynchroReplicaHelper $oP->AddUiBlock($oPanel); $oP->AddSubBlock(ButtonUIBlockFactory::MakeForSecondaryAction(Dict::S('UI:Button:Done')))->SetOnClickJsCode("window.location.href='$sCancelUrl'")->AddCSSClass('mt-5'); } -} \ No newline at end of file +} diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index 39d342f7e3..09a9ada7ef 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -2388,7 +2388,6 @@ class SynchroReplica extends DBObject implements iDisplay $oStatLog->AddTrace('<<< End of SynchroReplica::Synchro.', $this); } - /** * * @return \SynchroLog @@ -2412,11 +2411,11 @@ class SynchroReplica extends DBObject implements iDisplay $oStatLog->AddTrace('Manual synchro'); // Get the list of SQL columns - $aAttCodesExpected = array(); - $aAttCodesToReconcile = array(); - $aAttCodesToUpdate = array(); + $aAttCodesExpected = []; + $aAttCodesToReconcile = []; + $aAttCodesToUpdate = []; $sSelectAtt = 'SELECT SynchroAttribute WHERE sync_source_id = :source_id AND (update = 1 OR reconcile = 1)'; - $oSetAtt = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAtt), array() /* order by*/, array('source_id' => $oDataSource->GetKey()) /* aArgs */); + $oSetAtt = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAtt), [] /* order by*/, ['source_id' => $oDataSource->GetKey()] /* aArgs */); while ($oSyncAtt = $oSetAtt->Fetch()) { if ($oSyncAtt->Get('update')) { $aAttCodesToUpdate[$oSyncAtt->Get('attcode')] = $oSyncAtt; @@ -2433,7 +2432,7 @@ class SynchroReplica extends DBObject implements iDisplay $aReconciliationKeys = $aAttCodesToReconcile; } elseif ($oDataSource->Get('reconciliation_policy') == 'use_primary_key') { // Override the settings made at the attribute level ! - $aReconciliationKeys = array('primary_key' => null); + $aReconciliationKeys = ['primary_key' => null]; } if (count($aAttCodesToUpdate) == 0) { @@ -2445,8 +2444,7 @@ class SynchroReplica extends DBObject implements iDisplay throw new SynchroExceptionNotStarted('No attribute for reconciliation'); } - - $aAttributesToUpdate = array(); + $aAttributesToUpdate = []; foreach ($aAttCodesToUpdate as $sAttCode => $oSyncAtt) { $oAttDef = MetaModel::GetAttributeDef($oDataSource->GetTargetClass(), $sAttCode); if ($oAttDef->IsWritable()) { @@ -2470,7 +2468,6 @@ class SynchroReplica extends DBObject implements iDisplay return $oStatLog; } - /** * Updates the destination object with the Extended data found in the synchro_data_XXXX table * @@ -2819,17 +2816,17 @@ class SynchroReplica extends DBObject implements iDisplay $aActions = []; //Delete if (UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE)) { - $aActions['UI:Menu:Delete'] = array( + $aActions['UI:Menu:Delete'] = [ 'label' => Dict::S('UI:Menu:Delete'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$sId{$sContext}", 'tooltip' => Dict::S('Class:SynchroReplica/Action:delete+'), - ); + ]; } if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) { if (count($aActions) > 0) { $sSeparator = ''; - $aActions['sep_0'] = array('label' => $sSeparator, 'url' => ''); + $aActions['sep_0'] = ['label' => $sSeparator, 'url' => '']; } $sUrl = "{$sRootUrl}synchro/replica.php?operation=unlink&class=$sClass&id=$sId{$sContext}"; $aActions['Class:SynchroReplica/Action:unlink'] = [ From 1123a83490c688f9a38b21e3e878e5cb5edc851a Mon Sep 17 00:00:00 2001 From: odain Date: Mon, 15 Jun 2026 22:23:19 +0200 Subject: [PATCH 24/36] :green_heart: Fix PHP code styles --- pages/UI.php | 1 - 1 file changed, 1 deletion(-) diff --git a/pages/UI.php b/pages/UI.php index 625739f32f..1cd6383d2f 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -1355,4 +1355,3 @@ try { $sOperationToLog = $operation ?? 'N/A'; IssueLog::Debug('UI.php operation='.$sOperationToLog.', error='.$e->getMessage()."\n".$sErrorStackTrace, LogChannels::CONSOLE); } - From 0240d120070d6a26b37b04c2dc27aec95f49a768 Mon Sep 17 00:00:00 2001 From: Timmy38 Date: Tue, 16 Jun 2026 10:13:00 +0200 Subject: [PATCH 25/36] =?UTF-8?q?N=C2=B09690=20Fix=20multiple=20added=5Fex?= =?UTF-8?q?tensions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../simulate-audit-from-setup.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php index eec10f1c46..dbfca2a4ea 100644 --- a/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php +++ b/tests/php-unit-tests/unitary-tests/datamodels/2.x/combodo-data-feature-removal/simulate-audit-from-setup.php @@ -12,6 +12,9 @@ $sToken = SetupUtils::CreateSetupToken(); function GetLastestInstallFile(): ?string { $aFiles = glob(APPROOT.'/log/install-*.xml'); + if ($aFiles === false) { + return null; + } rsort($aFiles); $iLatestCtime = 0; $sLastFilePath = null; @@ -36,14 +39,15 @@ $aParams = new XMLParameters($sPath); $aSelectedModules = $aParams->Get('selected_modules', []); $aSelectedExtensions = $aParams->Get('selected_extensions', []); -$sAddedExtensions = utils::ReadParam('added_extensions', ''); +$sAddedExtensions = utils::ReadParam('added_extensions', '', false, 'raw'); $aAddedExtensions = []; if (mb_strlen($sAddedExtensions) > 0) { $aAddedExtensions = explode(',', $sAddedExtensions); } $oExtensionMap = new iTopExtensionsMap(); -foreach ($aAddedExtensions as $sExtensionCode) { +foreach ($aAddedExtensions as $iIndex => $sExtensionCode) { if (mb_strlen($sExtensionCode) <= 0) { + unset($aAddedExtensions[$iIndex]); continue; } $oExtension = $oExtensionMap->GetFromExtensionCode($sExtensionCode); @@ -69,8 +73,8 @@ $aRemovedExtensions = array_combine($aRemovedExtensionsAndModules, $aRemovedExte $aAddedExtensions = array_combine($aAddedExtensions, $aAddedExtensions); $aPostParams = [ - "auth_user" => 'admin', - "auth_pwd" => 'admin', + 'auth_user' => 'admin', + 'auth_pwd' => 'admin', 'login_mode' => 'form', 'operation' => 'AnalysisResult', 'authent' => $sToken, From 207ac373d0e5e55908d26375eafd5603bc6dc49c Mon Sep 17 00:00:00 2001 From: odain-cbd <56586767+odain-cbd@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:22:05 +0200 Subject: [PATCH 26/36] =?UTF-8?q?=20N=C2=B09678=20-=20Datamodel=20not=20ad?= =?UTF-8?q?ded=20after=20extension=20management=20setup=20(#936)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * N°9678 - Datamodel not added after extension management setup * N°9678 - fix selecting extensions from extensions folder in extension mgt view * cleanup: remove cached stuff in runtimeenv * N°9678 - Refactor iTopExtensionsMap instantiation to use GetExtensionsMap method * N°9678 - Refactor data model compilation and improve directory scanning logic * N°9678 - Handle MySQLException in ModuleInstallationRepository and remove unnecessary error logging in WizStepModulesChoice --------- Co-authored-by: Eric Espie --- .../DataFeatureRemovalController.php | 13 +- .../DataFeatureRemoverExtensionService.php | 2 +- .../src/Service/CoreUpdater.php | 2 +- datamodels/2.x/itop-hub-connector/land.php | 4 +- datamodels/2.x/itop-hub-connector/launch.php | 2 +- .../2.x/itop-hub-connector/myextensions.php | 2 +- .../src/Controller/HubController.php | 2 +- setup/extensionsmap.class.inc.php | 87 +++---- .../DryRemovalRuntimeEnvironment.php | 23 +- setup/feature_removal/SetupAudit.php | 5 + setup/itopextension.class.inc.php | 15 +- .../ModuleInstallationRepository.php | 7 +- setup/runtimeenv.class.inc.php | 110 ++++---- setup/sequencers/DataAuditSequencer.php | 2 - setup/setuputils.class.inc.php | 2 +- .../InstallationFileService.php | 2 +- setup/wizardsteps/WizStepModulesChoice.php | 3 +- sources/Controller/AjaxRenderController.php | 7 +- .../Service/UnitTestRunTimeEnvironment.php | 2 +- .../unitary-tests/setup/ExtensionsMapTest.php | 21 +- .../setup/RunTimeEnvironmentTest.php | 57 ++-- .../setup/feature_removal/SetupAuditTest.php | 30 ++- .../finalclass_ext3/extension.xml | 22 ++ .../datamodel.finalclass_ext3_module1.xml | 76 ++++++ .../model.finalclass_ext3_module1.php | 16 ++ .../module.finalclass_ext3_module1.php | 51 ++++ .../production-modules/prod-module1/.gitkeep | 0 .../datamodels/2.x/installation.xml | 243 ++++++++++++++++++ .../datamodels/2.x/source1/.gitkeep | 0 .../resources2/extensions/extension1/.gitkeep | 0 30 files changed, 620 insertions(+), 188 deletions(-) create mode 100644 tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/extension.xml create mode 100644 tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/finalclass_ext3_module1/datamodel.finalclass_ext3_module1.xml create mode 100644 tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/finalclass_ext3_module1/model.finalclass_ext3_module1.php create mode 100644 tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/finalclass_ext3_module1/module.finalclass_ext3_module1.php create mode 100644 tests/php-unit-tests/unitary-tests/setup/resources2/data/production-modules/prod-module1/.gitkeep create mode 100644 tests/php-unit-tests/unitary-tests/setup/resources2/datamodels/2.x/installation.xml create mode 100644 tests/php-unit-tests/unitary-tests/setup/resources2/datamodels/2.x/source1/.gitkeep create mode 100644 tests/php-unit-tests/unitary-tests/setup/resources2/extensions/extension1/.gitkeep diff --git a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php index a1915a9a61..a58e2377a9 100644 --- a/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php +++ b/datamodels/2.x/combodo-data-feature-removal/src/Controller/DataFeatureRemovalController.php @@ -145,7 +145,7 @@ class DataFeatureRemovalController extends Controller $bForceCompilation = Session::Get('bForceCompilation', false); try { - $this->Compile($aRemoveExtensionCodes, $bForceCompilation); + $this->Compile($aAddedExtensions, $aRemoveExtensionCodes, $bForceCompilation); } catch (CoreException $e) { $aParams['DataFeatureRemovalErrorMessage'] = $e->getHtmlDesc(); $this->DisplayPage($aParams, 'AnalysisResult'); @@ -162,7 +162,7 @@ class DataFeatureRemovalController extends Controller $aSelectedExtensions = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap()->GetSelectedExtensions($oConfig, array_keys($aAddedExtensions), array_keys($aRemovedExtensions)); $aHiddenInputs['selected_extensions'] = $this->ConvertIntoSetupFormat($aSelectedExtensions); - $oRunTimeEnvironment = $this->GetRuntimeEnvironment($aRemovedExtensions); + $oRunTimeEnvironment = $this->GetRuntimeEnvironment($aAddedExtensions, $aRemovedExtensions); $aSearchDirs = [$oRunTimeEnvironment->GetBuildDir()]; $aSelectedModules = $oRunTimeEnvironment->GetModulesToLoadFromChoices($oConfig, $aSelectedExtensions, $aSearchDirs); $aHiddenInputs['selected_modules'] = $this->ConvertIntoSetupFormat($aSelectedModules); @@ -213,13 +213,14 @@ class DataFeatureRemovalController extends Controller } /** +* @param array $aAddedExtensions * @param array $aRemovedExtensions * @param bool $bForceCompilation * @return void * @throws \ConfigException * @throws \CoreException */ - private function Compile(array $aRemovedExtensions, bool $bForceCompilation = true): void + private function Compile(array $aAddedExtensions, array $aRemovedExtensions, bool $bForceCompilation = true): void { $sSourceEnv = MetaModel::GetEnvironment(); $sBuildDir = APPROOT."/env-$sSourceEnv-build"; @@ -234,15 +235,15 @@ class DataFeatureRemovalController extends Controller null, ['sSourceEnv' => $sSourceEnv, 'sBuildDir' => $sBuildDir, 'bIsDirEmpty' => $bIsDirEmpty, glob("$sBuildDir/*")] ); - $this->GetRuntimeEnvironment($aRemovedExtensions)->CompileFrom($sSourceEnv); + $this->GetRuntimeEnvironment($aAddedExtensions, $aRemovedExtensions)->CompileFrom($sSourceEnv); } } - private function GetRuntimeEnvironment(array $aRemovedExtensions): RunTimeEnvironment + private function GetRuntimeEnvironment(array $aAddedExtensions, array $aRemovedExtensions): RunTimeEnvironment { if (is_null($this->oRuntimeEnvironment)) { $sSourceEnv = MetaModel::GetEnvironment(); - $this->oRuntimeEnvironment = new DryRemovalRuntimeEnvironment($sSourceEnv, $aRemovedExtensions); + $this->oRuntimeEnvironment = new DryRemovalRuntimeEnvironment($sSourceEnv, $aAddedExtensions, $aRemovedExtensions); } return $this->oRuntimeEnvironment; diff --git a/datamodels/2.x/combodo-data-feature-removal/src/Service/DataFeatureRemoverExtensionService.php b/datamodels/2.x/combodo-data-feature-removal/src/Service/DataFeatureRemoverExtensionService.php index e25c5a755a..3e3f207339 100644 --- a/datamodels/2.x/combodo-data-feature-removal/src/Service/DataFeatureRemoverExtensionService.php +++ b/datamodels/2.x/combodo-data-feature-removal/src/Service/DataFeatureRemoverExtensionService.php @@ -66,7 +66,7 @@ class DataFeatureRemoverExtensionService public function GetExtensionMap(): iTopExtensionsMap { if (is_null($this->oMap)) { - $this->oMap = new iTopExtensionsMap(); + $this->oMap = iTopExtensionsMap::GetExtensionsMap(); $this->oMap->LoadInstalledExtensionsFromDatabase(MetaModel::GetConfig()); } return $this->oMap; diff --git a/datamodels/2.x/itop-core-update/src/Service/CoreUpdater.php b/datamodels/2.x/itop-core-update/src/Service/CoreUpdater.php index 31b56e074b..116da96a56 100644 --- a/datamodels/2.x/itop-core-update/src/Service/CoreUpdater.php +++ b/datamodels/2.x/itop-core-update/src/Service/CoreUpdater.php @@ -163,7 +163,7 @@ final class CoreUpdater $oRuntimeEnv->LoadData($aAvailableModules, false /* no sample data*/); $oRuntimeEnv->CallInstallerHandlers($aAvailableModules, 'AfterDataLoad'); $sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion(); - $oExtensionsMap = new iTopExtensionsMap(); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(); // Default choices = as before $oExtensionsMap->LoadChoicesFromDatabase($oConfig); foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) { diff --git a/datamodels/2.x/itop-hub-connector/land.php b/datamodels/2.x/itop-hub-connector/land.php index b3553af0af..ed3cd0d0d1 100644 --- a/datamodels/2.x/itop-hub-connector/land.php +++ b/datamodels/2.x/itop-hub-connector/land.php @@ -20,7 +20,7 @@ function DisplayStatus(WebPage $oPage) if (is_dir($sPath)) { $aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory } - $oExtensionsMap = new iTopExtensionsMap(ITOP_DEFAULT_ENV, $aExtraDirs); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(ITOP_DEFAULT_ENV); $oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig()); foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) { @@ -154,7 +154,7 @@ function DoInstall(WebPage $oPage) if (is_dir($sPath)) { $aExtraDirs[] = $sPath; // Also read the extra downloaded-modules directory } - $oExtensionsMap = new iTopExtensionsMap(ITOP_DEFAULT_ENV, $aExtraDirs); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(ITOP_DEFAULT_ENV); $oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig()); foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) { diff --git a/datamodels/2.x/itop-hub-connector/launch.php b/datamodels/2.x/itop-hub-connector/launch.php index 1f3e520840..5318b2c7a9 100644 --- a/datamodels/2.x/itop-hub-connector/launch.php +++ b/datamodels/2.x/itop-hub-connector/launch.php @@ -193,7 +193,7 @@ function collect_configuration() } // iTop Installation Options, i.e. "Extensions" - $oExtensionMap = new iTopExtensionsMap(); + $oExtensionMap = iTopExtensionsMap::GetExtensionsMap(); $oExtensionMap->LoadChoicesFromDatabase($oConfig); $aConfiguration['itop_extensions'] = []; foreach ($oExtensionMap->GetChoices() as $oExtension) { diff --git a/datamodels/2.x/itop-hub-connector/myextensions.php b/datamodels/2.x/itop-hub-connector/myextensions.php index 6da581a462..f3398f172b 100644 --- a/datamodels/2.x/itop-hub-connector/myextensions.php +++ b/datamodels/2.x/itop-hub-connector/myextensions.php @@ -41,7 +41,7 @@ function GetExtensionInfoComponent(iTopExtension $oExtension): UIBlock } try { - $oExtensionsMap = new iTopExtensionsMap(); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(); $oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig()); $oPage->AddUiBlock(TitleUIBlockFactory::MakeForPage(Dict::S('iTopHub:InstalledExtensions'))); diff --git a/datamodels/2.x/itop-hub-connector/src/Controller/HubController.php b/datamodels/2.x/itop-hub-connector/src/Controller/HubController.php index fdbe2fbc8c..c091f1e094 100644 --- a/datamodels/2.x/itop-hub-connector/src/Controller/HubController.php +++ b/datamodels/2.x/itop-hub-connector/src/Controller/HubController.php @@ -208,7 +208,7 @@ class HubController // Record the installation so that the "about box" knows about the installed modules $sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion(); - $oExtensionsMap = new iTopExtensionsMap(); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(); // Default choices = as before $oExtensionsMap->LoadChoicesFromDatabase($oConfig); diff --git a/setup/extensionsmap.class.inc.php b/setup/extensionsmap.class.inc.php index 9ab8410986..b5d603797a 100644 --- a/setup/extensionsmap.class.inc.php +++ b/setup/extensionsmap.class.inc.php @@ -14,6 +14,8 @@ require_once(APPROOT.'/setup/moduleinstaller.class.inc.php'); */ class iTopExtensionsMap { + private static array $aInstancesByEnvironment = []; + /** * The list of all discovered extensions * @var array $aExtensions @@ -30,25 +32,36 @@ class iTopExtensionsMap * The list of directories browsed using the ReadDir method when building the map * @var string[] */ - protected $aScannedDirs; + protected array $aScannedDirs; /** @var bool $bHasXmlInstallationFile : false when legacy 1.x package with no installation.xml */ protected $bHasXmlInstallationFile = true; - //extension dirs apart from package - protected array $aExtraDirs = []; + /** + * @throws \Exception + */ + public static function GetExtensionsMap(string $sFromEnvironment = ITOP_DEFAULT_ENV, ?string $sAppRootForTests = null): iTopExtensionsMap + { + if (!is_null($sAppRootForTests)) { + return new iTopExtensionsMap($sFromEnvironment, $sAppRootForTests); + } + + if (!isset(self::$aInstancesByEnvironment[$sFromEnvironment])) { + self::$aInstancesByEnvironment[$sFromEnvironment] = new iTopExtensionsMap($sFromEnvironment); + } + + return self::$aInstancesByEnvironment[$sFromEnvironment]; + } /** * The list of all discovered extensions * * @param string $sFromEnvironment The environment to scan - * @param array $aExtraDirs extensions dir to scan - * @param array $aExtraDirs extensions dir to scan - * @param string|null $sAppRootForTests + * @param string|null $sAppRootForTests * - * @return void + * @throws \Exception */ - public function __construct(string $sFromEnvironment = ITOP_DEFAULT_ENV, array $aExtraDirs = [], ?string $sAppRootForTests = null) + private function __construct(string $sFromEnvironment = ITOP_DEFAULT_ENV, ?string $sAppRootForTests = null) { $this->aExtensions = []; $this->aExtensionsByCode = []; @@ -56,18 +69,6 @@ class iTopExtensionsMap $sAppRoot = $sAppRootForTests ?? APPROOT; $this->ScanDisk($sFromEnvironment, $sAppRoot); - - $this->aExtraDirs = $aExtraDirs; - if (is_dir($sAppRoot.'extensions')) { - $this->aExtraDirs [] = $sAppRoot.'extensions'; - } - if (is_dir($sAppRoot.'data/'.$sFromEnvironment.'-modules')) { - $this->aExtraDirs [] = $sAppRoot.'data/'.$sFromEnvironment.'-modules'; - } - - foreach ($aExtraDirs as $sDir) { - $this->ReadDir($sDir, iTopExtension::SOURCE_REMOTE); - } $this->CheckDependencies($sAppRoot); } @@ -83,13 +84,15 @@ class iTopExtensionsMap if (!$this->ReadInstallationWizard($sAppRoot.'/datamodels/2.x')) { $this->bHasXmlInstallationFile = false; //no installation xml found in 2.x: let's read all extensions in 2.x first - if (!$this->ReadDir($sAppRoot.'datamodels/2.x', iTopExtension::SOURCE_WIZARD)) { + if (!$this->ReadDir($sAppRoot.'datamodels/2.x', iTopExtension::SOURCE_WIZARD, bIsRootDir: true)) { //nothing found in 2.x : fallback read in 1.x (flat structure) - $this->ReadDir($sAppRoot.'datamodels/1.x', iTopExtension::SOURCE_WIZARD); + $this->ReadDir($sAppRoot.'datamodels/1.x', iTopExtension::SOURCE_WIZARD, bIsRootDir: true); } + } else { + $this->aScannedDirs[] = $sAppRoot.'datamodels/2.x'; } - $this->ReadDir($sAppRoot.'extensions', iTopExtension::SOURCE_MANUAL); - $this->ReadDir($sAppRoot.'data/'.$sEnvironment.'-modules', iTopExtension::SOURCE_REMOTE); + $this->ReadDir($sAppRoot.'extensions', iTopExtension::SOURCE_MANUAL, bIsRootDir: true); + $this->ReadDir($sAppRoot.'data/'.$sEnvironment.'-modules', iTopExtension::SOURCE_REMOTE, bIsRootDir: true); } /** @@ -274,14 +277,14 @@ class iTopExtensionsMap * * @return boolean false if we cannot open dir */ - protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null) + protected function ReadDir($sSearchDir, $sSource, $sParentExtensionId = null, bool $bIsRootDir = false) { if (!is_readable($sSearchDir)) { return false; } $hDir = opendir($sSearchDir); if ($hDir !== false) { - if ($sParentExtensionId == null) { + if ($bIsRootDir) { // We're not recursing, let's add the directory to the list of scanned dirs $this->aScannedDirs[] = $sSearchDir; } @@ -478,16 +481,8 @@ class iTopExtensionsMap */ protected function CheckDependencies(string $sAppRoot) { - $aSearchDirs = []; - - if (is_dir($sAppRoot.'/datamodels/2.x')) { - $aSearchDirs[] = $sAppRoot.'/datamodels/2.x'; - } elseif (is_dir($sAppRoot.'/datamodels/1.x')) { - $aSearchDirs[] = $sAppRoot.'/datamodels/1.x'; - } - $aSearchDirs = array_merge($aSearchDirs, $this->aScannedDirs); try { - ModuleDiscovery::GetModulesOrderedByDependencies($aSearchDirs, true); + ModuleDiscovery::GetModulesOrderedByDependencies($this->aScannedDirs, true); } catch (MissingDependencyException $e) { // Some modules have missing dependencies // Let's check what is the impact at the "extensions" level @@ -536,7 +531,7 @@ class iTopExtensionsMap public function GetAllExtensionsToDisplayInSetup(bool $bKeepExtensionsHavingMissingDependencies = false, bool $bRemoteExtensionsShouldBeMandatory = true): array { // all extensions are loaded at first screen when no installation xml: flat display - //otherwhile wizard screen displays choice screens along extension tree (cf installation.xml) + // otherwhile wizard screen displays choice screens along extension tree (cf installation.xml) $aRes = []; foreach ($this->GetAllExtensionsWithPreviouslyInstalled() as $oExtension) { /** @var \iTopExtension $oExtension */ @@ -596,7 +591,7 @@ class iTopExtensionsMap { $oExtension = $this->GetFromExtensionCode($sExtensionCode); if (!is_null($oExtension)) { - $oExtension->bMarkedAsChosen = $bMark; + $oExtension->MarkAsChosen($bMark); } } @@ -609,7 +604,7 @@ class iTopExtensionsMap { $oExtension = $this->GetFromExtensionCode($sExtensionCode); if (!is_null($oExtension)) { - return $oExtension->bMarkedAsChosen; + return $oExtension->IsMarkedAsChosen(); } return false; @@ -636,8 +631,9 @@ class iTopExtensionsMap public function GetChoices() { $aResult = []; + /** @var \iTopExtension $oExtension */ foreach ($this->aExtensions as $oExtension) { - if ($oExtension->bMarkedAsChosen) { + if ($oExtension->IsMarkedAsChosen()) { $aResult[] = $oExtension; } } @@ -745,11 +741,6 @@ class iTopExtensionsMap return array_merge($aDbChoices, $aAddedExtensions); } - public function GetExtraDirs(): array - { - return $this->aExtraDirs; - } - /** * Tells if the given module name is "chosen" since it is part of a "chosen" extension (in the specified source dir) * @param string $sModuleNameToFind @@ -759,13 +750,17 @@ class iTopExtensionsMap public function ModuleIsChosenAsPartOfAnExtension($sModuleNameToFind, $sInSourceOnly = iTopExtension::SOURCE_REMOTE) { foreach ($this->GetAllExtensions() as $oExtension) { - if (($oExtension->sSource == $sInSourceOnly) && - ($oExtension->bMarkedAsChosen == true) && + if ($oExtension->IsMarkedAsChosen() && (array_key_exists($sModuleNameToFind, $oExtension->aModuleVersion))) { return true; } } + return false; } + public function GetScannedModulesRootDirs(): array + { + return $this->aScannedDirs; + } } diff --git a/setup/feature_removal/DryRemovalRuntimeEnvironment.php b/setup/feature_removal/DryRemovalRuntimeEnvironment.php index 6d1835e82e..fc4ffd84c2 100644 --- a/setup/feature_removal/DryRemovalRuntimeEnvironment.php +++ b/setup/feature_removal/DryRemovalRuntimeEnvironment.php @@ -2,22 +2,24 @@ namespace Combodo\iTop\Setup\FeatureRemoval; -use iTopExtensionsMap; +use Config; use RunTimeEnvironment; use SetupUtils; class DryRemovalRuntimeEnvironment extends RunTimeEnvironment { protected array $aExtensionsToRemoveByCode; + protected array $aExtensionCodesToAddByCode; /** * Toolset for building a run-time environment * * @param string $sSourceEnv: environment from which setup is inspired to simulate extension removal and usee CompileFrom... */ - public function __construct($sSourceEnv = ITOP_DEFAULT_ENV, array $aExtensionCodesToRemove = []) + public function __construct($sSourceEnv = ITOP_DEFAULT_ENV, array $aExtensionCodesToAdd = [], array $aExtensionCodesToRemove = []) { parent::__construct($sSourceEnv, false); + $this->aExtensionCodesToAddByCode = $aExtensionCodesToAdd; $this->aExtensionsToRemoveByCode = $aExtensionCodesToRemove; $this->Prepare($sSourceEnv, $this->sBuildEnv); } @@ -34,13 +36,18 @@ class DryRemovalRuntimeEnvironment extends RunTimeEnvironment SetupUtils::copydir(APPROOT."/data/$sSourceEnv-modules", APPROOT."/data/$sBuildEnv-modules"); SetupUtils::copydir(APPROOT."/conf/$sSourceEnv", APPROOT."/conf/$sBuildEnv"); - $this->DeclareExtensionAsRemoved($this->aExtensionsToRemoveByCode); - } + $oSourceConfig = new Config(APPCONF.$sSourceEnv.'/'.ITOP_CONFIG_FILE); + $sSourceDir = $oSourceConfig->Get('source_dir'); + list($aExtraDirs, ) = $this->GetDirsToCompile($sSourceDir, $sSourceEnv); - private function DeclareExtensionAsRemoved(array $aExtensionCodes): void - { - $oExtensionsMap = new iTopExtensionsMap($this->sBuildEnv); - $oExtensionsMap->DeclareExtensionAsRemoved($aExtensionCodes); + $this->InitExtensionMap($aExtraDirs, $oSourceConfig); + $this->GetExtensionMap()->DeclareExtensionAsRemoved($this->aExtensionsToRemoveByCode); + + foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) { + if (array_key_exists($oExtension->sCode, $this->aExtensionCodesToAddByCode)) { + $oExtension->MarkAsChosen(); + } + } } public function Cleanup(): void diff --git a/setup/feature_removal/SetupAudit.php b/setup/feature_removal/SetupAudit.php index 35c4628d9b..9e842b4a45 100644 --- a/setup/feature_removal/SetupAudit.php +++ b/setup/feature_removal/SetupAudit.php @@ -17,6 +17,11 @@ class SetupAudit extends AbstractSetupAudit $this->sEnvAfter = $sEnvAfter ?? "$sEnvBefore-build"; } + public function GetEnvAfter(): string + { + return $this->sEnvAfter; + } + public function ComputeClasses(): void { if ($this->bClassesInitialized) { diff --git a/setup/itopextension.class.inc.php b/setup/itopextension.class.inc.php index e2d7302c0b..681f8e4850 100644 --- a/setup/itopextension.class.inc.php +++ b/setup/itopextension.class.inc.php @@ -1,8 +1,5 @@ sSource === self::SOURCE_REMOTE; } + + public function IsMarkedAsChosen(): bool + { + return $this->bMarkedAsChosen; + } + + public function MarkAsChosen(bool $bMarkedAsChosen = true): void + { + $this->bMarkedAsChosen = $bMarkedAsChosen; + } } diff --git a/setup/moduleinstallation/ModuleInstallationRepository.php b/setup/moduleinstallation/ModuleInstallationRepository.php index 149899171e..f139941d2e 100644 --- a/setup/moduleinstallation/ModuleInstallationRepository.php +++ b/setup/moduleinstallation/ModuleInstallationRepository.php @@ -68,7 +68,12 @@ WHERE parent_id='$iRootId' OR id='$iRootId' SQL; - return CMDBSource::QueryToArray($sSQL); + try { + return CMDBSource::QueryToArray($sSQL); + } catch (MySQLException $e) { + SetupLog::Exception(__METHOD__, $e); + throw $e; + } } private function GetTableWithPrefix(Config $oConfig) diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index f7331c610e..6f0f440b0a 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -71,15 +71,37 @@ class RunTimeEnvironment return $this->oExtensionsMap; } - public function InitExtensionMap($aExtraDirs, $oSourceConfig) + protected function GetDirsToCompile(string $sSourceDir, string $sSourceEnv): array { - // Actually read the modules available for the build environment, - // but get the selection from the source environment and finally - // mark as (automatically) chosen all the "remote" modules present in the - // build environment (data/-modules) - // The actual choices will be recorded by RecordInstallation below - $this->oExtensionsMap = new iTopExtensionsMap($this->sBuildEnv, $aExtraDirs); - $this->oExtensionsMap->LoadChoicesFromDatabase($oSourceConfig); + $sSourceDirFull = APPROOT.$sSourceDir; + if (!is_dir($sSourceDirFull)) { + throw new Exception("The source directory '$sSourceDirFull' does not exist (or could not be read)"); + } + $aDirsToCompile = [$sSourceDirFull]; + if (is_dir(APPROOT.'extensions')) { + $aDirsToCompile[] = APPROOT.'extensions'; + } + $sExtraDir = utils::GetDataPath().$this->sBuildEnv.'-modules/'; + if (is_dir($sExtraDir)) { + $aDirsToCompile[] = $sExtraDir; + } + + $aExtraDirs = $this->GetExtraDirsToScan($aDirsToCompile); + $aDirsToCompile = array_merge($aDirsToCompile, $aExtraDirs); + return [$aExtraDirs, $aDirsToCompile]; + } + + public function InitExtensionMap(array $aExtraDirs, Config $oSourceConfig) + { + if (is_null($this->oExtensionsMap)) { + // Actually read the modules available for the build environment, + // but get the selection from the source environment and finally + // mark as (automatically) chosen all the "remote" modules present in the + // build environment (data/-modules) + // The actual choices will be recorded by RecordInstallation below + $this->oExtensionsMap = iTopExtensionsMap::GetExtensionsMap($this->sBuildEnv); + $this->oExtensionsMap->LoadChoicesFromDatabase($oSourceConfig); + } } /** @@ -168,7 +190,7 @@ class RunTimeEnvironment MetaModel::Startup($oConfig, $bModelOnly, $bUseCache, false, $this->sBuildEnv); if ($this->oExtensionsMap === null) { - $this->oExtensionsMap = new iTopExtensionsMap($this->sBuildEnv); + $this->oExtensionsMap = iTopExtensionsMap::GetExtensionsMap($this->sBuildEnv); } } @@ -444,32 +466,12 @@ class RunTimeEnvironment /** * Get the installed modules (only the installed ones) + * @return \MFModule[] */ - protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir) + protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir): array { - $sSourceDirFull = APPROOT.$sSourceDir; - if (!is_dir($sSourceDirFull)) { - throw new Exception("The source directory '$sSourceDirFull' does not exist (or could not be read)"); - } - $aDirsToCompile = [$sSourceDirFull]; - if (is_dir(APPROOT.'extensions')) { - $aDirsToCompile[] = APPROOT.'extensions'; - } - $sExtraDir = utils::GetDataPath().$this->sBuildEnv.'-modules/'; - if (is_dir($sExtraDir)) { - $aDirsToCompile[] = $sExtraDir; - } - - $aExtraDirs = $this->GetExtraDirsToScan($aDirsToCompile); - $aDirsToCompile = array_merge($aDirsToCompile, $aExtraDirs); - + list($aExtraDirs, $aDirsToCompile) = $this->GetDirsToCompile($sSourceDir, $sSourceEnv); $oSourceConfig = new Config(APPCONF.$sSourceEnv.'/'.ITOP_CONFIG_FILE); - - // Actually read the modules available for the build environment, - // but get the selection from the source environment and finally - // mark as (automatically) chosen all the "remote" modules present in the - // build environment (data/-modules) - // The actual choices will be recorded by RecordInstallation below $this->InitExtensionMap($aExtraDirs, $oSourceConfig); $this->GetExtensionMap()->LoadChoicesFromDatabase($oSourceConfig); foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) { @@ -477,12 +479,10 @@ class RunTimeEnvironment $this->GetExtensionMap()->MarkAsChosen($oExtension->sCode); } } - $aModulesToLoad = $this->GetModulesToLoad($this->sFinalEnv, $aDirsToCompile); $aAvailableModules = $this->AnalyzeInstallation($oSourceConfig, $aDirsToCompile, true, $aModulesToLoad); // Do load the required modules - // $oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries'); $aRet = []; @@ -1419,8 +1419,6 @@ class RunTimeEnvironment * @param array $aSelectedExtensionCodes * @param array $aRemovedExtensionCodes * @param array $aSelectedModules - * @param string $sSourceDir - * @param string $sExtensionDir * @param boolean $bUseSymbolicLinks * * @return void @@ -1428,33 +1426,13 @@ class RunTimeEnvironment * @throws \CoreException * */ - public function DoCompile(array $aSelectedExtensionCodes, array $aRemovedExtensionCodes, array $aSelectedModules, string $sSourceDir, string $sExtensionDir, bool $bUseSymbolicLinks = false): void + public function DoCompile(array $aSelectedExtensionCodes, array $aRemovedExtensionCodes, array $aSelectedModules, bool $bUseSymbolicLinks = false): void { SetupLog::Info('Compiling data model.'); $sEnvironment = $this->sBuildEnv; $sBuildPath = $this->GetBuildDir(); - $sSourcePath = APPROOT.$sSourceDir; - $aDirsToScan = [$sSourcePath]; - $sExtensionsPath = APPROOT.$sExtensionDir; - if (is_dir($sExtensionsPath)) { - // if the extensions dir exists, scan it for additional modules as well - $aDirsToScan[] = $sExtensionsPath; - } - $sExtraPath = APPROOT.'/data/'.$sEnvironment.'-modules/'; - if (is_dir($sExtraPath)) { - // if the extra dir exists, scan it for additional modules as well - $aDirsToScan[] = $sExtraPath; - } - - if (!is_dir($sSourcePath)) { - $sErrorMessage = "Failed to find the source directory '$sSourcePath', please check the rights of the web server"; - $e = new CoreException($sErrorMessage); - IssueLog::Exception($sErrorMessage, $e); - throw $e; - } - if (!is_dir($sBuildPath)) { if (!mkdir($sBuildPath)) { $sErrorMessage = "Failed to create directory '$sBuildPath', please check the rights of the web server"; @@ -1471,7 +1449,7 @@ class RunTimeEnvironment SetupUtils::tidydir($sBuildPath); } - $oExtensionsMap = new iTopExtensionsMap($this->GetFinalEnv(), $aDirsToScan); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap($this->GetFinalEnv()); // Removed modules are stored as static for FindModules() $oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes); @@ -1481,7 +1459,7 @@ class RunTimeEnvironment $aNoCodeExtensionLabelsThatBreakSetup = []; foreach ($oExtensionsMap->GetAllExtensions() as $oExtension) { if (in_array($oExtension->sCode, $aSelectedExtensionCodes)) { - $oExtension->bMarkedAsChosen = true; + $oExtension->MarkAsChosen(); } if (empty($oExtension->sCode)) { @@ -1493,7 +1471,7 @@ class RunTimeEnvironment $aNoCodeExtensionSourceDirs [$sExtensionLabel] = $oExtension->sSourceDir; } - if ($oExtension->bMarkedAsChosen) { + if ($oExtension->IsMarkedAsChosen()) { $aNoCodeExtensionLabelsThatBreakSetup[] = $sExtensionLabel; $bSetupFailure = true; } @@ -1511,7 +1489,7 @@ class RunTimeEnvironment } } - $oFactory = new ModelFactory($aDirsToScan); + $oFactory = new ModelFactory($oExtensionsMap->GetScannedModulesRootDirs()); $oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries'); $oFactory->LoadModule($oDictModule); @@ -1700,21 +1678,29 @@ class RunTimeEnvironment } $aExtensionDirs = []; + $aFromSelectedExtensionModules = []; foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) { - if ($oExtension->bMarkedAsChosen && is_dir($oExtension->sSourceDir)) { + if ($oExtension->IsMarkedAsChosen() && is_dir($oExtension->sSourceDir)) { $aExtensionDirs [] = $oExtension->sSourceDir; + $aFromSelectedExtensionModules = array_merge($aFromSelectedExtensionModules, $oExtension->aModules); } } SetupLog::Info(__METHOD__, null, ['ext_dirs' => $aExtensionDirs]); $aModuleIdsToLoad = InstallationChoicesToModuleConverter::GetInstance()->GetModules($aChoices, $aSearchDirs, $sInstallFilePath, $aExtensionDirs); $aModulesToLoad = []; + foreach ($aModuleIdsToLoad as $sModuleId) { $oModule = new Module($sModuleId); $sModuleName = $oModule->GetModuleName(); $aModulesToLoad[] = $sModuleName; } + foreach ($aFromSelectedExtensionModules as $sModuleName) { + if (! in_array($sModuleName, $aModulesToLoad)) { + $aModulesToLoad[] = $sModuleName; + } + } return $aModulesToLoad; } diff --git a/setup/sequencers/DataAuditSequencer.php b/setup/sequencers/DataAuditSequencer.php index f8df7fdf4d..2b68cfb560 100644 --- a/setup/sequencers/DataAuditSequencer.php +++ b/setup/sequencers/DataAuditSequencer.php @@ -74,8 +74,6 @@ class DataAuditSequencer extends StepSequencer $aSelectedExtensionCodes, $aRemovedExtensionCodes, $aSelectedModules, - $sSourceDir, - $sExtensionDir, $bUseSymbolicLinks ); return $this->ComputeNextStep($sStep); diff --git a/setup/setuputils.class.inc.php b/setup/setuputils.class.inc.php index 91df4621fa..20700605e4 100644 --- a/setup/setuputils.class.inc.php +++ b/setup/setuputils.class.inc.php @@ -1602,7 +1602,7 @@ JS } $oProductionEnv = new RunTimeEnvironment($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV)); $aRemovedExtensionCodes = json_decode($oWizard->GetParameter('removed_extensions'), true) ?? []; - $oExtensionsMap = new iTopExtensionsMap($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV), $aDirsToScan); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap($oWizard->GetParameter('target_env', ITOP_DEFAULT_ENV)); $oExtensionsMap->DeclareExtensionAsRemoved($aRemovedExtensionCodes); $aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, $aDirsToScan, $bAbortOnMissingDependency, $aModulesToLoad); diff --git a/setup/unattended-install/InstallationFileService.php b/setup/unattended-install/InstallationFileService.php index fe4959bff1..33de7dac52 100644 --- a/setup/unattended-install/InstallationFileService.php +++ b/setup/unattended-install/InstallationFileService.php @@ -79,7 +79,7 @@ class InstallationFileService public function GetItopExtensionsMap(): iTopExtensionsMap { if (is_null($this->oItopExtensionsMap)) { - $this->oItopExtensionsMap = new iTopExtensionsMap($this->sTargetEnvironment); + $this->oItopExtensionsMap = iTopExtensionsMap::GetExtensionsMap($this->sTargetEnvironment); } return $this->oItopExtensionsMap; } diff --git a/setup/wizardsteps/WizStepModulesChoice.php b/setup/wizardsteps/WizStepModulesChoice.php index 8914e4bed2..c4ff129507 100644 --- a/setup/wizardsteps/WizStepModulesChoice.php +++ b/setup/wizardsteps/WizStepModulesChoice.php @@ -55,7 +55,7 @@ class WizStepModulesChoice extends AbstractWizStepInstall { parent::__construct($oWizard, $sCurrentState); $this->bChoicesFromDatabase = false; - $this->oExtensionsMap = new iTopExtensionsMap(); + $this->oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(); $sPreviousSourceDir = $this->oWizard->GetParameter('previous_version_dir', ''); $sConfigPath = null; if (($sPreviousSourceDir !== '') && is_readable($sPreviousSourceDir.'/conf/'.ITOP_DEFAULT_ENV.'/config-itop.php')) { @@ -81,7 +81,6 @@ class WizStepModulesChoice extends AbstractWizStepInstall // Sanity check (not stopper, to let developers go further...) try { $aModulesToLoad = json_decode($oWizard->GetParameter('selected_modules'), true) ?? null; - SetupLog::Error(__METHOD__, null, [$aModulesToLoad]); $this->aAnalyzeInstallationModules = SetupUtils::AnalyzeInstallation($this->oWizard, true, $aModulesToLoad, $this->oConfig); } catch (MissingDependencyException $e) { $this->oMissingDependencyException = $e; diff --git a/sources/Controller/AjaxRenderController.php b/sources/Controller/AjaxRenderController.php index f4538713a1..a8a3969dfe 100644 --- a/sources/Controller/AjaxRenderController.php +++ b/sources/Controller/AjaxRenderController.php @@ -7,7 +7,6 @@ namespace Combodo\iTop\Controller; -use Combodo\iTop\Application\WebPage\AjaxPage; use ApplicationContext; use ApplicationMenu; use AttributeLinkedSet; @@ -21,7 +20,8 @@ use CMDBObjectSet; use CMDBSource; use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings; use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory; -use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectSummary; +use Combodo\iTop\Application\WebPage\AjaxPage; +use Combodo\iTop\Application\WebPage\JsonPage; use DBObjectSearch; use DBObjectSet; use DBSearch; @@ -34,7 +34,6 @@ use FunctionExpression; use IssueLog; use iTopExtension; use iTopExtensionsMap; -use Combodo\iTop\Application\WebPage\JsonPage; use LogChannels; use MetaModel; use ormSet; @@ -963,7 +962,7 @@ EOF $oPage->add('
    '); require_once(APPROOT.'setup/extensionsmap.class.inc.php'); - $oExtensionsMap = new iTopExtensionsMap(); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(); $oExtensionsMap->LoadChoicesFromDatabase(MetaModel::GetConfig()); $aChoices = $oExtensionsMap->GetChoices(); foreach ($aChoices as $oExtension) { diff --git a/tests/php-unit-tests/src/Service/UnitTestRunTimeEnvironment.php b/tests/php-unit-tests/src/Service/UnitTestRunTimeEnvironment.php index 4cd3b9d900..72d6c2cbf1 100644 --- a/tests/php-unit-tests/src/Service/UnitTestRunTimeEnvironment.php +++ b/tests/php-unit-tests/src/Service/UnitTestRunTimeEnvironment.php @@ -108,7 +108,7 @@ class UnitTestRunTimeEnvironment extends RunTimeEnvironment /** * @inheritDoc */ - protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir) + protected function GetMFModulesToCompile($sSourceEnv, $sSourceDir): array { \SetupLog::Info(__METHOD__); $aRet = parent::GetMFModulesToCompile($sSourceEnv, $sSourceDir); diff --git a/tests/php-unit-tests/unitary-tests/setup/ExtensionsMapTest.php b/tests/php-unit-tests/unitary-tests/setup/ExtensionsMapTest.php index 0dec4dc9d5..1ca9554881 100644 --- a/tests/php-unit-tests/unitary-tests/setup/ExtensionsMapTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/ExtensionsMapTest.php @@ -19,7 +19,7 @@ class ExtensionsMapTest extends ItopTestCase public function testGetAllExtensionsWithPreviouslyInstalledDoesNotCrash() { - $oExtensionsMap = new iTopExtensionsMap(); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(); $aExtensions = $oExtensionsMap->GetAllExtensionsWithPreviouslyInstalled(); $this->assertGreaterThan(0, count($aExtensions)); } @@ -84,7 +84,7 @@ class ExtensionsMapTest extends ItopTestCase private function GiveExtensionMapWithAllTypeOfExtensions(): iTopExtensionsMap { - $oExtensionsMap = new iTopExtensionsMap(); + $oExtensionsMap = iTopExtensionsMap::GetExtensionsMap(); $this->SetNonPublicProperty($oExtensionsMap, 'aInstalledExtensions', []); $this->SetNonPublicProperty($oExtensionsMap, 'aExtensions', []); @@ -153,9 +153,7 @@ class ExtensionsMapTest extends ItopTestCase public function testiTopExtensionsMapInit() { - $oiTopExtensionsMap = new iTopExtensionsMap(sAppRootForTests:__DIR__."/ressources"); - - //file_put_contents(__DIR__.'/ressources/all_extensions_from_datamodels.json', json_encode($this->SerializeExtensionMap($oiTopExtensionsMap), JSON_PRETTY_PRINT)); + $oiTopExtensionsMap = iTopExtensionsMap::GetExtensionsMap(sAppRootForTests: __DIR__."/ressources/"); $sExpected = file_get_contents(__DIR__.'/ressources/all_extensions_from_datamodels.json'); $sExpected = str_replace('"sVersion": "ITOP_VERSION"', '"sVersion": "'.ITOP_VERSION.'"', $sExpected); @@ -166,6 +164,19 @@ class ExtensionsMapTest extends ItopTestCase $this->assertEquals($sExpected, $actual); } + public function testGetScannedModulesRootDirs() + { + $sAppRootForTest = __DIR__."/resources2/"; + $oiTopExtensionsMap = iTopExtensionsMap::GetExtensionsMap(sAppRootForTests: $sAppRootForTest); + + $aExpectedDirs = [ + "{$sAppRootForTest}datamodels/2.x", + "{$sAppRootForTest}extensions", + "{$sAppRootForTest}data/production-modules", + ]; + self::assertEquals($aExpectedDirs, $oiTopExtensionsMap->GetScannedModulesRootDirs()); + } + public function SerializeExtensionMap(iTopExtensionsMap $oiTopExtensionsMap): array { $aRes = []; diff --git a/tests/php-unit-tests/unitary-tests/setup/RunTimeEnvironmentTest.php b/tests/php-unit-tests/unitary-tests/setup/RunTimeEnvironmentTest.php index a1ccbd70ef..fee866700d 100644 --- a/tests/php-unit-tests/unitary-tests/setup/RunTimeEnvironmentTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/RunTimeEnvironmentTest.php @@ -9,30 +9,15 @@ use RunTimeEnvironment; class RunTimeEnvironmentTest extends ItopTestCase { - private ?string $sFixtureRootDir = null; - private ?string $sBuildDirToCleanup = null; - protected function setUp(): void { parent::setUp(); $this->RequireOnceItopFile('/setup/runtimeenv.class.inc.php'); } - protected function tearDown(): void - { - if (($this->sFixtureRootDir !== null) && is_dir($this->sFixtureRootDir)) { - self::RecurseRmdir($this->sFixtureRootDir); - } - if (($this->sBuildDirToCleanup !== null) && is_dir($this->sBuildDirToCleanup)) { - self::RecurseRmdir($this->sBuildDirToCleanup); - } - - parent::tearDown(); - } - public function testDoCompileDoesNotThrowWhenUnselectedExtensionCodeIsMissing(): void { - [$sToken, $sSourceDirRelative, $sExtensionsDirRelative] = $this->CreateFixtureContext('runtimeenv-missing-code-'); + [$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-code-'); $sInvalidExtensionXml = << @@ -41,20 +26,20 @@ class RunTimeEnvironmentTest extends ItopTestCase Test extension without code 1.0.0 false - + XML; file_put_contents(APPROOT.$sExtensionsDirRelative.'/extension.xml', $sInvalidExtensionXml); - $oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sToken); + $oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sEnvironment); //early DOMFormatException to avoid any real compilation $this->expectException(DOMFormatException::class); - $oRunTimeEnvironment->DoCompile([], [], [], $sSourceDirRelative, $sExtensionsDirRelative); + $oRunTimeEnvironment->DoCompile([], [], []); } public function testDoCompileThrowsWhenSelectedExtensionCodeIsMissing(): void { - [$sToken, $sSourceDirRelative, $sExtensionsDirRelative] = $this->CreateFixtureContext('runtimeenv-missing-code-'); + [$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-code-'); $sInvalidExtensionXml = << @@ -63,21 +48,21 @@ XML; Test extension without code 1.0.0 false - + XML; file_put_contents(APPROOT.$sExtensionsDirRelative.'/extension.xml', $sInvalidExtensionXml); - $oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sToken); + $oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sEnvironment); $this->expectException(Exception::class); $this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code (Broken extension)"); - $oRunTimeEnvironment->DoCompile([""], [], [], $sSourceDirRelative, $sExtensionsDirRelative); + $oRunTimeEnvironment->DoCompile([""], [], []); } public function testDoCompileThrowsWhenSelectedExtensionCodeAndLabelAreMissing(): void { - [$sToken, $sSourceDirRelative, $sExtensionsDirRelative] = $this->CreateFixtureContext('runtimeenv-missing-label-'); + [$sEnvironment, $sExtensionsDirRelative] = $this->CreateFixtureContext('env-missing-label-'); $sInvalidExtensionXml = << @@ -85,36 +70,34 @@ XML; Test extension without code and label 1.0.0 false - + XML; $sExtensionsDirAbsolute = APPROOT.$sExtensionsDirRelative; file_put_contents($sExtensionsDirAbsolute.'/extension.xml', $sInvalidExtensionXml); - $oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sToken); + $oRunTimeEnvironment = $this->CreateRunTimeEnvironment($sEnvironment); $this->expectException(Exception::class); $this->expectExceptionMessage("Selected extension(s) cannot be installed: Missing extension code ($sExtensionsDirAbsolute)"); - $oRunTimeEnvironment->DoCompile([""], [], [], $sSourceDirRelative, $sExtensionsDirRelative, false); + $oRunTimeEnvironment->DoCompile([""], [], [], false); } - private function CreateFixtureContext(string $sTokenPrefix): array + private function CreateFixtureContext(string $sEnvPrefix): array { - $sToken = str_replace('.', '-', uniqid($sTokenPrefix, true)); - $this->sFixtureRootDir = APPROOT.'data/'.$sToken; - $sSourceDirRelative = 'data/'.$sToken.'/source'; - $sExtensionsDirRelative = 'data/'.$sToken.'/extensions'; + $sEnvironment = str_replace('.', '-', uniqid($sEnvPrefix, true)); + $sExtensionsDirRelative = 'data/'.$sEnvironment.'-modules'; - mkdir(APPROOT.$sSourceDirRelative, 0777, true); mkdir(APPROOT.$sExtensionsDirRelative, 0777, true); + $this->aFileToClean[] = APPROOT.$sExtensionsDirRelative; - return [$sToken, $sSourceDirRelative, $sExtensionsDirRelative]; + return [$sEnvironment, $sExtensionsDirRelative]; } - private function CreateRunTimeEnvironment(string $sToken): RunTimeEnvironment + private function CreateRunTimeEnvironment(string $sEnvironment): RunTimeEnvironment { - $oRunTimeEnvironment = new RunTimeEnvironment('test-'.$sToken, false); - $this->sBuildDirToCleanup = $oRunTimeEnvironment->GetBuildDir(); + $oRunTimeEnvironment = new RunTimeEnvironment($sEnvironment, false); + $this->aFileToClean[] = $oRunTimeEnvironment->GetBuildDir(); return $oRunTimeEnvironment; } diff --git a/tests/php-unit-tests/unitary-tests/setup/feature_removal/SetupAuditTest.php b/tests/php-unit-tests/unitary-tests/setup/feature_removal/SetupAuditTest.php index c9efefa4bf..7fa90bc60b 100644 --- a/tests/php-unit-tests/unitary-tests/setup/feature_removal/SetupAuditTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/feature_removal/SetupAuditTest.php @@ -9,11 +9,14 @@ use Combodo\iTop\Setup\FeatureRemoval\SetupAudit; use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase; use Combodo\iTop\Test\UnitTest\Service\UnitTestRunTimeEnvironment; use MetaModel; +use SetupUtils; class SetupAuditTest extends ItopCustomDatamodelTestCase { public const ENVT = 'php-unit-extensionremoval-tests'; + private ?string $sDirPathToRemoveFromExtensionFolder = null; + public function GetDatamodelDeltaAbsPath(): string { //no delta: empty path provided @@ -44,6 +47,14 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase $this->RequireOnceItopFile('/setup/feature_removal/DryRemovalRuntimeEnvironment.php'); } + protected function tearDown(): void + { + parent::tearDown(); + if (!is_null($this->sDirPathToRemoveFromExtensionFolder) && is_dir($this->sDirPathToRemoveFromExtensionFolder)) { + SetupUtils::rrmdir($this->sDirPathToRemoveFromExtensionFolder); + } + } + public function GetTestEnvironment(): string { return self::ENVT; @@ -51,7 +62,11 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase public function testComputeDryRemoval() { - $oDryRemovalRuntimeEnvt = new DryRemovalRuntimeEnvironment($this->GetTestEnvironment(), ['nominal_ext1', 'finalclass_ext2']); + $this->sDirPathToRemoveFromExtensionFolder = APPROOT.'/extensions/finalclass_ext3'; + SetupUtils::copydir(__DIR__.'/other_features/finalclass_ext3', $this->sDirPathToRemoveFromExtensionFolder); + clearstatcache(); + + $oDryRemovalRuntimeEnvt = new DryRemovalRuntimeEnvironment($this->GetTestEnvironment(), ['finalclass_ext3' => 'Ext For Test3'], ['nominal_ext1', 'finalclass_ext2']); $oDryRemovalRuntimeEnvt->CompileFrom($this->GetTestEnvironment()); $oSetupAudit = new SetupAudit($this->GetTestEnvironment()); @@ -67,6 +82,19 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase "FinalClassFeature2Module1MyFinalClassFromLocation" => 0, ]; $this->assertEqualsCanonicalizing($expected, $oSetupAudit->RunDataAudit()); + + $aClassesAfter = ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment($oSetupAudit->GetEnvAfter()); + $expected = [ + "FinalClassFeature3Module1MyClass", + "FinalClassFeature3Module1MyFinalClassFromLocation", + ]; + foreach ($expected as $sAddedClass) { + $this->assertContains( + $sAddedClass, + $aClassesAfter, + "After DryRemoval compilation DM should contain classes coming from finalclass_ext3 extension" + ); + } } public function testGetRemovedClassesFromSetupWizard() diff --git a/tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/extension.xml b/tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/extension.xml new file mode 100644 index 0000000000..bfdc739120 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/extension.xml @@ -0,0 +1,22 @@ + + + finalclass_ext3 + Combodo SARL + + + + 6.6.6 + + + finalclass_ext3_module1 + tags/6.6.6 + + + 2023-07-19 + + 3.2.0 + + false + + \ No newline at end of file diff --git a/tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/finalclass_ext3_module1/datamodel.finalclass_ext3_module1.xml b/tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/finalclass_ext3_module1/datamodel.finalclass_ext3_module1.xml new file mode 100644 index 0000000000..84049f9809 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/finalclass_ext3_module1/datamodel.finalclass_ext3_module1.xml @@ -0,0 +1,76 @@ + + + + + + + bizmodel,searchable + false + FinalClassFeature3Module1MyFinalClassFromLocation + + + + + + + + + + + + + + + + + + + + + name2 + + false + + + + + Location + + + + bizmodel,searchable + false + FinalClassFeature3Module1MyClass + + + + + + + + + + + + + + + + + + + name + + false + + + + + + cmdbAbstractObject + + + + + + \ No newline at end of file diff --git a/tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/finalclass_ext3_module1/model.finalclass_ext3_module1.php b/tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/finalclass_ext3_module1/model.finalclass_ext3_module1.php new file mode 100644 index 0000000000..acf219fa12 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/feature_removal/other_features/finalclass_ext3/finalclass_ext3_module1/model.finalclass_ext3_module1.php @@ -0,0 +1,16 @@ + 'Ext For Test', + 'category' => 'business', + + // Setup + // + 'dependencies' => [ + 'itop-structure/3.2.0', + ], + 'mandatory' => false, + 'visible' => true, + 'installer' => '', + + // Components + // + 'datamodel' => [ + 'model.finalclass_ext3_module1.php', + ], + 'webservice' => [], + 'data.struct' => [// add your 'structure' definition XML files here, + ], + 'data.sample' => [// add your sample data XML files here, + ], + + // Documentation + // + 'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any + 'doc.more_information' => '', // hyperlink to more information, if any + + // Default settings + // + 'settings' => [// Module specific settings go here, if any + ], + ] +); diff --git a/tests/php-unit-tests/unitary-tests/setup/resources2/data/production-modules/prod-module1/.gitkeep b/tests/php-unit-tests/unitary-tests/setup/resources2/data/production-modules/prod-module1/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/php-unit-tests/unitary-tests/setup/resources2/datamodels/2.x/installation.xml b/tests/php-unit-tests/unitary-tests/setup/resources2/datamodels/2.x/installation.xml new file mode 100644 index 0000000000..70d3866c17 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/resources2/datamodels/2.x/installation.xml @@ -0,0 +1,243 @@ + + + + + Configuration Management options + The options below allow you to configure the type of elements that are to be managed inside iTop.]]> + /images/icons/icons8-apps-tab.svg + + + itop-config-mgmt-core + Configuration Management Core + All the base objects that are mandatory in the iTop CMDB: Organizations, Locations, Teams, Persons, etc. + + itop-structure + itop-config-mgmt + itop-attachments + itop-profiles-itil + itop-welcome-itil + itop-tickets + itop-files-information + combodo-db-tools + itop-core-update + itop-hub-connector + itop-oauth-client + combodo-backoffice-darkmoon-theme + combodo-backoffice-fullmoon-high-contrast-theme + combodo-backoffice-fullmoon-protanopia-deuteranopia-theme + combodo-backoffice-fullmoon-tritanopia-theme + itop-themes-compat + combodo-my-account + combodo-my-account-user-info + combodo-oauth2-client + itop-attribute-class-set + itop-attribute-encrypted-password + itop-ui-copypaste + + true + + + itop-config-mgmt-datacenter + Data Center Devices + Manage Data Center devices such as Racks, Enclosures, PDUs, etc. + + itop-datacenter-mgmt + + true + + + itop-config-mgmt-end-user + End-User Devices + Manage devices related to end-users: PCs, Phones, Tablets, etc. + + itop-endusers-devices + + true + + + itop-config-mgmt-storage + Storage Devices + Manage storage devices such as NAS, SAN Switches, Tape Libraries and Tapes, etc. + + itop-storage-mgmt + + true + + + itop-config-mgmt-virtualization + Virtualization + Manage Hypervisors, Virtual Machines and Farms. + + itop-virtualization-mgmt + + true + + + + itop-container-mgmt + Containerization + + + itop-container-mgmt + + false + + + + + + + + Service Management options + Select the choice that best describes the relationships between the services and the IT infrastructure in your IT environment.]]> + /images/icons/icons8-services.svg + + + itop-service-mgmt-enterprise + Service Management for Enterprises + Select this option if the IT delivers services based on a shared infrastructure. For example if different organizations within your company subscribe to services (like Mail and Print services) delivered by a single shared backend. + + itop-service-mgmt + + true + + + itop-service-mgmt-service-provider + Service Management for Service Providers + Select this option if the IT manages the infrastructure of independent customers. This is the most flexible model, since the services can be delivered with a mix of shared and customer specific infrastructure devices. + + itop-service-mgmt-provider + + + + + + Tickets Management options + Select the type of tickets you want to use in order to respond to user requests and incidents.]]> + /images/icons/icons8-discussion-forum.svg + + + itop-ticket-mgmt-simple-ticket + Simple Ticket Management + Select this option to use one single type of tickets for all kind of requests. + + itop-request-mgmt + + true + + + + itop-ticket-mgmt-simple-ticket-enhanced-portal + Customer Portal + + + itop-portal + itop-portal-base + + true + + + + + + itop-ticket-mgmt-itil + ITIL Compliant Tickets Management + Select this option to have different types of ticket for managing user requests and incidents. Each type of ticket has a specific life cycle and specific fields + + + + itop-ticket-mgmt-itil-user-request + User Request Management + Manage User Request tickets in iTop + + itop-request-mgmt-itil + + + + itop-ticket-mgmt-itil-incident + Incident Management + Manage Incidents tickets in iTop + + itop-incident-mgmt-itil + + + + itop-ticket-mgmt-itil-enhanced-portal + Customer Portal + + + itop-portal + itop-portal-base + + true + + + + + + itop-ticket-mgmt-none + No Tickets Management + Don't manage incidents or user requests in iTop + + + + + + + Change Management options + Select the type of tickets you want to use in order to manage changes to the IT infrastructure.]]> + /images/icons/icons8-change.svg + + + itop-change-mgmt-simple + Simple Change Management + Select this option to use one type of ticket for all kind of changes. + + itop-change-mgmt + + true + + + itop-change-mgmt-itil + ITIL Change Management + Select this option to use Normal/Routine/Emergency change tickets. + + itop-change-mgmt-itil + + + + itop-change-mgmt-none + No Change Management + Don't manage changes in iTop + + + + + + + Additional ITIL tickets + Pick from the list below the additional ITIL processes that are to be implemented in iTop.]]> + /images/icons/icons8-important-book.svg + + + + itop-kown-error-mgmt + Known Errors Management and FAQ + Select this option to track "Known Errors" and FAQs in iTop. + + itop-faq-light + itop-knownerror-mgmt + + + + itop-problem-mgmt + Problem Management + Select this option to track "Problems" in iTop. + + itop-problem-mgmt + + + + + + diff --git a/tests/php-unit-tests/unitary-tests/setup/resources2/datamodels/2.x/source1/.gitkeep b/tests/php-unit-tests/unitary-tests/setup/resources2/datamodels/2.x/source1/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/php-unit-tests/unitary-tests/setup/resources2/extensions/extension1/.gitkeep b/tests/php-unit-tests/unitary-tests/setup/resources2/extensions/extension1/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 From d4d6fa149dff85761b0c30983b7d3d12990cde39 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 17 Jun 2026 14:31:50 +0200 Subject: [PATCH 27/36] =?UTF-8?q?N=C2=B09412=20-=20revert=20fresh-install.?= =?UTF-8?q?xml=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unattended-install/xml_setup/fresh-install.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/setup/unattended-install/xml_setup/fresh-install.xml b/setup/unattended-install/xml_setup/fresh-install.xml index 97146edbc2..f44b7cdb80 100644 --- a/setup/unattended-install/xml_setup/fresh-install.xml +++ b/setup/unattended-install/xml_setup/fresh-install.xml @@ -13,10 +13,10 @@ production - localhost - iTop - blob99 - configcomments + + + + @@ -24,9 +24,9 @@ /usr/bin/dot - admin - admin - FR FR + + + From 75433d33906fc6c970751fd2df4a5c02d9dbe574 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 17 Jun 2026 15:12:46 +0200 Subject: [PATCH 28/36] Enhance unattended CLI to log fatal error just like setup wizardinstead of silent failure --- setup/unattended-install/unattended-install.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/setup/unattended-install/unattended-install.php b/setup/unattended-install/unattended-install.php index b6362bd723..e55352b2cf 100644 --- a/setup/unattended-install/unattended-install.php +++ b/setup/unattended-install/unattended-install.php @@ -3,6 +3,17 @@ require_once(dirname(__FILE__, 3).'/approot.inc.php'); require_once(__DIR__.'/InstallationFileService.php'); +function fatalHandler() +{ + $error = error_get_last(); + if ($error) { + SetupLog::Error("Fatal error during setup", null, $error); + echo "Fatal Error occured: {$error["message"]}"; + } +} + +register_shutdown_function("fatalHandler"); + function PrintUsageAndExit() { echo << Date: Wed, 17 Jun 2026 15:21:30 +0200 Subject: [PATCH 29/36] =?UTF-8?q?N=C2=B09144=20-=20fix=20commit=20operatio?= =?UTF-8?q?n=20rmdir=20conf/production-build=20failed=20as=20not=20always?= =?UTF-8?q?=20empty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup/runtimeenv.class.inc.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index 6f0f440b0a..758cc92a91 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -1032,7 +1032,8 @@ class RunTimeEnvironment @chmod($sFinalConfig, 0770); // In case it exists: RWX for owner and group, nothing for others $this->CommitFile($sBuildConfig, $sFinalConfig); @chmod($sFinalConfig, 0440); // Read-only for owner and group, nothing for others - @rmdir(dirname($sBuildConfig)); // Cleanup the temporary build dir if empty + + SetupUtils::rrmdir(dirname($sBuildConfig)); // Cleanup the temporary build dir if empty if (! isset($_SESSION)) { //used in all UI setups (not unattended) From 6a8dc159c169eaf8e3cfd0b5b8dc96bfe25f3439 Mon Sep 17 00:00:00 2001 From: odain Date: Wed, 17 Jun 2026 15:38:22 +0200 Subject: [PATCH 30/36] revert 75433d33906fc6c970751fd2df4a5c02d9dbe574: Enhance unattended CLI to log fatal error --- setup/unattended-install/unattended-install.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/setup/unattended-install/unattended-install.php b/setup/unattended-install/unattended-install.php index e55352b2cf..b6362bd723 100644 --- a/setup/unattended-install/unattended-install.php +++ b/setup/unattended-install/unattended-install.php @@ -3,17 +3,6 @@ require_once(dirname(__FILE__, 3).'/approot.inc.php'); require_once(__DIR__.'/InstallationFileService.php'); -function fatalHandler() -{ - $error = error_get_last(); - if ($error) { - SetupLog::Error("Fatal error during setup", null, $error); - echo "Fatal Error occured: {$error["message"]}"; - } -} - -register_shutdown_function("fatalHandler"); - function PrintUsageAndExit() { echo << Date: Thu, 18 Jun 2026 11:05:09 +0200 Subject: [PATCH 31/36] =?UTF-8?q?N=C2=B09701=20Add=20id=20to=20badges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cs.dictionary.itop.extensions-details.php | 6 ++ .../da.dictionary.itop.extensions-details.php | 6 ++ .../de.dictionary.itop.extensions-details.php | 6 ++ .../en.dictionary.itop.extensions-details.php | 6 ++ ..._cr.dictionary.itop.extensions-details.php | 6 ++ .../fr.dictionary.itop.extensions-details.php | 6 ++ .../hu.dictionary.itop.extensions-details.php | 6 ++ .../it.dictionary.itop.extensions-details.php | 6 ++ .../ja.dictionary.itop.extensions-details.php | 6 ++ .../nl.dictionary.itop.extensions-details.php | 6 ++ .../pl.dictionary.itop.extensions-details.php | 6 ++ ..._br.dictionary.itop.extensions-details.php | 6 ++ .../ru.dictionary.itop.extensions-details.php | 6 ++ .../sk.dictionary.itop.extensions-details.php | 6 ++ .../tr.dictionary.itop.extensions-details.php | 6 ++ ..._cn.dictionary.itop.extensions-details.php | 6 ++ setup/wizardsteps/WizStepModulesChoice.php | 12 ++-- .../ExtensionDetailsUIBlockFactory.php | 63 ++++++++++++++++--- .../setup/WizStepModulesChoiceTest.php | 20 +++--- 19 files changed, 166 insertions(+), 25 deletions(-) diff --git a/dictionaries/ui/layouts/extensions-details/cs.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/cs.dictionary.itop.extensions-details.php index 04e71d6511..f7fe7ceff0 100644 --- a/dictionaries/ui/layouts/extensions-details/cs.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/cs.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('CS CZ', 'Czech', 'Čeština', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/da.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/da.dictionary.itop.extensions-details.php index 73e4f4efe4..2eb24fb0d2 100644 --- a/dictionaries/ui/layouts/extensions-details/da.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/da.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('DA DA', 'Danish', 'Dansk', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/de.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/de.dictionary.itop.extensions-details.php index 816dd69e78..6b4aa2ba99 100644 --- a/dictionaries/ui/layouts/extensions-details/de.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/de.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('DE DE', 'German', 'Deutsch', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/en.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/en.dictionary.itop.extensions-details.php index 4e01932f53..0cf540d414 100644 --- a/dictionaries/ui/layouts/extensions-details/en.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/en.dictionary.itop.extensions-details.php @@ -10,11 +10,17 @@ Dict::Add('EN US', 'English', 'English', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall', diff --git a/dictionaries/ui/layouts/extensions-details/es_cr.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/es_cr.dictionary.itop.extensions-details.php index 0a128e19e8..270144f306 100644 --- a/dictionaries/ui/layouts/extensions-details/es_cr.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/es_cr.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/fr.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/fr.dictionary.itop.extensions-details.php index 4fb6090335..6b85232cf4 100644 --- a/dictionaries/ui/layouts/extensions-details/fr.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/fr.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('FR FR', 'French', 'Français', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installé', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'Cette extension fait partie de l\'installation actuelle.', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'va être installé', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'Cette extension sera installée lors de l\'installation.', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'pas installé', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'Cette extension ne fait pas partie de l\'installation actuelle.', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'va être désinstallé', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'Cette extension sera désinstallée lors de l\'installation.', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'non désinstallable', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Une fois cette extension installée, elle ne devrait pas être désinstallée.', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'supprimé du disque', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'Le dossier local de l\'extension a été supprimé du disque. Cela forcera la désinstallation de cette extension.', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'À propos de %1$s', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'Plus d\'informations', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Forcer la désinstallation', diff --git a/dictionaries/ui/layouts/extensions-details/hu.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/hu.dictionary.itop.extensions-details.php index 5f81e69874..e17a4d3ef2 100644 --- a/dictionaries/ui/layouts/extensions-details/hu.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/hu.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('HU HU', 'Hungarian', 'Magyar', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/it.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/it.dictionary.itop.extensions-details.php index 7c44bc63ef..fd9a53dce7 100644 --- a/dictionaries/ui/layouts/extensions-details/it.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/it.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('IT IT', 'Italian', 'Italiano', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/ja.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/ja.dictionary.itop.extensions-details.php index a3b527c0bb..b2d0ba8c02 100644 --- a/dictionaries/ui/layouts/extensions-details/ja.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/ja.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('JA JP', 'Japanese', '日本語', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/nl.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/nl.dictionary.itop.extensions-details.php index 53fcb5b4a8..20d6e58733 100644 --- a/dictionaries/ui/layouts/extensions-details/nl.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/nl.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('NL NL', 'Dutch', 'Nederlands', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/pl.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/pl.dictionary.itop.extensions-details.php index 8234013376..caaad67c6e 100644 --- a/dictionaries/ui/layouts/extensions-details/pl.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/pl.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('PL PL', 'Polish', 'Polski', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/pt_br.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/pt_br.dictionary.itop.extensions-details.php index 4683a2b433..dd98f2ec28 100644 --- a/dictionaries/ui/layouts/extensions-details/pt_br.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/pt_br.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('PT BR', 'Brazilian', 'Brazilian', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/ru.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/ru.dictionary.itop.extensions-details.php index ef233f68b0..146cafdad3 100644 --- a/dictionaries/ui/layouts/extensions-details/ru.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/ru.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('RU RU', 'Russian', 'Русский', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/sk.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/sk.dictionary.itop.extensions-details.php index f4db3fabc9..53cd4851af 100644 --- a/dictionaries/ui/layouts/extensions-details/sk.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/sk.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/tr.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/tr.dictionary.itop.extensions-details.php index 0e66dc741e..7e98c6e52c 100644 --- a/dictionaries/ui/layouts/extensions-details/tr.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/tr.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('TR TR', 'Turkish', 'Türkçe', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/dictionaries/ui/layouts/extensions-details/zh_cn.dictionary.itop.extensions-details.php b/dictionaries/ui/layouts/extensions-details/zh_cn.dictionary.itop.extensions-details.php index 802d156699..8344417981 100644 --- a/dictionaries/ui/layouts/extensions-details/zh_cn.dictionary.itop.extensions-details.php +++ b/dictionaries/ui/layouts/extensions-details/zh_cn.dictionary.itop.extensions-details.php @@ -12,11 +12,17 @@ */ Dict::Add('ZH CN', 'Chinese', '简体中文', [ 'UI:Layout:ExtensionsDetails:BadgeInstalled' => 'installed~~', + 'UI:Layout:ExtensionsDetails:BadgeInstalled+' => 'This extension is part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled' => 'to be installed~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeInstalled+' => 'This extension will be installed during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotInstalled' => 'not installed~~', + 'UI:Layout:ExtensionsDetails:BadgeNotInstalled+' => 'This extension is not part of the current installation.~~', 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled' => 'to be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+' => 'This extension will be uninstalled during the setup.~~', 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable' => 'cannot be uninstalled~~', + 'UI:Layout:ExtensionsDetails:BadgeNotUninstallable+' => 'Once this extension has been installed, it should not be uninstalled.~~', 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk' => 'missing from disk~~', + 'UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+' => 'The local extension folder has been removed from the disk. This will force the uninstallation of this extension.~~', 'UI:Layout:ExtensionsDetails:MenuAboutTitle' => 'About %1$s~~', 'UI:Layout:ExtensionsDetails:MenuAbout' => 'More informations~~', 'UI:Layout:ExtensionsDetails:MenuForce' => 'Force uninstall~~', diff --git a/setup/wizardsteps/WizStepModulesChoice.php b/setup/wizardsteps/WizStepModulesChoice.php index c4ff129507..0c1e4bbfee 100644 --- a/setup/wizardsteps/WizStepModulesChoice.php +++ b/setup/wizardsteps/WizStepModulesChoice.php @@ -869,18 +869,18 @@ EOF $sTooltip = ''; if ($aFlags['missing']) { - $sTooltip .= '
    source removed
    '; + $sTooltip .= '
    source removed
    '; } if ($aFlags['installed']) { - $sTooltip .= '
    installed
    '; + $sTooltip .= '
    installed
    '; - $sTooltip .= '
    to be uninstalled
    '; + $sTooltip .= '
    to be uninstalled
    '; } else { - $sTooltip .= '
    to be installed
    '; - $sTooltip .= '
    not installed
    '; + $sTooltip .= '
    to be installed
    '; + $sTooltip .= '
    not installed
    '; } if (!$aFlags['uninstallable']) { - $sTooltip .= '
    cannot be uninstalled
    '; + $sTooltip .= '
    cannot be uninstalled
    '; } $sMetadata = ''; diff --git a/sources/Application/UI/Base/Layout/Extension/ExtensionDetailsUIBlockFactory.php b/sources/Application/UI/Base/Layout/Extension/ExtensionDetailsUIBlockFactory.php index 7a1934f019..9fff89696c 100644 --- a/sources/Application/UI/Base/Layout/Extension/ExtensionDetailsUIBlockFactory.php +++ b/sources/Application/UI/Base/Layout/Extension/ExtensionDetailsUIBlockFactory.php @@ -13,6 +13,13 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory /** @inheritDoc */ public const UI_BLOCK_CLASS_NAME = ExtensionDetails::class; + private const BADGE_ID_INSTALLED = 'installed'; + private const BADGE_ID_NOT_INSTALLED = 'not-installed'; + private const BADGE_ID_TO_BE_INSTALLED = 'to-be-installed'; + private const BADGE_ID_TO_BE_UNINSTALLED = 'to-be-uninstalled'; + private const BADGE_ID_NOT_UNINSTALLABLE = 'not-uninstallable'; + private const BADGE_ID_MISSING_FROM_DISK = 'missing-from-disk'; + public static function MakeInstalled(string $sCode, string $sLabel, string $sDescription = '', array $aMetaData = [], array $aExtraFlags = [], string $sAbout = '') { $aBadges = []; @@ -21,11 +28,19 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory $bSelected = $aExtraFlags['selected'] ?? true; $bDisabled = $aExtraFlags['disabled'] ?? false; $bRemote = $aExtraFlags['remote'] ?? false; - self::AddExtraBadges($aBadges, $bUninstallable, $bMissingFromDisk); - $oBadgeInstalled = BadgeUIBlockFactory::MakeGreen(Dict::S('UI:Layout:ExtensionsDetails:BadgeInstalled')); + self::AddExtraBadges($aBadges, $bUninstallable, $bMissingFromDisk, $sCode); + $oBadgeInstalled = BadgeUIBlockFactory::MakeGreen( + Dict::S('UI:Layout:ExtensionsDetails:BadgeInstalled'), + Dict::S('UI:Layout:ExtensionsDetails:BadgeInstalled+'), + self::GetBadgeId($sCode, self::BADGE_ID_INSTALLED) + ); $oBadgeInstalled->AddCSSClass('checked'); $aBadges[] = $oBadgeInstalled; - $oBadgeToBeUninstalled = BadgeUIBlockFactory::MakeRed(Dict::S('UI:Layout:ExtensionsDetails:BadgeToBeUninstalled')); + $oBadgeToBeUninstalled = BadgeUIBlockFactory::MakeRed( + Dict::S('UI:Layout:ExtensionsDetails:BadgeToBeUninstalled'), + Dict::S('UI:Layout:ExtensionsDetails:BadgeToBeUninstalled+'), + self::GetBadgeId($sCode, self::BADGE_ID_TO_BE_UNINSTALLED) + ); $oBadgeToBeUninstalled->AddCSSClass('unchecked'); $aBadges[] = $oBadgeToBeUninstalled; @@ -56,11 +71,19 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory $bUninstallable = $aExtraFlags['uninstallable'] ?? true; $bSelected = $aExtraFlags['selected'] ?? false; $bDisabled = $aExtraFlags['disabled'] ?? false; - self::AddExtraBadges($aBadges, $bUninstallable, false); - $oBadgeInstalled = BadgeUIBlockFactory::MakeGrey(Dict::S('UI:Layout:ExtensionsDetails:BadgeNotInstalled')); + self::AddExtraBadges($aBadges, $bUninstallable, false, $sCode); + $oBadgeInstalled = BadgeUIBlockFactory::MakeGrey( + Dict::S('UI:Layout:ExtensionsDetails:BadgeNotInstalled'), + Dict::S('UI:Layout:ExtensionsDetails:BadgeNotInstalled+'), + self::GetBadgeId($sCode, self::BADGE_ID_NOT_INSTALLED) + ); $oBadgeInstalled->AddCSSClass('unchecked'); $aBadges[] = $oBadgeInstalled; - $oBadgeToBeUninstalled = BadgeUIBlockFactory::MakeCyan(Dict::S('UI:Layout:ExtensionsDetails:BadgeToBeInstalled')); + $oBadgeToBeUninstalled = BadgeUIBlockFactory::MakeCyan( + Dict::S('UI:Layout:ExtensionsDetails:BadgeToBeInstalled'), + Dict::S('UI:Layout:ExtensionsDetails:BadgeToBeInstalled+'), + self::GetBadgeId($sCode, self::BADGE_ID_TO_BE_INSTALLED) + ); $oBadgeToBeUninstalled->AddCSSClass('checked'); $aBadges[] = $oBadgeToBeUninstalled; $oExtensionDetails = new ExtensionDetails($sCode, $sLabel, $sDescription, $aMetaData, $aBadges, $sAbout); @@ -76,13 +99,35 @@ class ExtensionDetailsUIBlockFactory extends AbstractUIBlockFactory return $oExtensionDetails; } - private static function AddExtraBadges(array &$aBadges, bool $bUninstallable, bool $bMissingFromDisk) + private static function AddExtraBadges(array &$aBadges, bool $bUninstallable, bool $bMissingFromDisk, string $sCode) { if (!$bUninstallable) { - $aBadges[] = BadgeUIBlockFactory::MakeOrange(Dict::S('UI:Layout:ExtensionsDetails:BadgeNotUninstallable')); + $aBadges[] = BadgeUIBlockFactory::MakeOrange( + Dict::S('UI:Layout:ExtensionsDetails:BadgeNotUninstallable'), + Dict::S('UI:Layout:ExtensionsDetails:BadgeNotUninstallable+'), + self::GetBadgeId($sCode, self::BADGE_ID_NOT_UNINSTALLABLE) + ); } if ($bMissingFromDisk) { - $aBadges[] = BadgeUIBlockFactory::MakeRed(Dict::S('UI:Layout:ExtensionsDetails:BadgeMissingFromDisk')); + $aBadges[] = BadgeUIBlockFactory::MakeRed( + Dict::S('UI:Layout:ExtensionsDetails:BadgeMissingFromDisk'), + Dict::S('UI:Layout:ExtensionsDetails:BadgeMissingFromDisk+'), + self::GetBadgeId($sCode, self::BADGE_ID_MISSING_FROM_DISK) + ); } } + + /** + * Generate a badge ID for an extension + * + * @param string $sExtensionCode The extension code + * @param string $sBadgeType The badge type (one of the BADGE_ID_* constants) + * + * @return string The badge ID in the format: badge--{extensionCode}--{badgeType} + */ + public static function GetBadgeId(string $sExtensionCode, string $sBadgeType): string + { + return 'badge--'.$sExtensionCode.'--'.$sBadgeType; + } + } diff --git a/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceTest.php b/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceTest.php index 45ef05f7a6..9d91d50aa2 100644 --- a/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/WizStepModulesChoiceTest.php @@ -1004,7 +1004,7 @@ class WizStepModulesChoiceTest extends ItopTestCase
    -
    to be installed
    not installed
    +
    to be installed
    not installed