Merge pull request #23939 from civicrm/5.51
[civicrm-core.git] / CRM / Core / BAO / Translation.php
CommitLineData
fd1ea018
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
10 */
11
12/**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
ee44263e 17class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements \Civi\Core\HookInterface {
7782deca
TO
18
19 use CRM_Core_DynamicFKAccessTrait;
fd1ea018
TO
20
21 /**
22 * Get a list of valid statuses for translated-strings.
23 *
815facd4 24 * @return array[]
fd1ea018 25 */
815facd4
CW
26 public static function getStatuses() {
27 return [
fd1ea018
TO
28 ['id' => 1, 'name' => 'active', 'label' => ts('Active')],
29 ['id' => 2, 'name' => 'draft', 'label' => ts('Draft')],
30 ];
fd1ea018
TO
31 }
32
33 /**
34 * Get a list of tables with translatable strings.
35 *
36 * @return string[]
37 * Ex: ['civicrm_event' => 'civicrm_event']
38 */
39 public static function getEntityTables() {
40 if (!isset(Civi::$statics[__CLASS__]['allTables'])) {
41 $tables = array_keys(self::getTranslatedFields());
42 Civi::$statics[__CLASS__]['allTables'] = array_combine($tables, $tables);
43 }
44 return Civi::$statics[__CLASS__]['allTables'];
45 }
46
47 /**
48 * Get a list of fields with translatable strings.
49 *
50 * @return string[]
51 * Ex: ['title' => 'title', 'description' => 'description']
52 */
53 public static function getEntityFields() {
54 if (!isset(Civi::$statics[__CLASS__]['allFields'])) {
55 $allFields = [];
56 foreach (self::getTranslatedFields() as $columns) {
57 foreach ($columns as $column => $sqlExpr) {
58 $allFields[$column] = $column;
59 }
60 }
61 Civi::$statics[__CLASS__]['allFields'] = $allFields;
62 }
63 return Civi::$statics[__CLASS__]['allFields'];
64 }
65
fd1ea018
TO
66 /**
67 * @return array
68 * List of data fields to translate, organized by table and column.
69 * Omitted/unlisted fields are not translated. Any listed field may be translated.
70 * Values should be TRUE.
71 * Ex: $fields['civicrm_event']['summary'] = TRUE
72 */
73 public static function getTranslatedFields() {
74 $key = 'translatedFields';
75 $cache = Civi::cache('fields');
76 if (($r = $cache->get($key)) !== NULL) {
77 return $r;
78 }
79
80 $f = [];
81 \CRM_Utils_Hook::translateFields($f);
82
83 // Future: Assimilate defaults originating in XML (incl extension-entities)
84 // e.g. CRM_Core_I18n_SchemaStructure::columns() will grab core fields
85
86 $cache->set($key, $f);
87 return $f;
88 }
89
7782deca
TO
90 /**
91 * When manipulating strings via the `Translation` entity (APIv4), ensure that the references are well-formed.
92 *
93 * @param \Civi\Api4\Event\ValidateValuesEvent $e
94 */
95 public static function self_civi_api4_validate(\Civi\Api4\Event\ValidateValuesEvent $e) {
815facd4 96 $statuses = array_column(self::getStatuses(), 'id');
7782deca 97 $dataTypes = [CRM_Utils_Type::T_STRING, CRM_Utils_Type::T_TEXT, CRM_Utils_Type::T_LONGTEXT];
9ac45bdf 98 $htmlTypes = ['Text', 'TextArea', 'RichTextEditor', ''];
7782deca
TO
99
100 foreach ($e->records as $r => $record) {
815facd4 101 if (array_key_exists('status_id', $record) && !in_array($record['status_id'], $statuses)) {
7782deca
TO
102 $e->addError($r, 'status_id', 'invalid', ts('Invalid status'));
103 }
104
105 $entityIdFields = ['entity_table', 'entity_field', 'entity_id'];
106 $entityIdCount = (empty($record['entity_table']) ? 0 : 1)
107 + (empty($record['entity_field']) ? 0 : 1)
108 + (empty($record['entity_id']) ? 0 : 1);
109 if ($entityIdCount === 0) {
110 continue;
111 }
112 elseif ($entityIdCount < 3) {
113 $e->addError($r, $entityIdFields, 'full_entity', ts('Must specify all entity identification fields'));
114 }
115
116 $simpleName = '/^[a-zA-Z0-9_]+$/';
117 if (!preg_match($simpleName, $record['entity_table']) || !preg_match($simpleName, $record['entity_field']) || !is_numeric($record['entity_id'])) {
118 $e->addError($r, $entityIdFields, 'malformed_entity', ts('Entity reference is malformed'));
119 continue;
120 }
121
122 // Which fields support translation?
123 // - One could follow the same path as "Multilingual". Use
124 // $translatable = CRM_Core_I18n_SchemaStructure::columns();
125 // if (!isset($translatable[$record['entity_table']][$record['entity_field']])) {
126 // - Or, since we don't need schema-changes, we could be more generous and allow all freeform text fields...
127
128 $daoClass = CRM_Core_DAO_AllCoreTables::getClassForTable($record['entity_table']);
129 if (!$daoClass) {
130 $e->addError($r, 'entity_table', 'bad_table', ts('Entity reference specifies a non-existent or non-translatable table'));
131 continue;
132 }
133
134 $dao = new $daoClass();
135 $dao->id = $record['entity_id'];
136
137 $field = $dao->getFieldSpec($record['entity_field']);
138 if (!$field || !in_array($field['type'] ?? '', $dataTypes) || !in_array($field['html']['type'] ?? '', $htmlTypes)) {
139 $e->addError($r, 'entity_field', 'bad_field', ts('Entity reference specifies a non-existent or non-translatable field'));
140 }
141 if (!$dao->find()) {
142 $e->addError($r, 'entity_id', 'nonexistent_id', ts('Entity does not exist'));
143 }
144 }
145 }
146
fd1ea018 147}