diff --git a/htdocs/modulebuilder/template/class/myobject.class.php b/htdocs/modulebuilder/template/class/myobject.class.php
index 56d915f945e..e754403b0af 100644
--- a/htdocs/modulebuilder/template/class/myobject.class.php
+++ b/htdocs/modulebuilder/template/class/myobject.class.php
@@ -47,7 +47,7 @@ class MyObject extends CommonObject
* @var string Prefix to check for any trigger code of any business class to prevent bad value for trigger code.
* @see CommonTrigger::call_trigger()
*/
- public $TRIGGER_PREFIX = 'MYMODULE_MYOBJECT'; // Will be used to build trgiger keys 'MYMODULE_MYOBJECT_MODIFY', ...
+ public $TRIGGER_PREFIX = 'MYMODULE_MYOBJECT'; // Will be used to build trigger keys 'MYMODULE_MYOBJECT_MODIFY', ...
/**
* @var string Name of table without prefix where object is stored. This is also the key used for extrafields management (so extrafields know the link to the parent table).
@@ -636,7 +636,7 @@ class MyObject extends CommonObject
if (!$error && !$notrigger) {
// Call trigger
- $result = $this->call_trigger('MYOBJECT_VALIDATE', $user);
+ $result = $this->call_trigger('MYMODULE_MYOBJECT_VALIDATE', $user);
if ($result < 0) {
$error++;
}
@@ -1019,12 +1019,14 @@ class MyObject extends CommonObject
if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
global $langs;
//$langs->load("mymodule@mymodule");
- $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
- $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
- $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
- $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
- $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
- $this->labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
+ // Build status labels from the 'status' field arrayofkeyval so that the badge (LibStatut),
+ // the list filter and the select all show the same, configurable labels.
+ if (!empty($this->fields['status']['arrayofkeyval']) && is_array($this->fields['status']['arrayofkeyval'])) {
+ foreach ($this->fields['status']['arrayofkeyval'] as $statuskey => $statuslabel) {
+ $this->labelStatus[$statuskey] = $langs->transnoentitiesnoconv($statuslabel);
+ $this->labelStatusShort[$statuskey] = $langs->transnoentitiesnoconv($statuslabel);
+ }
+ }
}
$statusType = 'status'.$status;
@@ -1033,7 +1035,7 @@ class MyObject extends CommonObject
$statusType = 'status6';
}
- return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode, '', $paramsBadge);
+ return dolGetStatus($this->labelStatus[$status] ?? '', $this->labelStatusShort[$status] ?? '', '', $statusType, $mode, '', $paramsBadge);
}
/**
diff --git a/htdocs/modulebuilder/template/myobject_card.php b/htdocs/modulebuilder/template/myobject_card.php
index f3b1b0c7d0c..0324075719e 100644
--- a/htdocs/modulebuilder/template/myobject_card.php
+++ b/htdocs/modulebuilder/template/myobject_card.php
@@ -375,7 +375,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
// require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
// $notify = new Notify($db);
// $text .= '
';
- // $text .= $notify->confirmMessage('MYOBJECT_CLOSE', $object->socid, $object);
+ // $text .= $notify->confirmMessage('MYMODULE_MYOBJECT_CLOSE', $object->socid, $object);
// }
// $formquestion = array();
diff --git a/htdocs/modulebuilder/template/sql/data.sql b/htdocs/modulebuilder/template/sql/data.sql
index 9cb515a736e..17e8913ef24 100644
--- a/htdocs/modulebuilder/template/sql/data.sql
+++ b/htdocs/modulebuilder/template/sql/data.sql
@@ -24,9 +24,9 @@
-- new types of automatic events to record in agenda
--- 'code' must be a value matching 'MYOBJECT_ACTION'
+-- 'code' must be a value matching 'MYMODULE_MYOBJECT_ACTION'
-- 'elementtype' must be value 'mymodule' ('myobject@mymodule' may be possible but should not be required)
---insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MYOBJECT_VALIDATE','MyObject validated','Executed when myobject is validated', 'myobject@mymodule', 1000);
---insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MYOBJECT_UNVALIDATE','MyObject unvalidated','Executed when myobject is unvalidated', 'myobject@mymodule', 1001);
---insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MYOBJECT_DELETE','MyObject deleted','Executed when myobject deleted', 'myobject@mymodule', 1004);
+--insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MYMODULE_MYOBJECT_VALIDATE','MyObject validated','Executed when myobject is validated', 'myobject@mymodule', 1000);
+--insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MYMODULE_MYOBJECT_UNVALIDATE','MyObject unvalidated','Executed when myobject is unvalidated', 'myobject@mymodule', 1001);
+--insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('MYMODULE_MYOBJECT_DELETE','MyObject deleted','Executed when myobject deleted', 'myobject@mymodule', 1004);
diff --git a/test/phpunit/ModuleBuilderTemplateConventionsTest.php b/test/phpunit/ModuleBuilderTemplateConventionsTest.php
new file mode 100644
index 00000000000..5f58bcbf55c
--- /dev/null
+++ b/test/phpunit/ModuleBuilderTemplateConventionsTest.php
@@ -0,0 +1,120 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file test/phpunit/ModuleBuilderTemplateConventionsTest.php
+ * \ingroup modulebuilder
+ * \brief PHPUnit test for ModuleBuilder template conventions: status labels derived from
+ * arrayofkeyval, and normalized trigger naming (MYMODULE_MYOBJECT_ACTION).
+ */
+
+global $conf, $user, $langs, $db;
+
+require_once dirname(__FILE__).'/../../htdocs/master.inc.php';
+require_once dirname(__FILE__).'/CommonClassTest.class.php';
+
+if (empty($user->id)) {
+ print "Load permissions for admin user nb 1\n";
+ $user->fetch(1);
+ $user->loadRights();
+}
+$conf->global->MAIN_DISABLE_ALL_MAILS = 1;
+
+/**
+ * Class ModuleBuilderTemplateConventionsTest
+ *
+ * @backupGlobals disabled
+ * @backupStaticAttributes enabled
+ * @remarks backupGlobals must be disabled to have db,conf,user and lang not erased.
+ * @phan-file-suppress PhanUndeclaredExtendedClass
+ * @phan-file-suppress PhanUndeclaredClass
+ * @phan-file-suppress PhanUndeclaredMethod
+ */
+class ModuleBuilderTemplateConventionsTest extends CommonClassTest
+{
+ /**
+ * @var string Absolute path to the object class template.
+ */
+ const CLASS_TPL = __DIR__.'/../../htdocs/modulebuilder/template/class/myobject.class.php';
+
+ /**
+ * @var string Absolute path to the object card template.
+ */
+ const CARD_TPL = __DIR__.'/../../htdocs/modulebuilder/template/myobject_card.php';
+
+ /**
+ * @var string Absolute path to the seed data SQL template.
+ */
+ const DATA_SQL = __DIR__.'/../../htdocs/modulebuilder/template/sql/data.sql';
+
+ /**
+ * getLibStatut() must use the label defined in the status field arrayofkeyval, not a hardcoded one.
+ *
+ * @return void
+ */
+ public function testLibStatutLabelsMatchArrayofkeyval()
+ {
+ global $db;
+ require_once self::CLASS_TPL;
+ $object = new MyObject($db);
+
+ $expected = $object->fields['status']['arrayofkeyval'][MyObject::STATUS_VALIDATED];
+ $badge = strip_tags($object->LibStatut(MyObject::STATUS_VALIDATED, 1));
+
+ $this->assertStringContainsString($expected, $badge, 'Validated badge label should match arrayofkeyval label');
+ $this->assertStringNotContainsString('Enabled', $badge, 'Validated badge must not show the hardcoded "Enabled" label');
+ }
+
+ /**
+ * The LibStatut block must no longer hardcode Enabled/Disabled labels.
+ *
+ * @return void
+ */
+ public function testNoHardcodedEnabledDisabledInLibStatut()
+ {
+ $content = file_get_contents(self::CLASS_TPL);
+ $this->assertSame(0, preg_match('/labelStatus(Short)?\[[^\]]+\]\s*=\s*\$langs->transnoentitiesnoconv\(\'(Enabled|Disabled)\'\)/', $content), 'Hardcoded Enabled/Disabled label assignment still present in LibStatut');
+ }
+
+ /**
+ * The validate trigger code must carry the module prefix (policy MYMODULE_MYOBJECT_ACTION).
+ *
+ * @return void
+ */
+ public function testValidateTriggerHasModulePrefix()
+ {
+ $content = file_get_contents(self::CLASS_TPL);
+ $this->assertStringContainsString("call_trigger('MYMODULE_MYOBJECT_VALIDATE'", $content, 'Validate trigger must use MYMODULE_MYOBJECT_VALIDATE');
+ $this->assertSame(0, preg_match("/call_trigger\('MYOBJECT_VALIDATE'/", $content), 'Legacy unprefixed MYOBJECT_VALIDATE trigger still present');
+ }
+
+ /**
+ * No legacy unprefixed trigger code (MYOBJECT_) must remain in the templates.
+ * Configuration constants (ADDON_PDF, DRAFT_WATERMARK, QUICKSEARCH_ON_FIELDS) are not triggers
+ * and are excluded by the action whitelist below.
+ *
+ * @return void
+ */
+ public function testNoLegacyTriggerCodeInTemplates()
+ {
+ $legacy = '/(?assertSame(0, preg_match($legacy, $content), 'Legacy unprefixed trigger code found in '.basename($tpl));
+ }
+ }
+}