Merge pull request #13566 from pradpnayak/ModifiedBy
[civicrm-core.git] / CRM / Contact / BAO / GroupNestingCache.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
6a488035
TO
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
6a488035
TO
32 */
33class CRM_Contact_BAO_GroupNestingCache {
67d19299 34
35 /**
36 * Update cache.
37 *
38 * @throws \Exception
39 */
6a488035
TO
40 static public function update() {
41 // lets build the tree in memory first
42
43 $sql = "
44SELECT n.child_group_id as child ,
45 n.parent_group_id as parent
46FROM civicrm_group_nesting n,
47 civicrm_group gc,
48 civicrm_group gp
49WHERE n.child_group_id = gc.id
50 AND n.parent_group_id = gp.id
51";
52
53 $dao = CRM_Core_DAO::executeQuery($sql);
54
55 $tree = array();
56 while ($dao->fetch()) {
57 if (!array_key_exists($dao->child, $tree)) {
6ea503d4
TO
58 $tree[$dao->child] = array(
59 'children' => array(),
6a488035
TO
60 'parents' => array(),
61 );
62 }
63
64 if (!array_key_exists($dao->parent, $tree)) {
6ea503d4
TO
65 $tree[$dao->parent] = array(
66 'children' => array(),
6a488035
TO
67 'parents' => array(),
68 );
69 }
70
71 $tree[$dao->child]['parents'][] = $dao->parent;
72 $tree[$dao->parent]['children'][] = $dao->child;
73 }
74
75 if (self::checkCyclicGraph($tree)) {
5d6aaf6b 76 CRM_Core_Error::fatal(ts("We detected a cycle which we can't handle. aborting"));
6a488035
TO
77 }
78
79 // first reset the current cache entries
80 $sql = "
81UPDATE civicrm_group
82SET parents = null,
83 children = null
84";
85 CRM_Core_DAO::executeQuery($sql);
86
87 $values = array();
88 foreach (array_keys($tree) as $id) {
353ffa53 89 $parents = implode(',', $tree[$id]['parents']);
6a488035 90 $children = implode(',', $tree[$id]['children']);
353ffa53 91 $parents = $parents == NULL ? 'null' : "'$parents'";
6a488035 92 $children = $children == NULL ? 'null' : "'$children'";
353ffa53 93 $sql = "
6a488035
TO
94UPDATE civicrm_group
95SET parents = $parents ,
96 children = $children
97WHERE id = $id
98";
99 CRM_Core_DAO::executeQuery($sql);
100 }
101
102 // this tree stuff is quite useful, so lets store it in the cache
103 CRM_Core_BAO_Cache::setItem($tree, 'contact groups', 'nestable tree hierarchy');
104 }
105
86538308
EM
106 /**
107 * @param $tree
108 *
109 * @return bool
110 */
00be9182 111 public static function checkCyclicGraph(&$tree) {
b44e3f84 112 // lets keep this simple, we should probably use a graph algorithm here at some stage
6a488035
TO
113
114 // foreach group that has a parent or a child, ensure that
115 // the ancestors and descendants dont intersect
116 foreach ($tree as $id => $dontCare) {
117 if (self::isCyclic($tree, $id)) {
118 return TRUE;
119 }
120 }
121
122 return FALSE;
123 }
124
86538308
EM
125 /**
126 * @param $tree
100fef9d 127 * @param int $id
86538308
EM
128 *
129 * @return bool
130 */
00be9182 131 public static function isCyclic(&$tree, $id) {
6a488035
TO
132 $parents = $children = array();
133 self::getAll($parent, $tree, $id, 'parents');
134 self::getAll($child, $tree, $id, 'children');
135
136 $one = array_intersect($parents, $children);
137 $two = array_intersect($children, $parents);
138 if (!empty($one) ||
139 !empty($two)
140 ) {
141 CRM_Core_Error::debug($id, $tree);
142 CRM_Core_Error::debug($id, $one);
143 CRM_Core_Error::debug($id, $two);
144 return TRUE;
145 }
146 return FALSE;
147 }
148
86538308 149 /**
100fef9d 150 * @param int $id
86538308
EM
151 * @param $groups
152 *
153 * @return array
154 */
00be9182 155 public static function getPotentialCandidates($id, &$groups) {
6a488035
TO
156 $tree = CRM_Core_BAO_Cache::getItem('contact groups', 'nestable tree hierarchy');
157
158 if ($tree === NULL) {
159 self::update();
160 $tree = CRM_Core_BAO_Cache::getItem('contact groups', 'nestable tree hierarchy');
161 }
162
163 $potential = $groups;
164
165 // remove all descendants
166 self::invalidate($potential, $tree, $id, 'children');
167
168 // remove all ancestors
169 self::invalidate($potential, $tree, $id, 'parents');
170
171 return array_keys($potential);
172 }
173
86538308
EM
174 /**
175 * @param $potential
176 * @param $tree
100fef9d 177 * @param int $id
86538308
EM
178 * @param $token
179 */
00be9182 180 public static function invalidate(&$potential, &$tree, $id, $token) {
6a488035
TO
181 unset($potential[$id]);
182
183 if (!isset($tree[$id]) ||
184 empty($tree[$id][$token])
185 ) {
186 return;
187 }
188
189 foreach ($tree[$id][$token] as $tokenID) {
190 self::invalidate($potential, $tree, $tokenID, $token);
191 }
192 }
193
86538308
EM
194 /**
195 * @param $all
196 * @param $tree
100fef9d 197 * @param int $id
86538308
EM
198 * @param $token
199 */
00be9182 200 public static function getAll(&$all, &$tree, $id, $token) {
6a488035
TO
201 // if seen before, dont do anything
202 if (isset($all[$id])) {
203 return;
204 }
205
206 $all[$id] = 1;
207 if (!isset($tree[$id]) ||
208 empty($tree[$id][$token])
209 ) {
210 return;
211 }
212
213 foreach ($tree[$id][$token] as $tokenID) {
214 self::getAll($all, $tree, $tokenID, $token);
215 }
216 }
217
86538308
EM
218 /**
219 * @return string
220 */
00be9182 221 public static function json() {
6a488035
TO
222 $tree = CRM_Core_BAO_Cache::getItem('contact groups', 'nestable tree hierarchy');
223
224 if ($tree === NULL) {
225 self::update();
226 $tree = CRM_Core_BAO_Cache::getItem('contact groups', 'nestable tree hierarchy');
227 }
228
229 // get all the groups
230 $groups = CRM_Core_PseudoConstant::group();
231
232 foreach ($groups as $id => $name) {
233 $string = "id:'$id', name:'$name'";
234 if (isset($tree[$id])) {
235 $children = array();
236 if (!empty($tree[$id]['children'])) {
237 foreach ($tree[$id]['children'] as $child) {
238 $children[] = "{_reference:'$child'}";
239 }
240 $children = implode(',', $children);
241 $string .= ", children:[$children]";
242 if (empty($tree[$id]['parents'])) {
243 $string .= ", type:'rootGroup'";
244 }
245 else {
246 $string .= ", type:'middleGroup'";
247 }
248 }
249 else {
250 $string .= ", type:'leafGroup'";
251 }
252 }
253 else {
254 $string .= ", children:[], type:'rootGroup'";
255 }
256 $values[] = "{ $string }";
257 }
258
259 $items = implode(",\n", $values);
260 $json = "{
261 identifier:'id',
262 label:'name',
263 items:[ $items ]
264}";
265 return $json;
266 }
96025800 267
6a488035 268}