Metadata: Add html:label for Contribution Status ID
[civicrm-core.git] / CRM / Admin / Page / AJAX.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
18 /**
19 * This class contains all the function that are called using AJAX.
20 */
21 class CRM_Admin_Page_AJAX {
22
23 /**
24 * Outputs menubar data (json format) for the current user.
25 */
26 public static function navMenu() {
27 if (CRM_Core_Session::getLoggedInContactID()) {
28
29 $menu = CRM_Core_BAO_Navigation::buildNavigationTree();
30 CRM_Core_BAO_Navigation::buildHomeMenu($menu);
31 CRM_Utils_Hook::navigationMenu($menu);
32 CRM_Core_BAO_Navigation::fixNavigationMenu($menu);
33 CRM_Core_BAO_Navigation::orderByWeight($menu);
34 CRM_Core_BAO_Navigation::filterByPermission($menu);
35 self::formatMenuItems($menu);
36
37 $output = [
38 'menu' => $menu,
39 'search' => CRM_Utils_Array::makeNonAssociative(self::getSearchOptions()),
40 ];
41 // Encourage browsers to cache for a long time - 1 year
42 $ttl = 60 * 60 * 24 * 364;
43 CRM_Utils_System::setHttpHeader('Expires', gmdate('D, d M Y H:i:s \G\M\T', time() + $ttl));
44 CRM_Utils_System::setHttpHeader('Cache-Control', "max-age=$ttl, public");
45 CRM_Utils_System::setHttpHeader('Content-Type', 'application/json');
46 print (json_encode($output));
47 }
48 CRM_Utils_System::civiExit();
49 }
50
51 /**
52 * @param array $menu
53 */
54 public static function formatMenuItems(&$menu) {
55 foreach ($menu as $key => &$item) {
56 $props = $item['attributes'];
57 unset($item['attributes']);
58 if (!empty($props['separator'])) {
59 $item['separator'] = ($props['separator'] == 1 ? 'bottom' : 'top');
60 }
61 if (!empty($props['icon'])) {
62 $item['icon'] = $props['icon'];
63 }
64 if (!empty($props['attr'])) {
65 $item['attr'] = $props['attr'];
66 }
67 if (!empty($props['url'])) {
68 $item['url'] = CRM_Utils_System::evalUrl(CRM_Core_BAO_Navigation::makeFullyFormedUrl($props['url']));
69 }
70 if (!empty($props['label'])) {
71 $item['label'] = ts($props['label'], ['context' => 'menu']);
72 }
73 $item['name'] = !empty($props['name']) ? $props['name'] : CRM_Utils_String::munge(CRM_Utils_Array::value('label', $props));
74 if (!empty($item['child'])) {
75 self::formatMenuItems($item['child']);
76 }
77 }
78 $menu = array_values($menu);
79 }
80
81 public static function getSearchOptions() {
82 $searchOptions = Civi::settings()->get('quicksearch_options');
83 $labels = CRM_Core_SelectValues::quicksearchOptions();
84 $result = [];
85 foreach ($searchOptions as $key) {
86 $label = $labels[$key];
87 if (strpos($key, 'custom_') === 0) {
88 $key = 'custom_' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', substr($key, 7), 'id', 'name');
89 $label = array_slice(explode(': ', $label, 2), -1);
90 }
91 $result[$key] = $label;
92 }
93 return $result;
94 }
95
96 /**
97 * Process drag/move action for menu tree.
98 */
99 public static function menuTree() {
100 CRM_Core_BAO_Navigation::processNavigation($_GET);
101 }
102
103 /**
104 * Build status message while enabling/ disabling various objects.
105 */
106 public static function getStatusMsg() {
107 require_once 'api/v3/utils.php';
108 $recordID = CRM_Utils_Type::escape($_GET['id'], 'Integer');
109 $entity = CRM_Utils_Type::escape($_GET['entity'], 'String');
110 $ret = [];
111
112 if ($recordID && $entity && $recordBAO = _civicrm_api3_get_BAO($entity)) {
113 switch ($recordBAO) {
114 case 'CRM_Core_BAO_UFGroup':
115 $method = 'getUFJoinRecord';
116 $result = [$recordBAO, $method];
117 $ufJoin = call_user_func_array(($result), [$recordID, TRUE]);
118 if (!empty($ufJoin)) {
119 $ret['content'] = ts('This profile is currently used for %1.', [1 => implode(', ', $ufJoin)]) . ' <br/><br/>' . ts('If you disable the profile - it will be removed from these forms and/or modules. Do you want to continue?');
120 }
121 else {
122 $ret['content'] = ts('Are you sure you want to disable this profile?');
123 }
124 break;
125
126 case 'CRM_Price_BAO_PriceSet':
127 $usedBy = CRM_Price_BAO_PriceSet::getUsedBy($recordID);
128 $priceSet = CRM_Price_BAO_PriceSet::getTitle($recordID);
129
130 if (!CRM_Utils_System::isNull($usedBy)) {
131 $template = CRM_Core_Smarty::singleton();
132 $template->assign('usedBy', $usedBy);
133 $comps = [
134 'Event' => 'civicrm_event',
135 'Contribution' => 'civicrm_contribution_page',
136 'EventTemplate' => 'civicrm_event_template',
137 ];
138 $contexts = [];
139 foreach ($comps as $name => $table) {
140 if (array_key_exists($table, $usedBy)) {
141 $contexts[] = $name;
142 }
143 }
144 $template->assign('contexts', $contexts);
145
146 $ret['illegal'] = TRUE;
147 $table = $template->fetch('CRM/Price/Page/table.tpl');
148 $ret['content'] = ts('Unable to disable the \'%1\' price set - it is currently in use by one or more active events, contribution pages or contributions.', [
149 1 => $priceSet,
150 ]) . "<br/> $table";
151 }
152 else {
153 $ret['content'] = ts('Are you sure you want to disable \'%1\' Price Set?', [1 => $priceSet]);
154 }
155 break;
156
157 case 'CRM_Event_BAO_Event':
158 $ret['content'] = ts('Are you sure you want to disable this Event?');
159 break;
160
161 case 'CRM_Core_BAO_UFField':
162 $ret['content'] = ts('Are you sure you want to disable this CiviCRM Profile field?');
163 break;
164
165 case 'CRM_Contribute_BAO_Product':
166 $ret['content'] = ts('Are you sure you want to disable this premium? This action will remove the premium from any contribution pages that currently offer it. However it will not delete the premium record - so you can re-enable it and add it back to your contribution page(s) at a later time.');
167 break;
168
169 case 'CRM_Contact_BAO_Relationship':
170 $ret['content'] = ts('Are you sure you want to disable this relationship?');
171 break;
172
173 case 'CRM_Contact_BAO_RelationshipType':
174 $ret['content'] = ts('Are you sure you want to disable this relationship type?') . '<br/><br/>' . ts('Users will no longer be able to select this value when adding or editing relationships between contacts.');
175 break;
176
177 case 'CRM_Financial_BAO_FinancialType':
178 $ret['content'] = ts('Are you sure you want to disable this financial type?');
179 break;
180
181 case 'CRM_Financial_BAO_FinancialAccount':
182 if (!CRM_Financial_BAO_FinancialAccount::getARAccounts($recordID)) {
183 $ret['illegal'] = TRUE;
184 $ret['content'] = ts('The selected financial account cannot be disabled because at least one Accounts Receivable type account is required (to ensure that accounting transactions are in balance).');
185 }
186 else {
187 $ret['content'] = ts('Are you sure you want to disable this financial account?');
188 }
189 break;
190
191 case 'CRM_Financial_BAO_PaymentProcessor':
192 $ret['content'] = ts('Are you sure you want to disable this payment processor?') . ' <br/><br/>' . ts('Users will no longer be able to select this value when adding or editing transaction pages.');
193 break;
194
195 case 'CRM_Financial_BAO_PaymentProcessorType':
196 $ret['content'] = ts('Are you sure you want to disable this payment processor type?');
197 break;
198
199 case 'CRM_Core_BAO_LocationType':
200 $ret['content'] = ts('Are you sure you want to disable this location type?') . ' <br/><br/>' . ts('Users will no longer be able to select this value when adding or editing contact locations.');
201 break;
202
203 case 'CRM_Event_BAO_ParticipantStatusType':
204 $ret['content'] = ts('Are you sure you want to disable this Participant Status?') . '<br/><br/> ' . ts('Users will no longer be able to select this value when adding or editing Participant Status.');
205 break;
206
207 case 'CRM_Mailing_BAO_MailingComponent':
208 $ret['content'] = ts('Are you sure you want to disable this component?');
209 break;
210
211 case 'CRM_Core_BAO_CustomField':
212 $ret['content'] = ts('Are you sure you want to disable this custom data field?');
213 break;
214
215 case 'CRM_Core_BAO_CustomGroup':
216 $ret['content'] = ts('Are you sure you want to disable this custom data group? Any profile fields that are linked to custom fields of this group will be disabled.');
217 break;
218
219 case 'CRM_Core_BAO_MessageTemplate':
220 $ret['content'] = ts('Are you sure you want to disable this message tempate?');
221 break;
222
223 case 'CRM_ACL_BAO_ACL':
224 $ret['content'] = ts('Are you sure you want to disable this ACL?');
225 break;
226
227 case 'CRM_ACL_BAO_EntityRole':
228 $ret['content'] = ts('Are you sure you want to disable this ACL Role Assignment?');
229 break;
230
231 case 'CRM_Member_BAO_MembershipType':
232 $ret['content'] = ts('Are you sure you want to disable this membership type?');
233 break;
234
235 case 'CRM_Member_BAO_MembershipStatus':
236 $ret['content'] = ts('Are you sure you want to disable this membership status rule?');
237 break;
238
239 case 'CRM_Price_BAO_PriceField':
240 $ret['content'] = ts('Are you sure you want to disable this price field?');
241 break;
242
243 case 'CRM_Contact_BAO_Group':
244 $ret['content'] = ts('Are you sure you want to disable this Group?');
245 $ret['content'] .= '<br /><br /><strong>' . ts('WARNING - Disabling this group will disable all the child groups associated if any.') . '</strong>';
246 break;
247
248 case 'CRM_Core_BAO_OptionGroup':
249 $ret['content'] = ts('Are you sure you want to disable this Option?');
250 break;
251
252 case 'CRM_Contact_BAO_ContactType':
253 $ret['content'] = ts('Are you sure you want to disable this Contact Type?');
254 break;
255
256 case 'CRM_Core_BAO_OptionValue':
257 $label = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $recordID, 'label');
258 $ret['content'] = ts('Are you sure you want to disable the \'%1\' option ?', [1 => $label]);
259 $ret['content'] .= '<br /><br />' . ts('WARNING - Disabling an option which has been assigned to existing records will result in that option being cleared when the record is edited.');
260 break;
261
262 case 'CRM_Contribute_BAO_ContributionRecur':
263 $recurDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($recordID);
264 $ret['content'] = ts('Are you sure you want to mark this recurring contribution as cancelled?');
265 $ret['content'] .= '<br /><br /><strong>' . ts('WARNING - This action sets the CiviCRM recurring contribution status to Cancelled, but does NOT send a cancellation request to the payment processor. You will need to ensure that this recurring payment (subscription) is cancelled by the payment processor.') . '</strong>';
266 if ($recurDetails->membership_id) {
267 $ret['content'] .= '<br /><br /><strong>' . ts('This recurring contribution is linked to an auto-renew membership. If you cancel it, the associated membership will no longer renew automatically. However, the current membership status will not be affected.') . '</strong>';
268 }
269 break;
270
271 default:
272 $ret['content'] = ts('Are you sure you want to disable this record?');
273 break;
274 }
275 }
276 else {
277 $ret = ['status' => 'error', 'content' => 'Error: Unknown entity type.', 'illegal' => TRUE];
278 }
279 CRM_Core_Page_AJAX::returnJsonResponse($ret);
280 }
281
282 /**
283 * Get a list of mappings.
284 *
285 * This appears to be only used by scheduled reminders.
286 */
287 public static function mappingList() {
288 if (empty($_GET['mappingID'])) {
289 CRM_Utils_JSON::output(['status' => 'error', 'error_msg' => 'required params missing.']);
290 }
291
292 $mapping = CRM_Core_BAO_ActionSchedule::getMapping($_GET['mappingID']);
293 $dateFieldLabels = $mapping ? $mapping->getDateFields() : [];
294
295 // The UX here is quirky -- for "Activity" types, there's a simple drop "Recipients"
296 // dropdown which is always displayed. For other types, the "Recipients" drop down is
297 // conditional upon the weird isLimit ('Limit To / Also Include / Neither') dropdown.
298 $noThanksJustKidding = !$_GET['isLimit'];
299 if ($mapping instanceof CRM_Activity_ActionMapping || !$noThanksJustKidding) {
300 $entityRecipientLabels = $mapping ? ($mapping->getRecipientTypes() + CRM_Core_BAO_ActionSchedule::getAdditionalRecipients()) : [];
301 }
302 else {
303 $entityRecipientLabels = CRM_Core_BAO_ActionSchedule::getAdditionalRecipients();
304 }
305 $recipientMapping = array_combine(array_keys($entityRecipientLabels), array_keys($entityRecipientLabels));
306
307 $output = [
308 'sel4' => CRM_Utils_Array::makeNonAssociative($dateFieldLabels),
309 'sel5' => CRM_Utils_Array::makeNonAssociative($entityRecipientLabels),
310 'recipientMapping' => $recipientMapping,
311 ];
312
313 CRM_Utils_JSON::output($output);
314 }
315
316 /**
317 * (Scheduled Reminders) Get the list of possible recipient filters.
318 *
319 * Ex: GET /civicrm/ajax/recipientListing?mappingID=contribpage&recipientType=
320 */
321 public static function recipientListing() {
322 $mappingID = filter_input(INPUT_GET, 'mappingID', FILTER_VALIDATE_REGEXP, [
323 'options' => [
324 'regexp' => '/^[a-zA-Z0-9_\-]+$/',
325 ],
326 ]);
327 $recipientType = filter_input(INPUT_GET, 'recipientType', FILTER_VALIDATE_REGEXP, [
328 'options' => [
329 'regexp' => '/^[a-zA-Z0-9_\-]+$/',
330 ],
331 ]);
332
333 CRM_Utils_JSON::output([
334 'recipients' => CRM_Utils_Array::makeNonAssociative(CRM_Core_BAO_ActionSchedule::getRecipientListing($mappingID, $recipientType)),
335 ]);
336 }
337
338 /**
339 * Outputs one branch in the tag tree
340 *
341 * Used by jstree to incrementally load tags
342 */
343 public static function getTagTree() {
344 $parent = CRM_Utils_Type::escape(($_GET['parent_id'] ?? 0), 'Integer');
345 $substring = CRM_Utils_Type::escape(CRM_Utils_Array::value('str', $_GET), 'String');
346 $result = [];
347
348 $whereClauses = ['is_tagset <> 1'];
349 $orderColumn = 'name';
350
351 // fetch all child tags in Array('parent_tag' => array('child_tag_1', 'child_tag_2', ...)) format
352 $childTagIDs = CRM_Core_BAO_Tag::getChildTags($substring);
353 $parentIDs = array_keys($childTagIDs);
354
355 if ($parent) {
356 $whereClauses[] = "parent_id = $parent";
357 }
358 elseif ($substring) {
359 $whereClauses['substring'] = " name LIKE '%$substring%' ";
360 if (!empty($parentIDs)) {
361 $whereClauses['substring'] = sprintf(" %s OR id IN (%s) ", $whereClauses['substring'], implode(',', $parentIDs));
362 }
363 $orderColumn = 'id';
364 }
365 else {
366 $whereClauses[] = "parent_id IS NULL";
367 }
368
369 $dao = CRM_Utils_SQL_Select::from('civicrm_tag')
370 ->where($whereClauses)
371 ->groupBy('id')
372 ->orderBy($orderColumn)
373 ->execute();
374
375 while ($dao->fetch()) {
376 if (!empty($substring)) {
377 $result[] = $dao->id;
378 if (!empty($childTagIDs[$dao->id])) {
379 $result = array_merge($result, $childTagIDs[$dao->id]);
380 }
381 }
382 else {
383 $hasChildTags = !empty($childTagIDs[$dao->id]);
384 $usedFor = (array) explode(',', $dao->used_for);
385 $tag = [
386 'id' => $dao->id,
387 'text' => $dao->name,
388 'a_attr' => [
389 'class' => 'crm-tag-item',
390 ],
391 'children' => $hasChildTags,
392 'data' => [
393 'description' => (string) $dao->description,
394 'is_selectable' => (bool) $dao->is_selectable,
395 'is_reserved' => (bool) $dao->is_reserved,
396 'used_for' => $usedFor,
397 'color' => $dao->color ? $dao->color : '#ffffff',
398 'usages' => civicrm_api3('EntityTag', 'getcount', [
399 'entity_table' => ['IN' => $usedFor],
400 'tag_id' => $dao->id,
401 ]),
402 ],
403 ];
404 if ($dao->description || $dao->is_reserved) {
405 $tag['li_attr']['title'] = ((string) $dao->description) . ($dao->is_reserved ? ' (*' . ts('Reserved') . ')' : '');
406 }
407 if ($dao->is_reserved) {
408 $tag['li_attr']['class'] = 'is-reserved';
409 }
410 if ($dao->color) {
411 $tag['a_attr']['style'] = "background-color: {$dao->color}; color: " . CRM_Utils_Color::getContrast($dao->color);
412 }
413 $result[] = $tag;
414 }
415 }
416
417 if ($substring) {
418 $result = array_values(array_unique($result));
419 }
420
421 if (!empty($_REQUEST['is_unit_test'])) {
422 return $result;
423 }
424
425 CRM_Utils_JSON::output($result);
426 }
427
428 }