Merge pull request #21644 from sunilpawar/processed_token
[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 */
7782deca
TO
17class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements \Civi\Test\HookInterface {
18
19 use CRM_Core_DynamicFKAccessTrait;
fd1ea018
TO
20
21 /**
22 * Get a list of valid statuses for translated-strings.
23 *
24 * @return string[]
25 */
26 public static function getStatuses($context = NULL) {
27 $options = [
28 ['id' => 1, 'name' => 'active', 'label' => ts('Active')],
29 ['id' => 2, 'name' => 'draft', 'label' => ts('Draft')],
30 ];
31 return self::formatPsuedoconstant($context, $options);
32 }
33
34 /**
35 * Get a list of tables with translatable strings.
36 *
37 * @return string[]
38 * Ex: ['civicrm_event' => 'civicrm_event']
39 */
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);
44 }
45 return Civi::$statics[__CLASS__]['allTables'];
46 }
47
48 /**
49 * Get a list of fields with translatable strings.
50 *
51 * @return string[]
52 * Ex: ['title' => 'title', 'description' => 'description']
53 */
54 public static function getEntityFields() {
55 if (!isset(Civi::$statics[__CLASS__]['allFields'])) {
56 $allFields = [];
57 foreach (self::getTranslatedFields() as $columns) {
58 foreach ($columns as $column => $sqlExpr) {
59 $allFields[$column] = $column;
60 }
61 }
62 Civi::$statics[__CLASS__]['allFields'] = $allFields;
63 }
64 return Civi::$statics[__CLASS__]['allFields'];
65 }
66
67 /**
68 * Given a constant list of of id/name/label options, convert to the
69 * format required by pseudoconstant.
70 *
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']]
75 *
76 * @return array|false
77 */
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));
83 }
84
85 /**
86 * @return array
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
91 */
92 public static function getTranslatedFields() {
93 $key = 'translatedFields';
94 $cache = Civi::cache('fields');
95 if (($r = $cache->get($key)) !== NULL) {
96 return $r;
97 }
98
99 $f = [];
100 \CRM_Utils_Hook::translateFields($f);
101
102 // Future: Assimilate defaults originating in XML (incl extension-entities)
103 // e.g. CRM_Core_I18n_SchemaStructure::columns() will grab core fields
104
105 $cache->set($key, $f);
106 return $f;
107 }
108
7782deca
TO
109 /**
110 * When manipulating strings via the `Translation` entity (APIv4), ensure that the references are well-formed.
111 *
112 * @param \Civi\Api4\Event\ValidateValuesEvent $e
113 */
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];
9ac45bdf 117 $htmlTypes = ['Text', 'TextArea', 'RichTextEditor', ''];
7782deca
TO
118
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'));
122 }
123
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) {
129 continue;
130 }
131 elseif ($entityIdCount < 3) {
132 $e->addError($r, $entityIdFields, 'full_entity', ts('Must specify all entity identification fields'));
133 }
134
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'));
138 continue;
139 }
140
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...
146
147 $daoClass = CRM_Core_DAO_AllCoreTables::getClassForTable($record['entity_table']);
148 if (!$daoClass) {
149 $e->addError($r, 'entity_table', 'bad_table', ts('Entity reference specifies a non-existent or non-translatable table'));
150 continue;
151 }
152
153 $dao = new $daoClass();
154 $dao->id = $record['entity_id'];
155
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'));
159 }
160 if (!$dao->find()) {
161 $e->addError($r, 'entity_id', 'nonexistent_id', ts('Entity does not exist'));
162 }
163 }
164 }
165
fd1ea018 166}