Merge pull request #23509 from eileenmcnaughton/import_except
[civicrm-core.git] / CRM / Admin / Page / AJAX.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
ce064e4f 19 * This class contains all the function that are called using AJAX.
6a488035
TO
20 */
21class CRM_Admin_Page_AJAX {
22
23 /**
b30809e4 24 * Outputs menubar data (json format) for the current user.
73538697 25 */
b30809e4
CW
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));
73538697 47 }
0a15daa2 48 CRM_Utils_System::civiExit();
73538697
CW
49 }
50
b30809e4
CW
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
4235341b 81 public static function getSearchOptions() {
4e086328 82 $searchOptions = Civi::settings()->get('quicksearch_options');
b30809e4
CW
83 $labels = CRM_Core_SelectValues::quicksearchOptions();
84 $result = [];
85 foreach ($searchOptions as $key) {
86 $label = $labels[$key];
4235341b 87 if (strpos($key, 'custom_') === 0) {
b30809e4
CW
88 $key = 'custom_' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', substr($key, 7), 'id', 'name');
89 $label = array_slice(explode(': ', $label, 2), -1);
4235341b 90 }
b30809e4 91 $result[$key] = $label;
4235341b 92 }
b30809e4 93 return $result;
4235341b
CW
94 }
95
6a488035 96 /**
ce064e4f 97 * Process drag/move action for menu tree.
6a488035 98 */
00be9182 99 public static function menuTree() {
73538697 100 CRM_Core_BAO_Navigation::processNavigation($_GET);
6a488035
TO
101 }
102
103 /**
ce064e4f 104 * Build status message while enabling/ disabling various objects.
6a488035 105 */
00be9182 106 public static function getStatusMsg() {
02fc859b 107 require_once 'api/v3/utils.php';
353ffa53 108 $recordID = CRM_Utils_Type::escape($_GET['id'], 'Integer');
12798ddc 109 $entity = CRM_Utils_Type::escape($_GET['entity'], 'String');
be2fb01f 110 $ret = [];
6a488035 111
4d17a233 112 if ($recordID && $entity && $recordBAO = _civicrm_api3_get_BAO($entity)) {
6a488035
TO
113 switch ($recordBAO) {
114 case 'CRM_Core_BAO_UFGroup':
6a488035 115 $method = 'getUFJoinRecord';
be2fb01f
CW
116 $result = [$recordBAO, $method];
117 $ufJoin = call_user_func_array(($result), [$recordID, TRUE]);
6a488035 118 if (!empty($ufJoin)) {
be2fb01f 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?');
6a488035
TO
120 }
121 else {
4d17a233 122 $ret['content'] = ts('Are you sure you want to disable this profile?');
6a488035
TO
123 }
124 break;
125
9da8dc8c 126 case 'CRM_Price_BAO_PriceSet':
9da8dc8c 127 $usedBy = CRM_Price_BAO_PriceSet::getUsedBy($recordID);
128 $priceSet = CRM_Price_BAO_PriceSet::getTitle($recordID);
6a488035
TO
129
130 if (!CRM_Utils_System::isNull($usedBy)) {
131 $template = CRM_Core_Smarty::singleton();
132 $template->assign('usedBy', $usedBy);
be2fb01f 133 $comps = [
6a488035
TO
134 'Event' => 'civicrm_event',
135 'Contribution' => 'civicrm_contribution_page',
21dfd5f5 136 'EventTemplate' => 'civicrm_event_template',
be2fb01f
CW
137 ];
138 $contexts = [];
6a488035
TO
139 foreach ($comps as $name => $table) {
140 if (array_key_exists($table, $usedBy)) {
141 $contexts[] = $name;
142 }
143 }
144 $template->assign('contexts', $contexts);
145
4d17a233 146 $ret['illegal'] = TRUE;
353ffa53 147 $table = $template->fetch('CRM/Price/Page/table.tpl');
be2fb01f 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.', [
0d48f1cc
TO
149 1 => $priceSet,
150 ]) . "<br/> $table";
6a488035
TO
151 }
152 else {
be2fb01f 153 $ret['content'] = ts('Are you sure you want to disable \'%1\' Price Set?', [1 => $priceSet]);
6a488035
TO
154 }
155 break;
156
157 case 'CRM_Event_BAO_Event':
4d17a233 158 $ret['content'] = ts('Are you sure you want to disable this Event?');
6a488035
TO
159 break;
160
161 case 'CRM_Core_BAO_UFField':
4d17a233 162 $ret['content'] = ts('Are you sure you want to disable this CiviCRM Profile field?');
6a488035
TO
163 break;
164
37828d4f 165 case 'CRM_Contribute_BAO_Product':
4d17a233 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.');
6a488035
TO
167 break;
168
6e4cdf9c
CW
169 case 'CRM_Contact_BAO_Relationship':
170 $ret['content'] = ts('Are you sure you want to disable this relationship?');
171 break;
172
6a488035 173 case 'CRM_Contact_BAO_RelationshipType':
4d17a233 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.');
6a488035
TO
175 break;
176
7b3622bf 177 case 'CRM_Financial_BAO_FinancialType':
4d17a233 178 $ret['content'] = ts('Are you sure you want to disable this financial type?');
6a488035 179 break;
8ef12e64 180
7b3622bf
PN
181 case 'CRM_Financial_BAO_FinancialAccount':
182 if (!CRM_Financial_BAO_FinancialAccount::getARAccounts($recordID)) {
4d17a233
CW
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).');
7b3622bf
PN
185 }
186 else {
4d17a233 187 $ret['content'] = ts('Are you sure you want to disable this financial account?');
7b3622bf
PN
188 }
189 break;
6a488035 190
8ef12e64 191 case 'CRM_Financial_BAO_PaymentProcessor':
4d17a233 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.');
6a488035
TO
193 break;
194
195 case 'CRM_Financial_BAO_PaymentProcessorType':
4d17a233 196 $ret['content'] = ts('Are you sure you want to disable this payment processor type?');
6a488035
TO
197 break;
198
199 case 'CRM_Core_BAO_LocationType':
4d17a233 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.');
6a488035
TO
201 break;
202
203 case 'CRM_Event_BAO_ParticipantStatusType':
4d17a233 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.');
6a488035
TO
205 break;
206
4825de4a 207 case 'CRM_Mailing_BAO_MailingComponent':
4d17a233 208 $ret['content'] = ts('Are you sure you want to disable this component?');
6a488035
TO
209 break;
210
211 case 'CRM_Core_BAO_CustomField':
4d17a233 212 $ret['content'] = ts('Are you sure you want to disable this custom data field?');
6a488035
TO
213 break;
214
215 case 'CRM_Core_BAO_CustomGroup':
4d17a233 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.');
6a488035
TO
217 break;
218
c6327d7d 219 case 'CRM_Core_BAO_MessageTemplate':
4d17a233 220 $ret['content'] = ts('Are you sure you want to disable this message tempate?');
6a488035
TO
221 break;
222
223 case 'CRM_ACL_BAO_ACL':
4d17a233 224 $ret['content'] = ts('Are you sure you want to disable this ACL?');
6a488035
TO
225 break;
226
7d9f34bf 227 case 'CRM_ACL_BAO_ACLEntityRole':
4d17a233 228 $ret['content'] = ts('Are you sure you want to disable this ACL Role Assignment?');
6a488035
TO
229 break;
230
231 case 'CRM_Member_BAO_MembershipType':
4d17a233 232 $ret['content'] = ts('Are you sure you want to disable this membership type?');
6a488035
TO
233 break;
234
235 case 'CRM_Member_BAO_MembershipStatus':
4d17a233 236 $ret['content'] = ts('Are you sure you want to disable this membership status rule?');
6a488035
TO
237 break;
238
9da8dc8c 239 case 'CRM_Price_BAO_PriceField':
4d17a233 240 $ret['content'] = ts('Are you sure you want to disable this price field?');
6a488035
TO
241 break;
242
243 case 'CRM_Contact_BAO_Group':
4d17a233 244 $ret['content'] = ts('Are you sure you want to disable this Group?');
240c0ebd 245 $ret['content'] .= '<br /><br /><strong>' . ts('WARNING - Disabling this group will disable all the child groups associated if any.') . '</strong>';
6a488035
TO
246 break;
247
248 case 'CRM_Core_BAO_OptionGroup':
4d17a233 249 $ret['content'] = ts('Are you sure you want to disable this Option?');
6a488035
TO
250 break;
251
252 case 'CRM_Contact_BAO_ContactType':
4d17a233 253 $ret['content'] = ts('Are you sure you want to disable this Contact Type?');
6a488035
TO
254 break;
255
256 case 'CRM_Core_BAO_OptionValue':
6a488035 257 $label = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $recordID, 'label');
be2fb01f 258 $ret['content'] = ts('Are you sure you want to disable the \'%1\' option ?', [1 => $label]);
4d17a233 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.');
6a488035
TO
260 break;
261
262 case 'CRM_Contribute_BAO_ContributionRecur':
263 $recurDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($recordID);
4d17a233
CW
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>';
6a488035 266 if ($recurDetails->membership_id) {
4d17a233 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>';
6a488035
TO
268 }
269 break;
8ef12e64 270
6a488035 271 default:
4d17a233 272 $ret['content'] = ts('Are you sure you want to disable this record?');
6a488035
TO
273 break;
274 }
275 }
4d17a233 276 else {
be2fb01f 277 $ret = ['status' => 'error', 'content' => 'Error: Unknown entity type.', 'illegal' => TRUE];
4d17a233
CW
278 }
279 CRM_Core_Page_AJAX::returnJsonResponse($ret);
6a488035
TO
280 }
281
f8b84800 282 /**
283 * Get a list of mappings.
284 *
285 * This appears to be only used by scheduled reminders.
286 */
62d3ee27 287 public static function mappingList() {
581c7be2 288 if (empty($_GET['mappingID'])) {
be2fb01f 289 CRM_Utils_JSON::output(['status' => 'error', 'error_msg' => 'required params missing.']);
6a488035
TO
290 }
291
0905ee5d 292 $mapping = CRM_Core_BAO_ActionSchedule::getMapping($_GET['mappingID']);
be2fb01f 293 $dateFieldLabels = $mapping ? $mapping->getDateFields() : [];
9d97a648
TO
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) {
be2fb01f 300 $entityRecipientLabels = $mapping ? ($mapping->getRecipientTypes() + CRM_Core_BAO_ActionSchedule::getAdditionalRecipients()) : [];
9d97a648
TO
301 }
302 else {
303 $entityRecipientLabels = CRM_Core_BAO_ActionSchedule::getAdditionalRecipients();
304 }
0905ee5d 305 $recipientMapping = array_combine(array_keys($entityRecipientLabels), array_keys($entityRecipientLabels));
6a488035 306
be2fb01f 307 $output = [
dde482e0
CW
308 'sel4' => CRM_Utils_Array::makeNonAssociative($dateFieldLabels),
309 'sel5' => CRM_Utils_Array::makeNonAssociative($entityRecipientLabels),
0905ee5d 310 'recipientMapping' => $recipientMapping,
be2fb01f 311 ];
6a488035 312
581c7be2 313 CRM_Utils_JSON::output($output);
6a488035
TO
314 }
315
bf2ce887
TO
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() {
be2fb01f
CW
322 $mappingID = filter_input(INPUT_GET, 'mappingID', FILTER_VALIDATE_REGEXP, [
323 'options' => [
bf2ce887 324 'regexp' => '/^[a-zA-Z0-9_\-]+$/',
be2fb01f
CW
325 ],
326 ]);
327 $recipientType = filter_input(INPUT_GET, 'recipientType', FILTER_VALIDATE_REGEXP, [
328 'options' => [
bf2ce887 329 'regexp' => '/^[a-zA-Z0-9_\-]+$/',
be2fb01f
CW
330 ],
331 ]);
bf2ce887 332
be2fb01f 333 CRM_Utils_JSON::output([
dde482e0 334 'recipients' => CRM_Utils_Array::makeNonAssociative(CRM_Core_BAO_ActionSchedule::getRecipientListing($mappingID, $recipientType)),
be2fb01f 335 ]);
bf2ce887
TO
336 }
337
d3cbd0a5
CW
338 /**
339 * Outputs one branch in the tag tree
340 *
341 * Used by jstree to incrementally load tags
342 */
343 public static function getTagTree() {
2975f0aa 344 $parent = CRM_Utils_Type::escape(($_GET['parent_id'] ?? 0), 'Integer');
6e80ee11 345 $substring = CRM_Utils_Type::escape(CRM_Utils_Array::value('str', $_GET), 'String');
be2fb01f 346 $result = [];
d3cbd0a5 347
be2fb01f 348 $whereClauses = ['is_tagset <> 1'];
97dcf1d8 349 $orderColumn = 'name';
dfe61fcf 350
351 // fetch all child tags in Array('parent_tag' => array('child_tag_1', 'child_tag_2', ...)) format
6e80ee11 352 $childTagIDs = CRM_Core_BAO_Tag::getChildTags($substring);
353 $parentIDs = array_keys($childTagIDs);
354
97dcf1d8 355 if ($parent) {
356 $whereClauses[] = "parent_id = $parent";
357 }
358 elseif ($substring) {
6e80ee11 359 $whereClauses['substring'] = " name LIKE '%$substring%' ";
360 if (!empty($parentIDs)) {
97dcf1d8 361 $whereClauses['substring'] = sprintf(" %s OR id IN (%s) ", $whereClauses['substring'], implode(',', $parentIDs));
6e80ee11 362 }
97dcf1d8 363 $orderColumn = 'id';
364 }
365 else {
366 $whereClauses[] = "parent_id IS NULL";
6e80ee11 367 }
368
369 $dao = CRM_Utils_SQL_Select::from('civicrm_tag')
62d3ee27
SL
370 ->where($whereClauses)
371 ->groupBy('id')
372 ->orderBy($orderColumn)
373 ->execute();
dfe61fcf 374
d3cbd0a5 375 while ($dao->fetch()) {
6e80ee11 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 {
91768280 383 $hasChildTags = !empty($childTagIDs[$dao->id]);
6e80ee11 384 $usedFor = (array) explode(',', $dao->used_for);
28dd94bd 385 $tag = [
6e80ee11 386 'id' => $dao->id,
387 'text' => $dao->name,
28dd94bd 388 'a_attr' => [
6e80ee11 389 'class' => 'crm-tag-item',
28dd94bd 390 ],
6e80ee11 391 'children' => $hasChildTags,
28dd94bd 392 'data' => [
6e80ee11 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',
28dd94bd
CW
398 'usages' => civicrm_api3('EntityTag', 'getcount', [
399 'entity_table' => ['IN' => $usedFor],
6e80ee11 400 'tag_id' => $dao->id,
28dd94bd
CW
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;
d3cbd0a5 414 }
d3cbd0a5
CW
415 }
416
97dcf1d8 417 if ($substring) {
418 $result = array_values(array_unique($result));
419 }
420
dfe61fcf 421 if (!empty($_REQUEST['is_unit_test'])) {
422 return $result;
423 }
424
d3cbd0a5
CW
425 CRM_Utils_JSON::output($result);
426 }
427
6a488035 428}