1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
36:
37: 38: 39: 40: 41:
42: abstract class x2base extends X2Controller {
43: 44: 45: 46: 47: 48: 49: 50: 51:
52:
53: 54: 55: 56:
57: public $layout = '//layouts/column3';
58:
59: 60: 61:
62: public $noBackdrop = false;
63:
64: 65: 66:
67: public = array();
68:
69: 70: 71: 72: 73:
74: public $breadcrumbs = array();
75: public $portlets = array();
76: public $leftPortlets = array();
77: public $modelClass;
78: public = array();
79: public $leftWidgets = array();
80:
81:
82: private $_pageTitle;
83:
84: 85: 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',
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: 126: 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: 168: 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: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 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: 221: 222: 223: 224: 225:
226: public function checkPermissions(&$model, $action = null) {
227: return $this->PermissionsBehavior->checkPermissions($model, $action);
228: }
229:
230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240:
241: public function view(&$model,$type=null,$params=array()) {
242: $this->noBackdrop = true;
243:
244:
245: if($type === 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: 267: 268: 269: 270: 271: 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: 299: 300: 301: 302: 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: 334: 335: 336: 337: 338:
339: private static function compareChunks($a, $b) {
340: return $a[1] - $b[1];
341: }
342:
343: 344: 345: 346: 347: 348: 349:
350: public static function convertUrls($text, $convertLineBreaks = true) {
351: 352: 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: 358: 359: 360: 361:
362:
363:
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:
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:
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:
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:
398: $matches = array();
399:
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:
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:
417: $text = preg_replace('/(^|[>\s\.])#(\w\w+)/u', $template, $text);
418: }
419:
420:
421: if ($convertLineBreaks)
422: return Formatter::convertLineBreaks($text, true, false);
423: else
424: return $text;
425: }
426:
427:
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: 437: 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: 452: 453: 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: 468:
469: public function index($model, $name) {
470: $this->render('index', array('model' => $model));
471: }
472:
473: 474: 475: 476: 477:
478: public function admin($model, $name) {
479: $this->render('admin', array('model' => $model));
480: }
481:
482: 483: 484: 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: 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('/"/u', '"', $str);
501: }
502:
503: public function encodeQuotes($str) {
504:
505: return preg_replace('/"/u', '"', $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: 520: 521: 522: 523: 524: 525: 526: 527: 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:
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:
539: if($from === null || in_array($from,Credentials::$sysUseId)) {
540: $from = (int) Credentials::model()->getDefaultUserAccount($from);
541:
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:
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)) {
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: 606: 607: 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: 619: 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: 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' => '#',
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:
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: 715: 716: 717: 718: 719:
720: protected function (&$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:
729:
730: $item = $menuItems[$i];
731:
732:
733: if (!in_array($item['name'], $selectOptions)) {
734: unset($menuItems[$i]);
735: }
736:
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: 755: 756: 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:
765: if(($layoutFile=$this->getLayoutFile($this->layout))!==false) {
766: $output = $this->renderLayout ($layoutFile, $output);
767: }
768:
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: 783: 784: 785: 786: 787:
788: public function setPageTitle($value) {
789: $this->_pageTitle = $value;
790: }
791:
792: 793: 794: 795: 796: 797: 798:
799: public function getPageTitle() {
800: if($this->_pageTitle!==null) {
801: return $this->_pageTitle;
802: } else {
803: $name=ucfirst(basename($this->getId()));
804:
805:
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: Yii::app()->settings->appName.' - '.
815: ucfirst($this->getAction()->getId()).' '.$name;
816: } else {
817: return $this->_pageTitle=
818: Yii::app()->settings->appName.' - '.$name;
819: }
820: }
821: }
822:
823: 824: 825: 826:
827: 828: 829:
830:
831: 832: 833: 834: 835: 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:
845: if ($run) $widget->run();
846:
847: return ob_get_clean();
848: }
849: else
850: {
851: $widget=$this->createWidget($className,$properties);
852:
853: if ($run) $widget->run();
854:
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: 880: 881:
882: protected function updateChangelog($model, $changes) {
883: return $model;
884: }
885:
886: 887: 888: 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:
904: foreach ($recordInfo as $info) {
905: $model = $this->getModelFromTypeAndId ($info[0], $info[1]);
906: $models[] = $model;
907: }
908: return $models;
909: }
910: }
911: