1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
36:
37: Yii::import('application.components.util.AuxLib');
38:
39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50:
51: class X2PermissionsBehavior extends ModelPermissionsBehavior {
52:
53: 54: 55: 56: 57:
58: const QUERY_ALL = 3;
59:
60: 61: 62: 63: 64: 65: 66:
67: const QUERY_PUBLIC = 2;
68:
69: 70: 71: 72: 73: 74:
75: const QUERY_SELF = 1;
76:
77: 78: 79: 80: 81:
82: const QUERY_NONE = 0;
83:
84: 85: 86: 87:
88: const VISIBILITY_PRIVATE = 0;
89:
90: 91: 92: 93:
94: const VISIBILITY_PUBLIC = 1;
95:
96: 97: 98: 99:
100: const VISIBILITY_GROUPS = 2;
101:
102: 103: 104:
105: const SQL_PARAMS_PREFIX = 'X2PermissionsBehavior';
106:
107: private $_assignmentAttr;
108: private $_visibilityAttr;
109:
110: 111: 112: 113: 114: 115:
116: private $_isAssignedTo = array();
117:
118: 119: 120: 121: 122:
123: private $_isVisibleTo = array();
124:
125: public function clearCache () {
126: $this->_isVisibleTo = $this->_isAssignedTo = array ();
127: }
128:
129: 130: 131: 132:
133: public function getAccessCriteria(
134: $tableAlias = 't', $paramsNamespace = 'X2PermissionsBehavior', $showHidden = false) {
135:
136: $criteria = new CDbCriteria;
137: $criteria->alias = $tableAlias;
138: $accessLevel = $this->getAccessLevel();
139:
140: $conditions = $this->getAccessConditions(
141: $accessLevel, $tableAlias, $paramsNamespace, $showHidden);
142: foreach ($conditions as $arr) {
143: $criteria->addCondition($arr['condition'], $arr['operator']);
144: if (!empty($arr['params']))
145: $criteria->params = array_merge($criteria->params, $arr['params']);
146: }
147:
148: return $criteria;
149: }
150:
151: 152: 153:
154: public function getAccessSQLCondition($tableAlias = 't') {
155: $criteria = $this->getAccessCriteria($tableAlias);
156: return array('(' . $criteria->condition . ')', $criteria->params);
157: }
158:
159: 160: 161: 162: 163: 164: 165: 166:
167: public function getAccessLevel($uid=null) {
168: $module = ucfirst($this->owner->module);
169:
170: if ($uid) {
171: } elseif (Yii::app()->isInSession) {
172: $uid = Yii::app()->user->id;
173: } else {
174: $uid = Yii::app()->getSuID();
175: }
176: $accessLevel = self::QUERY_NONE;
177:
178: if (Yii::app()->params->isAdmin ||
179: Yii::app()->authManager->checkAccess($module . 'Admin', $uid)) {
180:
181: if ($accessLevel < self::QUERY_ALL)
182: $accessLevel = self::QUERY_ALL;
183: }elseif (Yii::app()->authManager->checkAccess($module . 'ReadOnlyAccess', $uid)) {
184: if ($accessLevel < self::QUERY_PUBLIC)
185: $accessLevel = self::QUERY_PUBLIC;
186: }elseif (Yii::app()->authManager->checkAccess($module . 'PrivateReadOnlyAccess', $uid)) {
187: if ($accessLevel < self::QUERY_SELF)
188: $accessLevel = self::QUERY_SELF;
189: }
190:
191:
192: $visibilityAttr = $this->getVisibilityAttr();
193: if ($accessLevel === self::QUERY_PUBLIC && $visibilityAttr === false)
194: $accessLevel = self::QUERY_ALL;
195:
196: return $accessLevel;
197: }
198:
199: 200: 201: 202:
203: public function getAssignmentAttr() {
204: if (!isset($this->_assignmentAttr)) {
205: $this->_assignmentAttr = false;
206: if ($this->owner->hasAttribute('assignedTo')) {
207: return $this->_assignmentAttr = 'assignedTo';
208: } elseif ($this->owner->hasAttribute('createdBy')) {
209: return $this->_assignmentAttr = 'createdBy';
210: } elseif ($this->owner instanceof X2Model) {
211: $fields = $this->owner->getFields();
212: foreach ($fields as $field) {
213:
214: if ($field->type == 'assignment') {
215: $assignAttr = $field->fieldName;
216: return $this->_assignmentAttr = $field->fieldName;
217: }
218: }
219: }
220: }
221: return $this->_assignmentAttr;
222: }
223:
224: 225: 226:
227: public function getVisibilityAttr() {
228: if (!isset($this->_visibilityAttr)) {
229: $this->_visibilityAttr = false;
230: if ($this->owner->hasAttribute('visibility')) {
231: return $this->_visibilityAttr = 'visibility';
232: } elseif ($this->owner instanceof X2Model) {
233: $fields = $this->owner->getFields();
234: foreach ($fields as $field) {
235:
236: if ($field->type == 'visibility') {
237: $assignAttr = $field->fieldName;
238: return $this->_visibilityAttr = $field->fieldName;
239: }
240: }
241: }
242: }
243: return $this->_visibilityAttr;
244: }
245:
246: 247: 248: 249:
250: public static function getVisibilityOptions() {
251: return array(
252: self::VISIBILITY_PUBLIC => Yii::t('app', 'Public'),
253: self::VISIBILITY_PRIVATE => Yii::t('app', 'Private'),
254: self::VISIBILITY_GROUPS => Yii::t('app', 'User\'s Groups')
255: );
256: }
257:
258: 259: 260: 261: 262: 263: 264: 265: 266: 267:
268: public function getAccessConditions(
269: $accessLevel, $tableAlias = 't', $paramsNamespace = 'X2PermissionsBehavior',
270: $showHidden = false) {
271:
272: $assignmentAttr = $this->getAssignmentAttr();
273: $visibilityAttr = $this->getVisibilityAttr();
274: $ret = array();
275: $prefix = empty($tableAlias) ? '' : "$tableAlias.";
276:
277: switch ($accessLevel) {
278: case self::QUERY_ALL:
279:
280: if (!$assignmentAttr || !$visibilityAttr || $showHidden) {
281: $ret[] = array('condition' => 'TRUE', 'operator' => 'AND', 'params' => array());
282: } else {
283: $ret[] = array(
284: 'condition' =>
285: "NOT (" . $prefix . "$visibilityAttr=" . self::VISIBILITY_PRIVATE . " AND "
286: . $prefix . "$assignmentAttr='Anyone')",
287: 'operator' => 'OR',
288: 'params' => array());
289: }
290: break;
291: case self::QUERY_PUBLIC:
292:
293: if ($visibilityAttr != false) {
294: $ret[] = array(
295: 'condition' => $prefix . "$visibilityAttr=" . self::VISIBILITY_PUBLIC,
296: 'operator' => 'OR',
297: 'params' => array()
298: );
299: }
300:
301:
302: $groupmatesRegex = self::getGroupmatesRegex();
303: if (!empty($groupmatesRegex)) {
304: $ret[] = array(
305: 'condition' =>
306: "(" . $prefix . "$visibilityAttr=" . self::VISIBILITY_GROUPS . ' ' .
307: "AND " . $prefix . "$assignmentAttr
308: REGEXP BINARY :" . $paramsNamespace . "groupmatesRegex)",
309: 'operator' => 'OR',
310: 'params' => array(
311: ':' . $paramsNamespace . 'groupmatesRegex' => $groupmatesRegex
312: ),
313: );
314: }
315:
316: case self::QUERY_SELF:
317:
318:
319: if ($assignmentAttr) {
320: list($assignedToCondition, $params) = $this->getAssignedToCondition(false, $tableAlias, null, $paramsNamespace);
321: $ret[] = array(
322: 'condition' => $assignedToCondition,
323: 'operator' => 'OR',
324: 'params' => $params
325: );
326: }
327:
328: $groupRegex = self::getGroupIdRegex();
329: if (!empty($groupRegex)) {
330: $ret[] = array(
331: 'condition' => "(" . $prefix . "$assignmentAttr REGEXP BINARY
332: :" . $paramsNamespace . "visibilityGroupIdRegex)",
333: 'operator' => 'OR',
334: 'params' => array(
335: ':' . $paramsNamespace . 'visibilityGroupIdRegex' => $groupRegex
336: )
337: );
338: }
339: if ($assignmentAttr && $visibilityAttr) {
340: $ret[] = array(
341: 'condition' =>
342: "NOT (" . $prefix . "$visibilityAttr=" . self::VISIBILITY_PRIVATE . " AND "
343: . $prefix . "$assignmentAttr='Anyone')",
344: 'operator' => 'AND',
345: 'params' => array());
346: }
347: break;
348: case self::QUERY_NONE:
349: default:
350: $ret[] = array('condition' => 'FALSE', 'operator' => 'AND', 'params' => array());
351: }
352: return $ret;
353: }
354:
355: 356: 357: 358: 359: 360: 361: 362:
363: public function isAssignedTo($username, $excludeAnyone = false) {
364: if (isset($this->_isAssignedTo[$username][$excludeAnyone]))
365: return $this->_isAssignedTo[$username][$excludeAnyone];
366: if (!$this->assignmentAttr)
367: return true;
368:
369:
370: $user = $username === Yii::app()->getSuName() ?
371: Yii::app()->getSuModel() :
372: User::model()->findByAttributes(array('username' => $username));
373:
374: $isAssignedTo = false;
375: $assignees = explode(', ', $this->owner->getAttribute($this->assignmentAttr));
376: $groupIds = array_filter($assignees, 'ctype_digit');
377: $usernames = array_diff($assignees, $groupIds);
378:
379:
380: foreach ($usernames as $assignee) {
381: if ($assignee === 'Anyone' || (sizeof($assignees) === 1 && $assignee === '')) {
382: if (!$excludeAnyone) {
383: $isAssignedTo = true;
384: break;
385: } else {
386: continue;
387: }
388: } else if ($assignee === $username) {
389: $isAssignedTo = true;
390: break;
391: }
392: }
393:
394:
395: if (!$isAssignedTo && !empty($groupIds) && $user instanceof User) {
396: $userGroupsAssigned = array_intersect($groupIds, Groups::getUserGroups($user->id));
397: if (!empty($userGroupsAssigned)) {
398: $isAssignedTo = true;
399: }
400: }
401: $this->_isAssignedTo[$username][$excludeAnyone] = $isAssignedTo;
402: return $isAssignedTo;
403: }
404:
405: public function getHiddenCondition ($tableAlias = 't') {
406: $assignmentAttr = $this->getAssignmentAttr();
407: $visibilityAttr = $this->getVisibilityAttr();
408: if ($assignmentAttr && $visibilityAttr) {
409: return "(NOT ($tableAlias.$assignmentAttr='Anyone' AND
410: $tableAlias.$visibilityAttr = ".self::VISIBILITY_PRIVATE."))";
411: } else {
412: return 'TRUE';
413: }
414: }
415:
416: private function isHidden () {
417: $assignmentAttr = $this->getAssignmentAttr();
418: $visibilityAttr = $this->getVisibilityAttr();
419: if ($assignmentAttr && $visibilityAttr) {
420: if ($this->owner->$assignmentAttr === 'Anyone' &&
421: $this->owner->$visibilityAttr == self::VISIBILITY_PRIVATE) {
422:
423: return true;
424: }
425: }
426: return false;
427: }
428:
429: 430: 431: 432: 433: 434: 435:
436: public function isVisibleTo($user) {
437: if ($user) {
438: $username = $user->username;
439: $uid = $user->id;
440: } else {
441: $username = 'Guest';
442: $uid = null;
443: }
444: if (!isset($this->_isVisibleTo[$username])) {
445: $accessLevel = $this->getAccessLevel($uid);
446:
447: $hasViewPermission = false;
448:
449: if (!$this->isHidden ()) {
450: switch ($accessLevel) {
451: case self::QUERY_ALL:
452: $hasViewPermission = true;
453: break;
454: case self::QUERY_PUBLIC:
455: if ($this->owner->getAttribute($this->visibilityAttr) ==
456: self::VISIBILITY_PUBLIC) {
457:
458: $hasViewPermission = true;
459: break;
460: }
461:
462:
463:
464: if ($this->owner->getAttribute($this->visibilityAttr) ==
465: self::VISIBILITY_GROUPS &&
466: (bool) $this->assignmentAttr &&
467: (bool) ($groupmatesRegex = self::getGroupmatesRegex()) &&
468: preg_match(
469: '/' . $groupmatesRegex . '/',
470: $this->owner->getAttribute($this->assignmentAttr))) {
471:
472: $hasViewPermission = true;
473: break;
474: }
475: case self::QUERY_SELF:
476:
477: if ($this->isAssignedTo($username, true)) {
478: $hasViewPermission = true;
479: break;
480: }
481: case self::QUERY_NONE:
482: break;
483: }
484: }
485: $this->_isVisibleTo[$username] = $hasViewPermission;
486: }
487: return $this->_isVisibleTo[$username];
488: }
489:
490: 491: 492: 493: 494: 495: 496:
497: public function getAssignedToCondition(
498: $includeAnyone = true, $alias = null, $username = null,
499: $paramsNamespace = 'X2PermissionsBehavior') {
500:
501: $username = $username === null ? Yii::app()->getSuName() : $username;
502: $prefix = empty($alias) ? '' : "$alias.";
503: $groupIdsRegex = self::getGroupIdRegex($username);
504: $condition = "(" . ($includeAnyone ?
505: ($prefix . $this->assignmentAttr . "='Anyone' OR assignedTo='' OR ") : '') .
506: $prefix . $this->assignmentAttr .
507: " REGEXP BINARY :" . $paramsNamespace . "userNameRegex";
508: $params = array(
509: ':' . $paramsNamespace . 'userNameRegex' => self::getUserNameRegex($username),
510: );
511: if ($groupIdsRegex !== '') {
512: $condition .= " OR $prefix" . $this->assignmentAttr .
513: " REGEXP BINARY :" . $paramsNamespace . "groupIdsRegex";
514: $params[':' . $paramsNamespace . 'groupIdsRegex'] = $groupIdsRegex;
515: }
516: $condition .= ')';
517: return array($condition, $params);
518: }
519:
520: 521: 522: 523: 524: 525:
526: public function getAssigneeNames($value = false) {
527: $assignment = !$value ? $this->owner->getAttribute($this->getAssignmentAttr()) : $value;
528: $assignees = !is_array($assignment) ? explode(', ', $assignment) : $assignment;
529:
530: $groupIds = array_filter($assignees, 'ctype_digit');
531: $userNames = array_diff($assignees, $groupIds);
532: $userNameParam = AuxLib::bindArray($userNames);
533: $userFullNames = !empty($userNames) ? array_map(function($u) {
534: return Formatter::fullName($u['firstName'], $u['lastName']);
535: }, Yii::app()->db->createCommand()->select('firstName,lastName')
536: ->from(User::model()->tableName())
537: ->where('username IN ' . AuxLib::arrToStrList(
538: array_keys($userNameParam)), $userNameParam)
539: ->queryAll()) : array();
540: $groupIdParam = AuxLib::bindArray($groupIds);
541: $groupNames = !empty($groupIds) ? Yii::app()->db->createCommand()
542: ->select('name')->from(Groups::model()->tableName())
543: ->where('id IN ' . AuxLib::arrToStrList(array_keys($groupIdParam)), $groupIdParam)
544: ->queryColumn() : array();
545: return array_merge($userFullNames, $groupNames);
546: }
547:
548: 549: 550: 551: 552: 553: 554:
555: public function getAssignees($getUsernamesFromGroups = false) {
556: $assignment = $this->owner->getAttribute($this->getAssignmentAttr());
557: $assignees = !is_array($assignment) ? explode(', ', $assignment) : $assignment;
558:
559: $assigneesNames = array();
560:
561: if ($getUsernamesFromGroups) {
562:
563: $groupIds = array_filter($assignees, 'ctype_digit');
564: if (!empty($groupIds)) {
565:
566: $groupIdParam = AuxLib::bindArray($groupIds);
567: $groupUsers = Yii::app()->db->createCommand()
568: ->select('username')
569: ->from('x2_group_to_user')
570: ->where('groupId IN ' .
571: AuxLib::arrToStrList(array_keys($groupIdParam)), $groupIdParam)
572: ->queryColumn();
573: foreach ($groupUsers as $username)
574: $assigneesNames[] = $username;
575: }
576: }
577: foreach ($assignees as $assignee) {
578: if ($assignee === 'Anyone') {
579: continue;
580: } else if (!ctype_digit($assignee)) {
581:
582: if (CActiveRecord::model('Profile')->exists('username=:u', array(
583: ':u' => $assignee))) {
584: $assigneesNames[] = $assignee;
585: }
586: }
587: }
588:
589: return array_unique($assigneesNames);
590: }
591:
592: 593: 594: 595: 596:
597: public static function getGroupIdRegex($username = null) {
598: if ($username !== null) {
599: $user = User::model()->findByAttributes(array('username' => $username));
600: if (!$user)
601: throw new CException('invalid username: ' . $username);
602: $userId = $user->id;
603: } else {
604: $userId = Yii::app()->getSuId();
605: }
606: $groupIds = Groups::getUserGroups($userId);
607: $groupIdRegex = '';
608: $i = 0;
609: foreach ($groupIds as $id) {
610: if ($i++ > 0)
611: $groupIdRegex .= '|';
612: $groupIdRegex .= '((^|, )' . $id . '($|,))';
613: }
614: return $groupIdRegex;
615: }
616:
617: 618: 619: 620: 621:
622: public static function getUsernameListRegex($usernames) {
623: return '(^|, )(' . implode('|', $usernames) . ')($|, )';
624: }
625:
626: public static function getGroupmatesRegex() {
627: $groupmates = Groups::getGroupmates(Yii::app()->getSuId());
628: return empty($groupmates) ? null : self::getUsernameListRegex($groupmates);
629: }
630:
631: }
632:
633: ?>
634: