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