[REF] Extract the code to determine the DAO name into a functions
[civicrm-core.git] / CRM / Case / XMLRepository.php
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 * The XMLRepository is responsible for loading XML for case-types.
17 * It includes any bulk operations that apply across the list of all XML
18 * documents of all case-types.
19 */
20 class CRM_Case_XMLRepository {
21 private static $singleton;
22
23 /**
24 * @var array
25 * <String,SimpleXMLElement>
26 */
27 protected $xml = [];
28
29 /**
30 * @var array|null
31 */
32 protected $hookCache = NULL;
33
34 /**
35 * Symbolic names of case-types.
36 *
37 * @var array|null
38 */
39 protected $allCaseTypes = NULL;
40
41 /**
42 * @param bool $fresh
43 * @return CRM_Case_XMLRepository
44 */
45 public static function singleton($fresh = FALSE) {
46 if (!self::$singleton || $fresh) {
47 self::$singleton = new static();
48 }
49 return self::$singleton;
50 }
51
52 public function flush() {
53 $this->xml = [];
54 $this->hookCache = NULL;
55 $this->allCaseTypes = NULL;
56 CRM_Core_DAO::$_dbColumnValueCache = [];
57 }
58
59 /**
60 * Class constructor.
61 *
62 * @param array $allCaseTypes
63 * @param array $xml
64 */
65 public function __construct($allCaseTypes = NULL, $xml = []) {
66 $this->allCaseTypes = $allCaseTypes;
67 $this->xml = $xml;
68 }
69
70 /**
71 * Retrieve case.
72 *
73 * @param string $caseType
74 *
75 * @return FALSE|\SimpleXMLElement
76 * @throws \CRM_Core_Exception
77 */
78 public function retrieve($caseType) {
79 // check if xml definition is defined in db
80 $definition = CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseType, 'definition', 'name');
81
82 if (!empty($definition)) {
83 list ($xml, $error) = CRM_Utils_XML::parseString($definition);
84 if (!$xml) {
85 throw new CRM_Core_Exception("Failed to parse CaseType XML: $error");
86 }
87 return $xml;
88 }
89
90 // TODO In 4.6 or 5.0, remove support for weird machine-names
91 //if (!CRM_Case_BAO_CaseType::isValidName($caseType)) {
92 // // perhaps caller provider a the label instead of the name?
93 // throw new CRM_Core_Exception("Cannot load caseType with malformed name [$caseType]");
94 //}
95
96 if (empty($this->xml[$caseType])) {
97 $fileXml = $this->retrieveFile($caseType);
98 if ($fileXml) {
99 $this->xml[$caseType] = $fileXml;
100 }
101 else {
102 return FALSE;
103 }
104 }
105 return $this->xml[$caseType];
106 }
107
108 /**
109 * Retrieve file.
110 *
111 * @param string $caseType
112 * @return SimpleXMLElement|FALSE
113 */
114 public function retrieveFile($caseType) {
115 $fileName = NULL;
116 $fileXml = NULL;
117
118 if (CRM_Case_BAO_CaseType::isValidName($caseType)) {
119 // Search for a file based directly on the $caseType name
120 $fileName = $this->findXmlFile($caseType);
121 }
122
123 // For backward compatibility, also search for double-munged file names
124 // TODO In 4.6 or 5.0, remove support for loading double-munged file names
125 if (!$fileName || !file_exists($fileName)) {
126 $fileName = $this->findXmlFile(CRM_Case_XMLProcessor::mungeCaseType($caseType));
127 }
128
129 if ($fileName && file_exists($fileName)) {
130 // read xml file
131 $dom = new DomDocument();
132 $xmlString = file_get_contents($fileName);
133 $dom->loadXML($xmlString);
134 $dom->documentURI = $fileName;
135 $dom->xinclude();
136 $fileXml = simplexml_import_dom($dom);
137 }
138
139 return $fileXml;
140 }
141
142 /**
143 * Find xml file.
144 *
145 * @param string $caseType
146 * @return null|string
147 * file path
148 */
149 public function findXmlFile($caseType) {
150 // first check custom templates directory
151 $fileName = NULL;
152
153 if (!$fileName || !file_exists($fileName)) {
154 $caseTypesViaHook = $this->getCaseTypesViaHook();
155 if (isset($caseTypesViaHook[$caseType], $caseTypesViaHook[$caseType]['file'])) {
156 $fileName = $caseTypesViaHook[$caseType]['file'];
157 }
158 }
159
160 if (!$fileName || !file_exists($fileName)) {
161 $config = CRM_Core_Config::singleton();
162 if (isset($config->customTemplateDir) && $config->customTemplateDir) {
163 // check if the file exists in the custom templates directory
164 $fileName = implode(DIRECTORY_SEPARATOR,
165 [
166 $config->customTemplateDir,
167 'CRM',
168 'Case',
169 'xml',
170 'configuration',
171 "$caseType.xml",
172 ]
173 );
174 }
175 }
176
177 if (!$fileName || !file_exists($fileName)) {
178 if (!file_exists($fileName)) {
179 // check if file exists locally
180 $fileName = implode(DIRECTORY_SEPARATOR,
181 [
182 dirname(__FILE__),
183 'xml',
184 'configuration',
185 "$caseType.xml",
186 ]
187 );
188 }
189
190 if (!file_exists($fileName)) {
191 // check if file exists locally
192 $fileName = implode(DIRECTORY_SEPARATOR,
193 [
194 dirname(__FILE__),
195 'xml',
196 'configuration.sample',
197 "$caseType.xml",
198 ]
199 );
200 }
201 }
202 return file_exists($fileName) ? $fileName : NULL;
203 }
204
205 /**
206 * @return array
207 * @see CRM_Utils_Hook::caseTypes
208 */
209 public function getCaseTypesViaHook() {
210 if ($this->hookCache === NULL) {
211 $this->hookCache = [];
212 CRM_Utils_Hook::caseTypes($this->hookCache);
213 }
214 return $this->hookCache;
215 }
216
217 /**
218 * @return array<string> symbolic names of case-types
219 */
220 public function getAllCaseTypes() {
221 if ($this->allCaseTypes === NULL) {
222 $this->allCaseTypes = CRM_Case_PseudoConstant::caseType("name");
223 }
224 return $this->allCaseTypes;
225 }
226
227 /**
228 * @return array<string> symbolic-names of activity-types
229 */
230 public function getAllDeclaredActivityTypes() {
231 $result = [];
232
233 $p = new CRM_Case_XMLProcessor_Process();
234 foreach ($this->getAllCaseTypes() as $caseTypeName) {
235 $caseTypeXML = $this->retrieve($caseTypeName);
236 $result = array_merge($result, $p->getDeclaredActivityTypes($caseTypeXML));
237 }
238
239 $result = array_unique($result);
240 sort($result);
241 return $result;
242 }
243
244 /**
245 * Relationships are straight from XML, described from perspective of non-client
246 *
247 * @return array<string> symbolic-names of relationship-types
248 */
249 public function getAllDeclaredRelationshipTypes() {
250 $result = [];
251
252 $p = new CRM_Case_XMLProcessor_Process();
253 foreach ($this->getAllCaseTypes() as $caseTypeName) {
254 $caseTypeXML = $this->retrieve($caseTypeName);
255 $result = array_merge($result, $p->getDeclaredRelationshipTypes($caseTypeXML));
256 }
257
258 $result = array_unique($result);
259 sort($result);
260 return $result;
261 }
262
263 /**
264 * Determine the number of times a particular activity-type is
265 * referenced in CiviCase XML.
266 *
267 * @param string $activityType
268 * Symbolic-name of an activity type.
269 * @return int
270 */
271 public function getActivityReferenceCount($activityType) {
272 $p = new CRM_Case_XMLProcessor_Process();
273 $count = 0;
274 foreach ($this->getAllCaseTypes() as $caseTypeName) {
275 $caseTypeXML = $this->retrieve($caseTypeName);
276 if (in_array($activityType, $p->getDeclaredActivityTypes($caseTypeXML))) {
277 $count++;
278 }
279 }
280 return $count;
281 }
282
283 /**
284 * Determine the number of times a particular activity-type is
285 * referenced in CiviCase XML.
286 *
287 * @param string $relationshipTypeName
288 * Symbolic-name of a relationship-type.
289 * @return int
290 */
291 public function getRelationshipReferenceCount($relationshipTypeName) {
292 $p = new CRM_Case_XMLProcessor_Process();
293 $count = 0;
294 foreach ($this->getAllCaseTypes() as $caseTypeName) {
295 $caseTypeXML = $this->retrieve($caseTypeName);
296 if (in_array($relationshipTypeName, $p->getDeclaredRelationshipTypes($caseTypeXML))) {
297 $count++;
298 }
299 }
300 return $count;
301 }
302
303 }