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

  • AdminController
  • Api2Controller
  • ApiController
  • BugReportsController
  • CommonSiteControllerBehavior
  • ProfileController
  • RelationshipsController
  • SearchController
  • SiteController
  • StudioController
  • TemplatesController
  • TopicsController
  • x2base
  • X2Controller
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /*****************************************************************************************
  3:  * X2Engine Open Source Edition is a customer relationship management program developed by
  4:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
  5:  * 
  6:  * This program is free software; you can redistribute it and/or modify it under
  7:  * the terms of the GNU Affero General Public License version 3 as published by the
  8:  * Free Software Foundation with the addition of the following permission added
  9:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 10:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
 11:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 12:  * 
 13:  * This program is distributed in the hope that it will be useful, but WITHOUT
 14:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 15:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 16:  * details.
 17:  * 
 18:  * You should have received a copy of the GNU Affero General Public License along with
 19:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
 20:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 21:  * 02110-1301 USA.
 22:  * 
 23:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
 24:  * California 95067, USA. or at email address contact@x2engine.com.
 25:  * 
 26:  * The interactive user interfaces in modified source and object code versions
 27:  * of this program must display Appropriate Legal Notices, as required under
 28:  * Section 5 of the GNU Affero General Public License version 3.
 29:  * 
 30:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 31:  * these Appropriate Legal Notices must retain the display of the "Powered by
 32:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
 33:  * technical reasons, the Appropriate Legal Notices must display the words
 34:  * "Powered by X2Engine".
 35:  *****************************************************************************************/
 36: 
 37: /**
 38:  * Base controller for all application controllers with CRUD operations
 39:  *
 40:  * @package application.controllers
 41:  */
 42: abstract class x2base extends X2Controller {
 43:     /*
 44:      * Class design:
 45:      * Basic create method (mostly overridden, but should have basic functionality to avoid using Gii
 46:      * Index method: Ability to pass a data provider to filter properly
 47:      * Delete method -> unviersal.
 48:      * Basic user permissions (access rules)
 49:      * Update method -> Similar to create
 50:      * View method -> Similar to index
 51:      */
 52: 
 53:     /**
 54:      * @var string the default layout for the controller view. Defaults to '//layouts/column1',
 55:      * meaning using a single column layout. See 'protected/views/layouts/column1.php'.
 56:      */
 57:     public $layout = '//layouts/column3';
 58: 
 59:     /**
 60:      * @var bool $noBackdrop If true, then the content will not have a backdrop
 61:      */
 62:     public $noBackdrop = false;
 63:     
 64:     /**
 65:      * @var array context menu items. This property will be assigned to {@link CMenu::items}.
 66:      */
 67:     public $menu = array();
 68: 
 69:     /**
 70:      * @var array the breadcrumbs of the current page. The value of this property will
 71:      * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links}
 72:      * for more details on how to specify this property.
 73:      */
 74:     public $breadcrumbs = array();
 75:     public $portlets = array(); // This is the array of widgets on the sidebar.
 76:     public $leftPortlets = array(); // additional menu blocks on the left mneu
 77:     public $modelClass;
 78:     public $actionMenu = array();
 79:     public $leftWidgets = array();
 80: 
 81: 
 82:     private $_pageTitle;
 83: 
 84:     /**
 85:      * @return array action filters
 86:      */
 87:     public function filters() {
 88:         return array(
 89:             array(
 90:                 'application.components.filters.X2AjaxHandlerFilter',
 91:             ),
 92:             array(
 93:                 'application.components.filters.FileUploadsFilter'
 94:             ),
 95:             'setPortlets', // performs widget ordering and show/hide on each page
 96:         );
 97:     }
 98: 
 99:     public function behaviors() {
100:         return array(
101:             'CommonControllerBehavior' => array(
102:                 'class' => 'application.components.CommonControllerBehavior'),
103:             'PermissionsBehavior' => array(
104:                 'class' => 'application.components.permissions.'.Yii::app()->params->controllerPermissions),
105:         );
106:     }
107: 
108:     protected function beforeAction($action = null) {
109:         return $this->PermissionsBehavior->beforeAction($action) && parent::beforeAction ($action);
110:     }
111: 
112:     public function appLockout() {
113:         header("HTTP/1.1 503 Unavailable");
114:         header("Content-type: text/plain; charset=utf-8");
115:         echo Yii::t('app','X2Engine is undergoing maintenance; it has been locked by an administrator. Please try again later.');
116:         Yii::app()->end();
117:     }
118: 
119:     public function denied() {
120:         throw new CHttpException(
121:             403, Yii::t('app','You are not authorized to perform this action.'));
122:     }
123: 
124:     /**
125:      * @param string $status 'success'|'failure'|'error'|'warning' 
126:      * @param string $message 
127:      */
128:     public function ajaxResponse ($status, $message=null) {
129:         $response = array ();
130:         $response['status'] = $status;
131:         if ($message !== null) $response['message'] = $message;
132:         return CJSON::encode ($response);
133:     }
134: 
135:     public function getModuleObj () {
136:         return Modules::model ()->findByAttributes (array ('name' => $this->module->name));
137:     }
138: 
139:     public function actions() {
140:         $actions = array_merge (parent::actions (), array(
141:             'ajaxGetModelAutocomplete' => array(
142:                 'class' => 'application.components.actions.AjaxGetModelAutocompleteAction',
143:             ),
144:             'x2GridViewMassAction' => array(
145:                 'class' => 'X2GridViewMassActionAction',
146:             ),
147:             'inlineEmail' => array(
148:                 'class' => 'InlineEmailAction',
149:             ),
150:         ));
151:         if ($this->module) {
152:             $module = Modules::model ()->findByAttributes (array ('name' => $this->module->name));
153:             if ($module->enableRecordAliasing) {
154:                 $actions = array_merge ($actions, RecordAliases::getActions ());
155:             }
156:         }
157:         if ($this->modelClass !== '') {
158:             $modelClass = $this->modelClass;
159:             if ($modelClass::model ()->asa ('X2ModelConversionBehavior')) {
160:                 $actions = array_merge ($actions, X2ModelConversionBehavior::getActions ());
161:             }
162:         }
163:         return $actions;
164:     }
165: 
166:     /**
167:      * Returns rendered detail view for given model 
168:      * @param object $model
169:      */
170:     public function getDetailView ($model) {
171:         if (!is_subclass_of ($model, 'X2Model'))
172:             throw new CException (Yii::t ('app', '$model is not a subclass of X2Model'));
173: 
174:         return $this->widget ('DetailView', array(
175:             'model' => $model
176:         ), true, true);
177:     }
178: 
179:     /**
180:      * Renders a view with any attached scripts, WITHOUT the core scripts.
181:      *
182:      * This method fixes the problem with {@link renderPartial()} where an AJAX request with
183:      * $processOutput=true includes the core scripts, breaking everything on the page
184:      * in rendering a partial view, or an AJAX response.
185:      *
186:      * @param string $view name of the view to be rendered. See {@link getViewFile} for details
187:      * about how the view script is resolved.
188:      * @param array $data data to be extracted into PHP variables and made available to the view 
189:      *  script
190:      * @param boolean $return whether the rendering result should be returned instead of being 
191:      *  displayed to end users
192:      * @return string the rendering result. Null if the rendering result is not required.
193:      * @throws CException if the view does not exist
194:      */
195:     public function renderPartialAjax(
196:         $view, $data = null, $return = false, $includeScriptFiles = false) {
197: 
198:         if (($viewFile = $this->getViewFile($view)) !== false) {
199: 
200:             $output = $this->renderFile($viewFile, $data, true);
201: 
202:             $cs = Yii::app()->clientScript;
203:             Yii::app()->setComponent('clientScript', new X2ClientScript);
204:             $output = $this->renderPartial($view, $data, true);
205:             $output .= Yii::app()->clientScript->renderOnRequest($includeScriptFiles);
206:             Yii::app()->setComponent('clientScript', $cs);
207: 
208:             if ($return)
209:                 return $output;
210:             else
211:                 echo $output;
212:         } else {
213:             throw new CException(
214:                 Yii::t('yii', '{controller} cannot find the requested view "{view}".', 
215:                     array('{controller}' => get_class($this), '{view}' => $view)));
216:         }
217:     }
218: 
219:     /**
220:      * Determines if we have permission to edit something based on the assignedTo field.
221:      *
222:      * @param mixed $model The model in question (subclass of {@link CActiveRecord} or {@link X2Model}
223:      * @param string $action
224:      * @return boolean
225:      */
226:     public function checkPermissions(&$model, $action = null) {
227:         return $this->PermissionsBehavior->checkPermissions($model, $action);
228:     }
229: 
230:     /**
231:      * Displays a particular model.
232:      *
233:      * This method is called in child controllers
234:      * which pass it a model to display and what type of model it is (i.e. Contact,
235:      * Opportunity, Account).  It also creates an action history and provides appropriate
236:      * variables to the view.
237:      *
238:      * @param mixed $model The model to be displayed (subclass of {@link CActiveRecord} or {@link X2Model}
239:      * @param String $type The type of the module being displayed
240:      */
241:     public function view(&$model,$type=null,$params=array()) {
242:         $this->noBackdrop = true;
243: 
244:         // should only happen when the model is known to have X2LinkableBehavior
245:         if($type === null)    // && $model->asa('X2LinkableBehavior') !== null)    
246:             $type = $model->module;
247: 
248:         if(!isset($_GET['ajax'])){
249:             $log=new ViewLog;
250:             $log->user=Yii::app()->user->getName();
251:             $log->recordType=get_class($model);
252:             $log->recordId=$model->id;
253:             $log->timestamp=time();
254:             $log->save();
255:             X2Flow::trigger('RecordViewTrigger',array('model'=>$model));
256:         }
257: 
258:         $this->render('view', array_merge($params,array(
259:             'model' => $model,
260:             'actionHistory' => $this->getHistory($model,$type),
261:             'currentWorkflow' => $this->getCurrentWorkflow($model->id,$type),
262:         )));
263:     }
264: 
265:     /**
266:      * Obtain the history of actions associated with a model.
267:      *
268:      * Returns the data provider that references the history.
269:      * @param mixed $model The model in question (subclass of {@link CActiveRecord} or {@link X2Model}
270:      * @param mixed $type The association type (type of the model)
271:      * @return CActiveDataProvider
272:      */
273:     public function getHistory(&$model, $type = null) {
274: 
275:         if (!isset($type))
276:             $type = get_class($model);
277: 
278:         $filters = array(
279:             'actions'=>' AND type IS NULL',
280:             'comments'=>' AND type="note"',
281:             'attachments'=>' AND type="attachment"',
282:             'all'=>''
283:         );
284: 
285:         $history = 'all';
286:         if(isset($_GET['history']) && array_key_exists($_GET['history'],$filters))
287:             $history = $_GET['history'];
288: 
289:         return new CActiveDataProvider('Actions',array(
290:             'criteria'=>array(
291:                 'order'=>'GREATEST(createDate, IFNULL(completeDate,0), IFNULL(dueDate,0), IFNULL(lastUpdated,0)) DESC',
292:                 'condition'=>'associationId='.$model->id.' AND associationType="'.$type.'" '.$filters[$history].' AND (visibility="1" OR assignedTo="admin" OR assignedTo="'.Yii::app()->user->getName().'")'
293:             )
294:         ));
295:     }
296: 
297:     /**
298:      * Obtains the current worflow for a model of given type and id.
299:      * Prioritizes incomplete workflows over completed ones.
300:      * @param integer $id the ID of the record
301:      * @param string $type the associationType of the record
302:      * @return int the ID of the current workflow (0 if none are found)
303:      */
304:     public function getCurrentWorkflow($id, $type) {
305:         $currentWorkflow = Yii::app()->db->createCommand()
306:             ->select('workflowId,completeDate,createDate')
307:             ->from('x2_actions')
308:             ->where(
309:                 'type="workflow" AND associationType=:type AND associationId=:id',
310:                 array(':type'=>$type,':id'=>$id))
311:             ->order('IF(completeDate = 0 OR completeDate IS NULL,1,0) DESC, createDate DESC')
312:             ->limit(1)
313:             ->queryRow(false);
314: 
315:         if($currentWorkflow === false || !isset($currentWorkflow[0])) {
316:             $defaultWorkflow = Yii::app()->db->createCommand("
317:                 select x2_workflows.id
318:                 from x2_workflows, x2_modules
319:                 where x2_workflows.isDefault=1 or 
320:                     x2_modules.id=:moduleId and x2_modules.defaultWorkflow=x2_workflows.id
321:                 limit 1
322:             ")->queryScalar (array (
323:                 ':moduleId' => $this->getModuleObj ()->id
324:             ));
325:             if($defaultWorkflow !== false)
326:                 return $defaultWorkflow;
327:             return 0;
328:         }
329:         return $currentWorkflow[0];
330:     }
331: 
332:     /**
333:      * Used in function convertUrls
334:      *
335:      * @param mixed $a
336:      * @param mixed $b
337:      * @return mixed
338:      */
339:     private static function compareChunks($a, $b) {
340:         return $a[1] - $b[1];
341:     }
342: 
343:     /**
344:      * Replaces any URL in text with an html link (supports mailto links)
345:      *
346:      * @todo refactor this out of controllers
347:      * @param string $text Text to be converted
348:      * @param boolean $convertLineBreaks
349:      */
350:     public static function convertUrls($text, $convertLineBreaks = true) {
351:         /* URL matching regex from the interwebs:
352:          * http://www.regexguru.com/2008/11/detecting-urls-in-a-block-of-text/
353:          */
354:         $url_pattern = '/\b(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])/i';
355:         $email_pattern = '/(([_A-Za-z0-9-]+)(\\.[_A-Za-z0-9-]+)*@([A-Za-z0-9-]+)(\\.[A-Za-z0-9-]+)*)/i';
356: 
357:         /* First break the text into two arrays, one containing <a> tags and the like
358:          * which should not have any replacements, and another with all the text that
359:          * should have URLs activated.  Each piece of each array has its offset from
360:          * original string so we can piece it back together later
361:          */
362: 
363:         //add any additional tags to be passed over here
364:         $tags_with_urls = "/(<a[^>]*>.*<\/a>)|(<img[^>]*>)|(<iframe[^>]*>.*<\/iframe>)|(<script[^>]*>.*<\/script>)/i";
365:         $text_to_add_links = preg_split($tags_with_urls, $text, NULL, PREG_SPLIT_OFFSET_CAPTURE);
366:         $matches = array();
367:         preg_match_all($tags_with_urls, $text, $matches, PREG_OFFSET_CAPTURE);
368:         $text_to_leave = $matches[0];
369: 
370:         // Convert all URLs into html links
371:         foreach ($text_to_add_links as $i => $value) {
372:             $text_to_add_links[$i][0] = preg_replace(
373:                     array($url_pattern,
374:                 $email_pattern), array("<a href=\"\\0\">\\0</a>",
375:                 "<a href=\"mailto:\\0\">\\0</a>"), $text_to_add_links[$i][0]
376:             );
377:         }
378: 
379:         // Merge the arrays and sort to be in the original order
380:         $all_text_chunks = array_merge($text_to_add_links, $text_to_leave);
381: 
382:         usort($all_text_chunks, 'x2base::compareChunks');
383: 
384:         $new_text = "";
385:         foreach ($all_text_chunks as $chunk) {
386:             $new_text = $new_text . $chunk[0];
387:         }
388:         $text = $new_text;
389: 
390:         // Make sure all links open in new window, and have http:// if missing
391:         $text = preg_replace(
392:                 array('/<a([^>]+)target=("[^"]+"|\'[^\']\'|[^\s]+)([^>]+)/i',
393:             '/<a([^>]+href="?\'?)(www\.|ftp\.)/i'), array('<a\\1 target=\\2\\3',
394:             '<a\\1http://\\2'), $text
395:         );
396: 
397:         //convert any tags into links
398:         $matches = array();
399:         // avoid matches that end with </span></a>, like other record links
400:         preg_match('/(^|[>\s\.])(#\w\w+)(?!.*<\/span><\/a>)/u', $text, $matches);
401:         $tags = Yii::app()->cache->get('x2_taglinks');
402:         if ($tags === false) {
403:             $dependency = new CDbCacheDependency('SELECT MAX(timestamp) FROM x2_tags');
404:             $tags = Yii::app()->db->createCommand()
405:                     ->selectDistinct('tag')
406:                     ->from('x2_tags')
407:                     ->queryColumn();
408:             // cache either 10min or until a new tag is added
409:             Yii::app()->cache->set('x2_taglinks', $tags, 600, $dependency);
410:         }
411:         if (sizeof ($matches) > 1 && $matches[2] !== null && 
412:             array_search($matches[2], $tags) !== false) {
413: 
414:             $template = "\\1<a href=" . Yii::app()->createUrl('/search/search') . 
415:                 '?term=%23\\2' . ">#\\2</a>";
416:             //$text = preg_replace('/(^|[>\s\.])#(\w\w+)($|[<\s\.])/u',$template,$text);
417:             $text = preg_replace('/(^|[>\s\.])#(\w\w+)/u', $template, $text);
418:         }
419: 
420:         //TODO: separate convertUrl and convertLineBreak concerns
421:         if ($convertLineBreaks)
422:             return Formatter::convertLineBreaks($text, true, false);
423:         else
424:             return $text;
425:     }
426: 
427:     // Deletes a note action
428:     public function actionDeleteNote($id) {
429:         $note = X2Model::model('Actions')->findByPk($id);
430:         if ($note->delete()) {
431:             $this->redirect(array('view', 'id' => $note->associationId));
432:         }
433:     }
434: 
435:     /**
436:      * Creates a new model.
437:      * If creation is successful, the browser will be redirected to the 'view' page.
438:      */
439:     public function create($model, $oldAttributes, $api) {
440:         if($model->save()) {
441:             if($api == 0)
442:                 $this->redirect(array('view', 'id' => $model->id));
443:             else
444:                 return true;
445:         } else {
446:             return false;
447:         }
448:     }
449: 
450:     /**
451:      * Updates a particular model.
452:      * If update is successful, the browser will be redirected to the 'view' page.
453:      * @param integer $id the ID of the model to be updated
454:      */
455:     public function update($model, $oldAttributes, $api) {
456:         if($model->save()) {
457:             if($api == 0)
458:                 $this->redirect(array('view', 'id' => $model->id));
459:             else
460:                 return true;
461:         } else {
462:             return false;
463:         }
464:     }
465: 
466:     /**
467:      * Lists all models.
468:      */
469:     public function index($model, $name) {
470:         $this->render('index', array('model' => $model));
471:     }
472: 
473:     /**
474:      * Manages all models.
475:      * @param $model The model to use admin on, created in a controller subclass.  The model must be constucted with the parameter 'search'
476:      * @param $name The name of the model being viewed (Opportunities, Actions, etc.)
477:      */
478:     public function admin($model, $name) {
479:         $this->render('admin', array('model' => $model));
480:     }
481: 
482:     /**
483:      * Search for a term.  Defined in X2Base so that all Controllers can use, but
484:      * it makes a call to the SearchController.
485:      */
486:     public function actionSearch() {
487:         $term = $_GET['term'];
488:         $this->redirect(Yii::app()->controller->createAbsoluteUrl('/search/search',array('term'=>$term)));
489:     }
490: 
491:     /**
492:      * Delete all tags associated with a model
493:      */
494:     public function cleanUpTags($model) {
495:         Tags::model()->deleteAllByAttributes(array('itemId' => $model->id));
496:     }
497: 
498:     
499:     public function decodeQuotes($str) {
500:         return preg_replace('/&quot;/u', '"', $str);
501:     }
502: 
503:     public function encodeQuotes($str) {
504:         // return htmlspecialchars($str);
505:         return preg_replace('/"/u', '&quot;', $str);
506:     }
507: 
508:     public function getPhpMailer($sendAs = -1) {
509:         $mail = new InlineEmail;
510:         $mail->credId = $sendAs;
511:         return $mail->mailer;
512:     }
513: 
514:     public function throwException($message) {
515:         throw new Exception($message);
516:     }
517: 
518:     /**
519:      * Send an email from X2Engine, returns an array with status code/message
520:      *
521:      * @param array addresses
522:      * @param string $subject the subject for the email
523:      * @param string $message the body of the email
524:      * @param array $attachments array of attachments to send
525:      * @param array|integer $from from and reply to address for the email array(name, address)
526:      *     or, if integer, the ID of a email credentials record to use for delivery.
527:      * @return array
528:      */
529:     public function sendUserEmail($addresses, $subject, $message, $attachments = null, $from = null){
530:         $eml = new InlineEmail();
531:         if(is_array($addresses) ? count($addresses)==0 : true)
532:             throw new Exception('Invalid argument 1 sent to x2base.sendUserEmail(); expected a non-empty array, got instead: '.var_export($addresses,1));
533:         // Set recipients:
534:         if(array_key_exists('to',$addresses) || array_key_exists('cc',$addresses) || array_key_exists('bcc',$addresses)) {
535:             $eml->mailingList = $addresses;
536:         } else
537:             return array('code'=>500,'message'=>'No recipients specified for email; array given for argument 1 of x2base.sendUserEmail does not have a "to", "cc" or "bcc" key.');
538:         // Resolve sender (use stored email credentials or system default):
539:         if($from === null || in_array($from,Credentials::$sysUseId)) {
540:             $from = (int) Credentials::model()->getDefaultUserAccount($from);
541:             // Set to the user's name/email if no valid defaults found:
542:             if($from == Credentials::LEGACY_ID)
543:                 $from = array('name' => Yii::app()->params->profile->fullName, 'address'=> Yii::app()->params->profile->emailAddress);
544:         }
545: 
546:         if(is_numeric($from))
547:             $eml->credId = $from;
548:         else
549:             $eml->from = $from;
550:         // Set other attributes
551:         $eml->subject = $subject;
552:         $eml->message = $message;
553:         $eml->attachments = $attachments;
554:         return $eml->deliver();
555:     }
556: 
557:     public function parseEmailTo($string) {
558: 
559:         if (empty($string))
560:             return false;
561:         $mailingList = array();
562:         $splitString = explode(',', $string);
563: 
564:         require_once('protected/components/phpMailer/class.phpmailer.php');
565: 
566:         foreach ($splitString as &$token) {
567: 
568:             $token = trim($token);
569:             if (empty($token))
570:                 continue;
571: 
572:             $matches = array();
573: 
574:             if (PHPMailer::ValidateAddress($token)) { // if it's just a simple email, we're done!
575:                 $mailingList[] = array('', $token);
576:             } else if (preg_match('/^"?([^"]*)"?\s*<(.+)>$/i', $token, $matches)) {
577:                 if (count($matches) == 3 && PHPMailer::ValidateAddress($matches[2]))
578:                     $mailingList[] = array($matches[1], $matches[2]);
579:                 else
580:                     return false;
581:             } else
582:                 return false;
583:         }
584: 
585:         if (count($mailingList) < 1)
586:             return false;
587: 
588:         return $mailingList;
589:     }
590: 
591:     public function mailingListToString($list, $encodeQuotes = false) {
592:         $string = '';
593:         if (is_array($list)) {
594:             foreach ($list as &$value) {
595:                 if (!empty($value[0]))
596:                     $string .= '"' . $value[0] . '" <' . $value[1] . '>, ';
597:                 else
598:                     $string .= $value[1] . ', ';
599:             }
600:         }
601:         return $encodeQuotes ? $this->encodeQuotes($string) : $string;
602:     }
603: 
604:     /**
605:      * Obtain the widget list for the current web user.
606:      *
607:      * @param CFilterChain $filterChain
608:      */
609:     public function filterSetPortlets($filterChain) {
610:         if (!Yii::app()->user->isGuest) {
611:             $themeURL = Yii::app()->theme->getBaseUrl();
612:             $this->portlets = Profile::getWidgets();
613:         }
614:         $filterChain->run();
615:     }
616: 
617:     /**
618:      * Performs the AJAX validation.
619:      * @param CModel the model to be validated
620:      */
621:     protected function performAjaxValidation($model) {
622:         if (isset($_POST['ajax'])) {
623:             echo CActiveForm::validate($model);
624:             Yii::app()->end();
625:         }
626:     }
627: 
628:     /**
629:      * Calls renderInput for model and input type with given names and returns the result.
630:      */
631:     public function actionGetX2ModelInput ($modelName, $fieldName) {
632:         if (!isset ($modelName) || !isset ($fieldName)) {
633:             throw new CHttpException (400, 'modelName or fieldName not set');
634:             return;
635:         }
636:         $model = X2Model::model ($modelName);
637:         if (!$model) {
638:             throw new CHttpException (400, 'Invalid model name');
639:             return;
640:         }
641:         $field = $model->getField ($fieldName);
642:         if (!$model) {
643:             throw new CHttpException (400, 'Invalid field name');
644:             return;
645:         }
646:         $input = '';
647:         if ($fieldName == 'associationName') {
648:             $input .= CHtml::activeDropDownList(
649:                 $model, 'associationType', 
650:                 array_merge(
651:                     array(
652:                         'none' => Yii::t('app', 'None'), 
653:                         'calendar' => Yii::t('calendar', 'Calendar')), 
654:                     Fields::getDisplayedModelNamesList()
655:                 ), 
656:                 array(
657:                 'ajax' => array(
658:                     'type' => 'POST', 
659:                     'url' => CController::createUrl('/actions/actions/parseType'), 
660:                     'update' => '#', //selector to update
661:                     'data' => 'js:$(this).serialize()',
662:                     'success' => 'function(data){
663:                         if(data){
664:                             $("#auto_select").autocomplete("option","source",data);
665:                             $("#auto_select").val("");
666:                             $("#auto_complete").show();
667:                         }else{
668:                             $("#auto_complete").hide();
669:                         }
670:                     }'
671:                 )
672:             ));
673:             $input .= "<div id='auto_complete' style='display: none'>";
674:             $input .= $this->widget('zii.widgets.jui.CJuiAutoComplete', array(
675:                 'name' => 'auto_select',
676:                 'value' => $model->associationName,
677:                 'source' => ($model->associationType !== 'Calendar' ? 
678:                     $this->createUrl(X2Model::model($modelName)->autoCompleteSource) : ''),
679:                 'options' => array(
680:                     'minLength' => '2',
681:                     'select' => 'js:function( event, ui ) {
682:                         $("#'.CHtml::activeId($model, 'associationId').'").val(ui.item.id);
683:                         $(this).val(ui.item.value);
684:                         return false;
685:                     }',
686:                 ),
687:             ), true);
688:             $input .= "</div>";
689:         } else {
690:             $input .= $model->renderInput ($fieldName);
691:         }
692: 
693:         // force loading of scripts normally rendered in view
694:         $input .= '<br /><br /><script id="x2-model-render-input-scripts">'."\n";
695:         if (isset (Yii::app()->clientScript->scripts[CClientScript::POS_READY])) {
696:             foreach(
697:                 Yii::app()->clientScript->scripts[CClientScript::POS_READY] as $id => $script) {
698: 
699:                 if(strpos($id,'logo')===false)
700:                 $input .= "$script\n";
701:             }
702:         }
703:         $input .= "</script>";
704:         $response = array (
705:             'input' => $input,
706:             'field' => array (
707:                 'type' => $field->type
708:             )
709:         );
710:         echo CJSON::encode ($response);
711:     }
712: 
713:     /**
714:      * Helper method to hide specific menu options or unset
715:      * links before the menu is rendered
716:      * @param array $menuItems Original menu items
717:      * @param array|true $selectOptions Menu items to include. If set to true, all default menu
718:      *  items will get displayed
719:      */
720:     protected function prepareMenu(&$menuItems, $selectOptions) {
721:         if ($selectOptions === true) {
722:             $selectOptions = array_map (function ($item) {
723:                 return $item['name'];
724:             }, $menuItems);
725:         }
726:         $currAction = $this->action->id;
727:         for ($i = count($menuItems) - 1; $i >= 0; $i--) {
728:             // Iterate over the items from the end to avoid consistency issues
729:             // while items are being removed
730:             $item = $menuItems[$i];
731: 
732:             // Remove requested items
733:             if (!in_array($item['name'], $selectOptions)) {
734:                 unset($menuItems[$i]);
735:             }
736:             // Hide links to requested items
737:             else if ((is_array($item['url']) && in_array($currAction, $item['url']))
738:                     || $item['url'] === $currAction) {
739:                 unset($menuItems[$i]['url']);
740:             }
741:         }
742:     }
743: 
744:     protected function renderLayout ($layoutFile, $output) {
745:         $output = $this->renderFile (
746:             $layoutFile,
747:             array (
748:                 'content'=>$output
749:             ), true);
750:         return $output;
751:     }
752: 
753:     /**
754:      * Override parent method so that layout business logic can be moved to controller 
755:      * This method is Copyright (c) 2008-2014 by Yii Software LLC
756:      * http://www.yiiframework.com/license/ 
757:      */
758:     public function render($view,$data=null,$return=false)
759:     {
760:         if($this->beforeRender($view))
761:         {
762:             $output=$this->renderPartial($view,$data,true);
763: 
764:             /* x2modstart */ 
765:             if(($layoutFile=$this->getLayoutFile($this->layout))!==false) {
766:                 $output = $this->renderLayout ($layoutFile, $output);
767:             }
768:             /* x2modend */ 
769: 
770:             $this->afterRender($view,$output);
771: 
772:             $output=$this->processOutput($output);
773: 
774:             if($return)
775:                 return $output;
776:             else
777:                 echo $output;
778:         }
779:     }
780: 
781:     /**
782:      * Overrides parent method so that x2base's _pageTitle property is used instead of 
783:      * CController's.
784:      *
785:      * This method is Copyright (c) 2008-2014 by Yii Software LLC
786:      * http://www.yiiframework.com/license/
787:      */
788:     public function setPageTitle($value) {
789:         $this->_pageTitle = $value;
790:     }
791: 
792:     /**
793:      * Overrides parent method so that configurable app name is used instead of name
794:      * from the config file.
795:      *
796:      * This method is Copyright (c) 2008-2014 by Yii Software LLC
797:      * http://www.yiiframework.com/license/
798:      */
799:     public function getPageTitle() {
800:         if($this->_pageTitle!==null) {
801:             return $this->_pageTitle;
802:         } else {
803:             $name=ucfirst(basename($this->getId()));
804: 
805:             // Try and load the configured module name
806:             $moduleName = Modules::displayName(true, $name);
807:             if (!empty($moduleName))
808:                 $name = $moduleName;
809: 
810:             if($this->getAction()!==null && 
811:                strcasecmp($this->getAction()->getId(),$this->defaultAction)) {
812: 
813:                 return $this->_pageTitle=
814:                     /* x2modstart */Yii::app()->settings->appName/* x2modend */.' - '.
815:                         ucfirst($this->getAction()->getId()).' '.$name;
816:             } else {
817:                 return $this->_pageTitle=
818:                     /* x2modstart */Yii::app()->settings->appName/* x2modend */.' - '.$name;
819:             }
820:         }
821:     }
822: 
823:     /**
824:      * Assumes that convention of (<module name> === ucfirst (<modelClass>)) is followed. 
825:      * @return Module module associated with this controller.  
826:      */
827:     /*public function getModuleModel () {
828:         return Modules::model()->findByAttributes (array ('name' => ucfirst ($this->modelClass)));
829:     }*/
830: 
831:     /**
832:      * Overridden to add $run param
833:      * 
834:      * This method is Copyright (c) 2008-2014 by Yii Software LLC
835:      * http://www.yiiframework.com/license/
836:      */
837:     public function widget($className,$properties=array(),$captureOutput=false,$run=true)
838:     {
839:         if($captureOutput)
840:         {
841:             ob_start();
842:             ob_implicit_flush(false);
843:             $widget=$this->createWidget($className,$properties);
844:             /* x2modstart */ 
845:             if ($run) $widget->run();
846:             /* x2modend */ 
847:             return ob_get_clean();
848:         }
849:         else
850:         {
851:             $widget=$this->createWidget($className,$properties);
852:             /* x2modstart */ 
853:             if ($run) $widget->run();
854:             /* x2modend */ 
855:             return $widget;
856:         }
857:     }
858: 
859:     public function actionQuickView ($id) {
860:         $model = $this->loadModel($id);
861:         if (!FormLayout::model()->findByAttributes (array ('model' => get_class ($model)))) {
862:             echo Yii::t('app', 'Quick view not supported');
863:         }
864:         if ($this->checkPermissions($model, 'view')) {
865:             $that = $this;
866:             X2Widget::ajaxRender (function () use ($model, $that) {
867:                 $that->widget ('DetailView', array_merge(array(
868:                     'model' => $model,
869:                     'scenario' => 'Inline',
870:                     'nameLink' => true
871:                 )));
872:             });
873:             return;
874:         }
875:         throw new CHttpException (403);
876:     }
877: 
878:     /**
879:      * DUMMY METHOD: left to avoid breaking old custom modules (now done in X2ChangeLogBehavior)
880:      * @deprecated
881:      */
882:     protected function updateChangelog($model, $changes) {
883:         return $model;
884:     }
885: 
886:     /**
887:      * DUMMY METHOD: left to avoid breaking old custom modules (now done in X2ChangeLogBehavior)
888:      * @deprecated
889:      */
890:     protected function calculateChanges($old, $new, &$model = null) {
891:         return array();
892:     }
893: 
894:     protected function getModelFromTypeAndId ($modelName, $modelId, $x2ModelOnly=true) {
895:         $model = X2Model::getModelOfTypeWithId ($modelName, $modelId);
896:         if (!$model || ($x2ModelOnly && !($model instanceof X2Model))) {
897:             throw new CHttpException (400, Yii::t('app', 'Invalid record type or record id')); 
898:         }
899:         return $model;
900:     }
901: 
902:     protected function getModelsFromTypeAndId (array $recordInfo) {
903:         // validate record info and look up models
904:         foreach ($recordInfo as $info) {
905:             $model = $this->getModelFromTypeAndId ($info[0], $info[1]);
906:             $models[] = $model;
907:         }
908:         return $models;
909:     }
910: }
911: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0