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 * @param bool $styleLabels
56 * @param string $styleIndent
58 public function __construct($styleLabels = FALSE, $styleIndent = " -- ") {
59 parent
::__construct();
60 $this->_styleLabels
= $styleLabels;
61 $this->_styleIndent
= $styleIndent;
67 public function setSortOrder($sortOrder) {
71 if ($sortOrder != self
::$_sortOrder) {
72 self
::$_sortOrder = $sortOrder;
78 // spit out some error, someday
85 public function getSortOrder() {
86 return self
::$_sortOrder;
92 public function getCurrentNestingLevel() {
93 return count($this->_parentStack
);
97 * Go back to the first element in the group nesting graph,
98 * which is the first group (according to _sortOrder) that
99 * has no parent groups
101 public function rewind() {
102 $this->_parentStack
= array();
103 // calling _getNextParentlessGroup w/ no arguments
104 // makes it return the first parentless group
105 $firstGroup = $this->_getNextParentlessGroup();
106 $this->_current
= $firstGroup;
107 $this->_lastParentlessGroup
= $firstGroup;
108 $this->_alreadyStyled
= FALSE;
114 public function current() {
115 if ($this->_styleLabels
&&
117 !$this->_alreadyStyled
119 $styledGroup = clone($this->_current
);
120 $nestingLevel = $this->getCurrentNestingLevel();
122 while ($nestingLevel--) {
123 $indent .= $this->_styleIndent
;
125 $styledGroup->title
= $indent . $styledGroup->title
;
127 $this->_current
= &$styledGroup;
128 $this->_alreadyStyled
= TRUE;
130 return $this->_current
;
136 public function key() {
137 $group = &$this->_current
;
139 foreach ($this->_parentStack
as $parentGroup) {
140 $ids[] = $parentGroup->id
;
142 $key = implode('-', $ids);
143 if (strlen($key) > 0) {
151 * @return CRM_Contact_BAO_Group|null
153 public function next() {
154 $currentGroup = &$this->_current
;
155 $childGroup = $this->_getNextChildGroup($currentGroup);
157 $nextGroup = &$childGroup;
158 $this->_parentStack
[] = &$this->_current
;
161 $nextGroup = $this->_getNextSiblingGroup($currentGroup);
163 // no sibling, find an ancestor w/ a sibling
165 // since we pop this array every time, we should be
166 // reasonably safe from infinite loops, I think :)
167 $ancestor = array_pop($this->_parentStack
);
168 $this->_current
= &$ancestor;
169 if ($ancestor == NULL) {
172 $nextGroup = $this->_getNextSiblingGroup($ancestor);
179 $this->_current
= &$nextGroup;
180 $this->_alreadyStyled
= FALSE;
187 public function valid() {
188 if ($this->_current
) {
199 * @return CRM_Contact_BAO_Group|null
201 public function _getNextParentlessGroup(&$group = NULL) {
202 $lastParentlessGroup = $this->_lastParentlessGroup
;
203 $nextGroup = new CRM_Contact_BAO_Group();
204 $nextGroup->order_by
= 'title ' . self
::$_sortOrder;
206 if ($group == NULL) {
212 while ($nextGroup->fetch()) {
213 if (!self
::hasParentGroups($nextGroup->id
) && $sawLast) {
216 elseif ($lastParentlessGroup->id
== $nextGroup->id
) {
224 * @param $parentGroup
227 * @return CRM_Contact_BAO_Group|null
229 public function _getNextChildGroup(&$parentGroup, &$group = NULL) {
230 $children = self
::getChildGroupIds($parentGroup->id
);
231 if (count($children) > 0) {
232 // we have child groups, so get the first one based on _sortOrder
233 $childGroup = new CRM_Contact_BAO_Group();
234 $cgQuery = "SELECT * FROM civicrm_group WHERE id IN (" . implode(',', $children) . ") ORDER BY title " . self
::$_sortOrder;
235 $childGroup->query($cgQuery);
236 $currentGroup = &$this->_current
;
237 if ($group == NULL) {
243 while ($childGroup->fetch()) {
247 elseif ($currentGroup->id
=== $childGroup->id
) {
258 * @return CRM_Contact_BAO_Group|null
260 public function _getNextSiblingGroup(&$group) {
261 $parentGroup = end($this->_parentStack
);
263 $nextGroup = $this->_getNextChildGroup($parentGroup, $group);
267 /* if we get here, it could be because we're out of siblings
268 * (in which case we return null) or because we're at the
269 * top level groups which do not have parents but may still
270 * have siblings, so check for that first.
273 $nextGroup = $this->_getNextParentlessGroup($group);
275 $this->_lastParentlessGroup
= $nextGroup;
283 * Adds a new child group identified by $childGroupId to the group
284 * identified by $groupId
286 * @param int $parentID
287 * Id of the group to add the child to.
288 * @param int $childID
289 * Id of the new child group.
294 public static function add($parentID, $childID) {
295 // TODO: Add checks here to make sure invalid nests can't be created
296 $dao = new CRM_Contact_DAO_GroupNesting();
297 $query = "REPLACE INTO civicrm_group_nesting (child_group_id, parent_group_id) VALUES ($childID,$parentID);";
302 * Removes a child group identified by $childGroupId from the group
303 * identified by $groupId; does not delete child group, just the
304 * association between the two
307 * The id of the group to remove the child from.
309 * The id of the child group being removed.
313 public static function remove($parentID, $childID) {
314 $dao = new CRM_Contact_DAO_GroupNesting();
315 $query = "DELETE FROM civicrm_group_nesting WHERE child_group_id = $childID AND parent_group_id = $parentID";
320 * Removes associations where a child group is identified by $childGroupId from the group
321 * identified by $groupId; does not delete child group, just the
322 * association between the two
324 * @param int $childID
325 * The id of the child group being removed.
329 public static function removeAllParentForChild($childID) {
330 $dao = new CRM_Contact_DAO_GroupNesting();
331 $query = "DELETE FROM civicrm_group_nesting WHERE child_group_id = $childID";
336 * Returns true if the association between parent and child is present,
340 * The parent id of the association.
342 * The child id of the association.
345 * True if association is found, false otherwise.
347 public static function isParentChild($parentID, $childID) {
348 $dao = new CRM_Contact_DAO_GroupNesting();
349 $query = "SELECT id FROM civicrm_group_nesting WHERE child_group_id = $childID AND parent_group_id = $parentID";
358 * Returns true if if the given groupId has 1 or more child groups,
362 * The id of the group to check for child groups.
365 * True if 1 or more child groups are found, false otherwise.
367 public static function hasChildGroups($groupId) {
368 $dao = new CRM_Contact_DAO_GroupNesting();
369 $query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id = $groupId LIMIT 1";
370 //print $query . "\n<br><br>";
379 * Returns true if the given groupId has 1 or more parent groups,
383 * The id of the group to check for parent groups.
386 * True if 1 or more parent groups are found, false otherwise.
388 public static function hasParentGroups($groupId) {
389 $dao = new CRM_Contact_DAO_GroupNesting();
390 $query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id = $groupId LIMIT 1";
399 * Returns true if checkGroupId is a parent of one of the groups in
400 * groupIds, false otherwise.
402 * @param array $groupIds
403 * of group ids (or one group id) to serve as the starting point.
404 * @param $checkGroupId
405 * The group id to check if it is a parent of the $groupIds group(s).
408 * True if $checkGroupId points to a group that is a parent of one of the $groupIds groups, false otherwise.
410 public static function isParentGroup($groupIds, $checkGroupId) {
411 if (!is_array($groupIds)) {
412 $groupIds = array($groupIds);
414 $dao = new CRM_Contact_DAO_GroupNesting();
415 $query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id IN (" . implode(',', $groupIds) . ")";
417 while ($dao->fetch()) {
418 $parentGroupId = $dao->parent_group_id
;
419 if ($parentGroupId == $checkGroupId) {
420 /* print "One of these: <pre>";
422 print "</pre> has groupId $checkGroupId as an ancestor.<br/>"; */
431 * Returns true if checkGroupId is a child of one of the groups in
432 * groupIds, false otherwise.
434 * @param array $groupIds
435 * of group ids (or one group id) to serve as the starting point.
436 * @param $checkGroupId
437 * The group id to check if it is a child of the $groupIds group(s).
440 * True if $checkGroupId points to a group that is a child of one of the $groupIds groups, false otherwise.
442 public static function isChildGroup($groupIds, $checkGroupId) {
444 if (!is_array($groupIds)) {
445 $groupIds = array($groupIds);
447 $dao = new CRM_Contact_DAO_GroupNesting();
448 $query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
451 while ($dao->fetch()) {
452 $childGroupId = $dao->child_group_id
;
453 if ($childGroupId == $checkGroupId) {
454 /* print "One of these: <pre>";
456 print "</pre> has groupId $checkGroupId as a descendent.<br/><br/>"; */
465 * Returns true if checkGroupId is an ancestor of one of the groups in
466 * groupIds, false otherwise.
468 * @param array $groupIds
469 * of group ids (or one group id) to serve as the starting point.
470 * @param $checkGroupId
471 * The group id to check if it is an ancestor of the $groupIds group(s).
474 * True if $checkGroupId points to a group that is an ancestor of one of the $groupIds groups, false otherwise.
476 public static function isAncestorGroup($groupIds, $checkGroupId) {
477 if (!is_array($groupIds)) {
478 $groupIds = array($groupIds);
480 $dao = new CRM_Contact_DAO_GroupNesting();
481 $query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id IN (" . implode(',', $groupIds) . ")";
483 $nextGroupIds = array();
484 $gotAtLeastOneResult = FALSE;
485 while ($dao->fetch()) {
486 $gotAtLeastOneResult = TRUE;
487 $parentGroupId = $dao->parent_group_id
;
488 if ($parentGroupId == $checkGroupId) {
489 /* print "One of these: <pre>";
491 print "</pre> has groupId $checkGroupId as an ancestor.<br/>"; */
495 $nextGroupIds[] = $parentGroupId;
497 if ($gotAtLeastOneResult) {
498 return self
::isAncestorGroup($nextGroupIds, $checkGroupId);
506 * Returns true if checkGroupId is a descendent of one of the groups in
507 * groupIds, false otherwise.
509 * @param array $groupIds
510 * of group ids (or one group id) to serve as the starting point.
511 * @param $checkGroupId
512 * The group id to check if it is a descendent of the $groupIds group(s).
515 * True if $checkGroupId points to a group that is a descendent of one of the $groupIds groups, false otherwise.
517 public static function isDescendentGroup($groupIds, $checkGroupId) {
518 if (!is_array($groupIds)) {
519 $groupIds = array($groupIds);
521 $dao = new CRM_Contact_DAO_GroupNesting();
522 $query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
524 $nextGroupIds = array();
525 $gotAtLeastOneResult = FALSE;
526 while ($dao->fetch()) {
527 $gotAtLeastOneResult = TRUE;
528 $childGroupId = $dao->child_group_id
;
529 if ($childGroupId == $checkGroupId) {
530 /* print "One of these: <pre>";
532 print "</pre> has groupId $checkGroupId as a descendent.<br/><br/>"; */
536 $nextGroupIds[] = $childGroupId;
538 if ($gotAtLeastOneResult) {
539 return self
::isDescendentGroup($nextGroupIds, $checkGroupId);
547 * Returns array of group ids of ancestor groups of the specified group.
549 * @param array $groupIds
550 * An array of valid group ids (passed by reference).
552 * @param bool $includeSelf
555 * List of groupIds that represent the requested group and its ancestors
557 public static function getAncestorGroupIds($groupIds, $includeSelf = TRUE) {
558 if (!is_array($groupIds)) {
559 $groupIds = array($groupIds);
561 $dao = new CRM_Contact_DAO_GroupNesting();
562 $query = "SELECT parent_group_id, child_group_id
563 FROM civicrm_group_nesting
564 WHERE child_group_id IN (" . implode(',', $groupIds) . ")";
566 $tmpGroupIds = array();
567 $parentGroupIds = array();
569 $parentGroupIds = $groupIds;
571 while ($dao->fetch()) {
572 // make sure we're not following any cyclical references
573 if (!array_key_exists($dao->child_group_id
, $parentGroupIds) && $dao->parent_group_id
!= $groupIds[0]) {
574 $tmpGroupIds[] = $dao->parent_group_id
;
577 if (!empty($tmpGroupIds)) {
578 $newParentGroupIds = self
::getAncestorGroupIds($tmpGroupIds);
579 $parentGroupIds = array_merge($parentGroupIds, $newParentGroupIds);
581 return $parentGroupIds;
585 * Returns array of ancestor groups of the specified group.
587 * @param array $groupIds
588 * An array of valid group ids (passed by reference).
590 * @param bool $includeSelf
592 * List of ancestor groups
594 public static function getAncestorGroups($groupIds, $includeSelf = TRUE) {
595 $groupIds = self
::getAncestorGroupIds($groupIds, $includeSelf);
596 $params['id'] = $groupIds;
597 return CRM_Contact_BAO_Group
::getGroups($params);
601 * Returns array of group ids of child groups of the specified group.
603 * @param array $groupIds
604 * An array of valid group ids (passed by reference).
607 * List of groupIds that represent the requested group and its children
609 public static function getChildGroupIds($groupIds) {
610 if (!is_array($groupIds)) {
611 $groupIds = array($groupIds);
613 $dao = new CRM_Contact_DAO_GroupNesting();
614 $query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
616 $childGroupIds = array();
617 while ($dao->fetch()) {
618 $childGroupIds[] = $dao->child_group_id
;
620 return $childGroupIds;
624 * Returns array of group ids of parent groups of the specified group.
626 * @param array $groupIds
627 * An array of valid group ids (passed by reference).
630 * List of groupIds that represent the requested group and its parents
632 public static function getParentGroupIds($groupIds) {
633 if (!is_array($groupIds)) {
634 $groupIds = array($groupIds);
636 $dao = new CRM_Contact_DAO_GroupNesting();
637 $query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id IN (" . implode(',', $groupIds) . ")";
639 $parentGroupIds = array();
640 while ($dao->fetch()) {
641 $parentGroupIds[] = $dao->parent_group_id
;
643 return $parentGroupIds;
647 * Returns array of group ids of descendent groups of the specified group.
649 * @param array $groupIds
650 * An array of valid group ids (passed by reference).
652 * @param bool $includeSelf
654 * List of groupIds that represent the requested group and its descendents
656 public static function getDescendentGroupIds($groupIds, $includeSelf = TRUE) {
657 if (!is_array($groupIds)) {
658 $groupIds = array($groupIds);
660 $dao = new CRM_Contact_DAO_GroupNesting();
661 $query = "SELECT child_group_id, parent_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
663 $tmpGroupIds = array();
664 $childGroupIds = array();
666 $childGroupIds = $groupIds;
668 while ($dao->fetch()) {
669 // make sure we're not following any cyclical references
670 if (!array_key_exists($dao->parent_group_id
, $childGroupIds) && $dao->child_group_id
!= $groupIds[0]) {
671 $tmpGroupIds[] = $dao->child_group_id
;
674 if (!empty($tmpGroupIds)) {
675 $newChildGroupIds = self
::getDescendentGroupIds($tmpGroupIds);
676 $childGroupIds = array_merge($childGroupIds, $newChildGroupIds);
678 return $childGroupIds;
682 * Returns array of descendent groups of the specified group.
684 * @param array $groupIds
685 * An array of valid group ids
687 * @param bool $includeSelf
689 * List of descendent groups
691 public static function getDescendentGroups($groupIds, $includeSelf = TRUE) {
692 $groupIds = self
::getDescendentGroupIds($groupIds, $includeSelf);
693 $params['id'] = $groupIds;
694 return CRM_Contact_BAO_Group
::getGroups($params);
698 * Returns array of group ids of valid potential child groups of the specified group.
701 * The group id to get valid potential children for.
704 * List of groupIds that represent the valid potential children of the group
706 public static function getPotentialChildGroupIds($groupId) {
707 $groups = CRM_Contact_BAO_Group
::getGroups();
708 $potentialChildGroupIds = array();
709 foreach ($groups as $group) {
710 $potentialChildGroupId = $group->id
;
711 // print "Checking if $potentialChildGroupId is a descendent/ancestor of $groupId<br/><br/>";
712 if (!self
::isDescendentGroup($groupId, $potentialChildGroupId) &&
713 !self
::isAncestorGroup($groupId, $potentialChildGroupId) &&
714 $potentialChildGroupId != $groupId
716 $potentialChildGroupIds[] = $potentialChildGroupId;
719 return $potentialChildGroupIds;
723 * @param int $contactId
724 * @param int $parentGroupId
728 public static function getContainingGroups($contactId, $parentGroupId) {
729 $groups = CRM_Contact_BAO_Group
::getGroups();
730 $containingGroups = array();
731 foreach ($groups as $group) {
732 if (self
::isDescendentGroup($parentGroupId, $group->id
)) {
733 $members = CRM_Contact_BAO_Group
::getMember($group->id
);
734 if ($members[$contactId]) {
735 $containingGroups[] = $group->title
;
740 return $containingGroups;