Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • None
  • system
    • base
    • caching
    • console
    • db
      • ar
      • schema
    • validators
    • web
      • actions
      • auth
      • helpers
      • widgets
        • captcha
        • pagers
  • zii
    • widgets
      • grid

Classes

  • ActionMetaData
  • ActionText
  • Admin
  • AmorphousModel
  • ApiHook
  • APIModel
  • ChartSetting
  • ContactForm
  • ContactList
  • Credentials
  • Criteria
  • Dropdowns
  • Events
  • EventsData
  • Fields
  • FormLayout
  • Imports
  • InlineEmail
  • LeadRouting
  • Locations
  • LoginForm
  • Maps
  • Modules
  • Notes
  • Notification
  • PhoneNumber
  • Profile
  • Record
  • Relationships
  • Roles
  • RoleToPermission
  • RoleToUser
  • RoleToWorkflow
  • Rules
  • Session
  • SessionLog
  • Social
  • Tags
  • TempFile
  • Tips
  • Tours
  • TrackEmail
  • TriggerLog
  • URL
  • ViewLog
  • Widgets
  • X2List
  • X2ListCriterion
  • X2ListItem
  • X2Model
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: 
   3: /*****************************************************************************************
   4:  * X2Engine Open Source Edition is a customer relationship management program developed by
   5:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
   6:  * 
   7:  * This program is free software; you can redistribute it and/or modify it under
   8:  * the terms of the GNU Affero General Public License version 3 as published by the
   9:  * Free Software Foundation with the addition of the following permission added
  10:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
  11:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
  12:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  13:  * 
  14:  * This program is distributed in the hope that it will be useful, but WITHOUT
  15:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
  17:  * details.
  18:  * 
  19:  * You should have received a copy of the GNU Affero General Public License along with
  20:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
  21:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  22:  * 02110-1301 USA.
  23:  * 
  24:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
  25:  * California 95067, USA. or at email address contact@x2engine.com.
  26:  * 
  27:  * The interactive user interfaces in modified source and object code versions
  28:  * of this program must display Appropriate Legal Notices, as required under
  29:  * Section 5 of the GNU Affero General Public License version 3.
  30:  * 
  31:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
  32:  * these Appropriate Legal Notices must retain the display of the "Powered by
  33:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
  34:  * technical reasons, the Appropriate Legal Notices must display the words
  35:  * "Powered by X2Engine".
  36:  *****************************************************************************************/
  37: 
  38: /**
  39:  * This is the model class for table "x2_events".
  40:  * @package application.models
  41:  */
  42: class Events extends X2ActiveRecord {
  43: 
  44:     public $photo;
  45: 
  46:     /**
  47:      * Returns the static model of the specified AR class.
  48:      * @return Imports the static model class
  49:      */
  50:     public static function model($className = __CLASS__) {
  51:         return parent::model($className);
  52:     }
  53: 
  54:     /**
  55:      * @return string the associated database table name
  56:      */
  57:     public function tableName() {
  58:         return 'x2_events';
  59:     }
  60: 
  61:     public function attributeNames () {
  62:          return array_merge (
  63:             parent::attributeNames (), 
  64:             array (
  65:                 'photo',
  66:             )
  67:         );
  68:     }
  69: 
  70:     public function relations() {
  71:         $relationships = array();
  72:         $relationships = array_merge($relationships, array(
  73:             'children' => array(
  74:                 self::HAS_MANY, 'Events', 'associationId', 
  75:                 'condition' => 'children.associationType="Events"'),
  76:             'profile' => array(self::HAS_ONE, 'Profile', 
  77:                 array ('username' => 'user')),
  78:             'userObj' => array(self::HAS_ONE, 'User', 
  79:                 array ('username' => 'user')),
  80:             'media' => array (
  81:                 self::MANY_MANY, 'Media', 'x2_events_to_media(eventsId, mediaId)'),
  82:             // only use this if $this->type === 'media'
  83:             'legacyMedia' => array (
  84:                 self::HAS_ONE, 'Media', array ('id' => 'associationId')),
  85:         ));
  86:         return $relationships;
  87:     }
  88: 
  89:     public function scopes () {
  90:         return array (  
  91:             'comments' => array (
  92:                 'condition' => 'associationType="Events" and associationId=:id',
  93:                 'params' => array (':id' => $this->id)
  94:             )
  95:         );
  96:     }
  97: 
  98:     /**
  99:      * @return array validation rules for model attributes.
 100:      */
 101:     public function rules() {
 102:         // NOTE: you should only define rules for those attributes that
 103:         // will receive user inputs.
 104:         return array(
 105:         );
 106:     }
 107: 
 108:     public function save ($runValidation=true, $attributes=null) {
 109:         if ($this->photo) {
 110: 
 111:             // save related photo record
 112:             $transaction = Yii::app()->db->beginTransaction ();
 113:             try {
 114:                 // save the event
 115:                 $ret = parent::save ($runValidation, $attributes);
 116:                 if (!$ret) {
 117:                     throw new CException (implode (';', $this->getAllErrorMessages ()));
 118:                 }
 119: 
 120:                 // add media record for file
 121:                 $media = new Media; 
 122:                 $media->setAttributes (array (
 123:                     'fileName' => $this->photo->getName (),
 124:                     'mimetype' => $this->photo->type,
 125:                 ), false);
 126:                 $media->resolveNameConflicts ();
 127:                 if (!$media->save ()) {
 128:                     throw new CException (implode (';', $media->getAllErrorMessages ()));
 129:                 }
 130: 
 131:                 // save the file
 132:                 $tempName = $this->photo->getTempName ();
 133:                 $username = Yii::app()->user->getName ();
 134:                 if (!FileUtil::ccopy(
 135:                     $tempName, 
 136:                     "uploads/protected/media/$username/{$media->fileName}")) {
 137: 
 138:                     throw new CException ();
 139:                 }
 140: 
 141:                 // relate file to event
 142:                 $join = new RelationshipsJoin ('insert', 'x2_events_to_media');
 143:                 $join->eventsId = $this->id;
 144:                 $join->mediaId = $media->id;
 145:                 if (!$join->save ()) {
 146:                     throw new CException (implode (';', $join->getAllErrorMessages ()));
 147:                 }
 148: 
 149:                 $transaction->commit ();
 150:                 return $ret;
 151:             } catch (CException $e) {
 152:                 $transaction->rollback ();
 153:                 return false;
 154:             }
 155:         } else {
 156:             return parent::save ($runValidation, $attributes);
 157:         }
 158:     }
 159: 
 160:     public function renderFrameLink ($htmlOptions) {
 161:         if (Yii::app()->params->isMobileApp &&
 162:             !X2LinkableBehavior::isMobileLinkableRecordType ($this->associationType)) {
 163: 
 164:             return Events::parseModelName ($this->associationType);
 165:         }
 166:         return CHtml::link(
 167:             Events::parseModelName($this->associationType),
 168:             '#', array_merge($htmlOptions, array(
 169:                 'class' => 'action-frame-link',
 170:                 'data-action-id' => $this->associationId
 171:             ))
 172:         );
 173:     }
 174: 
 175:     public function getRecipient () {
 176:         $recipUser = Yii::app()->db->createCommand()
 177:                 ->select('username')
 178:                 ->from('x2_users')
 179:                 ->where('id=:id', array(':id' => $this->associationId))
 180:                 ->queryScalar();
 181:         $recipient = '';
 182:         if ($this->user != $recipUser && $this->associationId != 0) {
 183:             if (Yii::app()->user->getId() == $this->associationId) {
 184:                 $recipient = 
 185:                     CHtml::link(
 186:                         Yii::t('app', 'You'), 
 187:                         Yii::app()->params->profile->getUrl ());
 188:             } else {
 189:                 $recipient = User::getUserLinks($recipUser);
 190:             }
 191:         }
 192:         return $recipient;
 193:     }
 194: 
 195:     /**
 196:      * Parse an associationType field and resolve the model name
 197:      * @param string $model Model type to resolve
 198:      * @return string Model's name
 199:      */
 200:     public static function parseModelName($model) {
 201:          
 202: 
 203: 
 204:         $customModule = Modules::model()->findByAttributes(array(
 205:             'custom' => 1,
 206:             'name' => $model,
 207:             'moduleType'=>'module',
 208:         ));
 209:         if ($customModule) {
 210:             //$model = $customModule->title;
 211:             $model = Modules::itemDisplayName($customModule->name);
 212:             $model = strtolower($model);
 213:         } else {
 214:             switch ($model) {
 215:                 case 'Product':
 216:                     $model .= 's'; break;
 217:                 case 'Quote':
 218:                     $model .= 's'; break;
 219:                 case 'Opportunity':
 220:                     $model = str_replace('y', 'ies', $model); break;
 221:             }
 222:             $requestedModel = $model;
 223:             $model = Modules::displayName(false, ucfirst($model));
 224:             $model = strtolower($model);
 225:             if (empty($model)) {
 226:                 // If the model type couldn't be resolved, check for special cases
 227:                 // of models without a dedicated module
 228:                 if ($requestedModel === 'AnonContact')
 229:                     $model = 'anonymous contact';
 230:                 else if ($requestedModel === 'Campaign')
 231:                     $model = 'campaign';
 232:             }
 233:         }
 234:         return Yii::t('app', $model);
 235:     }
 236: 
 237:     public function getText(array $params = array(), array $htmlOptions = array()) {
 238:         $truncated = (array_key_exists('truncated', $params)) ? $params['truncated'] : false;
 239:         $requireAbsoluteUrl = (array_key_exists('requireAbsoluteUrl', $params)) ? $params['requireAbsoluteUrl'] : false;
 240:         $text = "";
 241:         $authorText = "";
 242:         if (Yii::app()->user->getName() == $this->user) {
 243:             $authorText = CHtml::link(
 244:                 Yii::t('app', 'You'), $this->profile->url, $htmlOptions);
 245:         } else {
 246:             $authorText = User::getUserLinks($this->user);
 247:         }
 248:         if (!empty($authorText)) {
 249:             $authorText.=" ";
 250:         }
 251:         switch ($this->type) {
 252:             case 'notif':
 253:                 $parent = X2Model::model('Notification')->findByPk($this->associationId);
 254:                 if (isset($parent)) {
 255:                     $text = $parent->getMessage();
 256:                 } else {
 257:                     $text = Yii::t('app', "Notification not found");
 258:                 }
 259:                 break;
 260:             case 'record_create':
 261:                 $actionFlag = false;
 262:                 if (class_exists($this->associationType)) {
 263:                     if (count(X2Model::model($this->associationType)
 264:                         ->findAllByPk($this->associationId)) > 0) {
 265: 
 266:                         if ($this->associationType == 'Actions') {
 267:                             $action = X2Model::model('Actions')->findByPk($this->associationId);
 268:                             if (isset($action) && 
 269:                                 (strcasecmp($action->associationType, 'contacts') === 0 || 
 270:                                  in_array($action->type, array('call', 'note', 'time')))) {
 271:                                 // Special considerations for publisher-created actions, i.e. call,
 272:                                 // note, time, and anything associated with a contact
 273:                                 $actionFlag = true;
 274:                                 // Retrieve the assigned user from the related action
 275:                                 $relatedAction = Actions::model()->findByPk ($this->associationId);
 276:                                 if ($relatedAction){
 277:                                     $authorText = User::getUserLinks ($relatedAction->assignedTo);
 278:                                 }
 279:                             }
 280:                         }
 281:                         if ($actionFlag) {
 282:                             $authorText = empty($authorText) ? 
 283:                                 Yii::t('app', 'Someone') : $authorText;
 284:                             switch ($action->type) {
 285:                                 case 'call':
 286:                                     $text = Yii::t('app', '{authorText} logged a call ({duration}) with {modelLink}.', array(
 287:                                                 '{authorText}' => $authorText,
 288:                                                 '{duration}' => empty($action->dueDate) || empty($action->completeDate) ? Yii::t('app', 'duration unknown') : Formatter::formatTimeInterval($action->dueDate, $action->completeDate, '{hoursMinutes}'),
 289:                                                 '{modelLink}' => X2Model::getModelLink($action->associationId, ucfirst($action->associationType), $requireAbsoluteUrl)
 290:                                     ));
 291:                                     break;
 292:                                 case 'note':
 293:                                     $text = Yii::t('app', '{authorText} posted a comment on {modelLink}.', array(
 294:                                                 '{authorText}' => $authorText,
 295:                                                 '{modelLink}' => X2Model::getModelLink($action->associationId, ucfirst($action->associationType), $requireAbsoluteUrl)
 296:                                     ));
 297:                                     break;
 298:                                 case 'time':
 299:                                     $text = Yii::t('app', '{authorText} logged {time} on {modelLink}.', array(
 300:                                                 '{authorText}' => $authorText,
 301:                                                 '{time}' => Formatter::formatTimeInterval($action->dueDate, $action->dueDate + $action->timeSpent, '{hoursMinutes}'),
 302:                                                 '{modelLink}' => X2Model::getModelLink($action->associationId, ucfirst($action->associationType))
 303:                                     ));
 304:                                     break;
 305:                                 default:
 306:                                     if (!empty($authorText) 
 307:                                             && $authorText != Yii::t('app', 'Anyone') 
 308:                                             && $authorText != Yii::t('app', 'Someone')) {
 309:                                         $text = Yii::t(
 310:                                             'app', 
 311:                                             "A new {actionLink} associated with the contact ".
 312:                                             "{contactLink} has been assigned to {authorText}", 
 313:                                             array(
 314:                                                 '{authorText}' => $authorText,
 315:                                                 '{actionLink}' => $this->renderFrameLink (
 316:                                                     $htmlOptions),
 317:                                                 '{contactLink}' => X2Model::getModelLink(
 318:                                                     $action->associationId, 
 319:                                                     ucfirst($action->associationType), 
 320:                                                     $requireAbsoluteUrl
 321:                                                 )
 322:                                             )
 323:                                         );
 324:                                     } else {
 325:                                         $text = Yii::t(
 326:                                             'app', 
 327:                                             "A new {actionLink} associated with the contact ".
 328:                                             "{contactLink} has been created.", 
 329:                                             array(
 330:                                                 '{actionLink}' => $this->renderFrameLink (
 331:                                                     $htmlOptions),
 332:                                                 '{contactLink}' => X2Model::getModelLink(
 333:                                                     $action->associationId, 
 334:                                                     ucfirst($action->associationType), 
 335:                                                     $requireAbsoluteUrl
 336:                                                 )
 337:                                             )
 338:                                         );
 339:                                     }
 340:                             }
 341:                         } else {
 342:                             if (!empty($authorText)) {
 343:                                 $modelLink = X2Model::getModelLink (
 344:                                     $this->associationId, $this->associationType, $requireAbsoluteUrl);
 345:                                 if (isset($action) && $this->user !== $action->assignedTo) {
 346:                                     // Include the assignee if this is for an action assigned to someone other than the creator
 347:                                     $translateText = "created a new {modelName} for {assignee}, {modelLink}";
 348:                                 } elseif (
 349:                                      
 350:                                     $modelLink !== '') {
 351: 
 352:                                     $translateText = "created a new {modelName}, {modelLink}";
 353:                                 } else {
 354:                                     $translateText = "created a new {modelName}";
 355:                                 }
 356:                                 $text = $authorText . Yii::t('app', $translateText, array(
 357:                                     '{modelName}' => Events::parseModelName($this->associationType),
 358:                                     '{modelLink}' => $modelLink,
 359:                                     '{assignee}' => isset($action) ? User::getUserLinks ($action->assignedTo) : null,
 360:                                 ));
 361:                             } else {
 362:                                 $text = Yii::t('app', "A new {modelName}, {modelLink}, has been created.", array('{modelName}' => Events::parseModelName($this->associationType),
 363:                                             '{modelLink}' => X2Model::getModelLink($this->associationId, $this->associationType, $requireAbsoluteUrl)));
 364:                             }
 365:                         }
 366:                     } else {
 367:                         $deletionEvent = X2Model::model('Events')->findByAttributes(array('type' => 'record_deleted', 'associationType' => $this->associationType, 'associationId' => $this->associationId));
 368:                         if (isset($deletionEvent)) {
 369:                             if (!empty($authorText)) {
 370:                                 $text = $authorText . Yii::t('app', "created a new {modelName}, {deletionText}. It has been deleted.", array(
 371:                                             '{modelName}' => Events::parseModelName($this->associationType),
 372:                                             '{deletionText}' => $deletionEvent->text,
 373:                                 ));
 374:                             } else {
 375:                                 $text = Yii::t('app', "A {modelName}, {deletionText}, was created. It has been deleted.", array(
 376:                                             '{modelName}' => Events::parseModelName($this->associationType),
 377:                                             '{deletionText}' => $deletionEvent->text,
 378:                                 ));
 379:                             }
 380:                         } else {
 381:                             if (!empty($authorText)) {
 382:                                 $text = $authorText . Yii::t('app', "created a new {modelName}, but it could not be found.", array(
 383:                                             '{modelName}' => Events::parseModelName($this->associationType)
 384:                                 ));
 385:                             } else {
 386:                                 $text = Yii::t('app', "A {modelName} was created, but it could not be found.", array(
 387:                                             '{modelName}' => Events::parseModelName($this->associationType)
 388:                                 ));
 389:                             }
 390:                         }
 391:                     }
 392:                 }
 393:                 break;
 394:             case 'weblead_create':
 395:                 if (count(X2Model::model($this->associationType)->findAllByPk($this->associationId)) > 0) {
 396:                     $text = Yii::t('app', "A new web lead has come in: {modelLink}", array(
 397:                                 '{modelLink}' => X2Model::getModelLink($this->associationId, $this->associationType)
 398:                     ));
 399:                 } else {
 400:                     $deletionEvent = X2Model::model('Events')->findByAttributes(array('type' => 'record_deleted', 'associationType' => $this->associationType, 'associationId' => $this->associationId));
 401:                     if (isset($deletionEvent)) {
 402:                         $text = Yii::t('app', "A new web lead has come in: {deletionText}. It has been deleted.", array(
 403:                                     '{deletionText}' => $deletionEvent->text
 404:                         ));
 405:                     } else {
 406:                         $text = Yii::t('app', "A new web lead has come in, but it could not be found.");
 407:                     }
 408:                 }
 409:                 break;
 410:             case 'record_deleted':
 411:                 if (class_exists($this->associationType)) {
 412:                     if (((Yii::app()->params->profile !== null && Yii::app()->params->profile->language != 'en' && !empty(Yii::app()->params->profile->language)) ||
 413:                             (Yii::app()->params->profile === null && Yii::app()->language !== 'en')) ||
 414:                             (strpos($this->associationType, 'A') !== 0 && strpos($this->associationType, 'E') !== 0 && strpos($this->associationType, 'I') !== 0 &&
 415:                             strpos($this->associationType, 'O') !== 0 && strpos($this->associationType, 'U') !== 0)) {
 416:                         if (!empty($authorText)) {
 417:                             $text = $authorText . Yii::t('app', "deleted a {modelType}, {text}", array(
 418:                                         '{modelType}' => Events::parseModelName($this->associationType),
 419:                                         '{text}' => $this->text
 420:                             ));
 421:                         } else {
 422:                             $text = Yii::t('app', "A {modelType}, {text}, was deleted", array(
 423:                                         '{modelType}' => Events::parseModelName($this->associationType),
 424:                                         '{text}' => $this->text
 425:                             ));
 426:                         }
 427:                     } else {
 428:                         if (!empty($authorText)) {
 429:                             $text = $authorText . Yii::t('app', "deleted an {modelType}, {text}.", array(
 430:                                         '{modelType}' => Events::parseModelName($this->associationType),
 431:                                         '{text}' => $this->text
 432:                             ));
 433:                         } else {
 434:                             $text = Yii::t('app', "An {modelType}, {text}, was deleted.", array(
 435:                                         '{modelType}' => Events::parseModelName($this->associationType),
 436:                                         '{text}' => $this->text
 437:                             ));
 438:                         }
 439:                     }
 440:                 }
 441:                 break;
 442:             case 'workflow_start':
 443:                 $action = X2Model::model('Actions')->findByPk($this->associationId);
 444:                 if (isset($action)) {
 445:                     $record = X2Model::model(ucfirst($action->associationType))->findByPk($action->associationId);
 446:                     if (isset($record)) {
 447:                         if (isset($action->workflowStage)) {
 448:                             $text = $authorText . Yii::t('app', 'started the process stage "{stage}" for the {modelName} {modelLink}', array(
 449:                                         '{stage}' => $action->workflowStage->name,
 450:                                         '{modelName}' => Events::parseModelName($action->associationType),
 451:                                         '{modelLink}' => X2Model::getModelLink($action->associationId, $action->associationType)
 452:                             ));
 453:                         } else {
 454:                             $text = $authorText . Yii::t('app', "started a process stage for the {modelName} {modelLink}, but the process stage could not be found.", array(
 455:                                         '{modelName}' => Events::parseModelName($action->associationType),
 456:                                         '{modelLink}' => X2Model::getModelLink($action->associationId, $action->associationType)
 457:                             ));
 458:                         }
 459:                     } else {
 460:                         $text = $authorText . Yii::t('app', "started a process stage, but the associated {modelName} was not found.", array(
 461:                                     '{modelName}' => Events::parseModelName($action->associationType)
 462:                         ));
 463:                     }
 464:                 } else {
 465:                     $text = $authorText . Yii::t('app', "started a process stage, but the process record could not be found.");
 466:                 }
 467:                 break;
 468:             case 'workflow_complete':
 469:                 $action = X2Model::model('Actions')->findByPk($this->associationId);
 470:                 if (isset($action)) {
 471:                     $record = X2Model::model(ucfirst($action->associationType))->findByPk($action->associationId);
 472:                     if (isset($record)) {
 473:                         if (isset($action->workflowStage)) {
 474:                             $text = $authorText . Yii::t('app', 'completed the process stage "{stageName}" for the {modelName} {modelLink}', array(
 475:                                         '{stageName}' => $action->workflowStage->name,
 476:                                         '{modelName}' => Events::parseModelName($action->associationType),
 477:                                         '{modelLink}' => X2Model::getModelLink($action->associationId, $action->associationType)
 478:                             ));
 479:                         } else {
 480:                             $text = $authorText . Yii::t('app', "completed a process stage for the {modelName} {modelLink}, but the process stage could not be found.", array(
 481:                                         '{modelName}' => Events::parseModelName($action->associationType),
 482:                                         '{modelLink}' => X2Model::getModelLink($action->associationId, $action->associationType)
 483:                             ));
 484:                         }
 485:                     } else {
 486:                         $text = $authorText . Yii::t('app', "completed a process stage, but the associated {modelName} was not found.", array(
 487:                                     '{modelName}' => Events::parseModelName($action->associationType)
 488:                         ));
 489:                     }
 490:                 } else {
 491:                     $text = $authorText . Yii::t('app', "completed a process stage, but the process record could not be found.");
 492:                 }
 493:                 break;
 494:             case 'workflow_revert':
 495:                 $action = X2Model::model('Actions')->findByPk($this->associationId);
 496:                 if (isset($action)) {
 497:                     $record = X2Model::model(ucfirst($action->associationType))->findByPk($action->associationId);
 498:                     if (isset($record)) {
 499:                         if (isset($action->workflowStage)) {
 500:                             $text = $authorText . Yii::t('app', 'reverted the process stage "{stageName}" for the {modelName} {modelLink}', array(
 501:                                         '{stageName}' => $action->workflowStage->name,
 502:                                         '{modelName}' => Events::parseModelName($action->associationType),
 503:                                         '{modelLink}' => X2Model::getModelLink($action->associationId, $action->associationType)
 504:                             ));
 505:                         } else {
 506:                             $text = $authorText . Yii::t('app', "reverted a process stage for the {modelName} {modelLink}, but the process stage could not be found.", array(
 507:                                         '{modelName}' => Events::parseModelName($action->associationType),
 508:                                         '{modelLink}' => X2Model::getModelLink($action->associationId, $action->associationType)
 509:                             ));
 510:                         }
 511:                     } else {
 512:                         $text = $authorText . Yii::t('app', "reverted a process stage, but the associated {modelName} was not found.", array(
 513:                                     '{modelName}' => Events::parseModelName($action->associationType)
 514:                         ));
 515:                     }
 516:                 } else {
 517:                     $text = $authorText . Yii::t('app', "reverted a process stage, but the process record could not be found.");
 518:                 }
 519:                 break;
 520:             case 'structured-feed':
 521:             case 'feed':
 522:                 if (Yii::app()->user->getName() == $this->user) {
 523:                     $author = CHtml::link(Yii::t('app', 'You'), Yii::app()->controller->createAbsoluteUrl('/profile/view', array('id' => Yii::app()->user->getId())), $htmlOptions) . " ";
 524:                 } else {
 525: 
 526:                     $author = User::getUserLinks($this->user);
 527:                 }
 528:                 $modifier = '';
 529:                 $recipient = $this->getRecipient ();
 530:                 if ($recipient) {
 531:                     $modifier = ' &raquo; ';
 532:                 }
 533:                 $text = $author . $modifier . $recipient . ": " . ($truncated ? strip_tags(Formatter::convertLineBreaks(x2base::convertUrls($this->text), true, true), '<a></a>') : $this->text);
 534:                 break;
 535:             case 'email_sent':
 536:                 if (class_exists($this->associationType)) {
 537:                     $model = X2Model::model($this->associationType)->findByPk($this->associationId);
 538:                     if (!empty($model)) {
 539:                         switch ($this->subtype) {
 540:                             case 'quote':
 541:                                 $text = $authorText . Yii::t('app', "issued the {transModelName} \"{modelLink}\" via email", array(
 542:                                             '{transModelName}' => Yii::t('quotes', 'quote'),
 543:                                             '{modelLink}' => X2Model::getModelLink($this->associationId, $this->associationType)
 544:                                 ));
 545:                                 break;
 546:                             case 'invoice':
 547:                                 $text = $authorText . Yii::t('app', "issued the {transModelName} \"{modelLink}\" via email", array(
 548:                                             '{transModelName}' => Yii::t('quotes', 'invoice'),
 549:                                             '{modelLink}' => X2Model::getModelLink($this->associationId, $this->associationType)
 550:                                 ));
 551:                                 break;
 552:                             default:
 553:                                 $text = $authorText . Yii::t('app', "sent an email to the {transModelName} {modelLink}", array(
 554:                                             '{transModelName}' => Events::parseModelName($this->associationType),
 555:                                             '{modelLink}' => X2Model::getModelLink($this->associationId, $this->associationType)
 556:                                 ));
 557:                                 break;
 558:                         }
 559:                     } else {
 560:                         $deletionEvent = X2Model::model('Events')->findByAttributes(array('type' => 'record_deleted', 'associationType' => $this->associationType, 'associationId' => $this->associationId));
 561:                         switch ($this->subtype) {
 562:                             case 'quote':
 563:                                 if (isset($deletionEvent)) {
 564:                                     $text = $authorText . Yii::t('app', "issued a quote by email, but that record has been deleted.");
 565:                                 } else {
 566:                                     $text = $authorText . Yii::t('app', "issued a quote by email, but that record could not be found.");
 567:                                 }
 568:                                 break;
 569:                             case 'invoice':
 570:                                 if (isset($deletionEvent)) {
 571:                                     $text = $authorText . Yii::t('app', "issued an invoice by email, but that record has been deleted.");
 572:                                 } else {
 573:                                     $text = $authorText . Yii::t('app', "issued an invoice by email, but that record could not be found.");
 574:                                 }
 575:                                 break;
 576:                             default:
 577:                                 if (isset($deletionEvent)) {
 578:                                     $text = $authorText . Yii::t('app', "sent an email to a {transModelName}, but that record has been deleted.", array(
 579:                                                 '{transModelName}' => Events::parseModelName($this->associationType)
 580:                                     ));
 581:                                 } else {
 582:                                     $text = $authorText . Yii::t('app', "sent an email to a {transModelName}, but that record could not be found.", array(
 583:                                                 '{transModelName}' => Events::parseModelName($this->associationType)
 584:                                     ));
 585:                                 }
 586:                                 break;
 587:                         }
 588:                     }
 589:                 }
 590:                 break;
 591:             case 'email_opened':
 592:                 switch ($this->subtype) {
 593:                     case 'quote':
 594:                         $emailType = Yii::t('app', 'a quote email');
 595:                         break;
 596:                     case 'invoice':
 597:                         $emailType = Yii::t('app', 'an invoice email');
 598:                         break;
 599:                     default:
 600:                         $emailType = Yii::t('app', 'an email');
 601:                         break;
 602:                 }
 603:                 if (X2Model::getModelName($this->associationType) && count(X2Model::model($this->associationType)->findAllByPk($this->associationId)) > 0) {
 604:                     $text = X2Model::getModelLink($this->associationId, $this->associationType) . Yii::t('app', ' has opened {emailType}!', array(
 605:                                 '{emailType}' => $emailType,
 606:                                 '{modelLink}' => X2Model::getModelLink($this->associationId, $this->associationType)
 607:                     ));
 608:                 } else {
 609:                     $text = Yii::t('app', "A contact has opened {emailType}, but that contact cannot be found.", array('{emailType}' => $emailType));
 610:                 }
 611:                 break;
 612:             case 'email_clicked':
 613:                 if (count(X2Model::model($this->associationType)->findAllByPk($this->associationId)) > 0) {
 614:                     $text = X2Model::getModelLink($this->associationId, $this->associationType) . Yii::t('app', ' opened a link in an email campaign and is visiting your website!', array(
 615:                                 '{modelLink}' => X2Model::getModelLink($this->associationId, $this->associationType)
 616:                     ));
 617:                 } else {
 618:                     $text = Yii::t('app', "A contact has opened a link in an email campaign, but that contact cannot be found.");
 619:                 }
 620:                 break;
 621:             case 'web_activity':
 622:                 if (count(X2Model::model($this->associationType)->findAllByPk($this->associationId)) > 0) {
 623:                     $text = "";
 624:                     
 625:                     $text .= X2Model::getModelLink($this->associationId, $this->associationType) . " " . Yii::t('app', "is currently on your website!");
 626:                 }else {
 627:                     $text = Yii::t('app', "A contact was on your website, but that contact cannot be found.");
 628:                 }
 629:                 break;
 630:             case 'case_escalated':
 631:                 if (count(X2Model::model($this->associationType)->findAllByPk($this->associationId)) > 0) {
 632:                     $case = X2Model::model($this->associationType)->findByPk($this->associationId);
 633:                     $text = $authorText . Yii::t('app', "escalated service case {modelLink} to {userLink}", array(
 634:                                 '{modelLink}' => X2Model::getModelLink($this->associationId, $this->associationType),
 635:                                 '{userLink}' => User::getUserLinks($case->escalatedTo)
 636:                     ));
 637:                 } else {
 638:                     $text = $authorText . Yii::t('app', "escalated a service case but that case could not be found.");
 639:                 }
 640:                 break;
 641:             case 'calendar_event':
 642:                 $action = X2Model::model('Actions')->findByPk($this->associationId);
 643:                 if (!Yii::app()->params->isMobileApp ||
 644:                     X2LinkableBehavior::isMobileLinkableRecordType ('Calendar')) {
 645: 
 646:                     $calendarText = CHtml::link(
 647:                         Yii::t('calendar', 'Calendar'), 
 648:                         Yii::app()->controller->createAbsoluteUrl(
 649:                             '/calendar/calendar/index'), $htmlOptions);
 650:                 } else {
 651:                     $calendarText = Yii::t('calendar', 'Calendar');
 652:                 }
 653:                 if (isset($action)) {
 654:                     $text = Yii::t('app', "{calendarText} event: {actionDescription}", array(
 655:                                 '{calendarText}' => $calendarText,
 656:                                 '{actionDescription}' => CHtml::encode($action->actionDescription)
 657:                     ));
 658:                 } else {
 659:                     $text = Yii::t('app', "{calendarText} event: event not found.", array(
 660:                                 '{calendarText}' => $calendarText
 661:                     ));
 662:                 }
 663:                 break;
 664:             case 'action_reminder':
 665:                 $action = X2Model::model('Actions')->findByPk($this->associationId);
 666:                 if (isset($action)) {
 667:                     $text = Yii::t('app', "Reminder! The following {action} is due now: {transModelLink}", array(
 668:                         '{transModelLink}' => X2Model::getModelLink($this->associationId, $this->associationType),
 669:                         '{action}' => strtolower(Modules::displayName (false, 'Actions')),
 670:                     ));
 671:                 } else {
 672:                     $text = Yii::t('app', "An {action} is due now, but the record could not be found.", array(
 673:                         '{action}' => strtolower(Modules::displayName (false, 'Actions')),
 674:                     ));
 675:                 }
 676:                 break;
 677:             case 'action_complete':
 678:                 $action = X2Model::model('Actions')->findByPk($this->associationId);
 679:                 if (isset($action)) {
 680:                     $text = $authorText . Yii::t('app', "completed the following {action}: {actionDescription}", array(
 681:                             '{actionDescription}' => X2Model::getModelLink(
 682:                                 $this->associationId, $this->associationType, $requireAbsoluteUrl),
 683:                             '{action}' => strtolower(Modules::displayName (false, 'Actions')),
 684:                         )
 685:                     );
 686:                 } else {
 687:                     $text = $authorText . Yii::t('app', "completed an {action}, but the record could not be found.", array(
 688:                         '{action}' => strtolower(Modules::displayName (false, 'Actions')),
 689:                     ));
 690:                 }
 691:                 break;
 692:             case 'doc_update':
 693:                 $text = $authorText . Yii::t('app', 'updated a document, {docLink}', array(
 694:                             '{docLink}' => X2Model::getModelLink($this->associationId, $this->associationType)
 695:                 ));
 696:                 break;
 697:             case 'email_from':
 698:                 if (class_exists($this->associationType)) {
 699:                     if (count(X2Model::model($this->associationType)->findAllByPk($this->associationId)) > 0) {
 700:                         $email = Yii::t('app', 'email');
 701:                         if ($this->associationType === 'Actions' && $this->subtype = 'email') {
 702:                             $action = X2Model::model('Actions')->findByPk ($this->associationId);
 703:                             if ($action) {
 704:                                 $multiAssociations = $action->getMultiassociations();
 705:                                 if (isset($multiAssociations['Contacts'])) {
 706:                                     // Link to the first multiassociated Contact
 707:                                     $contact = $multiAssociations['Contacts'][0];
 708:                                     $modelName = Events::parseModelName('Contacts');
 709:                                     $emailLink = X2Model::getModelLink($action->id, 'Actions');
 710:                                     $modelLink = X2Model::getModelLink($contact->id, 'Contacts');
 711:                                     $email = Yii::t('app', 'email') . ' '. $emailLink;
 712:                                 }
 713:                             }
 714:                         } else {
 715:                             $modelName = Events::parseModelName($this->associationType);
 716:                             $modelLink = X2Model::getModelLink($this->associationId, $this->associationType);
 717:                         }
 718:                         $text = $authorText . Yii::t('app', "received an {email} from a {transModelName}, {modelLink}", array(
 719:                                 '{email}' => $email,
 720:                                 '{transModelName}' => $modelName,
 721:                                 '{modelLink}' => $modelLink,
 722:                         ));
 723:                     } else {
 724:                         $deletionEvent = X2Model::model('Events')->findByAttributes(array('type' => 'record_deleted', 'associationType' => $this->associationType, 'associationId' => $this->associationId));
 725:                         if (isset($deletionEvent)) {
 726:                             $text = $authorText . Yii::t('app', "received an email from a {transModelName}, but that record has been deleted.", array(
 727:                                         '{transModelName}' => Events::parseModelName($this->associationType)
 728:                             ));
 729:                         } else {
 730:                             $text = $authorText . Yii::t('app', "received an email from a {transModelName}, but that record could not be found.", array(
 731:                                         '{transModelName}' => Events::parseModelName($this->associationType)
 732:                             ));
 733:                         }
 734:                     }
 735:                 }
 736: 
 737:                 break;
 738:             case 'voip_call':
 739:                 if (count(X2Model::model($this->associationType)->findAllByPk($this->associationId)) > 0) {
 740:                     $text = Yii::t('app', "{modelLink} called.", array(
 741:                                 '{modelLink}' => X2Model::getModelLink($this->associationId, $this->associationType)
 742:                     ));
 743:                 } else {
 744:                     $deletionEvent = X2Model::model('Events')->findByAttributes(array('type' => 'record_deleted', 'associationType' => $this->associationType, 'associationId' => $this->associationId));
 745:                     if (isset($deletionEvent)) {
 746:                         $text = $authorText . Yii::t('app', "A contact called, but the contact record has been deleted.");
 747:                     } else {
 748:                         $text = $authorText . Yii::t('app', "Call from a contact whose record could not be found.");
 749:                     }
 750:                 }
 751: 
 752:                 break;
 753:             case 'media':
 754:                 $media = $this->legacyMedia;
 755:                 $text = substr($authorText, 0, -1) . ": " . $this->text;
 756:                 if (isset($media)) {
 757:                     if (!$truncated) {
 758:                         $text.="<br>" . Media::attachmentSocialText($media->getMediaLink(), true, true);
 759:                     } else {
 760:                         $text.="<br>" . Media::attachmentSocialText($media->getMediaLink(), true, false);
 761:                     }
 762:                 } else {
 763:                     $text.="<br>Media file not found.";
 764:                 }
 765:                 break;
 766:             case 'topic_reply':
 767:                 $reply = TopicReplies::model()->findByPk($this->associationId);
 768:                 if (isset($reply)) {
 769:                     if (!Yii::app()->params->isMobileApp) {
 770:                         $topicLink = X2Html::link(
 771:                             $reply->topic->name, 
 772:                             Yii::app()->controller->createUrl(
 773:                                 '/topics/topics/view', 
 774:                                 array('id' => $reply->topic->id, 'replyId' => $reply->id)));
 775:                     } else {
 776:                         $topicLink = $reply->topic->name;
 777:                     }
 778:                     $text = Yii::t('topics', '{poster} posted a new reply to {topic}.', array(
 779:                         '{poster}' => $authorText,
 780:                         '{topic}' => $topicLink,
 781:                     ));
 782:                     // TODO: add topic preview
 783:                     // reliable rich text truncation tool needed
 784: //                    $text .= '<br/>';
 785: //                    $text .= '<blockquote class="topic-preview">';
 786: //                    $text .= $reply->text;
 787: //                    $text .= '</blockquote>';
 788:                 } else {
 789:                     $text = Yii::t('topics', '{poster} posted a new reply to a topic, but that reply has been deleted.', array(
 790:                                 '{poster}' => $authorText,
 791:                     ));
 792:                 }
 793:                 break;
 794:             default:
 795:                 $text = $authorText . CHtml::encode($this->text);
 796:                 break;
 797:         }
 798:         if ($truncated && mb_strlen($text, 'UTF-8') > 250) {
 799:             $text = mb_substr($text, 0, 250, 'UTF-8') . "...";
 800:         }
 801:         return $text;
 802:     }
 803: 
 804:     public static $eventLabels = array(
 805:         'feed' => 'Social Posts',
 806:         'comment' => 'Comment',
 807:         'record_create' => 'Records Created',
 808:         'record_deleted' => 'Records Deleted',
 809:         'action_reminder' => 'Action Reminders',
 810:         'action_complete' => 'Actions Completed',
 811:         'calendar_event' => 'Calendar Events',
 812:         'case_escalated' => 'Cases Escalated',
 813:         'email_opened' => 'Emails Opened',
 814:         'email_sent' => 'Emails Sent',
 815:         'notif' => 'Notifications',
 816:         'weblead_create' => 'Webleads Created',
 817:         'web_activity' => 'Web Activity',
 818:         'workflow_complete' => 'Process Complete',
 819:         'workflow_revert' => 'Process Reverted',
 820:         'workflow_start' => 'Process Started',
 821:         'doc_update' => 'Doc Updates',
 822:         'email_from' => 'Email Received',
 823:         'media' => 'Media',
 824:         'voip_call' => 'VOIP Call',
 825:         'topic_reply' => 'Topic Replies',
 826:     );
 827: 
 828:     public static function parseType($type) {
 829:         if (array_key_exists($type, self::$eventLabels))
 830:             $type = self::$eventLabels[$type];
 831: 
 832:         return Yii::t('app', $type);
 833:     }
 834: 
 835:     /**
 836:      * Delete expired events (expiration defined by "Event Deletion Time" admin setting
 837:      */
 838:     public static function deleteOldEvents() {
 839:         $dateRange = Yii::app()->settings->eventDeletionTime;
 840:         if (!empty($dateRange)) {
 841:             $dateRange = $dateRange * 24 * 60 * 60;
 842:             $deletionTypes = json_decode(Yii::app()->settings->eventDeletionTypes, true);
 843:             if (!empty($deletionTypes)) {
 844:                 $deletionTypes = "('" . implode("','", $deletionTypes) . "')";
 845:                 $time = time() - $dateRange;
 846:                 X2Model::model('Events')->deleteAll(
 847:                         'lastUpdated < ' . $time . ' AND type IN ' . $deletionTypes);
 848:             }
 849:         }
 850:     }
 851: 
 852:     /**
 853:      * @param User $user
 854:      * @return bool true if event is visible to user, false otherwiser 
 855:      */
 856:     public function isVisibleTo ($user) {
 857:         if (Yii::app()->params->isAdmin) return true;
 858:         if (!$user) return false;
 859: 
 860:         $assignedUser = null;
 861:         if ($this->associationType === 'User') {
 862:             $assignedUser = User::model ()->findByPk ($this->associationId);
 863:         }
 864:         switch (Yii::app()->settings->historyPrivacy) { 
 865:             case 'group':
 866:                 if (in_array ($this->user, Groups::getGroupmates ($user->id)) ||
 867:                     ($this->associationType === 'User' && 
 868:                      $assignedUser &&
 869:                      in_array ($assignedUser->username, Groups::getGroupmates ($user->id)))) {
 870: 
 871:                     return true;
 872:                 }
 873:                 // fall through
 874:             case 'user':
 875:                 if ($this->user === $user->username ||
 876:                     ($this->associationType === 'User' && 
 877:                      ($this->associationId === $user->id))) {
 878: 
 879:                     return true;
 880:                 }
 881:                 break;
 882:             default: // default history privacy (public or assigned)
 883:                 return ($this->user === $user->username || $this->visibility ||
 884:                     $this->associationType === 'User' && $this->associationId === $user->id);
 885:         }
 886:         return false;
 887:     }
 888: 
 889:     /**
 890:      * @param Profile $profile Profile to filter events by. Used for profile feeds other than
 891:      *  the current user's
 892:      * @return CDbCriteria Events access criteria based on history privacy admin setting
 893:      */
 894:     public function getAccessCriteria (Profile $profile=null) {
 895:         $criteria = new CDbCriteria;
 896: 
 897:         // ensures that condition string can be appended to other conditions
 898:         $criteria->addCondition ('TRUE');
 899:         if (!Yii::app()->params->isAdmin) {
 900:             $criteria->params[':getAccessCriteria_username'] = Yii::app()->user->getName ();
 901:             $criteria->params[':getAccessCriteria_userId'] = Yii::app()->user->getId ();
 902:             $userCondition = '
 903:                 user=:getAccessCriteria_username OR
 904:                 associationType="User" AND associationId=:getAccessCriteria_userId
 905:             ';
 906:             if (Yii::app()->settings->historyPrivacy == 'user') {
 907:                 $criteria->addCondition ($userCondition);
 908:             } elseif (Yii::app()->settings->historyPrivacy == 'group') {
 909:                 $criteria->addCondition ("
 910:                     $userCondition OR
 911:                     user IN (
 912:                         SELECT DISTINCT b.username 
 913:                         FROM x2_group_to_user a JOIN x2_group_to_user b 
 914:                         ON a.groupId=b.groupId 
 915:                         WHERE a.username=:getAccessCriteria_username
 916:                     ) OR (
 917:                         associationType='User' AND associationId in (
 918:                             SELECT DISTINCT b.id
 919:                             FROM x2_group_to_user a JOIN x2_group_to_user b
 920:                             ON a.groupId=b.groupId
 921:                             WHERE a.userId=:getAccessCriteria_userId
 922:                         )
 923:                     )");
 924:             } else { // default history privacy (public or assigned)
 925:                 $criteria->addCondition ("
 926:                     $userCondition OR visibility=1
 927:                 ");
 928:             }
 929:         }
 930: 
 931:         if ($profile) {
 932:             $criteria->params[':getAccessCriteria_profileUsername'] = $profile->username;
 933:             /* only show events associated with current profile which current user has
 934:               permission to see */
 935:             $criteria->addCondition ("user=:getAccessCriteria_profileUsername");
 936:             if (!Yii::app()->params->isAdmin) {
 937:                 $criteria->addCondition ("visibility=1");
 938:             }
 939:         }
 940:         return $criteria;
 941:     }
 942: 
 943:     /**
 944:      * Checks permissions for this event
 945:      * TODO: add unit test
 946:      */
 947:     private $_permissions;
 948:     public function checkPermissions ($action=null, $refresh = false) {
 949:         if (!isset ($this->_permissions) || $refresh) {
 950:             if (!Yii::app()->params->isAdmin) {
 951:                 $username = Yii::app()->user->getName ();
 952:                 $userId = Yii::app()->user->getId ();
 953:                 $userCondition = '
 954:                     user=:getAccessCriteria_username OR
 955:                     associationType="User" AND associationId=:getAccessCriteria_userId
 956:                 ';
 957:                 $edit = false;
 958:                 $view = $this->user === $username ||
 959:                     strtolower ($this->associationType) === 'user' && 
 960:                     $this->associationId == $userId;
 961:                 if (Yii::app()->settings->historyPrivacy == 'user') {
 962:                 } elseif (Yii::app()->settings->historyPrivacy == 'group') {
 963:                     $view |= in_array (
 964:                         strtolower ($this->user), 
 965:                         Yii::app()->db->createCommand ("
 966:                             SELECT LOWER(DISTINCT b.username)
 967:                             FROM x2_group_to_user a JOIN x2_group_to_user b 
 968:                             ON a.groupId=b.groupId 
 969:                             WHERE a.username=:username
 970:                         ")->queryColumn (array (':username' => $username))) ||
 971:                         $this->associationType==='User' && 
 972:                         in_array (
 973:                             $this->associationId, 
 974:                             Yii::app()->db->createCommand ("
 975:                                 SELECT DISTINCT b.id
 976:                                 FROM x2_group_to_user a JOIN x2_group_to_user b
 977:                                 ON a.groupId=b.groupId
 978:                                 WHERE a.userId=:userId
 979:                             ")->queryColumn (array (':userId' => $userId)));
 980:                 } else { // default history privacy (public or assigned)
 981:                     $view |= $this->visibility;
 982:                 }
 983: 
 984:                 $edit = $view && $this->type === 'feed' && $this->user === $username;
 985:                 $delete = $view && $this->type === 'feed' && 
 986:                     ($this->user === $username || $this->associationId == $userId);
 987:             } else {
 988:                 $view = $edit = $delete = true;
 989:             }
 990:             $this->_permissions = array (
 991:                 'view' => $view,
 992:                 'edit' => $edit,
 993:                 'delete' => $delete,
 994:             );
 995:         } else {
 996:             extract ($this->_permissions);
 997:         }
 998: 
 999:         if (!$action) 
1000:             return array('view' => (bool)$view, 'edit' => (bool)$edit, 'delete' => (bool)$delete);
1001:         switch ($action) {
1002:             case 'view':
1003:                 return (bool)$view;
1004:             case 'edit':
1005:                 return (bool)$edit;
1006:             case 'delete':
1007:                 return (bool)$delete;
1008:             default:
1009:                 return false;
1010:         }
1011:     }
1012: 
1013:     /**
1014:      * Returns events filtered with feed filters. Saves filters in session.
1015:      * @param Profile $profile
1016:      * @param bool $privateProfile whether to display public or private profile
1017:      * @param array $filters
1018:      * @param bool $filtersOn
1019:      * @return array
1020:      *  'dataProvider' => <object>
1021:      *  'lastUpdated' => <integer>
1022:      *  'lastId' => <integer>
1023:      */
1024:     public static function getFilteredEventsDataProvider(
1025:         $profile, $privateProfile, $filters, $filtersOn) {
1026: 
1027:         $params = array(':username' => Yii::app()->user->getName ());
1028:         $accessCriteria = Events::model ()->getAccessCriteria (!$privateProfile ? $profile : null);
1029: 
1030:         $visibilityCondition = '';
1031:         if ($filtersOn || isset($_SESSION['filters'])) {
1032:             if ($filters) {
1033:                 unset($_SESSION['filters']);
1034:             } else {
1035:                 $filters = $_SESSION['filters'];
1036:                 $filters = array_map(function ($n) {
1037:                     return implode(',', $n);
1038:                 }, $filters);
1039:                 $filters['default'] = false;
1040:             }
1041:             $parsedFilters = Events::parseFilters($filters, $params);
1042: 
1043:             $visibilityFilter = $parsedFilters['filters']['visibility'];
1044:             $userFilter = $parsedFilters['filters']['users'];
1045:             $typeFilter = $parsedFilters['filters']['types'];
1046:             $subtypeFilter = $parsedFilters['filters']['subtypes'];
1047: 
1048:             $default = $filters['default'];
1049:             $_SESSION['filters'] = array(
1050:                 'visibility' => $visibilityFilter,
1051:                 'users' => $userFilter,
1052:                 'types' => $typeFilter,
1053:                 'subtypes' => $subtypeFilter
1054:             );
1055:             if ($default == 'true') {
1056:                 Yii::app()->params->profile->defaultFeedFilters = json_encode(
1057:                     $_SESSION['filters']);
1058:                 Yii::app()->params->profile->save();
1059:             }
1060:             $visibilityCondition .= $parsedFilters['conditions']['visibility'];
1061:             $userCondition = $parsedFilters['conditions']['users'];
1062:             $typeCondition = $parsedFilters['conditions']['types'];
1063:             $subtypeCondition = $parsedFilters['conditions']['subtypes'];
1064: 
1065:             $condition = "(associationType is null or associationType!='Events') AND 
1066:                 (type!='action_reminder' " .
1067:                 "OR user=:username) AND " .
1068:                 "(type!='notif' OR user=:username)" .
1069:                 $visibilityCondition . $userCondition . $typeCondition . $subtypeCondition;
1070:             $_SESSION['feed-condition'] = $condition;
1071:             $_SESSION['feed-condition-params'] = $params;
1072:         } else {
1073:             $condition = "(associationType is null or associationType!='Events') AND " .
1074:                     "(type!='action_reminder' OR user=:username) " .
1075:                     "AND (type!='notif' OR user=:username)" .
1076:                     $visibilityCondition;
1077:         }
1078: 
1079:         $condition.= " AND timestamp <= " . time();
1080:         $condition .= ' AND ('.$accessCriteria->condition.')';
1081: 
1082:         if (!isset($_SESSION['lastEventId'])) {
1083: 
1084:             $lastId = Yii::app()->db->createCommand()
1085:                 ->select('id')
1086:                 ->from('x2_events')
1087:                 ->where($condition, array_merge ($params, $accessCriteria->params))
1088:                 ->order('timestamp DESC, id DESC')
1089:                 ->limit(1)
1090:                 ->queryScalar();
1091: 
1092:             $_SESSION['lastEventId'] = $lastId;
1093:         } else {
1094:             $lastId = $_SESSION['lastEventId'];
1095:         }
1096:         $lastTimestamp = Yii::app()->db->createCommand()
1097:             ->select('MAX(timestamp)')
1098:             ->from('x2_events')
1099:             ->where($condition, array_merge ($params, $accessCriteria->params))
1100:             ->order('timestamp DESC, id DESC')
1101:             ->limit(1)
1102:             ->queryScalar();
1103:         if (empty($lastTimestamp)) {
1104:             $lastTimestamp = 0;
1105:         }
1106:         if (isset($_SESSION['lastEventId'])) {
1107:             $condition.=" AND id <= :lastEventId AND sticky = 0";
1108:             $params[':lastEventId'] = $_SESSION['lastEventId'];
1109:         }
1110: 
1111: 
1112:         $paginationClass = 'CPagination';
1113:          
1114:         $dataProvider = new CActiveDataProvider('Events', array(
1115:             'criteria' => array(
1116:                 'condition' => $condition,
1117:                 'order' => 'timestamp DESC, id DESC',
1118:                 'params' => array_merge ($params, $accessCriteria->params),
1119:             ),
1120:             'pagination' => array(
1121:                 'class' => $paginationClass,
1122:                 'pageSize' => 20
1123:             ),
1124:         ));
1125: 
1126:         return array(
1127:             'dataProvider' => $dataProvider,
1128:             'lastTimestamp' => $lastTimestamp,
1129:             'lastId' => $lastId
1130:         );
1131:     }
1132: 
1133:     private static function parseFilters($filters, &$params) {
1134:         unset($filters['filters']);
1135:         $visibility = $filters['visibility'];
1136:         $visibility = str_replace('Public', '1', $visibility);
1137:         $visibility = str_replace('Private', '0', $visibility);
1138:         $visibilityFilter = explode(",", $visibility);
1139:         if ($visibility != "") {
1140:             $visibilityParams = AuxLib::bindArray($visibilityFilter, 'visibility');
1141:             $params = array_merge($params, $visibilityParams);
1142:             $visibilityCondition = " AND visibility NOT IN (" . 
1143:                 implode(',', array_keys($visibilityParams)) . ")";
1144:         } else {
1145:             $visibilityCondition = "";
1146:             $visibilityFilter = array();
1147:         }
1148: 
1149:         $users = $filters['users'];
1150:         if ($users != "") {
1151:             $users = explode(",", $users);
1152:             $users[] = '';
1153:             $users[] = 'api';
1154:             $userFilter = $users;
1155:             if (sizeof($users)) {
1156:                 $usersParams = AuxLib::bindArray($users, 'users');
1157:                 $params = array_merge($params, $usersParams);
1158:                 $userCondition = " AND (user NOT IN (" . 
1159:                     implode(',', array_keys($usersParams)) . ")";
1160:             } else {
1161:                 $userCondition = "(";
1162:             }
1163:             if (!in_array('Anyone', $users)) {
1164:                 $userCondition.=" OR user IS NULL)";
1165:             } else {
1166:                 $userCondition.=")";
1167:             }
1168:         } else {
1169:             $userCondition = "";
1170:             $userFilter = array();
1171:         }
1172: 
1173:         $types = $filters['types'];
1174:         if ($types != "") {
1175:             $types = explode(",", $types);
1176:             $typeFilter = $types;
1177:             $typesParams = AuxLib::bindArray($types, 'types');
1178:             $params = array_merge($params, $typesParams);
1179:             $typeCondition = " AND (type NOT IN (" . 
1180:                 implode(',', array_keys($typesParams)) . ") OR important=1)";
1181:         } else {
1182:             $typeCondition = "";
1183:             $typeFilter = array();
1184:         }
1185:         $subtypes = $filters['subtypes'];
1186:         if (is_array($types) && $subtypes != "") {
1187:             $subtypes = explode(",", $subtypes);
1188:             $subtypeFilter = $subtypes;
1189:             if (sizeof($subtypes)) {
1190:                 $subtypeParams = AuxLib::bindArray($subtypes, 'subtypes');
1191:                 $params = array_merge($params, $subtypeParams);
1192:                 $subtypeCondition = " AND (
1193:                     type!='feed' OR subtype NOT IN (" . 
1194:                         implode(',', array_keys($subtypeParams)) . ") OR important=1)";
1195:             } else {
1196:                 $subtypeCondition = "";
1197:             }
1198:         } else {
1199:             $subtypeCondition = "";
1200:             $subtypeFilter = array();
1201:         }
1202:         $ret = array(
1203:             'filters' => array(
1204:                 'visibility' => $visibilityFilter,
1205:                 'users' => $userFilter,
1206:                 'types' => $typeFilter,
1207:                 'subtypes' => $subtypeFilter,
1208:             ),
1209:             'conditions' => array(
1210:                 'visibility' => $visibilityCondition,
1211:                 'users' => $userCondition,
1212:                 'types' => $typeCondition,
1213:                 'subtypes' => $subtypeCondition,
1214:             ),
1215:             'params' => $params,
1216:         );
1217:         return $ret;
1218:     }
1219: 
1220:     /**
1221:      * TODO: merge this method with getFilteredEventsDataProvider, and remove reliance on SESSION, 
1222:      *  regenerating condition on each request instead of storing it
1223:      * @param int $lastEventId
1224:      * @param string $lastTimestamp
1225:      * @param object $profile The current user's profile model
1226:      * @param object $profileId The profile model for which events are being requested.
1227:      */
1228:     public static function getEvents(
1229:         $lastEventId, $lastTimestamp, $limit = null, $profile = null, $privateProfile = true) {
1230: 
1231:         $user = Yii::app()->user->getName();
1232:         $criteria = new CDbCriteria();
1233:         $prefix = ':getEvents'; // to prevent name collisions with feed-condition-params
1234:         $sqlParams = array(
1235:             $prefix . 'username' => $user,
1236:             $prefix . 'maxTimestamp' => time(),
1237:         );
1238:         $parameters = array('order' => 'timestamp DESC, id DESC');
1239:         if (!is_null($limit) && is_numeric($limit)) {
1240:             $parameters['limit'] = $limit;
1241:         }
1242: 
1243:         $sqlParams[$prefix . 'id'] = $lastEventId;
1244:         $sqlParams[$prefix . 'timestamp'] = $lastTimestamp;
1245:         $accessCriteria = Events::model ()->getAccessCriteria (!$privateProfile ? $profile : null);
1246:         if (isset($_SESSION['feed-condition']) && isset($_SESSION['feed-condition-params'])) {
1247:             $sqlParams = array_merge($sqlParams, $_SESSION['feed-condition-params']);
1248:             $condition = $_SESSION['feed-condition'] . " AND 
1249:                 (`type`!='action_reminder' OR `user`=" . $prefix . "username) AND 
1250:                 (`type`!='notif' OR `user`=" . $prefix . "username) AND 
1251:                 (id > " . $prefix . "id AND (timestamp > " . $prefix . "timestamp AND timestamp < " . $prefix . "maxTimestamp))";
1252:         } else {
1253:             $condition = '(id > ' . $prefix . 'id AND (timestamp > ' . $prefix . 'timestamp AND timestamp < ' . $prefix . 'maxTimestamp)) AND 
1254:                  (`associationType` is null or `associationType`!="Events")' . " AND 
1255:                  (`type`!='action_reminder' OR `user`=" . $prefix . "username) AND 
1256:                  (`type`!='notif' OR `user`=" . $prefix . "username)";
1257:         }
1258: 
1259:         $sqlParams = array_merge($sqlParams, $accessCriteria->params);
1260:         $condition .= " AND ($accessCriteria->condition)";
1261: 
1262:         $parameters['condition'] = $condition;
1263:         $parameters['params'] = $sqlParams;
1264:         $criteria->scopes = array('findAll' => array($parameters));
1265: 
1266:         return array(
1267:             'events' => X2Model::model('Events')->findAll($criteria),
1268:         );
1269:     }
1270: 
1271:     public static function generateFeedEmail($filters, $userId, $range, $limit, $eventId, $deleteKey) {
1272:         $image = Yii::app()->getAbsoluteBaseUrl(true).'/images/x2engine.png';
1273: 
1274:         $msg = "<div id='wrap' style='width:6.5in;height:9in;margin-top:auto;margin-left:auto;margin-bottom:auto;margin-right:auto;'><html><body><center>";
1275:         $msg .= '<table border="0" cellpadding="0" cellspacing="0" height="100%" id="top-activity" style="background: white; font-family: \'Helvetica Neue\', \'Helvetica\', Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; font-size: 14px; line-height: 1; color: #222222; position: relative; -webkit-font-smoothing: antialiased;background-color:#FAFAFA;height:25% !important; margin:0; padding:0; width:100% !important;" width="100%">'
1276:                 . "<tbody><tr><td align=\"center\" style=\"padding-top:20px;\" valign=\"top\">"
1277:                 . '<table border="0" cellpadding="0" cellspacing="0" id="templateContainer" style="border: 1px solid #DDDDDD;background-color:#FFFFFF;" width="600"><tbody>';
1278:         $msg .= '<tr>
1279:                     <td align="center" valign="top"><!-- // Begin Template Header \\ -->
1280:             <table border="0" cellpadding="0" cellspacing="0" id="templateHeader" width="600">
1281:                             <tbody>
1282:                 <tr>
1283:                                     <td class="headerContent" style="color:#202020;font-weight:bold;line-height:100%;padding:0;text-align:center;vertical-align:middle;font-family: inherit;font-weight: normal;font-size: 14px;margin-bottom: 17px"><img id="headerImage campaign-icon" src="' . $image .'" style="border:0; height:auto; line-height:100%; outline:none; text-decoration:none;max-width:600px;" /></td>
1284:                                 </tr>
1285:                                 <tr>
1286:                                     <td style="color:#202020;font-weight:bold;padding:5px;text-align:center;vertical-align:middle;font-family: inherit;font-weight: normal;font-size: 14px;"><h2>' . Yii::t('profile', 'Activity Feed Report') . '</h2></td>
1287:                                 </tr>
1288:                             </tbody>
1289:             </table>
1290:                     <hr width="60%"></td><!-- // End Template Header \\ -->
1291:         </tr>';
1292: 
1293:         $msg.='<tr><td align="center" valign="top"><!-- // Begin Template Body \\ -->'
1294:                 . '<table border="0" cellpadding="0" cellspacing="0" id="templateBody" width="600"><tbody><tr>'
1295:                 . '<td valign="top"><!-- // Begin Module: Standard Content \\ -->'
1296:                 . '<table border="0" cellpadding="20" cellspacing="0" width="100%"><tbody>';
1297: 
1298:         $params = array();
1299: 
1300:         $userRecord = X2Model::model('Profile')->findByPk($userId);
1301:         $params[':username'] = $userRecord->username;
1302: 
1303:         $parsedFilters = Events::parseFilters($filters, $params);
1304: 
1305:         $visibilityCondition = $parsedFilters['conditions']['visibility'];
1306:         $accessCriteria = Events::model ()->getAccessCriteria ();
1307:         $userCondition = $parsedFilters['conditions']['users'];
1308:         $typeCondition = $parsedFilters['conditions']['types'];
1309:         $subtypeCondition = $parsedFilters['conditions']['subtypes'];
1310: 
1311:         $condition = "type!='comment' AND (type!='action_reminder' " .
1312:                 "OR user=:username) AND " .
1313:                 "(type!='notif' OR user=:username)" .
1314:                 $visibilityCondition . $userCondition . $typeCondition . $subtypeCondition . 
1315:                 ' AND ('.$accessCriteria->condition.')';
1316:         switch ($range) {
1317:             case 'daily':
1318:                 $timeRange = 24 * 60 * 60;
1319:                 break;
1320:             case 'weekly':
1321:                 $timeRange = 7 * 24 * 60 * 60;
1322:                 break;
1323:             case 'monthly':
1324:                 $timeRange = 30 * 24 * 60 * 60;
1325:                 break;
1326:             default:
1327:                 $timeRange = 24 * 60 * 60;
1328:                 break;
1329:         }
1330:         $condition .= " AND timestamp BETWEEN " . (time() - $timeRange) . " AND " . time();
1331: 
1332:         $topTypes = Yii::app()->db->createCommand()
1333:                 ->select('type, COUNT(type)')
1334:                 ->from('x2_events')
1335:                 ->where($condition, array_merge ($params, $accessCriteria->params))
1336:                 ->group('type')
1337:                 ->order('COUNT(type) DESC')
1338:                 ->limit(5)
1339:                 ->queryAll();
1340: 
1341:         $topUsers = Yii::app()->db->createCommand()
1342:                 ->select('user, COUNT(user)')
1343:                 ->from('x2_events')
1344:                 ->where($condition, array_merge ($params, $accessCriteria->params))
1345:                 ->group('user')
1346:                 ->order('COUNT(user) DESC')
1347:                 ->limit(5)
1348:                 ->queryAll();
1349: 
1350:         $msg .= "<tr><td style='text-align:center;'>";
1351:         $msg .= "<div>" . Yii::t('profile', "Here's your {range} update on what's been going on in X2Engine!", array(
1352:                     '{range}' => Yii::t('profile', $range))) . "</div><br>"
1353:                 . "<div>Time Range: <em>" . Formatter::formatDateTime(time() - $timeRange) . "</em> to <em>" . Formatter::formatDateTime(time()) . "</em></div>";
1354:         $msg .= "</tr></td>";
1355: 
1356:         $msg .= "<tr><td><table width='100%'><tbody>";
1357:         $msg .= "<tr><th>" . Yii::t('profile', "Top Activity") . "</th><th>" . Yii::t('profile', "Top Users") . "</th></tr>";
1358:         for ($i = 0; $i < 5; $i++) {
1359:             $msg .= "<tr><td style='text-align:center;'>";
1360:             if (isset($topTypes[$i])) {
1361:                 $type = Events::parseType($topTypes[$i]['type']);
1362:                 $count = $topTypes[$i]['COUNT(type)'];
1363:                 $msg .= $count . " " . $type;
1364:             }
1365:             $msg .= "</td><td style='text-align:center;'>";
1366:             if (isset($topUsers[$i]) && $topUsers[$i]['COUNT(user)'] > 0) {
1367:                 $username = User::getUserLinks($topUsers[$i]['user'], false, true);
1368:                 $count = $topUsers[$i]['COUNT(user)'];
1369:                 $msg .= $count . " " . Yii::t('profile', "events from") . " " . $username . ".";
1370:             }
1371:             $msg .= "</td></tr>";
1372:         }
1373:         $msg .= "</tbody></table></td></tr>";
1374:         $msg .= "<tr><td style='text-align:center'><hr width='60%'>";
1375:         $msg .= "<tr><td style='text-align:center;'>" .
1376:                 Yii::t('profile', "Here's the {limit} most recent items on the Activity Feed.", array('{limit}' => $limit))
1377:                 . "</td></tr>";
1378:         $msg .= "</td></tr>";
1379:         $msg .= "<tr><td style='text-align:center'><hr width='60%'><table><tbody>";
1380:         $events = new CActiveDataProvider('Events', array(
1381:             'criteria' => array(
1382:                 'condition' => $condition,
1383:                 'order' => 'timestamp DESC',
1384:                 'params' => array_merge ($params, $accessCriteria->params),
1385:             ),
1386:             'pagination' => array(
1387:                 'pageSize' => $limit
1388:             ),
1389:         ));
1390: 
1391:         foreach ($events->getData() as $event) {
1392:             $msg .= "<tr>";
1393:             $avatar = Yii::app()->db->createCommand()
1394:                 ->select('avatar')
1395:                 ->from('x2_profile')
1396:                 ->where('username=:user', array(':user' => $event->user))
1397:                 ->queryScalar();
1398:             if (!empty($avatar) && file_exists($avatar)) {
1399:                 $avatar = Profile::renderAvatarImage($id, 45, 45);
1400:             } else {
1401:                 $avatar = X2Html::x2icon('profile-large',
1402:                                 array(
1403:                             'class' => 'avatar-image default-avatar',
1404:                             'style' => "font-size: ${dimensionLimit}px",
1405:                 ));
1406:             }
1407:             $typeFile = $event->type;
1408:             if (in_array($event->type, array('email_sent', 'email_opened'))) {
1409:                 if (in_array($event->subtype, array('quote', 'invoice')))
1410:                     $typeFile .= "_{$event->subtype}";
1411:             }
1412:             if ($event->type == 'record_create') {
1413:                 switch ($event->subtype) {
1414:                     case 'call':
1415:                         $typeFile = 'voip_call';
1416:                         break;
1417:                     case 'time':
1418:                         $typeFile = 'log_time';
1419:                         break;
1420:                 }
1421:             }
1422:             $img = $avatar;
1423:             if (file_exists(Yii::app()->theme->getBasePath() . '/images/eventIcons/' . $typeFile . '.png')) {
1424:                 $imgFile = Yii::app()->getAbsoluteBaseUrl() . '/themes/' . Yii::app()->theme->getName() . '/images/eventIcons/' . $typeFile . '.png';
1425:                 $img = CHtml::image($imgFile, '',
1426:                                 array(
1427:                             'style' => 'width:45px;height:45px;float:left;margin-right:5px;',
1428:                 ));
1429:             }
1430: 
1431:             $msg .= "<td>" . $img . "</td>";
1432:             $msg .= "<td style='text-align:left'><span class='event-text'>" . $event->getText(array('requireAbsoluteUrl' => true), array('style' => 'text-decoration:none;')) . "</span></td>";
1433:             $msg .= "</tr>";
1434:         }
1435:         $msg .= "</tbody></table></td></tr>";
1436: 
1437:         $msg .= "<tr><td style='text-align:center'><hr width='60%'><table><tbody>";
1438:         $msg .= Yii::t('profile', "To stop receiving this report, ") . CHtml::link(Yii::t('profile', 'click here'), Yii::app()->getAbsoluteBaseUrl() . '/index.php/profile/deleteActivityReport?id=' . $eventId . '&deleteKey=' . $deleteKey);
1439:         $msg .= "</tbody></table></td></tr>";
1440: 
1441:         $msg .= '</tbody></table></td></tr></tbody></table></td></tr>';
1442: 
1443:         $msg .= "<tbody></table></td></tr></tbody></table></center></body></html></div>";
1444: 
1445:         return $msg;
1446:     }
1447: 
1448:     public function isTypeFeed () {
1449:         return $this->type === 'feed' || $this->type === 'structured-feed';
1450:     }
1451: 
1452:     protected function beforeSave() {
1453:         if (empty($this->timestamp))
1454:             $this->timestamp = time();
1455:         $this->lastUpdated = time();
1456:         if (!empty($this->user) && $this->isNewRecord) {
1457:             $eventsData = X2Model::model('EventsData')->findByAttributes(array('type' => $this->type, 'user' => $this->user));
1458:             if (isset($eventsData)) {
1459:                 $eventsData->count++;
1460:             } else {
1461:                 $eventsData = new EventsData;
1462:                 $eventsData->user = $this->user;
1463:                 $eventsData->type = $this->type;
1464:                 $eventsData->count = 1;
1465:             }
1466:             $eventsData->save();
1467:         }
1468:         if ($this->type == 'record_deleted') {
1469:             $this->text = preg_replace('/(<script(.*?)>|<\/script>)/', '', $this->text);
1470:         }
1471:         return parent::beforeSave();
1472:     }
1473: 
1474:     protected function beforeDelete() {
1475:         if (!empty($this->children)) {
1476:             foreach ($this->children as $child) {
1477:                 $child->delete();
1478:             }
1479:         }
1480:         return parent::beforeDelete();
1481:     }
1482: 
1483:     /**
1484:      * @return array customized attribute labels (name=>label)
1485:      */
1486:     public function attributeLabels() {
1487:         return array(
1488:             'id' => Yii::t('admin', 'ID'),
1489:             'type' => Yii::t('admin', 'Type'),
1490:             'text' => Yii::t('admin', 'Text'),
1491:             'associationType' => Yii::t('admin', 'Association Type'),
1492:             'associationId' => Yii::t('admin', 'Association ID'),
1493:         );
1494:     }
1495: 
1496: }
1497: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0