3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright U.S. PIRG Education Fund (c) 2007 |
7 | Licensed to CiviCRM under the Academic Free License version 3.0. |
8 +--------------------------------------------------------------------+
9 | This file is a part of CiviCRM. |
11 | CiviCRM is free software; you can copy, modify, and distribute it |
12 | under the terms of the GNU Affero General Public License |
13 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
15 | CiviCRM is distributed in the hope that it will be useful, but |
16 | WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
18 | See the GNU Affero General Public License for more details. |
20 | You should have received a copy of the GNU Affero General Public |
21 | License and the CiviCRM Licensing Exception along |
22 | with this program; if not, contact CiviCRM LLC |
23 | at info[AT]civicrm[DOT]org. If you have questions about the |
24 | GNU Affero General Public License or the licensing of CiviCRM, |
25 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
26 +--------------------------------------------------------------------+
32 * @copyright U.S. PIRG 2007
36 class CRM_Contact_BAO_GroupNesting
extends CRM_Contact_DAO_GroupNesting
implements Iterator
{
38 static $_sortOrder = 'ASC';
42 private $_parentStack = array();
44 private $_lastParentlessGroup;
46 private $_styleLabels;
48 private $_styleIndent;
50 private $_alreadyStyled = FALSE;
55 function __construct($styleLabels = FALSE, $styleIndent = " -- ") {
56 parent
::__construct();
57 $this->_styleLabels
= $styleLabels;
58 $this->_styleIndent
= $styleIndent;
64 function setSortOrder($sortOrder) {
68 if ($sortOrder != self
::$_sortOrder) {
69 self
::$_sortOrder = $sortOrder;
75 // spit out some error, someday
82 function getSortOrder() {
83 return self
::$_sortOrder;
89 function getCurrentNestingLevel() {
90 return count($this->_parentStack
);
94 * Go back to the first element in the group nesting graph,
95 * which is the first group (according to _sortOrder) that
96 * has no parent groups
99 $this->_parentStack
= array();
100 // calling _getNextParentlessGroup w/ no arguments
101 // makes it return the first parentless group
102 $firstGroup = $this->_getNextParentlessGroup();
103 $this->_current
= $firstGroup;
104 $this->_lastParentlessGroup
= $firstGroup;
105 $this->_alreadyStyled
= FALSE;
109 if ($this->_styleLabels
&&
111 !$this->_alreadyStyled
113 $styledGroup = clone($this->_current
);
114 $nestingLevel = $this->getCurrentNestingLevel();
116 while ($nestingLevel--) {
117 $indent .= $this->_styleIndent
;
119 $styledGroup->title
= $indent . $styledGroup->title
;
121 $this->_current
= &$styledGroup;
122 $this->_alreadyStyled
= TRUE;
124 return $this->_current
;
131 $group = &$this->_current
;
133 foreach ($this->_parentStack
as $parentGroup) {
134 $ids[] = $parentGroup->id
;
136 $key = implode('-', $ids);
137 if (strlen($key) > 0) {
145 * @return CRM_Contact_BAO_Group|null
148 $currentGroup = &$this->_current
;
149 $childGroup = $this->_getNextChildGroup($currentGroup);
151 $nextGroup = &$childGroup;
152 $this->_parentStack
[] = &$this->_current
;
155 $nextGroup = $this->_getNextSiblingGroup($currentGroup);
157 // no sibling, find an ancestor w/ a sibling
159 // since we pop this array everytime, we should be
160 // reasonably safe from infinite loops, I think :)
161 $ancestor = array_pop($this->_parentStack
);
162 $this->_current
= &$ancestor;
163 if ($ancestor == NULL) {
166 $nextGroup = $this->_getNextSiblingGroup($ancestor);
173 $this->_current
= &$nextGroup;
174 $this->_alreadyStyled
= FALSE;
182 if ($this->_current
) {
193 * @return CRM_Contact_BAO_Group|null
195 function _getNextParentlessGroup(&$group = NULL) {
196 $lastParentlessGroup = $this->_lastParentlessGroup
;
197 $nextGroup = new CRM_Contact_BAO_Group();
198 $nextGroup->order_by
= 'title ' . self
::$_sortOrder;
200 if ($group == NULL) {
206 while ($nextGroup->fetch()) {
207 if (!self
::hasParentGroups($nextGroup->id
) && $sawLast) {
210 elseif ($lastParentlessGroup->id
== $nextGroup->id
) {
218 * @param $parentGroup
221 * @return CRM_Contact_BAO_Group|null
223 function _getNextChildGroup(&$parentGroup, &$group = NULL) {
224 $children = self
::getChildGroupIds($parentGroup->id
);
225 if (count($children) > 0) {
226 // we have child groups, so get the first one based on _sortOrder
227 $childGroup = new CRM_Contact_BAO_Group();
228 $cgQuery = "SELECT * FROM civicrm_group WHERE id IN (" . implode(',', $children) . ") ORDER BY title " . self
::$_sortOrder;
229 $childGroup->query($cgQuery);
230 $currentGroup = &$this->_current
;
231 if ($group == NULL) {
237 while ($childGroup->fetch()) {
241 elseif ($currentGroup->id
=== $childGroup->id
) {
252 * @return CRM_Contact_BAO_Group|null
254 function _getNextSiblingGroup(&$group) {
255 $parentGroup = end($this->_parentStack
);
257 $nextGroup = $this->_getNextChildGroup($parentGroup, $group);
261 /* if we get here, it could be because we're out of siblings
262 * (in which case we return null) or because we're at the
263 * top level groups which do not have parents but may still
264 * have siblings, so check for that first.
267 $nextGroup = $this->_getNextParentlessGroup($group);
269 $this->_lastParentlessGroup
= $nextGroup;
277 * Adds a new child group identified by $childGroupId to the group
278 * identified by $groupId
280 * @param int $parentID id of the group to add the child to
281 * @param int $childID id of the new child group
287 static function add($parentID, $childID) {
288 // TODO: Add checks here to make sure invalid nests can't be created
289 $dao = new CRM_Contact_DAO_GroupNesting();
290 $query = "REPLACE INTO civicrm_group_nesting (child_group_id, parent_group_id) VALUES ($childID,$parentID);";
295 * Removes a child group identified by $childGroupId from the group
296 * identified by $groupId; does not delete child group, just the
297 * association between the two
299 * @param $parentID The id of the group to remove the child from
300 * @param $childID The id of the child group being removed
306 static function remove($parentID, $childID) {
307 $dao = new CRM_Contact_DAO_GroupNesting();
308 $query = "DELETE FROM civicrm_group_nesting WHERE child_group_id = $childID AND parent_group_id = $parentID";
313 * Removes associations where a child group is identified by $childGroupId from the group
314 * identified by $groupId; does not delete child group, just the
315 * association between the two
317 * @param int $childID The id of the child group being removed
323 static function removeAllParentForChild($childID) {
324 $dao = new CRM_Contact_DAO_GroupNesting();
325 $query = "DELETE FROM civicrm_group_nesting WHERE child_group_id = $childID";
330 * Returns true if the association between parent and child is present,
333 * @param $parentID The parent id of the association
334 * @param $childID The child id of the association
336 * @return boolean True if association is found, false otherwise.
340 static function isParentChild($parentID, $childID) {
341 $dao = new CRM_Contact_DAO_GroupNesting();
342 $query = "SELECT id FROM civicrm_group_nesting WHERE child_group_id = $childID AND parent_group_id = $parentID";
351 * Returns true if if the given groupId has 1 or more child groups,
354 * @param $groupId The id of the group to check for child groups
356 * @return boolean True if 1 or more child groups are found, false otherwise.
360 static function hasChildGroups($groupId) {
361 $dao = new CRM_Contact_DAO_GroupNesting();
362 $query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id = $groupId LIMIT 1";
363 //print $query . "\n<br><br>";
372 * Returns true if the given groupId has 1 or more parent groups,
375 * @param $groupId The id of the group to check for parent groups
377 * @return boolean True if 1 or more parent groups are found, false otherwise.
381 static function hasParentGroups($groupId) {
382 $dao = new CRM_Contact_DAO_GroupNesting();
383 $query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id = $groupId LIMIT 1";
392 * Returns true if checkGroupId is a parent of one of the groups in
393 * groupIds, false otherwise.
395 * @param $groupIds Array of group ids (or one group id) to serve as the starting point
396 * @param $checkGroupId The group id to check if it is a parent of the $groupIds group(s)
398 * @return boolean True if $checkGroupId points to a group that is a parent of one of the $groupIds groups, false otherwise.
402 static function isParentGroup($groupIds, $checkGroupId) {
403 if (!is_array($groupIds)) {
404 $groupIds = array($groupIds);
406 $dao = new CRM_Contact_DAO_GroupNesting();
407 $query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id IN (" . implode(',', $groupIds) . ")";
409 while ($dao->fetch()) {
410 $parentGroupId = $dao->parent_group_id
;
411 if ($parentGroupId == $checkGroupId) {
412 /* print "One of these: <pre>";
414 print "</pre> has groupId $checkGroupId as an ancestor.<br/>"; */
423 * Returns true if checkGroupId is a child of one of the groups in
424 * groupIds, false otherwise.
426 * @param $groupIds Array of group ids (or one group id) to serve as the starting point
427 * @param $checkGroupId The group id to check if it is a child of the $groupIds group(s)
429 * @return boolean True if $checkGroupId points to a group that is a child of one of the $groupIds groups, false otherwise.
433 static function isChildGroup($groupIds, $checkGroupId) {
435 if (!is_array($groupIds)) {
436 $groupIds = array($groupIds);
438 $dao = new CRM_Contact_DAO_GroupNesting();
439 $query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
442 while ($dao->fetch()) {
443 $childGroupId = $dao->child_group_id
;
444 if ($childGroupId == $checkGroupId) {
445 /* print "One of these: <pre>";
447 print "</pre> has groupId $checkGroupId as a descendent.<br/><br/>"; */
456 * Returns true if checkGroupId is an ancestor of one of the groups in
457 * groupIds, false otherwise.
459 * @param $groupIds Array of group ids (or one group id) to serve as the starting point
460 * @param $checkGroupId The group id to check if it is an ancestor of the $groupIds group(s)
462 * @return boolean True if $checkGroupId points to a group that is an ancestor of one of the $groupIds groups, false otherwise.
466 static function isAncestorGroup($groupIds, $checkGroupId) {
467 if (!is_array($groupIds)) {
468 $groupIds = array($groupIds);
470 $dao = new CRM_Contact_DAO_GroupNesting();
471 $query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id IN (" . implode(',', $groupIds) . ")";
473 $nextGroupIds = array();
474 $gotAtLeastOneResult = FALSE;
475 while ($dao->fetch()) {
476 $gotAtLeastOneResult = TRUE;
477 $parentGroupId = $dao->parent_group_id
;
478 if ($parentGroupId == $checkGroupId) {
479 /* print "One of these: <pre>";
481 print "</pre> has groupId $checkGroupId as an ancestor.<br/>"; */
485 $nextGroupIds[] = $parentGroupId;
487 if ($gotAtLeastOneResult) {
488 return self
::isAncestorGroup($nextGroupIds, $checkGroupId);
496 * Returns true if checkGroupId is a descendent of one of the groups in
497 * groupIds, false otherwise.
499 * @param $groupIds Array of group ids (or one group id) to serve as the starting point
500 * @param $checkGroupId The group id to check if it is a descendent of the $groupIds group(s)
502 * @return boolean True if $checkGroupId points to a group that is a descendent of one of the $groupIds groups, false otherwise.
506 static function isDescendentGroup($groupIds, $checkGroupId) {
507 if (!is_array($groupIds)) {
508 $groupIds = array($groupIds);
510 $dao = new CRM_Contact_DAO_GroupNesting();
511 $query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
513 $nextGroupIds = array();
514 $gotAtLeastOneResult = FALSE;
515 while ($dao->fetch()) {
516 $gotAtLeastOneResult = TRUE;
517 $childGroupId = $dao->child_group_id
;
518 if ($childGroupId == $checkGroupId) {
519 /* print "One of these: <pre>";
521 print "</pre> has groupId $checkGroupId as a descendent.<br/><br/>"; */
525 $nextGroupIds[] = $childGroupId;
527 if ($gotAtLeastOneResult) {
528 return self
::isDescendentGroup($nextGroupIds, $checkGroupId);
536 * Returns array of group ids of ancestor groups of the specified group.
538 * @param $groupIds An array of valid group ids (passed by reference)
540 * @param bool $includeSelf
542 * @return array $groupIdArray List of groupIds that represent the requested group and its ancestors@access public
544 static function getAncestorGroupIds($groupIds, $includeSelf = TRUE) {
545 if (!is_array($groupIds)) {
546 $groupIds = array($groupIds);
548 $dao = new CRM_Contact_DAO_GroupNesting();
549 $query = "SELECT parent_group_id, child_group_id
550 FROM civicrm_group_nesting
551 WHERE child_group_id IN (" . implode(',', $groupIds) . ")";
553 $tmpGroupIds = array();
554 $parentGroupIds = array();
556 $parentGroupIds = $groupIds;
558 while ($dao->fetch()) {
559 // make sure we're not following any cyclical references
560 if (!array_key_exists($dao->child_group_id
, $parentGroupIds) && $dao->parent_group_id
!= $groupIds[0]) {
561 $tmpGroupIds[] = $dao->parent_group_id
;
564 if (!empty($tmpGroupIds)) {
565 $newParentGroupIds = self
::getAncestorGroupIds($tmpGroupIds);
566 $parentGroupIds = array_merge($parentGroupIds, $newParentGroupIds);
568 return $parentGroupIds;
572 * Returns array of ancestor groups of the specified group.
574 * @param $groupIds An array of valid group ids (passed by reference)
576 * @param bool $includeSelf
577 * @return \An $groupArray List of ancestor groups@access public
579 static function getAncestorGroups($groupIds, $includeSelf = TRUE) {
580 $groupIds = self
::getAncestorGroupIds($groupIds, $includeSelf);
581 $params['id'] = $groupIds;
582 return CRM_Contact_BAO_Group
::getGroups($params);
586 * Returns array of group ids of child groups of the specified group.
588 * @param $groupIds An array of valid group ids (passed by reference)
590 * @return array $groupIdArray List of groupIds that represent the requested group and its children@access public
592 static function getChildGroupIds($groupIds) {
593 if (!is_array($groupIds)) {
594 $groupIds = array($groupIds);
596 $dao = new CRM_Contact_DAO_GroupNesting();
597 $query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
599 $childGroupIds = array();
600 while ($dao->fetch()) {
601 $childGroupIds[] = $dao->child_group_id
;
603 return $childGroupIds;
607 * Returns array of group ids of parent groups of the specified group.
609 * @param $groupIds An array of valid group ids (passed by reference)
611 * @return array $groupIdArray List of groupIds that represent the requested group and its parents@access public
613 static function getParentGroupIds($groupIds) {
614 if (!is_array($groupIds)) {
615 $groupIds = array($groupIds);
617 $dao = new CRM_Contact_DAO_GroupNesting();
618 $query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id IN (" . implode(',', $groupIds) . ")";
620 $parentGroupIds = array();
621 while ($dao->fetch()) {
622 $parentGroupIds[] = $dao->parent_group_id
;
624 return $parentGroupIds;
628 * Returns array of group ids of descendent groups of the specified group.
630 * @param $groupIds An array of valid group ids (passed by reference)
632 * @param bool $includeSelf
633 * @return array $groupIdArray List of groupIds that represent the requested group and its descendents@access public
635 static function getDescendentGroupIds($groupIds, $includeSelf = TRUE) {
636 if (!is_array($groupIds)) {
637 $groupIds = array($groupIds);
639 $dao = new CRM_Contact_DAO_GroupNesting();
640 $query = "SELECT child_group_id, parent_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
642 $tmpGroupIds = array();
643 $childGroupIds = array();
645 $childGroupIds = $groupIds;
647 while ($dao->fetch()) {
648 // make sure we're not following any cyclical references
649 if (!array_key_exists($dao->parent_group_id
, $childGroupIds) && $dao->child_group_id
!= $groupIds[0]) {
650 $tmpGroupIds[] = $dao->child_group_id
;
653 if (!empty($tmpGroupIds)) {
654 $newChildGroupIds = self
::getDescendentGroupIds($tmpGroupIds);
655 $childGroupIds = array_merge($childGroupIds, $newChildGroupIds);
657 return $childGroupIds;
661 * Returns array of descendent groups of the specified group.
663 * @param $groupIds An array of valid group ids (passed by reference)
665 * @param bool $includeSelf
666 * @return \An $groupArray List of descendent groups@access public
668 static function getDescendentGroups($groupIds, $includeSelf = TRUE) {
669 $groupIds = self
::getDescendentGroupIds($groupIds, $includeSelf);
670 $params['id'] = $groupIds;
671 return CRM_Contact_BAO_Group
::getGroups($params);
675 * Returns array of group ids of valid potential child groups of the specified group.
677 * @param $groupId The group id to get valid potential children for
679 * @return array $groupIdArray List of groupIds that represent the valid potential children of the group@access public
681 static function getPotentialChildGroupIds($groupId) {
682 $groups = CRM_Contact_BAO_Group
::getGroups();
683 $potentialChildGroupIds = array();
684 foreach ($groups as $group) {
685 $potentialChildGroupId = $group->id
;
686 // print "Checking if $potentialChildGroupId is a descendent/ancestor of $groupId<br/><br/>";
687 if (!self
::isDescendentGroup($groupId, $potentialChildGroupId) &&
688 !self
::isAncestorGroup($groupId, $potentialChildGroupId) &&
689 $potentialChildGroupId != $groupId
691 $potentialChildGroupIds[] = $potentialChildGroupId;
694 return $potentialChildGroupIds;
698 * @param int $contactId
699 * @param int $parentGroupId
703 static function getContainingGroups($contactId, $parentGroupId) {
704 $groups = CRM_Contact_BAO_Group
::getGroups();
705 $containingGroups = array();
706 foreach ($groups as $group) {
707 if (self
::isDescendentGroup($parentGroupId, $group->id
)) {
708 $members = CRM_Contact_BAO_Group
::getMember($group->id
);
709 if ($members[$contactId]) {
710 $containingGroups[] = $group->title
;
715 return $containingGroups;