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