Merge pull request #20357 from eileenmcnaughton/tax_add
[civicrm-core.git] / CRM / Core / BAO / Dashboard.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 use Civi\Api4\DashboardContact;
19
20 /**
21 * Class contains Contact dashboard related functions.
22 */
23 class CRM_Core_BAO_Dashboard extends CRM_Core_DAO_Dashboard {
24
25 /**
26 * Create or update Dashboard.
27 *
28 * @param array $params
29 *
30 * @return CRM_Core_DAO_Dashboard
31 */
32 public static function create($params) {
33 $hook = empty($params['id']) ? 'create' : 'edit';
34 CRM_Utils_Hook::pre($hook, 'Dashboard', CRM_Utils_Array::value('id', $params), $params);
35 $dao = self::addDashlet($params);
36 CRM_Utils_Hook::post($hook, 'Dashboard', $dao->id, $dao);
37 return $dao;
38 }
39
40 /**
41 * Get all available contact dashlets
42 *
43 * @return array
44 * array of dashlets
45 * @throws \API_Exception
46 */
47 public static function getContactDashlets(): array {
48 $cid = CRM_Core_Session::getLoggedInContactID();
49 if ($cid && !isset(Civi::$statics[__CLASS__][__FUNCTION__][$cid])) {
50 Civi::$statics[__CLASS__][__FUNCTION__][$cid] = [];
51 // If empty, then initialize default dashlets for this user.
52 if (0 === DashboardContact::get(FALSE)->selectRowCount()->addWhere('contact_id', '=', $cid)->execute()->count()) {
53 self::initializeDashlets();
54 }
55 $contactDashboards = (array) DashboardContact::get(FALSE)
56 ->addSelect('column_no', 'is_active', 'dashboard_id', 'weight', 'contact_id')
57 ->addWhere('contact_id', '=', $cid)
58 ->addOrderBy('weight')
59 ->execute()->indexBy('dashboard_id');
60
61 $params = [
62 'select' => ['*', 'dashboard_contact.*'],
63 'where' => [
64 ['domain_id', '=', 'current_domain'],
65 ],
66 ];
67
68 // Get Dashboard + any joined DashboardContact records.
69 $results = (array) civicrm_api4('Dashboard', 'get', $params);
70 foreach ($results as $item) {
71 $item['dashboard_contact.id'] = $contactDashboards[$item['id']]['id'] ?? NULL;
72 $item['dashboard_contact.contact_id'] = $contactDashboards[$item['id']]['contact_id'] ?? NULL;
73 $item['dashboard_contact.weight'] = $contactDashboards[$item['id']]['weight'] ?? NULL;
74 $item['dashboard_contact.column_no'] = $contactDashboards[$item['id']]['column_no'] ?? NULL;
75 $item['dashboard_contact.is_active'] = $contactDashboards[$item['id']]['is_active'] ?? NULL;
76 if ($item['is_active'] && self::checkPermission($item['permission'], $item['permission_operator'])) {
77 Civi::$statics[__CLASS__][__FUNCTION__][$cid][] = $item;
78 }
79 }
80 usort(Civi::$statics[__CLASS__][__FUNCTION__][$cid], static function ($a, $b) {
81 // Sort by dashboard contact weight, preferring not null to null.
82 // I had hoped to do this in mysql either by
83 // 1) making the dashboard contact part of the query NOT permissioned while
84 // the parent query IS or
85 // 2) using FIELD like
86 // $params['orderBy'] = ['FIELD(id,' . implode(',', array_keys($contactDashboards)) . ')' => 'ASC'];
87 // 3) or making the dashboard contact acl more inclusive such that 'view own contact'
88 // is not required to view own contact's acl
89 // but I couldn't see a way to make any of the above work. Perhaps improve in master?
90 if (!isset($b['dashboard_contact.weight']) && !isset($a[$b['dashboard_contact.weight']])) {
91 return 0;
92 }
93 if (!isset($b['dashboard_contact.weight'])) {
94 return -1;
95 }
96 if (!isset($a['dashboard_contact.weight'])) {
97 return 1;
98 }
99 return $a['dashboard_contact.weight'] <=> $b['dashboard_contact.weight'];
100 });
101 }
102 return Civi::$statics[__CLASS__][__FUNCTION__][$cid] ?? [];
103 }
104
105 /**
106 * Set default dashlets for new users.
107 *
108 * Called when a user accesses their dashboard for the first time.
109 */
110 public static function initializeDashlets() {
111 $allDashlets = (array) civicrm_api4('Dashboard', 'get', [
112 'where' => [['domain_id', '=', 'current_domain']],
113 ], 'name');
114 $defaultDashlets = [];
115 $defaults = ['blog' => 1, 'getting-started' => '0'];
116 foreach ($defaults as $name => $column) {
117 if (!empty($allDashlets[$name]) && !empty($allDashlets[$name]['id'])) {
118 $defaultDashlets[$name] = [
119 'dashboard_id' => $allDashlets[$name]['id'],
120 'is_active' => 1,
121 'column_no' => $column,
122 ];
123 }
124 }
125 CRM_Utils_Hook::dashboard_defaults($allDashlets, $defaultDashlets);
126 if (is_array($defaultDashlets) && !empty($defaultDashlets)) {
127 DashboardContact::save(FALSE)
128 ->setRecords($defaultDashlets)
129 ->setDefaults(['contact_id' => CRM_Core_Session::getLoggedInContactID()])
130 ->execute();
131 }
132 }
133
134 /**
135 * Check dashlet permission for current user.
136 *
137 * @param array $permissions
138 * @param string $operator
139 *
140 * @return bool
141 * true if user has permission to view dashlet
142 */
143 public static function checkPermission($permissions, $operator) {
144 if ($permissions) {
145 $config = CRM_Core_Config::singleton();
146
147 static $allComponents;
148 if (!$allComponents) {
149 $allComponents = CRM_Core_Component::getNames();
150 }
151
152 $hasPermission = FALSE;
153 foreach ($permissions as $key) {
154 $showDashlet = TRUE;
155
156 $componentName = NULL;
157 if (strpos($key, 'access') === 0) {
158 $componentName = trim(substr($key, 6));
159 if (!in_array($componentName, $allComponents)) {
160 $componentName = NULL;
161 }
162 }
163
164 // hack to handle case permissions
165 if (!$componentName
166 && in_array($key, ['access my cases and activities', 'access all cases and activities'])
167 ) {
168 $componentName = 'CiviCase';
169 }
170
171 //hack to determine if it's a component related permission
172 if ($componentName) {
173 if (!in_array($componentName, $config->enableComponents) ||
174 !CRM_Core_Permission::check($key)
175 ) {
176 $showDashlet = FALSE;
177 if ($operator == 'AND') {
178 return $showDashlet;
179 }
180 }
181 else {
182 $hasPermission = TRUE;
183 }
184 }
185 elseif (!CRM_Core_Permission::check($key)) {
186 $showDashlet = FALSE;
187 if ($operator == 'AND') {
188 return $showDashlet;
189 }
190 }
191 else {
192 $hasPermission = TRUE;
193 }
194 }
195
196 if (!$showDashlet && !$hasPermission) {
197 return FALSE;
198 }
199 else {
200 return TRUE;
201 }
202 }
203 else {
204 // if permission is not set consider everyone has permission to access it.
205 return TRUE;
206 }
207 }
208
209 /**
210 * Add dashlets.
211 *
212 * @param array $params
213 *
214 * @return object
215 * $dashlet returns dashlet object
216 */
217 public static function addDashlet(&$params) {
218
219 // special case to handle duplicate entries for report instances
220 $dashboardID = $params['id'] ?? NULL;
221
222 if (!empty($params['instanceURL'])) {
223 $query = "SELECT id
224 FROM `civicrm_dashboard`
225 WHERE url LIKE '" . CRM_Utils_Array::value('instanceURL', $params) . "&%'";
226 $dashboardID = CRM_Core_DAO::singleValueQuery($query);
227 }
228
229 $dashlet = new CRM_Core_DAO_Dashboard();
230
231 if (!$dashboardID) {
232 // Assign domain before search to allow identical dashlets in different domains.
233 $dashlet->domain_id = $params['domain_id'] ?? CRM_Core_Config::domainID();
234
235 // Try and find an existing dashlet - it will be updated if found.
236 if (!empty($params['name'])) {
237 $dashlet->name = $params['name'] ?? NULL;
238 $dashlet->find(TRUE);
239 }
240 }
241 else {
242 $dashlet->id = $dashboardID;
243 }
244
245 $dashlet->copyValues($params);
246 $dashlet->save();
247
248 // now we need to make dashlet entries for each contact
249 self::addContactDashlet($dashlet);
250
251 return $dashlet;
252 }
253
254 /**
255 * Update contact dashboard with new dashlet.
256 *
257 * @param object $dashlet
258 */
259 public static function addContactDashlet($dashlet) {
260 $admin = CRM_Core_Permission::check('administer CiviCRM');
261
262 // if dashlet is created by admin then you need to add it all contacts.
263 // else just add to contact who is creating this dashlet
264 $contactIDs = [];
265 if ($admin) {
266 $query = "SELECT distinct( contact_id )
267 FROM civicrm_dashboard_contact";
268 $dao = CRM_Core_DAO::executeQuery($query);
269 while ($dao->fetch()) {
270 $contactIDs[$dao->contact_id] = NULL;
271 }
272 }
273 else {
274 //Get the id of Logged in User
275 $contactID = CRM_Core_Session::getLoggedInContactID();
276 if (!empty($contactID)) {
277 $contactIDs[$contactID] = NULL;
278 }
279 }
280
281 // Remove contact ids that already have this dashlet to avoid DB
282 // constraint violation.
283 $query = "SELECT distinct( contact_id )
284 FROM civicrm_dashboard_contact WHERE dashboard_id = {$dashlet->id}";
285 $dao = CRM_Core_DAO::executeQuery($query);
286 while ($dao->fetch()) {
287 if (array_key_exists($dao->contact_id, $contactIDs)) {
288 unset($contactIDs[$dao->contact_id]);
289 }
290 }
291 if (!empty($contactIDs)) {
292 foreach ($contactIDs as $contactID => $value) {
293 $valuesArray[] = " ( {$dashlet->id}, {$contactID} )";
294 }
295 $valuesString = implode(',', $valuesArray);
296 $query = "
297 INSERT INTO civicrm_dashboard_contact ( dashboard_id, contact_id )
298 VALUES {$valuesString}";
299
300 CRM_Core_DAO::executeQuery($query);
301 }
302 }
303
304 }