3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 class CRM_Core_BAO_Translation
extends CRM_Core_DAO_Translation
implements \Civi\Test\HookInterface
{
19 use CRM_Core_DynamicFKAccessTrait
;
22 * Get a list of valid statuses for translated-strings.
26 public static function getStatuses($context = NULL) {
28 ['id' => 1, 'name' => 'active', 'label' => ts('Active')],
29 ['id' => 2, 'name' => 'draft', 'label' => ts('Draft')],
31 return self
::formatPsuedoconstant($context, $options);
35 * Get a list of tables with translatable strings.
38 * Ex: ['civicrm_event' => 'civicrm_event']
40 public static function getEntityTables() {
41 if (!isset(Civi
::$statics[__CLASS__
]['allTables'])) {
42 $tables = array_keys(self
::getTranslatedFields());
43 Civi
::$statics[__CLASS__
]['allTables'] = array_combine($tables, $tables);
45 return Civi
::$statics[__CLASS__
]['allTables'];
49 * Get a list of fields with translatable strings.
52 * Ex: ['title' => 'title', 'description' => 'description']
54 public static function getEntityFields() {
55 if (!isset(Civi
::$statics[__CLASS__
]['allFields'])) {
57 foreach (self
::getTranslatedFields() as $columns) {
58 foreach ($columns as $column => $sqlExpr) {
59 $allFields[$column] = $column;
62 Civi
::$statics[__CLASS__
]['allFields'] = $allFields;
64 return Civi
::$statics[__CLASS__
]['allFields'];
68 * Given a constant list of of id/name/label options, convert to the
69 * format required by pseudoconstant.
71 * @param string|NULL $context
72 * @param array $options
73 * List of options, each as a record of id+name+label.
74 * Ex: [['id' => 123, 'name' => 'foo_bar', 'label' => 'Foo Bar']]
78 private static function formatPsuedoconstant($context, array $options) {
79 // https://docs.civicrm.org/dev/en/latest/framework/pseudoconstant/#context
80 $key = ($context === 'match') ?
'name' : 'id';
81 $value = ($context === 'validate') ?
'name' : 'label';
82 return array_combine(array_column($options, $key), array_column($options, $value));
87 * List of data fields to translate, organized by table and column.
88 * Omitted/unlisted fields are not translated. Any listed field may be translated.
89 * Values should be TRUE.
90 * Ex: $fields['civicrm_event']['summary'] = TRUE
92 public static function getTranslatedFields() {
93 $key = 'translatedFields';
94 $cache = Civi
::cache('fields');
95 if (($r = $cache->get($key)) !== NULL) {
100 \CRM_Utils_Hook
::translateFields($f);
102 // Future: Assimilate defaults originating in XML (incl extension-entities)
103 // e.g. CRM_Core_I18n_SchemaStructure::columns() will grab core fields
105 $cache->set($key, $f);
110 * When manipulating strings via the `Translation` entity (APIv4), ensure that the references are well-formed.
112 * @param \Civi\Api4\Event\ValidateValuesEvent $e
114 public static function self_civi_api4_validate(\Civi\Api4\Event\ValidateValuesEvent
$e) {
115 $statuses = self
::getStatuses('validate');
116 $dataTypes = [CRM_Utils_Type
::T_STRING
, CRM_Utils_Type
::T_TEXT
, CRM_Utils_Type
::T_LONGTEXT
];
117 $htmlTypes = ['Text', 'TextArea', 'RichTextEditor', ''];
119 foreach ($e->records
as $r => $record) {
120 if (array_key_exists('status_id', $record) && !isset($statuses[$record['status_id']])) {
121 $e->addError($r, 'status_id', 'invalid', ts('Invalid status'));
124 $entityIdFields = ['entity_table', 'entity_field', 'entity_id'];
125 $entityIdCount = (empty($record['entity_table']) ?
0 : 1)
126 +
(empty($record['entity_field']) ?
0 : 1)
127 +
(empty($record['entity_id']) ?
0 : 1);
128 if ($entityIdCount === 0) {
131 elseif ($entityIdCount < 3) {
132 $e->addError($r, $entityIdFields, 'full_entity', ts('Must specify all entity identification fields'));
135 $simpleName = '/^[a-zA-Z0-9_]+$/';
136 if (!preg_match($simpleName, $record['entity_table']) ||
!preg_match($simpleName, $record['entity_field']) ||
!is_numeric($record['entity_id'])) {
137 $e->addError($r, $entityIdFields, 'malformed_entity', ts('Entity reference is malformed'));
141 // Which fields support translation?
142 // - One could follow the same path as "Multilingual". Use
143 // $translatable = CRM_Core_I18n_SchemaStructure::columns();
144 // if (!isset($translatable[$record['entity_table']][$record['entity_field']])) {
145 // - Or, since we don't need schema-changes, we could be more generous and allow all freeform text fields...
147 $daoClass = CRM_Core_DAO_AllCoreTables
::getClassForTable($record['entity_table']);
149 $e->addError($r, 'entity_table', 'bad_table', ts('Entity reference specifies a non-existent or non-translatable table'));
153 $dao = new $daoClass();
154 $dao->id
= $record['entity_id'];
156 $field = $dao->getFieldSpec($record['entity_field']);
157 if (!$field ||
!in_array($field['type'] ??
'', $dataTypes) ||
!in_array($field['html']['type'] ??
'', $htmlTypes)) {
158 $e->addError($r, 'entity_field', 'bad_field', ts('Entity reference specifies a non-existent or non-translatable field'));
161 $e->addError($r, 'entity_id', 'nonexistent_id', ts('Entity does not exist'));