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: Yii::import('application.components.X2LinkableBehavior');
40: Yii::import('application.components.X2ChangeLogBehavior');
41: Yii::import('application.components.x2flow.X2FlowTriggerBehavior');
42: Yii::import('application.components.X2TimestampBehavior');
43: Yii::import('application.components.TagBehavior');
44:
45: Yii::import('application.modules.actions.models.Actions');
46: Yii::import('application.modules.users.models.*');
47: Yii::import('application.models.X2Flow');
48:
49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62:
63: abstract class X2Model extends X2ActiveRecord {
64:
65: public $supportsFieldLevelPermissions = true;
66:
67: 68: 69:
70: public $supportsWorkflow = true;
71:
72: 73: 74: 75:
76: public $uid = null;
77:
78: 79: 80: 81:
82: public $dbPersistentGridSettings = false;
83:
84: 85: 86: 87:
88: public $disablePersistentGridSettings = false;
89:
90: 91: 92: 93: 94: 95: 96: 97: 98:
99: public $subScenario = '';
100:
101: protected $_oldAttributes = array();
102:
103: 104: 105: 106: 107:
108: public static $autoPopulateFields = true;
109:
110: 111: 112:
113: public static $associationModels = array(
114: 'bugreports' => 'BugReports',
115: 'media' => 'Media',
116: 'actions' => 'Actions',
117: 'calendar' => 'X2Calendar',
118: 'contacts' => 'Contacts',
119: 'accounts' => 'Accounts',
120: 'product' => 'Product',
121: 'products' => 'Product',
122: 'Campaign' => 'Campaign',
123: 'x2Leads' => 'X2Leads',
124: 'marketing' => 'Campaign',
125: 'quote' => 'Quote',
126: 'quotes' => 'Quote',
127: 'opportunities' => 'Opportunity',
128: 'social' => 'Social',
129: 'services' => 'Services',
130: 'users' => 'User',
131:
132: '' => ''
133: );
134:
135: 136: 137:
138: public static $modelNameToModuleName = array(
139: 'Accounts' => 'Accounts',
140: 'Actions' => 'Actions',
141: 'BugReports' => 'BugReports',
142: 'Campaign' => 'Marketing',
143: 'Contacts' => 'Contacts',
144: 'X2List' => 'Contacts',
145: 'Groups' => 'Groups',
146: 'Product' => 'Products',
147: 'Media' => 'Media',
148: 'Opportunity' => 'Opportunities',
149: 'Quote' => 'Quotes',
150: 'Services' => 'Services',
151: 'User' => 'Users',
152: 'WebForm' => 'Marketing',
153: 'Workflow' => 'Workflow',
154: 'X2Calendar' => 'Calendar',
155: 'X2Leads' => 'X2Leads',
156: );
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178: public static $translatedModelTitles = array();
179:
180: protected static $_editableFieldNames = array();
181:
182: 183: 184: 185:
186: protected static $_fields;
187:
188: 189: 190: 191: 192: 193:
194: protected static $_fieldPermissions = array();
195:
196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210:
211: protected static $_nameIdRefs;
212:
213: protected static $_linkedModels;
214: protected $_runAfterCreate;
215: protected $fieldFormatterClass = 'FieldFormatter';
216: private static $_modelNames;
217: private static $_attributeLabels;
218:
219: 220: 221:
222: private static $recordNames = array(
223: 'Actions' => 'action',
224: 'Contacts' => 'contact',
225: 'Accounts' => 'account',
226: 'Product' => 'product',
227: 'Campaign' => 'campaign',
228: 'Quote' => 'quote',
229: 'Opportunity' => 'opportunity',
230: 'Services' => 'case',
231: 'Groups' => 'group',
232: 'Docs' => 'doc',
233: 'X2Leads' => 'lead',
234: 'X2List' => 'list item',
235: );
236:
237: 238: 239: 240: 241: 242:
243:
244: public function __construct(
245: $scenario = 'insert', $uid = null, $dbPersistentGridSettings = false,
246: $disablePersistentGridSettings = false) {
247:
248: $this->uid = $uid;
249: $this->dbPersistentGridSettings = $dbPersistentGridSettings;
250: $this->disablePersistentGridSettings = $disablePersistentGridSettings;
251: $this->queryFields();
252: parent::__construct($scenario);
253: if ($this->getIsNewRecord() && $scenario == 'insert') {
254: foreach ($this->getFields() as $field) {
255: if ($field->defaultValue != null && !$field->readOnly) {
256: $this->{$field->fieldName} = $field->defaultValue;
257: }
258: }
259: }
260: }
261:
262: public static function model($className = 'CActiveRecord') {
263: $modelName = self::getModelName($className);
264: if (class_exists($modelName)) {
265: return parent::model($modelName);
266: } else {
267: throw new CHttpException(500, 'Class: ' . $className . " not found.");
268: }
269: }
270:
271: 272: 273:
274: public static function model2 ($className = 'CActiveRecord') {
275: $modelName = self::getModelName($className);
276: if (class_exists($modelName)) {
277: return parent::model($modelName);
278: } else {
279: return false;
280: }
281: }
282:
283: 284: 285: 286: 287:
288: public function runWithoutBehavior($behaviorName, $fn) {
289: $this->disableBehavior($behaviorName);
290: $fn();
291: $this->enableBehavior($behaviorName);
292: }
293:
294: 295: 296: 297: 298: 299:
300: public static function getRecordName($type, $plural = false) {
301: if (isset(self::$recordNames[$type])) {
302: $recordName = self::$recordNames[$type];
303: if ($plural) {
304: if (preg_match("/y$/", $recordName)) {
305: $recordName = preg_replace("/y$/", 'ies', $recordName);
306: } else {
307: $recordName .= 's';
308: }
309: }
310: return $recordName;
311: } else {
312: return $type;
313: }
314: }
315:
316: public static function getAllRecordNames() {
317: return self::$recordNames;
318: }
319:
320: 321: 322: 323:
324: public static function getAssociationType($modelName) {
325: $modelsToTypes = array_flip(X2Model::$associationModels);
326: if (isset($modelsToTypes[$modelName])) {
327: return $modelsToTypes[$modelName];
328: } else {
329: return strtolower($modelName);
330: }
331: }
332:
333: 334: 335: 336:
337: public static function getModelName($typeOrModuleName, $strict = false) {
338: if (array_key_exists(strtolower($typeOrModuleName), X2Model::$associationModels)) {
339: return X2Model::$associationModels[strtolower($typeOrModuleName)];
340: } else if (!$strict) {
341: if (class_exists(ucfirst($typeOrModuleName))) {
342: return ucfirst($typeOrModuleName);
343: } elseif (class_exists($typeOrModuleName)) {
344: return $typeOrModuleName;
345: } else {
346: return false;
347: }
348: }
349: }
350:
351: 352: 353: 354:
355: public static function getModelsFromNames (array $modelNames) {
356: return array_map (function ($name) {
357: return new $name ();
358: }, $modelNames);
359: }
360:
361: 362: 363: 364:
365: public static function getTableNames (array $models) {
366: return array_map (function ($model) {
367: return $model->tableName();
368: }, $models);
369: }
370:
371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385:
386: public static function getModelNames($criteria=null) {
387: if ($criteria !== null || !isset(self::$_modelNames)) {
388: $modelNames = array ();
389: if ($criteria === null) {
390: $modules = self::getModules ();
391: } else {
392: $criteria->addColumnCondition (
393: array('visible' => 1, 'editable' => true), 'AND', 'OR');
394: $modules = X2Model::model('Modules')->findAll ($criteria);
395: }
396: foreach ($modules as $module) {
397: if ($modelName = X2Model::getModelName($module->name)) {
398: $modelNames[$modelName] = Yii::t('app', $module->title);
399: } else {
400: $modelNames[ucfirst($module->name)] = Yii::t('app', $module->title);
401: }
402: }
403: asort ($modelNames);
404: if ($criteria !== null) {
405: return $modelNames;
406: } else {
407: self::$_modelNames = $modelNames;
408: }
409: }
410: return self::$_modelNames;
411: }
412:
413: 414: 415: 416: 417:
418: public static function isModuleModelName ($modelName) {
419: $moduleModelsByName = array_flip (self::getModuleModelNames ());
420: return isset ($moduleModelsByName[$modelName]);
421: }
422:
423: 424: 425:
426: private static $_moduleModelNames;
427: public static function getModuleModelNames () {
428: if (!isset (self::$_moduleModelNames)) {
429: $modules = self::getModules ();
430: $modelNames = array ();
431: foreach ($modules as $module) {
432: if ($modelName = X2Model::getModelName($module->name))
433: $modelNames [] = $modelName;
434: else
435: $modelNames [] = ucfirst($module->name);
436: }
437: self::$_moduleModelNames = $modelNames;
438: }
439: return self::$_moduleModelNames;
440: }
441:
442: 443: 444:
445: private static $_modules;
446: public static function getModules () {
447: if (!isset (self::$_modules)) {
448: self::$_modules = X2Model::model('Modules')
449: ->findAllByAttributes(array('visible' => 1, 'editable' => true));
450: }
451: return self::$_modules;
452: }
453:
454: 455: 456:
457: private static $_moduleModelsByName;
458: public static function getModuleModelsByName () {
459: if (!isset (self::$_moduleModelsByName)) {
460: $modules = self::getModules ();
461: $modelNames = array ();
462: foreach ($modules as $module) {
463: if ($modelName = X2Model::getModelName($module->name))
464: $modelNames[$modelName] = X2Model::model ($modelName);
465: else
466: $modelNames[ucfirst($module->name)] = X2Model::model ($modelName);
467: }
468: self::$_moduleModelsByName = $modelNames;
469: }
470: return self::$_moduleModelsByName;
471: }
472:
473: 474: 475: 476:
477: public static function getAssociationTypeOptions() {
478: $modelNames = array_keys(self::getModelNames());
479:
480:
481: $associationTypes = array();
482: foreach ($modelNames as $modelName) {
483: $associationTypes[self::getAssociationType($modelName)] = self::getModelTitle(
484: $modelName);
485: }
486: return $associationTypes;
487: }
488:
489: public function getDisplayName ($plural=true) {
490: $moduleName = X2Model::getModuleName (get_class ($this));
491: return Modules::displayName ($plural, $moduleName);
492: }
493:
494: 495: 496:
497: public static function getModelTitle($modelClass, $singular = false) {
498: if ($modelClass == 'Calendar')
499: $modelClass = 'X2Calendar';
500: if (!isset(self::$translatedModelTitles[$singular][$modelClass])) {
501: if (!isset (self::$translatedModelTitles)) {
502: self::$translatedModelTitles = array ();
503: }
504: self::$translatedModelTitles[$singular] = array ();
505: try {
506: $model = self::model ($modelClass);
507: } catch (Exception $e) {
508: $model = null;
509: }
510: if ($model) {
511: $title = $model->getDisplayName (!$singular);
512: } else {
513: $title = $modelClass;
514: }
515: self::$translatedModelTitles[$singular][$modelClass] = Yii::t(
516: ($model && isset(self::model($modelClass)->module)) ?
517: self::model($modelClass)->module : 'app', $title);
518: }
519: return self::$translatedModelTitles[$singular][$modelClass];
520: }
521:
522: public static function getTranslatedModelTitles($singular = false) {
523: $modelTitles = array();
524: foreach (self::$modelNameToModuleName as $model => $module) {
525: $modelTitles[$model] = self::getModelTitle($model, $singular);
526: }
527: return $modelTitles;
528: }
529:
530: 531: 532: 533: 534:
535: public static function getModuleName($modelName) {
536: if (isset(self::$modelNameToModuleName[$modelName])) {
537: return self::$modelNameToModuleName[$modelName];
538: } else {
539: return strtolower($modelName);
540: }
541: }
542:
543: 544: 545: 546: 547:
548: public static function getModuleModelName() {
549: return X2Model::getModelName(Yii::app()->controller->module->name);
550: }
551:
552: 553: 554: 555: 556:
557: public static function getModuleModel() {
558: return X2Model::model(X2Model::getModuleModelName());
559: }
560:
561: 562: 563: 564: 565:
566: public static function updateTimerTotals($modelId, $modelName = null) {
567: Yii::import('application.modules.actions.models.*');
568: $modelName = empty($modelName) ? get_called_class() : $modelName;
569: $model = self::model($modelName)->findByPk($modelId);
570: if (empty($model) || $model->asa('X2LinkableBehavior') == null)
571: return;
572:
573: $fields = array_filter($model->fields, function($f) {
574: return $f->type == 'timerSum';
575: });
576: foreach ($fields as $field) {
577: if ($field->linkType == null) {
578:
579:
580:
581:
582: $model->{$field->fieldName} = Yii::app()->db->createCommand()
583: ->select("SUM(timeSpent)")
584: ->from(Actions::model()->tableName())
585: ->where("associationId=:id
586: AND associationType=:module
587: AND type IN ('call','time')")
588: ->queryScalar(array(
589: ':module' => $model->module,
590: ':id' => $modelId
591: ));
592: } else {
593:
594: $model->{$field->fieldName} = Yii::app()->db->createCommand()
595: ->select("SUM(endtime-timestamp)")
596: ->from(ActionTimer::model()->tableName())
597: ->where("associationId=:id
598: AND associationType=:modelName
599: AND actionId IS NOT NULL
600: AND type=:type")
601: ->queryScalar(array(
602: ':id' => $modelId,
603: ':modelName' => $modelName,
604: ':type' => $field->linkType
605: ));
606: }
607: }
608: if (count($fields) > 0)
609: $model->update(array_map(function($f) {
610: return $f->fieldName;
611: }, $fields));
612: }
613:
614: 615: 616:
617: public function hide () {
618: $visibilityAttr = $this->getVisibilityAttr ();
619: $assignmentAttr = $this->getAssignmentATtr ();
620: $this->$visibilityAttr = X2PermissionsBehavior::VISIBILITY_PRIVATE;
621: $this->$assignmentAttr = 'Anyone';
622: }
623:
624: 625: 626: 627:
628: public function findByEmail($email) {
629: $criteria = new CDbCriteria;
630: $paramCount = 0;
631: foreach ($this->getFields() as $field) {
632: if ($field->type == 'email') {
633: $paramCount++;
634: $params[$param = ":email$paramCount"] = $email;
635: $criteria->addCondition("`{$field->fieldName}`=$param", 'OR');
636: }
637: }
638: $criteria->params = $params;
639: if($this->asa('X2DuplicateBehavior')){
640: $criteria->addCondition($this->getHiddenCondition(), 'AND');
641: }
642: return self::model(get_class($this))->find($criteria);
643: }
644:
645: 646: 647: 648: 649:
650: public function findByNameId($nameId) {
651: return self::model()->findByAttributes(compact('nameId'));
652: }
653:
654: public function findByAttributes($attributes, $condition = '', $params = array()) {
655: if ($this->asa('X2DuplicateBehavior')) {
656: $hiddenCondition = $this->getHiddenCondition();
657: if (empty($condition)) {
658: $condition = $hiddenCondition;
659: } else {
660: if (is_array($condition)) {
661: if (isset($condition['condition'])) {
662: $condition['condition'] .= ' AND ' . $hiddenCondition;
663: } else {
664: $condition['condition'] = $hiddenCondition;
665: }
666: } else {
667: $condition .= ' AND ' . $hiddenCondition;
668: }
669: }
670: }
671: return parent::findByAttributes($attributes, $condition, $params);
672: }
673:
674: 675: 676: 677:
678: public function getMyModelName() {
679: return self::getModelName(get_class($this));
680: }
681:
682:
683:
684:
685:
686: public function resetFieldsPropertyCache () {
687: $key = $this->tableName();
688: self::$_fields[$key] = null;
689: $this->queryFields();
690: }
691:
692: 693: 694: 695: 696: 697: 698: 699:
700: protected function queryFields() {
701: $key = $this->tableName();
702:
703:
704: if (!isset(self::$_fields[$key])) {
705:
706:
707: self::$_fields[$key] = Yii::app()->cache->get('fields_' . $key);
708: if (self::$_fields[$key] === false) {
709: $fieldList = CActiveRecord::model('Fields')->findAllByAttributes(
710: array('modelName' => get_class($this), 'isVirtual' => 0));
711: if (!empty($fieldList)) {
712: self::$_fields[$key] = $fieldList;
713:
714:
715: Yii::app()->cache->set('fields_' . $key, self::$_fields[$key], 0);
716: } else {
717: self::$_fields[$key] = $this->attributeLabels();
718: }
719: }
720: }
721: }
722:
723: public function relations() {
724: $relations = array();
725: $myClass = get_class($this);
726:
727:
728: foreach (self::$_fields[$this->tableName()] as &$_field) {
729: if ($_field->type === 'link' && class_exists($_field->linkType)) {
730: $relations[$alias = $_field->fieldName . 'Model'] = array(
731: self::BELONGS_TO,
732: $_field->linkType,
733: array($_field->fieldName => 'nameId'),
734: );
735: }
736: }
737: if (Yii::app()->contEd('pro')) {
738: $relations['gallery'] = array(
739: self::HAS_ONE, 'GalleryToModel',
740: 'modelId',
741: 'condition' => 'modelName="' . $myClass . '"');
742: }
743: return $relations;
744: }
745:
746: 747: 748: 749:
750: public function behaviors() {
751: $behaviors = array(
752: 'X2LinkableBehavior' => array('class' => 'X2LinkableBehavior'),
753: 'X2TimestampBehavior' => array('class' => 'X2TimestampBehavior'),
754: 'X2FlowTriggerBehavior' => array('class' => 'X2FlowTriggerBehavior'),
755: 'TagBehavior' => array('class' => 'TagBehavior'),
756: 'changelog' => array('class' => 'X2ChangeLogBehavior'),
757: 'permissions' => array('class' => Yii::app()->params->modelPermissions),
758: 'X2MergeableBehavior' => array('class' => 'X2MergeableBehavior'),
759: 'relationships' => array('class' => 'RelationshipsBehavior'),
760: );
761: if (Yii::app()->contEd('pro')) {
762: $behaviors['galleryBehavior'] = array(
763: 'class' => 'application.extensions.gallerymanager.GalleryBehavior',
764: 'idAttribute' => 'galleryId',
765: 'versions' => array(
766: 'small' => array(
767: 'centeredpreview' => array(98, 98),
768: ),
769: ),
770: 'name' => true,
771: 'description' => true,
772: );
773: }
774: return $behaviors;
775: }
776:
777: 778: 779:
780: public function afterFind() {
781: $this->_oldAttributes = $this->getAttributes();
782: parent::afterFind();
783: }
784:
785: 786: 787: 788:
789: public function beforeSave() {
790: if ($this->asa ('ContactsNameBehavior')) {
791: $this->asa ('ContactsNameBehavior')->setName ();
792: }
793:
794: $this->_runAfterCreate = $this->getIsNewRecord();
795: if (!$this->_runAfterCreate) {
796: $this->updateNameId();
797: } else {
798:
799:
800:
801:
802:
803: if ($this->hasAttribute('nameId')) {
804: $this->nameId = uniqid();
805: }
806: }
807: return parent::beforeSave();
808: }
809:
810: public function onAfterCreate($event) {
811: $this->raiseEvent('onAfterCreate', $event);
812: }
813:
814: public function afterCreate() {
815: $this->_runAfterCreate = false;
816: if ($this->hasEventHandler('onAfterCreate'))
817: $this->onAfterCreate(new CEvent($this));
818: }
819:
820: public function onAfterInsert($event) {
821: $this->raiseEvent('onAfterInsert', $event);
822: }
823:
824: public function onAfterUpdate($event) {
825: $this->raiseEvent('onAfterUpdate', $event);
826: }
827:
828: public function afterUpdate() {
829:
830:
831:
832: $this->updateNameIdRefs();
833:
834:
835: if ($this->hasEventHandler('onAfterUpdate')) {
836: $this->onAfterUpdate(new CEvent($this));
837: }
838: }
839:
840: 841: 842: 843: 844:
845: public function afterDelete() {
846:
847: $class = get_class($this);
848: Tags::model()->deleteAllByAttributes(array(
849: 'type' => $class,
850: 'itemId' => $this->id
851: ));
852:
853:
854: X2Model::model('PhoneNumber')->deleteAllByAttributes(array(
855: 'modelId' => $this->id,
856: 'modelType' => $class
857: ));
858:
859: RecordAliases::model ()->deleteAllByAttributes (array (
860: 'recordId' => $this->id,
861: 'recordType' => $class,
862: ));
863:
864:
865:
866: if ($this->hasAttribute('nameId') && $this->hasAttribute('name')) {
867: $this->_oldAttributes = $this->getAttributes();
868: $this->nameId = $this->name;
869: $this->updateNameIdRefs();
870: }
871:
872:
873: Actions::model()->deleteAllByAttributes(
874: array(
875: 'associationType' => strtolower(self::getAssociationType(get_class($this))),
876: 'associationId' => $this->id
877: ));
878:
879: if ($this->hasEventHandler('onAfterDelete'))
880: $this->onAfterDelete(new CEvent($this));
881: }
882:
883: 884: 885: 886: 887:
888: public function save($runValidation = true, $attributes = null) {
889: if (!$runValidation || $this->validate($attributes)) {
890:
891: if ($this->asa('X2FlowTriggerBehavior') &&
892: $this->asa('X2FlowTriggerBehavior')->enabled) {
893: $this->enableUpdateTrigger();
894: }
895: $retVal = $this->getIsNewRecord() ?
896: $this->insert($attributes) : $this->update($attributes);
897: if ($this->asa('X2FlowTriggerBehavior') &&
898: $this->asa('X2FlowTriggerBehavior')->enabled) {
899:
900: $this->disableUpdateTrigger();
901: }
902:
903: return $retVal;
904: } else {
905: return false;
906: }
907: }
908:
909: 910: 911: 912: 913: 914:
915: public function afterSave() {
916: if ($this->_runAfterCreate)
917: $this->afterCreate();
918: else
919: $this->afterUpdate();
920:
921: $phoneFields = array();
922: $linkFields = array();
923:
924:
925: foreach (self::$_fields[$this->tableName()] as &$_field) {
926: if ($_field->type === 'phone') {
927: $phoneFields[$_field->fieldName] = $this->getAttribute($_field->fieldName);
928: } elseif ($_field->type === 'link') {
929: $nameAndId = Fields::nameAndId($this->getAttribute($_field->fieldName));
930: $linkFields[$_field->fieldName] = array(
931: 'id' => $nameAndId[1],
932: 'type' => $_field->linkType
933: );
934: }
935: }
936:
937:
938: if (count($phoneFields)) {
939:
940: X2Model::model('PhoneNumber')->deleteAllByAttributes(
941: array('modelId' => $this->id, 'modelType' => get_class($this)));
942: }
943:
944:
945: foreach ($phoneFields as $field => &$number) {
946: if (!empty($number)) {
947: $num = new PhoneNumber;
948:
949: $num->number = preg_replace('/\D/', '', $number);
950: $num->modelId = $this->id;
951: $num->modelType = get_class($this);
952: $num->fieldName = $field;
953: $num->save();
954: }
955: }
956:
957: parent::afterSave();
958: }
959:
960: 961: 962: 963:
964: public function rules() {
965: return array_merge (
966: parent::rules (),
967: self::modelRules(self::$_fields[$this->tableName()], $this));
968: }
969:
970: public static function modelRules(&$fields, $model) {
971: $fieldTypes = array(
972: 'required',
973: 'email',
974: 'unique',
975: 'int',
976: 'numerical',
977:
978:
979: 'boolean',
980: 'safe',
981: 'search',
982: 'link',
983: 'foreignKey',
984: 'uniqueIndex',
985: );
986: $fieldRules = array_fill_keys($fieldTypes, array());
987: $validators = Fields::getFieldTypes('validator');
988:
989: foreach ($fields as &$_field) {
990:
991: $fieldRules['search'][] = $_field->fieldName;
992: if (isset($validators[$_field->type]) && $_field->safe) {
993: $fieldRules[$validators[$_field->type]][] = $_field->fieldName;
994: }
995:
996: if ($_field->required) {
997: $fieldRules['required'][] = $_field->fieldName;
998: }
999:
1000:
1001: if (!(property_exists ($model, 'subScenario') &&
1002: $model->subScenario === 'importOverwrite' && $_field->fieldName === 'id') &&
1003: $_field->uniqueConstraint) {
1004:
1005: $fieldRules['unique'][] = $_field->fieldName;
1006: }
1007:
1008: if ($_field->type == 'link' && $_field->required)
1009: $fieldRules['link'][] = $_field->fieldName;
1010:
1011: if ($_field->keyType === 'FOR') {
1012: $fieldRules['foreignKey'][] = $_field->fieldName;
1013: }
1014: if ($_field->keyType === 'UNI') {
1015: $fieldRules['uniqueIndex'][] = $_field->fieldName;
1016: }
1017: }
1018:
1019: $rules = array(
1020: array(implode(',', $fieldRules['foreignKey']),
1021: 'application.components.validators.X2ModelForeignKeyValidator'),
1022: array(implode(',', $fieldRules['uniqueIndex']),
1023: 'application.components.validators.X2ModelUniqueIndexValidator'),
1024: array(implode(',', $fieldRules['required']), 'required'),
1025: array(implode(',', $fieldRules['unique']), 'unique'),
1026: array(implode(',', $fieldRules['numerical']), 'numerical'),
1027: array(implode(',', $fieldRules['email']), 'email'),
1028: array(implode(',', $fieldRules['int']), 'numerical', 'integerOnly' => true),
1029: array(implode(',', $fieldRules['boolean']), 'boolean'),
1030: array(implode(',', $fieldRules['link']), 'application.components.ValidLinkValidator'),
1031: array(implode(',', $fieldRules['safe']), 'safe'),
1032: array(implode(',', $fieldRules['search']), 'safe', 'on' => 'search'),
1033: );
1034:
1035: return $rules;
1036: }
1037:
1038: 1039: 1040: 1041: 1042: 1043: 1044: 1045: 1046: 1047:
1048: public function getAttribute($name, $renderFlag = false, $makeLinks = false) {
1049:
1050: $nameParts = explode('.', $name);
1051:
1052: if (count($nameParts) > 1) {
1053:
1054:
1055: $linkField = array_shift($nameParts);
1056: $linkModel = $this->getLinkedModel($linkField);
1057:
1058:
1059: $name = implode('.', $nameParts);
1060:
1061: if (isset($linkModel)) {
1062: return $linkModel->getAttribute($name, $renderFlag);
1063: } else {
1064:
1065: $fieldInfo = $this->getField($linkField);
1066: if ($fieldInfo instanceof Fields && $fieldInfo->type == 'assignment') {
1067: $profRecord = X2Model::model('Profile')
1068: ->findByAttributes(array('username' => $this->$linkField));
1069:
1070: if (isset($profRecord)) {
1071: return $profRecord->getAttribute($name, false);
1072: }
1073: }
1074: }
1075: } else {
1076: if ($renderFlag) {
1077: return $this->renderAttribute($name, $makeLinks);
1078: } else {
1079: return parent::getAttribute($name);
1080: }
1081: }
1082: return null;
1083: }
1084:
1085: 1086: 1087: 1088: 1089: 1090:
1091: public function getLinkedAttribute($linkField, $attribute) {
1092: if (null !== $model = $this->getLinkedModel($linkField))
1093: return $model->getAttribute($attribute);
1094: return null;
1095: }
1096:
1097: 1098: 1099: 1100: 1101: 1102: 1103: 1104:
1105: public function renderLinkedAttribute($linkField, $attribute) {
1106: if (null !== $model = $this->getLinkedModel($linkField))
1107: return $model->renderAttribute($attribute);
1108: return null;
1109: }
1110:
1111: 1112: 1113: 1114: 1115: 1116: 1117: 1118: 1119:
1120: public function getLinkedModel($linkField, $lookup = true) {
1121: $nameId = $this->getAttribute($linkField);
1122: list($name, $id) = Fields::nameAndId($nameId);
1123:
1124: if (ctype_digit((string) $id)) {
1125: $field = $this->getField($linkField);
1126:
1127: if ($field !== null && $field->type === 'link') {
1128: $modelClass = $field->linkType;
1129:
1130: if (!$lookup) {
1131: return self::getLinkedModelMock($modelClass, $name, $id);
1132: }
1133:
1134:
1135: if (!isset(self::$_linkedModels[$modelClass][$id])) {
1136: self::$_linkedModels[$modelClass][$id] = X2Model::model($modelClass)->findByPk($id);
1137: if (self::$_linkedModels[$modelClass][$id] === null)
1138: self::$_linkedModels[$modelClass][$id] = false;
1139: }
1140:
1141: if (self::$_linkedModels[$modelClass][$id] !== false)
1142: return self::$_linkedModels[$modelClass][$id];
1143: }
1144: }
1145: return null;
1146: }
1147:
1148: 1149: 1150: 1151: 1152: 1153: 1154: 1155: 1156: 1157:
1158: public static function getLinkedModelMock($modelClass, $name, $id, $allowEmpty = false) {
1159: if ($id !== null || $allowEmpty) {
1160:
1161: $model = X2Model::model($modelClass);
1162: if (!$model instanceof X2Model) {
1163: throw new CException("Error: model $modelClass does not refer to an existing child class of X2Model.");
1164: }
1165: if ($model->hasAttribute('id') && $model->hasAttribute('name')) {
1166: $model->id = $id;
1167: $model->name = $name;
1168: return $model;
1169: } else {
1170: return null;
1171: }
1172: } else {
1173: return null;
1174: }
1175: }
1176:
1177: 1178: 1179: 1180: 1181: 1182: 1183:
1184: public static function getModelLink($id, $class, $requireAbsoluteUrl = false) {
1185: try {
1186: $model = X2Model::model($class)->findByPk($id);
1187: } catch (CHttpException $e) {
1188: $model = null;
1189: }
1190: if (isset($model) && !is_null($model->asa('X2LinkableBehavior'))) {
1191: if (isset(Yii::app()->controller) && method_exists(Yii::app()->controller, 'checkPermissions')) {
1192: if (Yii::app()->controller->checkPermissions($model, 'view')) {
1193: if ($requireAbsoluteUrl) {
1194: return $model->getUrlLink();
1195: } else {
1196: return $model->getLink();
1197: }
1198: } else {
1199: return $model->renderAttribute('name');
1200: }
1201: } else {
1202: if ($requireAbsoluteUrl) {
1203: return $model->getUrlLink();
1204: } else {
1205: return $model->getLink();
1206: }
1207: }
1208:
1209: } elseif (is_numeric($id)) {
1210: return '';
1211: } else {
1212: return $id;
1213: }
1214: }
1215:
1216: 1217: 1218:
1219: public function getStaticLinkedModels () {
1220: $linkFields = array_filter ($this->fields, function ($field) {
1221: return $field->type === 'link';
1222: });
1223: $linkedModels = array ();
1224: foreach ($linkFields as $field) {
1225: $linkedModels[$field->fieldName] = new $field->linkType ();
1226: }
1227: return $linkedModels;
1228: }
1229:
1230: 1231: 1232: 1233: 1234: 1235: 1236:
1237: public static function getModelLinkMock($modelClass, $nameId, $htmlOptions = array()) {
1238: list($name, $id) = Fields::nameAndId($nameId);
1239: $model = self::getLinkedModelMock($modelClass, $name, $id);
1240: if ($model instanceof X2Model && !is_null($model->asa('X2LinkableBehavior'))) {
1241: return $model->getLink($htmlOptions);
1242: } else {
1243: return CHtml::encode($name);
1244: }
1245: }
1246:
1247: 1248: 1249: 1250: 1251: 1252:
1253: public static function getModelTypes($assoc = false, $filter=null) {
1254: $modelTypes = Yii::app()->db->createCommand()
1255: ->selectDistinct('modelName')
1256: ->from('x2_fields')
1257: ->where('modelName!="Calendar"')
1258: ->order('modelName ASC')
1259: ->queryColumn();
1260: if ($filter) {
1261: $modelTypes = array_filter ($modelTypes, $filter);
1262: }
1263:
1264: if ($assoc === true) {
1265: $modelTypes = array_combine($modelTypes, array_map(function($type) {
1266: return X2Model::model ($type)->getDisplayName (true, false);
1267: }, $modelTypes));
1268: asort ($modelTypes);
1269: return $modelTypes;
1270: }
1271: $modelTypes = array_map(function($term) {
1272: return Yii::t('app', $term);
1273: }, $modelTypes);
1274: return $modelTypes;
1275: }
1276:
1277: 1278: 1279: 1280: 1281: 1282: 1283: 1284: 1285: 1286: 1287: 1288: 1289: 1290: 1291: 1292:
1293: private static $_modelsWhichSupportRelationships;
1294: public static function getModelTypesWhichSupportRelationships($assoc = false, $refresh = false) {
1295: if (!isset (self::$_modelsWhichSupportRelationships[$assoc]) || $refresh) {
1296: $modelTypes = self::getModelTypes(true);
1297: $filteredTypes = array ();
1298: foreach ($modelTypes as $type => $title) {
1299: if (X2Model::Model ($type)->asa('relationships')) {
1300: if ($assoc) {
1301: $filteredTypes[$type] = $title;
1302: } else {
1303: $filteredTypes[] = $type;
1304: }
1305: }
1306: }
1307: self::$_modelsWhichSupportRelationships[$assoc] = $filteredTypes;
1308: }
1309:
1310: return self::$_modelsWhichSupportRelationships[$assoc];
1311: }
1312:
1313: 1314: 1315: 1316: 1317: 1318:
1319: public static function getModelTypesWhichSupportWorkflow($assoc = false, $associationTypes = false) {
1320: $modelTypes = self::getModelTypes($assoc);
1321: $tmp = $assoc ? array_flip($modelTypes) : $modelTypes;
1322: $tmp = array_filter($tmp, function ($a) use ($assoc) {
1323: return X2Model::Model($a)->supportsWorkflow;
1324: });
1325: $tmp = $assoc ? array_flip($tmp) : $tmp;
1326: $tmp = array_intersect($modelTypes, $tmp);
1327: if($associationTypes){
1328: $arr = array();
1329: foreach ($tmp as $k => $v) {
1330: if ($assoc) {
1331: $arr[X2Model::getAssociationType($k)] = $v;
1332: } else {
1333: $arr[] = X2Model::getAssociationType($v);
1334: }
1335: }
1336: $tmp = $arr;
1337: }
1338: return $tmp;
1339: }
1340:
1341: 1342: 1343: 1344: 1345:
1346: public function translatedAttributeLabel($label) {
1347: return Yii::t((bool) $this->asa('X2LinkableBehavior') ?
1348: (empty($this->module) ? 'app' : $this->module) : 'app', $label);
1349: }
1350:
1351: 1352: 1353: 1354: 1355:
1356: public function getAttributeLabels () {
1357: $tableName = $this->tableName ();
1358: if (!isset (self::$_attributeLabels[$tableName])) {
1359: $labels = array();
1360:
1361: foreach (self::$_fields[$tableName] as &$_field) {
1362: $labels[$_field->fieldName] =
1363: $this->translatedAttributeLabel($_field->attributeLabel);
1364: }
1365:
1366: self::$_attributeLabels[$tableName] = $labels;
1367: }
1368: return self::$_attributeLabels[$tableName];
1369: }
1370:
1371: 1372: 1373: 1374: 1375:
1376: public function attributeLabels() {
1377: $labels = array();
1378:
1379: foreach (self::$_fields[$this->tableName()] as &$_field) {
1380: $labels[$_field->fieldName] = $this->translatedAttributeLabel($_field->attributeLabel);
1381: }
1382:
1383: return $labels;
1384: }
1385:
1386: 1387: 1388: 1389: 1390: 1391: 1392: 1393: 1394: 1395: 1396:
1397: public function getAttributeLabel($attribute) {
1398: $attributeLabels = $this->getAttributeLabels ();
1399: if (isset ($attributeLabels[$attribute])) return $attributeLabels[$attribute];
1400:
1401: if (isset(self::$_fields[$this->tableName()][$attribute])) {
1402: return self::$_fields[$this->tableName()][$attribute];
1403: }
1404:
1405: if (strpos($attribute, '.') !== false) {
1406: $segs = explode('.', $attribute);
1407: $name = array_pop($segs);
1408: $model = $this;
1409: foreach ($segs as $seg) {
1410: $relations = $model->getMetaData()->relations;
1411: if (isset($relations[$seg]))
1412: $model = X2Model::model($relations[$seg]->className);
1413: else
1414: break;
1415: }
1416: return $model->getAttributeLabel($name);
1417: } else
1418: return $this->generateAttributeLabel($attribute);
1419: }
1420:
1421: public function getOldAttributes() {
1422: return $this->_oldAttributes;
1423: }
1424:
1425: 1426: 1427: 1428: 1429: 1430:
1431: public function getReadableAttributeNames() {
1432: return array_keys(array_filter($this->getFieldPermissions(), function($p) {
1433: return $p >= Fields::READ_PERMISSION;
1434: }));
1435: }
1436:
1437: public function getEditableAttributeNames () {
1438: return array_keys(array_filter($this->getFieldPermissions(), function($p) {
1439: return $p >= Fields::WRITE_PERMISSION;
1440: }));
1441: }
1442:
1443: 1444: 1445: 1446:
1447: public function getVisibleAttributes () {
1448: if (!Yii::app()->params->isAdmin && !empty(Yii::app()->params->roles)) {
1449: $fieldPermissions = $this->getFieldPermissions();
1450: } else {
1451: return $this->getAttributes ();
1452: }
1453:
1454: $visibleAttributeNames = array ();
1455: foreach ($fieldPermissions as $fieldName => $permission) {
1456: if ($permission >= Fields::READ_PERMISSION) {
1457: $visibleAttributeNames[] = $fieldName;
1458: }
1459: }
1460: return $this->getAttributes ($visibleAttributeNames);
1461: }
1462:
1463: 1464: 1465: 1466: 1467: 1468:
1469: public function getFields($assoc = false, $filterFn=null,
1470: $requiredPermission=Fields::NO_PERMISSION){
1471:
1472: if($assoc){
1473: $fields = array();
1474: foreach(self::$_fields[$this->tableName()] as &$field) {
1475: if ($filterFn !== null) {
1476: if ($filterFn ($field)) {
1477: $fields[$field->fieldName] = $field;
1478: }
1479: } else {
1480: $fields[$field->fieldName] = $field;
1481: }
1482: }
1483: return $fields;
1484: }else{
1485: if ($filterFn !== null) {
1486: $fields = array();
1487: foreach(self::$_fields[$this->tableName()] as &$field) {
1488: if ($filterFn ($field)) {
1489: $fields[] = $field;
1490: }
1491: }
1492: return $fields;
1493: } else {
1494: return self::$_fields[$this->tableName()];
1495: }
1496: }
1497:
1498:
1499: if ($requiredPermission > Fields::NO_PERMISSION) {
1500: $permissions = $this->getFieldPermissions ();
1501: foreach ($permissions as $name => $permissionLevel) {
1502: if ($permissionLevel > $requiredPermission) {
1503: unset ($fields[$name]);
1504: }
1505: }
1506: }
1507: }
1508:
1509: 1510: 1511:
1512: public static function getFieldComparisonOptions () {
1513: return array(
1514: '=' => Yii::t('app','equals'),
1515: '>' => Yii::t('app','greater than'),
1516: '<' => Yii::t('app','less than'),
1517: '>=' => Yii::t('app','greater than or equal to'),
1518: '<=' => Yii::t('app','less than or equal to'),
1519: '<>' => Yii::t('app','not equal to'),
1520: 'list' => Yii::t('app','in list'),
1521: 'notList' => Yii::t('app','not in list'),
1522: 'empty' => Yii::t('app','empty'),
1523: 'notEmpty' => Yii::t('app','not empty'),
1524: 'contains' => Yii::t('app','contains'),
1525: 'noContains' => Yii::t('app','does not contain'),
1526: 'before' => Yii::t('app','before'),
1527: 'after' => Yii::t('app','after'),
1528: );
1529: }
1530:
1531: 1532: 1533: 1534: 1535: 1536: 1537:
1538: public function getFieldsForDropdown (
1539: $includeFieldsOfLinkedRecords=false, $condList=true, $filterFn=null, $separator='.') {
1540:
1541: if ($includeFieldsOfLinkedRecords) {
1542: $linkedModels = $this->getStaticLinkedModels ();
1543: $fieldsForDropdown = array ();
1544: $fieldsForDropdown[''] = $this->_getFieldsForDropdown (
1545: null, $condList, true, $filterFn, $separator);
1546: foreach ($linkedModels as $fieldName => $linkedModel) {
1547: if ($this->getField ($fieldName)) {
1548: $optGroupHeader = $this->getAttributeLabel ($fieldName);
1549: } else if (self::isModuleModelName ($fieldName)) {
1550: $optGroupHeader = self::getModelTitle ($fieldName);
1551: } else {
1552: throw new CException ('invalid field name');
1553: }
1554: $fieldsForDropdown[$optGroupHeader] = $linkedModel->_getFieldsForDropdown (
1555: $fieldName, $condList, true, $filterFn, $separator);
1556: }
1557: return $fieldsForDropdown;
1558: } else {
1559: return $this->_getFieldsForDropdown (null, $condList, true, $filterFn, $separator);
1560: }
1561: }
1562:
1563: 1564: 1565:
1566: public function getField($fieldName) {
1567: foreach (self::$_fields[$this->tableName()] as &$_field) {
1568: if ($_field->fieldName == $fieldName)
1569: return $_field;
1570: }
1571: return null;
1572: }
1573:
1574: 1575: 1576: 1577: 1578: 1579: 1580: 1581:
1582: public function getIsExemptFromFieldLevelPermissions() {
1583: return Yii::app()->params->isAdmin || empty(Yii::app()->params->roles);
1584: }
1585:
1586: public function insert($attributes = null) {
1587: $succeeded = parent::insert($attributes);
1588:
1589: if ($succeeded && self::$autoPopulateFields) {
1590: $this->updateNameId(true);
1591:
1592: if ($this->hasEventHandler('onAfterInsert'))
1593: $this->onAfterInsert(new CEvent($this));
1594: }
1595: return $succeeded;
1596: }
1597:
1598: 1599: 1600: 1601: 1602: 1603: 1604: 1605: 1606:
1607: public static function getPhoneNumber(
1608: $field, $class, $id, $encode = false, $makeLink = false, $default = null) {
1609:
1610: $phoneCheck = CActiveRecord::model('PhoneNumber')
1611: ->findByAttributes(
1612: array('modelId' => $id, 'modelType' => $class, 'fieldName' => $field));
1613: if ($phoneCheck instanceof PhoneNumber && strlen($phoneCheck->number) == 10 &&
1614: strpos($phoneCheck->number, '0') !== 0 && strpos($phoneCheck->number, '1') !== 0) {
1615:
1616: $number = (string) $phoneCheck->number;
1617: $fmtNumber = "(" . substr($number, 0, 3) . ") " . substr($number, 3, 3) . "-" .
1618: substr($number, 6, 4);
1619: } elseif ($default != null) {
1620: $number = (string) $default;
1621: $fmtNumber = $default;
1622: } else {
1623: $record = X2Model::model($class)->findByPk($id);
1624: if (isset($record) && $record->hasAttribute($field)) {
1625: $number = (string) $record->$field;
1626: $fmtNumber = $number;
1627: }
1628: }
1629: if ($encode && isset($fmtNumber)) {
1630: $fmtNumber = CHtml::encode($fmtNumber);
1631: }
1632: if (isset($fmtNumber) && $makeLink && !Yii::app()->params->profile->disablePhoneLinks) {
1633: return '<a href="tel:' . $number . '">' . $fmtNumber . '</a>';
1634: }
1635: return isset($fmtNumber) ? $fmtNumber : '';
1636: }
1637:
1638: public static function renderModelInput(CModel $model, $field, $htmlOptions = array()) {
1639: if (!$field->asa ('CommonFieldsBehavior')) {
1640: throw new Exception ('$field must have CommonFieldsBehavior');
1641: }
1642: if ($field->required) {
1643: if (isset($htmlOptions['class'])) {
1644: $htmlOptions['class'] .= ' x2-required';
1645: } else {
1646: $htmlOptions = array_merge(
1647: array(
1648: 'class' => 'x2-required'
1649: ), $htmlOptions
1650: );
1651: }
1652: }
1653: $fieldName = $field->fieldName;
1654: if (!isset($field))
1655: return null;
1656: switch ($field->type) {
1657: case 'text':
1658: return CHtml::activeTextArea($model, $field->fieldName,
1659: array_merge(
1660: array('title' => $field->attributeLabel),
1661: array_merge(array('encode' => false),
1662: $htmlOptions)));
1663:
1664: case 'date':
1665: $oldDateVal = $model->$fieldName;
1666: $model->$fieldName = Formatter::formatDate($model->$fieldName, 'medium');
1667: Yii::import('application.extensions.CJuiDateTimePicker.CJuiDateTimePicker');
1668: $pickerOptions = array(
1669: 'dateFormat' => Formatter::formatDatePicker(),
1670: 'changeMonth' => false,
1671: 'changeYear' => true,
1672: );
1673: if (Yii::app()->getLanguage() === 'fr')
1674: $pickerOptions['monthNamesShort'] = Formatter::getPlainAbbrMonthNames();
1675: $input = Yii::app()->controller->widget('CJuiDateTimePicker', array(
1676: 'model' => $model,
1677: 'attribute' => $fieldName,
1678: 'mode' => 'date',
1679: 'options' => $pickerOptions,
1680: 'htmlOptions' => array_merge(array(
1681: 'title' => $field->attributeLabel,
1682: ), $htmlOptions),
1683: 'language' => (Yii::app()->language == 'en') ? '' : Yii::app()->getLanguage(),
1684: ), true);
1685: $model->$fieldName = $oldDateVal;
1686: return $input;
1687: case 'dateTime':
1688: $oldDateTimeVal = $model->$fieldName;
1689: $pickerOptions = array(
1690: 'dateFormat' => Formatter::formatDatePicker('medium'),
1691: 'timeFormat' => Formatter::formatTimePicker(),
1692: 'ampm' => Formatter::formatAMPM(),
1693: 'changeMonth' => true,
1694: 'changeYear' => true,
1695: );
1696: if (Yii::app()->getLanguage() === 'fr')
1697: $pickerOptions['monthNamesShort'] = Formatter::getPlainAbbrMonthNames();
1698: $model->$fieldName = Formatter::formatDateTime($model->$fieldName);
1699: Yii::import('application.extensions.CJuiDateTimePicker.CJuiDateTimePicker');
1700: $input = Yii::app()->controller->widget('CJuiDateTimePicker', array(
1701: 'model' => $model,
1702: 'attribute' => $fieldName,
1703: 'mode' => 'datetime',
1704: 'options' => $pickerOptions,
1705: 'htmlOptions' => array_merge(array(
1706: 'title' => $field->attributeLabel,
1707: ), $htmlOptions),
1708: 'language' => (Yii::app()->language == 'en') ? '' : Yii::app()->getLanguage(),
1709: ), true);
1710: $model->$fieldName = $oldDateTimeVal;
1711: return $input;
1712: case 'dropdown':
1713:
1714:
1715: $om = $field->getDropdownOptions ();
1716: $multi = (bool) $om['multi'];
1717: $dropdowns = $om['options'];
1718: $curVal = $multi ? CJSON::decode($model->{$field->fieldName}) : $model->{$field->fieldName};
1719:
1720: $ajaxArray = array();
1721: if ($field instanceof Fields) {
1722: $dependencyCount = X2Model::model('Dropdowns')
1723: ->countByAttributes(array('parent' => $field->linkType));
1724: $fieldDependencyCount = X2Model::model('Fields')
1725: ->countByAttributes(array(
1726: 'modelName' => $field->modelName,
1727: 'type' => 'dependentDropdown',
1728: 'linkType' => $field->linkType));
1729: if ($dependencyCount > 0 && $fieldDependencyCount > 0) {
1730: $ajaxArray = array('ajax' => array(
1731: 'type' => 'GET',
1732: 'url' => Yii::app()->controller->createUrl('/site/dynamicDropdown'),
1733: 'data' => 'js:{
1734: "val":$(this).val(),
1735: "dropdownId":"' . $field->linkType . '",
1736: "field":true, "module":"' . $field->modelName . '"
1737: }',
1738: 'success' => '
1739: function(data){
1740: if(data){
1741: data=JSON.parse(data);
1742: if(data[0] && data[1]){
1743: $("#' . $field->modelName . '_"+data[0]).html(data[1]);
1744: }
1745: }
1746: }',
1747: ));
1748: }
1749: }
1750: $htmlOptions = array_merge(
1751: $htmlOptions, $ajaxArray, array('title' => $field->attributeLabel));
1752: if ($multi) {
1753: $multiSelectOptions = array();
1754: if (!is_array($curVal))
1755: $curVal = array();
1756: foreach ($curVal as $option)
1757: $multiSelectOptions[$option] = array('selected' => 'selected');
1758: $htmlOptions = array_merge(
1759: $htmlOptions, array(
1760: 'options' => $multiSelectOptions,
1761: 'multiple' => 'multiple'
1762: ));
1763: } elseif ($field->includeEmpty) {
1764: $htmlOptions = array_merge(
1765: $htmlOptions, array('empty' => Yii::t('app', "Select an option")));
1766: }
1767: return CHtml::activeDropDownList($model, $field->fieldName, $dropdowns, $htmlOptions);
1768:
1769: case 'dependentDropdown':
1770: return CHtml::activeDropDownList($model, $field->fieldName, array('' => '-'), array_merge(
1771: array(
1772: 'title' => $field->attributeLabel,
1773: ), $htmlOptions
1774: ));
1775: case 'link':
1776: $linkSource = null;
1777: $linkId = '';
1778: $name = '';
1779:
1780: if (class_exists($field->linkType)) {
1781:
1782: if (!empty($model->$fieldName)) {
1783: list($name, $linkId) = Fields::nameAndId($model->$fieldName);
1784: $linkModel = X2Model::getLinkedModelMock($field->linkType, $name, $linkId, true);
1785: } else {
1786: $linkModel = X2Model::model($field->linkType);
1787: }
1788: if ($linkModel instanceof X2Model && $linkModel->asa('X2LinkableBehavior') instanceof X2LinkableBehavior) {
1789: $linkSource = Yii::app()->controller->createUrl($linkModel->autoCompleteSource);
1790: $linkId = $linkModel->id;
1791: $oldLinkFieldVal = $model->$fieldName;
1792: $model->$fieldName = $name;
1793: }
1794: }
1795:
1796: static $linkInputCounter = 0;
1797: $hiddenInputId = $field->modelName . '_' . $fieldName . "_id".$linkInputCounter++;
1798: $input = CHtml::hiddenField(
1799: $field->modelName . '[' . $fieldName . '_id]', $linkId,
1800: array('id' => $hiddenInputId))
1801: .Yii::app()->controller->widget('zii.widgets.jui.CJuiAutoComplete', array(
1802: 'model' => $model,
1803: 'attribute' => $fieldName,
1804:
1805: 'source' => $linkSource,
1806: 'value' => $name,
1807: 'options' => array(
1808: 'minLength' => '1',
1809: 'select' => 'js:function( event, ui ) {
1810: $("#'.$hiddenInputId.'").
1811: val(ui.item.id);
1812: $(this).val(ui.item.value);
1813: return false;
1814: }',
1815: 'create' => $field->linkType == 'Contacts' ?
1816: 'js:function(event, ui) {
1817: $(this).data( "uiAutocomplete" )._renderItem =
1818: function(ul,item) {
1819: return $("<li>").data("item.autocomplete",item).
1820: append(x2.forms.renderContactLookup(item)).
1821: appendTo(ul);
1822: };
1823: }' : ($field->linkType == 'BugReports' ? 'js:function(event, ui) {
1824: $(this).data( "uiAutocomplete" )._renderItem =
1825: function( ul, item ) {
1826:
1827: var label = "<a style=\"line-height: 1;\">" + item.label;
1828:
1829: label += "<span style=\"font-size: 0.6em;\">";
1830:
1831: // add email if defined
1832: if(item.subject) {
1833: label += "<br>";
1834: label += item.subject;
1835: }
1836:
1837: label += "</span>";
1838: label += "</a>";
1839:
1840: return $( "<li>" )
1841: .data( "item.autocomplete", item )
1842: .append( label )
1843: .appendTo( ul );
1844: };
1845: }' : ''),
1846: ),
1847: 'htmlOptions' => array_merge(array(
1848: 'title' => $field->attributeLabel,
1849: ), $htmlOptions)
1850: ), true);
1851: if (isset ($oldLinkFieldVal)) $model->$fieldName = $oldLinkFieldVal;
1852: return $input;
1853: case 'rating':
1854: return Yii::app()->controller->widget('X2StarRating', array(
1855: 'model' => $model,
1856: 'attribute' => $field->fieldName,
1857: 'readOnly' => isset($htmlOptions['disabled']) && $htmlOptions['disabled'],
1858: 'minRating' => Fields::RATING_MIN,
1859: 'maxRating' => Fields::RATING_MAX,
1860: 'starCount' => Fields::RATING_MAX - Fields::RATING_MIN + 1,
1861: 'cssFile' => Yii::app()->theme->getBaseUrl() . '/css/rating/jquery.rating.css',
1862: 'htmlOptions' => $htmlOptions,
1863: 'callback'=>'function(value, link){
1864: if (typeof x2 !== "undefined" &&
1865: typeof x2.InlineEditor !== "undefined" &&
1866: typeof x2.InlineEditor.ratingFields !== "undefined") {
1867:
1868: x2.InlineEditor.ratingFields["'.
1869: $field->modelName.'['.$field->fieldName.']"] = value;
1870: }
1871: }',), true);
1872:
1873: case 'boolean':
1874: $checkbox = CHtml::openTag ('div', X2Html::mergeHtmlOptions (
1875: $htmlOptions, array (
1876: 'class' => 'checkboxWrapper'
1877: )
1878: ));
1879: $checkbox .= CHtml::activeCheckBox($model, $field->fieldName, array_merge(array(
1880: 'unchecked' => 0,
1881: 'title' => $field->attributeLabel,
1882: ), $htmlOptions));
1883: $checkbox .= CHtml::closeTag ('div');
1884: return $checkbox;
1885: case 'assignment':
1886: $oldAssignmentVal = $model->$fieldName;
1887: $model->$fieldName = !empty($model->$fieldName) ?
1888: ($field->linkType == 'multiple' && !is_array($model->$fieldName) ?
1889: explode(', ', $model->$fieldName) : $model->$fieldName) :
1890: X2Model::getDefaultAssignment();
1891: $dropdownList = CHtml::activeDropDownList (
1892: $model, $fieldName, X2Model::getAssignmentOptions (true, true),
1893: array_merge (array (
1894:
1895:
1896: 'title' => $field->attributeLabel,
1897: 'id' => $field->modelName . '_' . $fieldName . '_assignedToDropdown',
1898: 'multiple' =>
1899: ($field->linkType == 'multiple' ? 'multiple' : null),
1900: ), $htmlOptions)
1901: );
1902: $model->$fieldName = $oldAssignmentVal;
1903: return $dropdownList;
1904: case 'optionalAssignment':
1905:
1906: $users = User::getNames();
1907: unset($users['Anyone']);
1908:
1909: return CHtml::activeDropDownList($model, $fieldName, $users, array_merge(array(
1910:
1911:
1912: 'title' => $field->attributeLabel,
1913: 'empty' => '',
1914: ), $htmlOptions));
1915:
1916: case 'visibility':
1917: $permissionsBehavior = Yii::app()->params->modelPermissions;
1918: return CHtml::activeDropDownList($model, $field->fieldName, $permissionsBehavior::getVisibilityOptions(), array_merge(array(
1919: 'title' => $field->attributeLabel,
1920: 'id' => $field->modelName . "_visibility",
1921: ), $htmlOptions));
1922:
1923:
1924:
1925:
1926:
1927:
1928:
1929: case 'percentage':
1930: $htmlOptions['class'] = empty($htmlOptions['class']) ? 'input-percentage' : $htmlOptions['class'] . ' input-percentage';
1931: return CHtml::activeTextField($model, $field->fieldName, array_merge(array(
1932: 'title' => $field->attributeLabel,
1933: ), $htmlOptions));
1934:
1935: case 'currency':
1936: $fieldName = $field->fieldName;
1937: $elementId = isset($htmlOptions['id']) ?
1938: '#' . $htmlOptions['id'] :
1939: '#' . $field->modelName . '_' . $field->fieldName;
1940: Yii::app()->controller->widget('application.extensions.moneymask.MMask', array(
1941: 'element' => $elementId,
1942: 'currency' => Yii::app()->params['currency'],
1943: 'config' => array(
1944:
1945: 'affixStay' => true,
1946: 'decimal' => Yii::app()->locale->getNumberSymbol('decimal'),
1947: 'thousands' => Yii::app()->locale->getNumberSymbol('group'),
1948: )
1949: ));
1950:
1951: return CHtml::activeTextField($model, $field->fieldName, array_merge(array(
1952: 'title' => $field->attributeLabel,
1953: 'class' => 'currency-field',
1954: ), $htmlOptions));
1955: case 'credentials':
1956: $typeAlias = explode(':', $field->linkType);
1957: $type = $typeAlias[0];
1958: if (count($typeAlias) > 1) {
1959: $uid = Credentials::$sysUseId[$typeAlias[1]];
1960: } else {
1961: $uid = Yii::app()->user->id;
1962: }
1963: return Credentials::selectorField($model, $field->fieldName, $type, $uid);
1964:
1965: case 'timerSum':
1966:
1967: return $model->renderAttribute($field->fieldName);
1968: case 'float':
1969: case 'int':
1970: if (isset($model->$fieldName)) {
1971: $oldNumVal = $model->$fieldName;
1972: $model->$fieldName = Yii::app()->locale->numberFormatter->formatDecimal($model->$fieldName);
1973: }
1974: $input = CHtml::activeTextField($model, $field->fieldName, array_merge(array(
1975: 'title' => $field->attributeLabel,
1976: ), $htmlOptions));
1977: if (isset ($oldNumVal)) {
1978: $model->$fieldName = $oldNumVal;
1979: }
1980: return $input;
1981: default:
1982: return CHtml::activeTextField($model, $field->fieldName, array_merge(array(
1983: 'title' => $field->attributeLabel,
1984: ), $htmlOptions));
1985:
1986:
1987:
1988:
1989:
1990:
1991:
1992: }
1993: }
1994:
1995: public static function renderMergeInput($modelType, $idArray, $field) {
1996: $options = array();
1997: $equalFlag = true;
1998: $selected = $idArray[0];
1999: $lastValue = null;
2000: foreach ($idArray as $id) {
2001: $tmpModel = X2Model::model($modelType)->findByPk($id);
2002: $equalFlag = $equalFlag && (is_null($lastValue) || $lastValue == $tmpModel->{$field->fieldName});
2003: $lastValue = is_null($tmpModel->{$field->fieldName}) ? "" : $tmpModel->{$field->fieldName};
2004: $options[$id] = strlen($tmpModel->{$field->fieldName}) == 0 ? '' : $tmpModel->renderAttribute($field->fieldName, false, true, false, false);
2005: }
2006: if ($field->type !== 'text') {
2007: $valid = array_filter($options, 'strlen');
2008: if (key($valid) != $selected) {
2009: $selected = key($valid);
2010: }
2011: foreach ($options as &$option) {
2012: if (strlen($option) == 0) {
2013: $option = '-';
2014: }
2015: }
2016: return CHtml::dropDownList(X2Model::getModelName($modelType) . '[' . $field->fieldName . ']', $selected, $options, array(
2017: 'disabled' => $equalFlag ? 'disabled' : '',
2018: )) . ($equalFlag?CHtml::hiddenField(X2Model::getModelName($modelType) . '[' . $field->fieldName . ']', $selected):"");
2019: } else {
2020: $str = str_replace("<br />", "\n", implode("\n--\n", $options));
2021: return CHtml::textArea(X2Model::getModelName($modelType) . '[' . $field->fieldName . ']', $str, array(
2022: 'style' => 'height:100px;',
2023: ));
2024: }
2025: }
2026:
2027: 2028: 2029: 2030: 2031: 2032:
2033: public function renderInput($fieldName, $htmlOptions = array()) {
2034: $field = $this->getField($fieldName);
2035:
2036: if (!$field) {
2037: return $this->renderErroneousField();
2038: }
2039:
2040: if ($this->inputRenderer) {
2041:
2042: if ($input = $this->inputRenderer->renderInput ($field, $htmlOptions)) {
2043: return $input;
2044: }
2045: }
2046:
2047: return self::renderModelInput($this, $field, $htmlOptions);
2048: }
2049:
2050: 2051: 2052:
2053: public function renderErroneousField() {
2054: $html = '<span class="erroneous-field">';
2055: $html .= Yii::t('app', 'Field could not be found');
2056: $html .= '</span>';
2057: return $html;
2058: }
2059:
2060: 2061: 2062: 2063: 2064: 2065:
2066: public function setX2Fields(&$data, $filter = false, $bypassPermissions=false) {
2067: $editableFieldsFieldNames = $this->getEditableFieldNames();
2068:
2069:
2070: foreach (self::$_fields[$this->tableName()] as &$field) {
2071: $fieldName = $field->fieldName;
2072:
2073:
2074: if (!isset($data[$fieldName]) ||
2075: (!$bypassPermissions && !in_array($fieldName, $editableFieldsFieldNames))) {
2076:
2077: if (isset($data[$fieldName]) &&
2078: !in_array($fieldName, $editableFieldsFieldNames)) {
2079:
2080:
2081:
2082: }
2083: continue;
2084: }
2085:
2086:
2087: if ($data[$fieldName] === $this->getAttributeLabel($fieldName) &&
2088: $field->type !== 'dropdown') {
2089:
2090: $data[$fieldName] = null;
2091: }
2092:
2093: if ($field->type === 'currency') {
2094: $defaultCurrency = Yii::app()->settings->currency;
2095: $curSym = Yii::app()->locale->getCurrencySymbol($defaultCurrency);
2096: if (is_null($curSym))
2097: $curSym = $defaultCurrency;
2098: $data[$fieldName] = Fields::strToNumeric($data[$fieldName], 'currency', $curSym);
2099: }
2100:
2101: if ($field->type === 'link') {
2102:
2103:
2104:
2105:
2106: $linkId = null;
2107: if (isset($data[$fieldName . '_id'])) {
2108:
2109: $linkId = $data[$fieldName . '_id'];
2110: }
2111:
2112: if (ctype_digit((string) $linkId)) {
2113: $link = Yii::app()->db->createCommand()
2114: ->select('name,nameId')
2115: ->from(X2Model::model($field->linkType)->tableName())
2116: ->where('id=?', array($linkId))
2117: ->queryRow(true);
2118:
2119: if (isset($link['name']) && $link['name'] === $data[$fieldName]) {
2120: $data[$fieldName] = $link['nameId'];
2121: }
2122: }
2123: }
2124: $this->$fieldName = $field->parseValue($data[$fieldName], $filter);
2125: }
2126:
2127:
2128:
2129:
2130:
2131:
2132: if ($this->getIsNewRecord() && $this->scenario == 'insert') {
2133:
2134: foreach ($this->getFields(true) as $fieldName => $field) {
2135: if (!isset($data[$fieldName]) && $this->$fieldName == '' &&
2136: $field->defaultValue != null && !$field->readOnly) {
2137:
2138: $this->$fieldName = $field->defaultValue;
2139: } else if ($this->$fieldName === null && $field->defaultValue === null &&
2140: $field->type === 'assignment') {
2141:
2142: $this->$fieldName = self::getDefaultAssignment();
2143: }
2144: }
2145: }
2146: }
2147:
2148: 2149: 2150: 2151: 2152: 2153: 2154: 2155:
2156: public function searchBase($criteria, $pageSize = null, $showHidden = false) {
2157: if(isset($_GET['showHidden']) && $_GET['showHidden'] &&
2158: Yii::app()->user->checkAccess(self::getModuleName(get_class($this)).'Admin')){
2159:
2160: $showHidden = true;
2161: }
2162:
2163: if ($criteria === null){
2164: $criteria = $this->getAccessCriteria(
2165: 't', Yii::app()->params->modelPermissions, $showHidden);
2166: }else{
2167: $criteria->mergeWith(
2168: $this->getAccessCriteria('t', Yii::app()->params->modelPermissions, $showHidden));
2169: }
2170:
2171: $filterCriteria = new CDbCriteria;
2172: $this->compareAttributes($filterCriteria);
2173: $criteria->mergeWith ($filterCriteria);
2174: $criteria->with = array();
2175: $sort = new SmartSort(
2176: get_class($this), isset($this->uid) ? $this->uid : get_class($this));
2177: $sort->multiSort = false;
2178: $sort->attributes = $this->getSort();
2179: $sort->defaultOrder = 't.lastUpdated DESC, t.id DESC';
2180:
2181: if ($pageSize === null) {
2182: if (!Yii::app()->user->isGuest) {
2183: $pageSize = Profile::getResultsPerPage();
2184: } else {
2185: $pageSize = 20;
2186: }
2187: }
2188:
2189: $dataProvider = new SmartActiveDataProvider(get_class($this), array(
2190: 'sort' => $sort,
2191: 'pagination' => array(
2192: 'pageSize' => $pageSize,
2193: ),
2194: 'criteria' => $criteria,
2195: 'uid' => $this->uid,
2196: 'dbPersistentGridSettings' => $this->dbPersistentGridSettings,
2197: 'disablePersistentGridSettings' => $this->disablePersistentGridSettings,
2198: ));
2199: $sort->applyOrder($criteria);
2200: return $dataProvider;
2201: }
2202:
2203: public function getSort() {
2204: $attributes = array();
2205: foreach (self::$_fields[$this->tableName()] as &$field) {
2206: $fieldName = $field->fieldName;
2207: switch ($field->type) {
2208: default:
2209: $attributes[$fieldName] = array(
2210: 'asc' => 't.' . $fieldName . ' ASC',
2211: 'desc' => 't.' . $fieldName . ' DESC',
2212: );
2213: }
2214: }
2215: return $attributes;
2216: }
2217:
2218: 2219: 2220: 2221:
2222: public function unshiftOperator($string) {
2223: $retArr = array('', $string);
2224: if (strlen($string) > 1) {
2225: if (strlen($string) > 2 && preg_match("/^(<>|>=).*/", $string)) {
2226: $retArr = array(substr($string, 0, 2), substr($string, 2));
2227: } else if (preg_match("/^(<|>|=).*/", $string)) {
2228: $retArr = array($string[0], substr($string, 1));
2229: }
2230: }
2231:
2232: return $retArr;
2233: }
2234:
2235: 2236: 2237:
2238: protected function compareAttribute(&$criteria, $field) {
2239: $fieldName = $field->fieldName;
2240: switch ($field->type) {
2241: case 'boolean':
2242: $criteria->compare(
2243: 't.' . $fieldName, $this->compareBoolean($this->$fieldName), true);
2244: break;
2245: case 'assignment':
2246: $assignmentCriteria = new CDbCriteria;
2247: $assignmentVal = $this->compareAssignment($this->$fieldName);
2248:
2249: if ($field->linkType === 'multiple' && $this->$fieldName) {
2250: if (!is_array ($assignmentVal)) $assignmentVal = array ();
2251: $assignmentVal = array_map (function ($val) {
2252: return preg_quote ($val);
2253: }, $assignmentVal);
2254: if (strlen ($this->$fieldName) && strncmp (
2255: "Anyone", ucfirst ($this->$fieldName), strlen ($this->$fieldName)) === 0) {
2256:
2257: $assignmentVal[] = 'Anyone';
2258: }
2259: $assignmentRegex =
2260: '(^|, )('.implode ('|', $assignmentVal).')'.
2261: (in_array ('Anyone', $assignmentVal) ? '?' : '').'(, |$)';
2262:
2263: $assignmentParamName = CDbCriteria::PARAM_PREFIX.CDbCriteria::$paramCount;
2264: $criteria->params[$assignmentParamName] = $assignmentRegex;
2265: CDbCriteria::$paramCount++;
2266: $criteria->addCondition(
2267: 't.' . $fieldName .' REGEXP BINARY '.$assignmentParamName);
2268: } else {
2269: $assignmentCriteria->compare(
2270: 't.' . $fieldName, $assignmentVal, true);
2271: if (strlen ($this->$fieldName) && strncmp (
2272: "Anyone", ucfirst ($this->$fieldName), strlen ($this->$fieldName)) === 0) {
2273:
2274: $assignmentCriteria->compare('t.' . $fieldName, 'Anyone', false, 'OR');
2275: $assignmentCriteria->addCondition('t.' . $fieldName . ' = ""', 'OR');
2276: }
2277: }
2278: $criteria->mergeWith ($assignmentCriteria);
2279: break;
2280: case 'dropdown':
2281: $dropdownVal = $this->compareDropdown($field->linkType, $this->$fieldName);
2282: if (is_array ($dropdownVal)) {
2283: foreach ($dropdownVal as $val) {
2284: $dropdownRegex =
2285: '(^|((\\[|,)"))'.preg_quote ($val).'(("(,|\\]))|$)';
2286: $dropdownParamName = CDbCriteria::PARAM_PREFIX.CDbCriteria::$paramCount;
2287: $criteria->params[$dropdownParamName] = $dropdownRegex;
2288: CDbCriteria::$paramCount++;
2289: $criteria->addCondition(
2290: 't.' . $fieldName .' REGEXP BINARY '.$dropdownParamName);
2291: }
2292: } else {
2293: $criteria->compare('t.' . $fieldName, $dropdownVal, false);
2294: }
2295: break;
2296: case 'date':
2297: case 'dateTime':
2298: if (!empty($this->$fieldName)) {
2299:
2300: $retArr = $this->unshiftOperator($this->$fieldName);
2301:
2302: $operator = $retArr[0];
2303: $timestamp = Formatter::parseDate($retArr[1]);
2304: if (!$timestamp) {
2305:
2306:
2307:
2308: $criteria->addCondition('FALSE');
2309: } else if ($operator === '=' || $operator === '') {
2310: $criteria->addBetweenCondition(
2311: 't.' . $fieldName, $timestamp, $timestamp + 60 * 60 * 24);
2312: } else {
2313: $value = $operator . $timestamp;
2314: $criteria->compare('t.' . $fieldName, $value);
2315: }
2316: }
2317: break;
2318: case 'phone':
2319:
2320: default:
2321: $criteria->compare('t.' . $fieldName, $this->$fieldName, true);
2322: }
2323: }
2324:
2325: public function compareAttributes(&$criteria) {
2326: if ($this->asa ('TagBehavior') && $this->asa ('TagBehavior')->getEnabled () &&
2327: $this->tags) {
2328:
2329: $tagCriteria = new CDbCriteria;
2330: $this->compareTags ($tagCriteria);
2331: $criteria->mergeWith ($tagCriteria);
2332: }
2333:
2334: foreach (self::$_fields[$this->tableName()] as &$field) {
2335: $this->compareAttribute($criteria, $field);
2336: }
2337: }
2338:
2339: protected function compareBoolean($data) {
2340: if (is_null($data) || $data == '')
2341: return null;
2342:
2343:
2344: return in_array(
2345: mb_strtolower(
2346: trim($data)), array(0, 'f', 'false', Yii::t('actions', 'No')), true) ? 0 : 1;
2347: }
2348:
2349: public function compareAssignment($data) {
2350: if (is_null($data) || $data == '')
2351: return null;
2352: $userNames = Yii::app()->db->createCommand()
2353: ->select('username')
2354: ->from('x2_users')
2355: ->where(array('like', 'CONCAT(firstName," ",lastName)', "%$data%"))
2356: ->queryColumn();
2357: $groupIds = Yii::app()->db->createCommand()
2358: ->select('id')
2359: ->from('x2_groups')
2360: ->where(array('like', 'name', "%$data%"))
2361: ->queryColumn();
2362:
2363: return (count($groupIds) + count($userNames) == 0) ? -1 : $userNames + $groupIds;
2364: }
2365:
2366: protected function compareDropdown($ddId, $value) {
2367: if (is_null($value) || $value == '') {
2368: return null;
2369: }
2370: $dropdown = X2Model::model('Dropdowns')->findByPk($ddId);
2371: $multi = $dropdown->multi;
2372: if (isset($dropdown)) {
2373: $index = $dropdown->getDropdownIndex($ddId, $value, $multi);
2374: if (!is_null($index)) {
2375: return $index;
2376: }else {
2377: return -1;
2378: }
2379: }
2380: return -1;
2381: }
2382:
2383: 2384: 2385: 2386: 2387: 2388:
2389: 2390: 2391: 2392: 2393: 2394: 2395: 2396: 2397: 2398: 2399: 2400: 2401: 2402: 2403: 2404: 2405: 2406: 2407:
2408:
2409: 2410: 2411: 2412: 2413: 2414: 2415:
2416: public static function getAssociationModel($type, $id) {
2417: if ($id != 0 && $modelName = X2Model::getModelName($type))
2418: return X2Model::model($modelName)->findByPk($id);
2419: else
2420: return null;
2421: }
2422:
2423: 2424: 2425: 2426: 2427:
2428: public function findByPkInArray(array $params) {
2429: $pkc = $this->tableSchema->primaryKey;
2430: $pk = null;
2431: if (is_array($pkc)) {
2432: $pk = array();
2433: foreach ($pkc as $colName) {
2434: if (array_key_exists($colName, $params))
2435: $pk[$colName] = $params[$colName];
2436: else
2437: return null;
2438: }
2439: } elseif (array_key_exists($pkc, $params)) {
2440: $pk = $params[$pkc];
2441: } else {
2442: return null;
2443: }
2444: return $this->findByPk($pk);
2445: }
2446:
2447: 2448: 2449: 2450: 2451: 2452: 2453:
2454: public function updateNameId($save = false) {
2455: if (!$this->hasAttribute('nameId')) {
2456: return;
2457: }
2458: $this->_oldAttributes['nameId'] = $this->nameId;
2459: $this->nameId = Fields::nameId($this->name, $this->id);
2460: if ($save) {
2461: $that = $this;
2462: $this->runWithoutBehavior('X2FlowTriggerBehavior', function () use ($that) {
2463: $that->updateByPk($that->id, array('nameId' => $that->nameId));
2464: });
2465: }
2466: }
2467:
2468: 2469: 2470: 2471: 2472: 2473: 2474: 2475: 2476: 2477: 2478:
2479: public static function massUpdateNameId($modelName, $ids = array()) {
2480: $param = array();
2481: if ($modelName === 'Actions') {
2482:
2483: $idField = 'associationId';
2484: $nameField = 'associationName';
2485: $nameIdField = 'associationName';
2486: } else {
2487: $idField = 'id';
2488: $nameField = 'name';
2489: $nameIdField = 'nameId';
2490: }
2491: $sql = "UPDATE `" . self::model($modelName)->tableName() . "` "
2492: . "SET `".$nameIdField."`=CONCAT(`".$nameField."`,:delim,`".$idField."`)";
2493: if (is_array($ids) && count($ids) > 1) {
2494:
2495: $count = 0;
2496: foreach ($ids as $id) {
2497: $param[":id" . $count] = $id;
2498: $count++;
2499: }
2500: $sql .= ' WHERE `id` IN (' . implode(',', array_keys($param)) . ')';
2501: } else if (!empty($ids)) {
2502:
2503: $param[':id'] = is_array($ids) ? reset($ids) : $ids;
2504: $sql .= ' WHERE `id`=:id';
2505: } else {
2506:
2507: $sql .= ' WHERE 1';
2508: }
2509: $param[':delim'] = Fields::NAMEID_DELIM;
2510: $result = Yii::app()->db->createCommand($sql)->execute($param);
2511: if ($modelName === 'Actions') {
2512:
2513: $sql = 'UPDATE x2_actions '.
2514: 'SET associationName = LEFT(`associationName`, INSTR(`associationName`, "_0")-1)'.
2515: ' WHERE associationName like "%_0"';
2516: Yii::app()->db->createCommand($sql)->execute();
2517: }
2518: return $result;
2519: }
2520:
2521: 2522: 2523: 2524: 2525: 2526: 2527: 2528: 2529: 2530: 2531: 2532: 2533: 2534: 2535: 2536: 2537: 2538: 2539:
2540: public function updateNameIdRefs() {
2541:
2542:
2543:
2544:
2545:
2546: if (!$this->hasAttribute('nameId')) {
2547: return;
2548: }
2549: $nameId = $this->nameId;
2550: $oldNameId = isset($this->_oldAttributes['nameId']) ?
2551: $this->_oldAttributes['nameId'] : null;
2552: if ($nameId !== $oldNameId) {
2553:
2554: $modelName = get_class($this);
2555: if (!isset(self::$_nameIdRefs[$modelName])) {
2556:
2557: $cacheIndex = 'nameIdRefs_' . $modelName;
2558: self::$_nameIdRefs[$modelName] = Yii::app()->cache->get($cacheIndex);
2559:
2560: if (self::$_nameIdRefs[$modelName] == false) {
2561: $fields = Fields::model()->findAllByAttributes(array(
2562: 'type' => 'link',
2563: 'linkType' => $modelName
2564: ));
2565: self::$_nameIdRefs[$modelName] = array();
2566: foreach ($fields as $field) {
2567: $table = X2Model::model($field->modelName)->tableName();
2568: self::$_nameIdRefs[$modelName][$table][] = $field->fieldName;
2569: }
2570:
2571: Yii::app()->cache->set($cacheIndex, self::$_nameIdRefs[$modelName], 0);
2572: }
2573: }
2574:
2575:
2576: foreach (self::$_nameIdRefs[$modelName] as $table => $columns) {
2577: foreach ($columns as $column) {
2578: Yii::app()->db->createCommand()->update(
2579: $table, array($column => $nameId),
2580: "$column=:nid", array(':nid' => $oldNameId)
2581: );
2582: }
2583: }
2584: }
2585: }
2586:
2587: 2588: 2589:
2590: public function attributeChanged ($attr) {
2591: $oldAttributes = $this->getOldAttributes();
2592: return (!isset($oldAttributes[$attr]) && $this->isNewRecord) ||
2593: (in_array ($attr, array_keys ($oldAttributes)) &&
2594: $this->getAttribute($attr) != $oldAttributes[$attr]);
2595: }
2596:
2597: 2598: 2599: 2600:
2601: public static function getDefaultAssignment() {
2602: return Yii::app()->user->isGuest ? 'Anyone' : Yii::app()->user->getName();
2603: }
2604:
2605: 2606: 2607: 2608: 2609: 2610: 2611:
2612: public static function getAssignmentOptions(
2613: $anyone = true, $showGroups = true, $showSeparator = true) {
2614:
2615: $users = User::getNames();
2616: if ($anyone !== true)
2617: unset($users['Anyone']);
2618:
2619: if ($showGroups === true) {
2620: $groups = Groups::getNames();
2621: if (count($groups) > 0) {
2622: if ($showSeparator)
2623: $users = $users + array('' => '--------------------') + $groups;
2624: else
2625: $users = $users + $groups;
2626: }
2627: }
2628: return $users;
2629: }
2630:
2631: 2632: 2633: 2634: 2635: 2636:
2637: public function getEditableFieldNames($suppressAttributeLabels = true) {
2638: $class = get_class($this);
2639: if (!isset(self::$_editableFieldNames[$class])) {
2640: $editableFields = array_keys(
2641: array_filter($this->fieldPermissions, function($p) {
2642: return $p >= 2;
2643: }));
2644: if (sizeof($editableFields)) {
2645: $params = AuxLib::bindArray($editableFields);
2646: $in = AuxLib::arrToStrList(array_keys($params));
2647: self::$_editableFieldNames[$class] = Yii::app()->db->createCommand()
2648: ->select('fieldName, attributeLabel')
2649: ->from('x2_fields')
2650: ->where('readOnly!=1 AND modelName="' . get_class($this) . '" '
2651: . 'AND fieldName IN ' . $in, $params)
2652: ->queryAll();
2653: } else {
2654: self::$_editableFieldNames[$class] = array();
2655: }
2656: }
2657:
2658: $editableFieldNames = array();
2659: if (!$suppressAttributeLabels) {
2660: foreach (self::$_editableFieldNames[$class] as $fieldInfo) {
2661: $editableFieldNames[$fieldInfo['fieldName']] = $fieldInfo['attributeLabel'];
2662: }
2663: } else {
2664: foreach (self::$_editableFieldNames[$class] as $fieldInfo) {
2665: $editableFieldNames[] = $fieldInfo['fieldName'];
2666: }
2667: }
2668:
2669: return $editableFieldNames;
2670: }
2671:
2672: 2673: 2674: 2675:
2676: public function getFieldPermissions() {
2677: $class = get_class($this);
2678:
2679: if (!isset(self::$_fieldPermissions[$class])) {
2680: $roles = Roles::getUserRoles(Yii::app()->getSuId());
2681: if (!$this->isExemptFromFieldLevelPermissions) {
2682: $permRecords = Yii::app()->db->createCommand()
2683: ->select("f.fieldName,MAX(rtp.permission),f.readOnly")
2684: ->from(RoleToPermission::model()->tableName() . ' rtp')
2685: ->join(Fields::model()->tableName() . ' f', 'rtp.fieldId=f.id '
2686: . 'AND rtp.roleId IN ' . AuxLib::arrToStrList($roles) . ' '
2687: . 'AND f.modelName=:class', array(':class' => $class))
2688: ->group('f.fieldName')
2689: ->queryAll(false);
2690: } else {
2691: $permRecords = Yii::app()->db->createCommand()
2692: ->select("fieldName,CAST(2 AS UNSIGNED INTEGER),readOnly")
2693: ->from(Fields::model()->tableName() . ' f')
2694: ->where('modelName=:class', array(':class' => $class))
2695: ->queryAll(false);
2696: }
2697: $fieldPerms = array();
2698: foreach ($permRecords as $record) {
2699:
2700:
2701: $fieldPerms[$record[0]] = $record[1] -
2702: (integer) ((integer) $record[1] === 2 ? $record[2] : 0);
2703: }
2704: self::$_fieldPermissions[$class] = $fieldPerms;
2705: }
2706: return self::$_fieldPermissions[$class];
2707: }
2708:
2709: 2710: 2711: 2712: 2713: 2714:
2715: public static function getDefaultFormLayout($modelName) {
2716: $model = X2Model::model($modelName);
2717: $fields = Fields::model()->findAllByAttributes(
2718: array('modelName' => $modelName),
2719: new CDbCriteria(array('order' => 'attributeLabel ASC'))
2720: );
2721: $layout = array('sections' => array(array(
2722: 'collapsible' => false,
2723: 'title' => ucfirst($modelName) . ' Info',
2724: 'rows' => array(array(
2725: 'cols' => array(array(
2726: 'items' => array(),
2727: )),
2728: )),
2729: )));
2730:
2731: foreach ($fields as $field) {
2732: if ($field->readOnly)
2733: continue;
2734:
2735:
2736: if ($field->fieldName == 'associationType')
2737: continue;
2738:
2739: $newField = array(
2740: 'name' => 'formItem_' . $field->fieldName,
2741: 'labelType' => 'left',
2742: 'readOnly' => $field->readOnly,
2743: 'height' => 30,
2744: 'width' => 155,
2745: );
2746: $layout['sections'][0]['rows'][0]['cols'][0]['items'][] = $newField;
2747: }
2748:
2749: return json_encode($layout);
2750: }
2751:
2752: 2753: 2754: 2755: 2756: 2757:
2758: public static function checkThrowAttrError ($attribute) {
2759: if (is_array ($attribute)) {
2760: foreach ($attribute as $name) {
2761: self::checkThrowAttrError ($name);
2762: }
2763: return;
2764: }
2765:
2766:
2767: if (!self::model(get_called_class())->hasAttribute($attribute)) {
2768: throw new CException(
2769: Yii::t('app', '{attribute} is not an {modelClass} field.', array(
2770: '{attribute}' => $attribute,
2771: '{modelClass}' => get_called_class())));
2772: }
2773: }
2774:
2775: 2776: 2777: 2778: 2779: 2780:
2781: public static function getModelOfTypeWithId($type, $id, $isAssocType = false) {
2782: if ($isAssocType) {
2783: if (!(empty($type) || empty($id)) &&
2784: X2Model::getModelName($type)) {
2785: return X2Model::model(X2Model::getModelName($type))->findByPk($id);
2786: }
2787: } else {
2788: if (!(empty($type) || empty($id)) &&
2789: is_subclass_of($type, 'CActiveRecord')) {
2790: return X2Model::model($type)->findByPk($id);
2791: }
2792: }
2793: return null;
2794: }
2795:
2796: public static function getModelOfTypeWithName($type, $name) {
2797: $modelName = X2Model::getModelName($type);
2798: if (!(empty($type) || empty($name)) && $modelName) {
2799: $model = X2Model::model($modelName);
2800: if ($model->hasAttribute('name')) {
2801: return $model->findByAttributes(array(
2802: 'name' => $name
2803: ));
2804: }
2805: }
2806: return null;
2807: }
2808:
2809: 2810: 2811: 2812:
2813: public static function renderModelAutocomplete(
2814: $modelClass, $ajax = false, $htmlOptions = array(), $value=null) {
2815:
2816: $modelClass = self::getModelName ($modelClass);
2817:
2818: if (!class_exists($modelClass) || !$modelClass::model()->asa('X2LinkableBehavior')) {
2819: if ($ajax) {
2820: echo 'failure';
2821: return;
2822: } else {
2823: return 'failure';
2824: }
2825: 2826: 2827: 2828:
2829: }
2830:
2831: if ($ajax)
2832: Yii::app()->clientScript->scriptMap['*.css'] = false;
2833:
2834: $renderWidget = function () use ($modelClass, $htmlOptions, $value) {
2835: Yii::app()->controller->widget('zii.widgets.jui.CJuiAutoComplete', array(
2836: 'name' => (isset($htmlOptions['name']) ? $htmlOptions['name'] : 'recordName'),
2837: 'source' => Yii::app()->controller->createUrl(
2838: X2Model::model($modelClass)->autoCompleteSource),
2839: 'value' => $value ? $value : Yii::t('app', 'Start typing to suggest...'),
2840: 'options' => array(
2841: 'minLength' => '1',
2842: 'create' =>
2843: 'js:function (event, ui) {
2844: // check for callback in parent form
2845: if ($(this).closest ("form").data ("afterAutocompleteCreated")) {
2846: $(this).closest ("form").data ("afterAutocompleteCreated") ();
2847: }
2848: }',
2849: 'select' =>
2850: 'js:function (event, ui) {
2851: $(this).val(ui.item.value);
2852: // expects next input to be a hidden input which will contain the
2853: // record id
2854: $(this).nextAll ("input").val(ui.item.id);
2855: return false;
2856: }'
2857: ),
2858: 'htmlOptions' => array_merge(array(
2859: 'class' => 'record-name-autocomplete x2-default-field',
2860: 'data-default-text' => Yii::t('app', 'Start typing to suggest...'),
2861: 'style' => $value ? '' : 'color:#aaa',
2862: ), $htmlOptions),
2863: ));
2864: Yii::app()->clientScript->registerScript('renderModelAutocomplete', "
2865: x2.forms.enableDefaultText ($('.record-name-autocomplete'));
2866: ", CClientScript::POS_READY);
2867: };
2868:
2869: if ($ajax) {
2870: X2Widget::ajaxRender($renderWidget);
2871: } else {
2872: $renderWidget();
2873: }
2874: }
2875:
2876: 2877: 2878: 2879: 2880:
2881: private static $getInsertableAttributeTokensDepth = 0;
2882: public function getInsertableAttributeTokens() {
2883: X2Model::$getInsertableAttributeTokensDepth++;
2884: $tokens = array();
2885: if (X2Model::$getInsertableAttributeTokensDepth > 2)
2886: return $tokens;
2887:
2888:
2889: $tokens = array_merge(
2890: $tokens, array_map(function ($elem) {
2891: return '{' . $elem . '}';
2892: }, $this->attributeNames()));
2893:
2894:
2895: if (X2Model::$getInsertableAttributeTokensDepth < 2) {
2896: $assignmentFields = array_filter(
2897: $this->fields, function ($elem) {
2898: return $elem->type === 'assignment';
2899: });
2900: foreach ($assignmentFields as $field) {
2901: $assignmentModel = X2Model::model('Profile');
2902: $tokens = array_merge(
2903: $tokens, array_map(function ($elem) use ($field) {
2904: return '{' . $field->fieldName . '.' . $elem . '}';
2905: }, $assignmentModel->attributeNames())
2906: );
2907: }
2908: }
2909:
2910:
2911: if (X2Model::$getInsertableAttributeTokensDepth < 2) {
2912: $linkFields = array_filter($this->fields, function ($elem) {
2913: return $elem->type === 'link';
2914: });
2915: foreach ($linkFields as $field) {
2916: $linkModelName = $field->linkType;
2917: $linkModel = $linkModelName::model();
2918: $tokens = array_merge(
2919: $tokens, array_map(function ($elem) use ($field) {
2920: return '{' . $field->fieldName . '.' .
2921: preg_replace('/\{|\}/', '', $elem) . '}';
2922: }, $linkModel->getInsertableAttributeTokens())
2923: );
2924: }
2925: }
2926:
2927: X2Model::$getInsertableAttributeTokensDepth--;
2928: return $tokens;
2929: }
2930:
2931: 2932: 2933: 2934: 2935: 2936: 2937: 2938: 2939: 2940: 2941: 2942:
2943: public function findAll(
2944: $condition = '', $params = array(), $getCommand = false) {
2945:
2946: Yii::trace(get_class($this) . '.findAll()', 'system.db.ar.CActiveRecord');
2947: $criteria = $this->getCommandBuilder()->createCriteria($condition, $params);
2948: return $this->query($criteria, true, $getCommand);
2949: }
2950:
2951: public function duplicateFields() {
2952: return array(
2953: 'name',
2954: );
2955: }
2956:
2957: 2958: 2959: 2960: 2961: 2962: 2963: 2964: 2965: 2966: 2967: 2968:
2969: protected function query(
2970: $criteria, $all = false, $getCommand = false) {
2971:
2972: $this->beforeFind();
2973: $this->applyScopes($criteria);
2974:
2975: if (empty($criteria->with)) {
2976: if (!$all)
2977: $criteria->limit = 1;
2978: $command = $this->getCommandBuilder()
2979: ->createFindCommand($this->getTableSchema(), $criteria, $this->getTableAlias());
2980:
2981: if ($getCommand)
2982: return $command;
2983:
2984: return $all ? $this->populateRecords($command->queryAll(), true, $criteria->index) :
2985: $this->populateRecord($command->queryRow());
2986: }
2987: else {
2988:
2989: if ($getCommand) {
2990: return null;
2991: }
2992:
2993: $finder=$this->getActiveFinder($criteria->with);
2994: return $finder->query($criteria,$all);
2995: }
2996: }
2997:
2998: 2999: 3000: 3001: 3002: 3003: 3004: 3005: 3006: 3007:
3008: private function _getFieldsForDropdown (
3009: $parentAttribute=null, $condList=false, $sorted=true, $filterFn, $separator='.') {
3010:
3011: $fieldModels = $this->getFields(false, $filterFn, Fields::READ_PERMISSION);
3012: $permissions = $this->getFieldPermissions ();
3013: $fields = array();
3014:
3015: foreach($fieldModels as &$field) {
3016: if($field->isVirtual)
3017: continue;
3018:
3019: $fieldName = $field->fieldName;
3020: if ($this instanceof Actions && $fieldName === 'actionDescription') {
3021: $fieldName = 'ActionText.text';
3022: }
3023: $attributes = $field->getAttributes ();
3024: if ($parentAttribute !== null) {
3025: $fieldName = $parentAttribute.$separator.$fieldName;
3026: }
3027: if ($field->type === 'date' || $field->type === 'dateTime') {
3028: $dateFns = array ('year', 'month', 'day');
3029: if ($field->type === 'dateTime') {
3030: $dateFns = array_merge ($dateFns, array ('hour', 'minute', 'second'));
3031: }
3032: foreach ($dateFns as $fn) {
3033: $name = $fn.'('.$fieldName.')';
3034: $label = $this->getAttributeLabel($fieldName).' (' . Yii::t('app', $fn) . ')';
3035: if ($condList) {
3036: $fields[] = X2ConditionList::listOption (
3037: array_merge ($attributes, array (
3038: 'attributeLabel' => $label,
3039: 'type' => 'varchar',
3040: )), $name);
3041: } else {
3042: $fields[$name] = $label;
3043: }
3044: }
3045: }
3046: if ($condList) {
3047: $fields[] = X2ConditionList::listOption ($attributes, $fieldName);
3048: } else {
3049: $fields[$fieldName] = $this->getAttributeLabel($fieldName);
3050: }
3051: }
3052: if ($sorted) {
3053: if ($condList) {
3054: usort ($fields, function ($a, $b) {
3055: return strcasecmp ($a['label'], $b['label']);
3056: });
3057: } else {
3058: $fields = ArrayUtil::asorti ($fields);
3059: }
3060: }
3061: return $fields;
3062: }
3063:
3064: public function getSummaryFields () {
3065: $summaryFields = array ();
3066: if (isset ($this->name)) {
3067: $summaryFields[] = 'name';
3068: }
3069: if (isset ($this->email)) {
3070: $summaryFields[] = 'email';
3071: }
3072: if (isset ($this->phone)) {
3073: $summaryFields[] = 'phone';
3074: }
3075: if ($this->getField ('assignedTo')) {
3076: $summaryFields[] = 'assignedTo';
3077: }
3078: return $summaryFields;
3079: }
3080:
3081: }
3082: