Overview

Packages

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

Classes

  • ActionFormModel
  • ArrayUtil
  • ArrayValidator
  • AssociatedMediaBehavior
  • AuxLib
  • Changelog
  • DetailView
  • EncryptUtilTmp
  • EventsWidgetFieldFormatter
  • FailedLogins
  • FieldFormatter
  • FieldFormatterBase
  • FieldInputRenderer
  • FileFieldBehavior
  • FiltersForm
  • FilterUtil
  • FineDiff
  • FineDiffCopyOp
  • FineDiffDeleteOp
  • FineDiffInsertOp
  • FineDiffOp
  • FineDiffOps
  • FineDiffReplaceOp
  • GlobalCSSFormModel
  • GlobalImportFormModel
  • GoogleAuthenticator
  • JSONFieldsBehavior
  • JSONResponse
  • MediaFieldFormatter
  • MediaSelector
  • MobileActiveRecordFieldFormatter
  • MobileActivityFeed
  • MobileChartDashboard
  • MobileFieldFormatter
  • MobileFieldInputRenderer
  • ModuleModelNameValidator
  • MultiChildNode
  • MultiTypeAutocomplete
  • PasswordUtil
  • ProductFeature
  • ProfileWidgetLayout
  • QueryParamGenerator
  • RecordLimitBehavior
  • RecordView
  • RecordViewWidgetLayout
  • RelationshipsGridModel
  • RelationshipsJoin
  • RepairUserDataCommand
  • RequestUtil
  • RequiredIfNotSetValidator
  • ResponseUtil
  • RunMigrationScriptCommand
  • ServiceWebFormDesigner
  • Settings
  • StringUtil
  • TestEmailAction
  • TestEmailActionForm
  • ThemeGenerator
  • TimerUtil
  • TopicsFieldFormatter
  • TopicsWidgetLayout
  • TransactionalViewFieldFormatter
  • UrlUtil
  • ValidLinkValidator
  • WebFormDesigner
  • WebLeadFormDesigner
  • X2ActiveRecordBehavior
  • X2ActiveRecordFieldFormatter
  • X2ButtonColumn
  • X2ConditionList
  • X2ConsoleCommand
  • X2ControllerBehavior
  • X2DataColumn
  • X2DuplicateBehavior
  • X2Flashes
  • X2GridViewFieldFormatter
  • X2IPAddress
  • X2LeadsDataColumn
  • X2MergeableBehavior
  • X2MessageSource
  • X2MobileControllerBehavior
  • X2MobileProfileControllerBehavior
  • X2MobileQuotesControllerBehavior
  • X2MobileSiteControllerBehavior
  • X2MobileTopicsControllerBehavior
  • X2ModelConversionBehavior
  • X2ModelConversionWidget
  • X2ModelForeignKeyValidator
  • X2ModelUniqueIndexValidator
  • X2NonWebUser
  • X2StaticDropdown
  • X2StaticField
  • X2StaticFieldsBehavior
  • X2UrlManager
  • X2Validator
  • X2WidgetBehavior

Interfaces

  • AdminOwnedCredentials

Exceptions

  • CampaignMailingException
  • CodeExchangeException
  • GetCredentialsException
  • NoRefreshTokenException
  • NoUserIdException
  • StringUtilException

Functions

  • checkCurrency
  • checkDNS
  • checkServerVar
  • checkTimezone
  • decodeQuotes
  • echoIcons
  • encodeQuotes
  • exceptionForError
  • getField
  • getLanguageName
  • getModuleTitle
  • handleReqError
  • handleReqException
  • installer_t
  • installer_tr
  • isAllowedDir
  • mediaMigrationRrmdir
  • migrateMediaDir
  • printGraph
  • printR
  • renderFields
  • reqShutdown
  • RIP
  • translateOptions
  • tryGetRemote
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /*****************************************************************************************
  3:  * X2Engine Open Source Edition is a customer relationship management program developed by
  4:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
  5:  * 
  6:  * This program is free software; you can redistribute it and/or modify it under
  7:  * the terms of the GNU Affero General Public License version 3 as published by the
  8:  * Free Software Foundation with the addition of the following permission added
  9:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 10:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
 11:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 12:  * 
 13:  * This program is distributed in the hope that it will be useful, but WITHOUT
 14:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 15:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 16:  * details.
 17:  * 
 18:  * You should have received a copy of the GNU Affero General Public License along with
 19:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
 20:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 21:  * 02110-1301 USA.
 22:  * 
 23:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
 24:  * California 95067, USA. or at email address contact@x2engine.com.
 25:  * 
 26:  * The interactive user interfaces in modified source and object code versions
 27:  * of this program must display Appropriate Legal Notices, as required under
 28:  * Section 5 of the GNU Affero General Public License version 3.
 29:  * 
 30:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 31:  * these Appropriate Legal Notices must retain the display of the "Powered by
 32:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
 33:  * technical reasons, the Appropriate Legal Notices must display the words
 34:  * "Powered by X2Engine".
 35:  *****************************************************************************************/
 36: 
 37: /**
 38:  * Manages conversion between subclasses of X2Model 
 39:  */
 40: 
 41: class X2ModelConversionBehavior extends CActiveRecordBehavior {
 42: 
 43:     /**
 44:      * @var array $_fieldMappings
 45:      */
 46:     private $_fieldMappings = array (
 47:         'X2Leads' => array (
 48:             'Contacts' => array (
 49:                 'accountName' => 'company',
 50:                 'quoteAmount' => 'dealvalue',
 51:                 'salesStage' => 'dealstatus',
 52:                 'probability' => null,
 53:                 'description' => 'backgroundInfo',
 54:             ),
 55:             'Opportunity' => array (
 56:                 'firstName' => null, // ignore first and last name since full name will be mapped
 57:                 'lastName' => null,
 58:             ),
 59:         )
 60:     );
 61: 
 62:     public $deleteConvertedRecord = true;
 63: 
 64:     /**
 65:      * @var string|null $convertedField
 66:      */
 67:     public $convertedField; 
 68: 
 69:     /**
 70:      * @var string|null $conversionDateField
 71:      */
 72:     public $conversionDateField; 
 73: 
 74:     /**
 75:      * @var string|null $convertedToTypeField
 76:      */
 77:     public $convertedToTypeField;
 78: 
 79:     /**
 80:      * @var string|null $convertedToIdField
 81:      */
 82:     public $convertedToIdField;
 83: 
 84:     /**
 85:      * @var bool $conversionFailed
 86:      */
 87:     public $conversionFailed = false; 
 88: 
 89:     /**
 90:      * @var null|X2Model if conversion fails, this property gets set to the model that was attempted
 91:      *  to be saved. This gets used for displaying error output.
 92:      */
 93:     public $errorModel = null; 
 94: 
 95:     public static function getActions () {
 96:         return array (
 97:             'convert' => 
 98:                 'application.components.recordConversion.X2ModelConversionAction',
 99:         );
100:     }
101:         
102:    /**
103:     * @return array field map for this class and the target class, or an empty array if no map
104:     *   exists.
105:     */
106:     public function getFieldMap ($targetClass, $flip=false) {
107:         $modelClass = get_class ($this->owner);
108:         if (!isset ($this->_fieldMappings[$modelClass]) || 
109:             !isset ($this->_fieldMappings[$modelClass][$targetClass])) {
110: 
111:             return array ();
112:         }
113:         $fieldMappings = $this->_fieldMappings[$modelClass][$targetClass];
114:         if ($flip) {
115:             foreach ($fieldMappings as $key => $val) {
116:                 if ($val === null) { 
117:                     unset ($fieldMappings[$key]);
118:                 }
119:             }
120:             $fieldMappings = array_flip ($fieldMappings);
121:         }
122:         return $fieldMappings;
123:     }
124: 
125:     /**
126:      * Replace attribute names with the names they map to in the field map
127:      * @param bool $associative
128:      * @return array
129:      */
130:     public function mapFields (array $attributes, $targetClass, $associative=false) {
131:         $modelClass = get_class ($this->owner);
132:         $fieldMappings = $this->getFieldMap ($targetClass);
133:         if (!count ($fieldMappings)) {
134:             return $attributes;
135:         }
136:         $attributeNames = array_flip ($associative ? array_keys ($attributes) : $attributes);
137:         foreach ($fieldMappings as $source => $target) {
138:             if (isset ($attributeNames[$source])) {
139:                 if ($target !== null) {
140:                     if ($associative) {
141:                         $attributes[$target] = $attributes[$source];
142:                     } else {
143:                         $attributes[] = $target;
144:                     }
145:                 }
146:                 $key = $associative ? $source : array_search ($source, $attributes);
147:                 unset ($attributes[$key]);
148:             }
149:         }
150:         return $attributes;
151:     }
152: 
153:     /**
154:      * Excludes fields whose values shouldn't be transferred to the target record
155:      */
156:     public function attributeNames ($model) {
157:         $conversionAttrs = array ();
158:         if ($this->conversionDateField) $conversionAttrs[] = $this->conversionDateField;
159:         if ($this->convertedField) $conversionAttrs[] = $this->convertedField;
160:         if ($this->convertedToTypeField) $conversionAttrs[] = $this->convertedToTypeField;
161:         if ($this->convertedToIdField) $conversionAttrs[] = $this->convertedToIdField;
162:         return array_diff ($model->attributeNames (), $conversionAttrs);
163:     }
164: 
165:     /**
166:      * @return bool true if source and target types a conversion-compatible, false otherwise 
167:      */
168:     public function checkConversionCompatibility ($targetClass) {
169:         $targetModel = new $targetClass ();
170:         $fieldMap = $this->getFieldMap ($targetClass, true);
171: 
172:         // don't convert if this model has fields not in target model
173:         $fieldDiff = array_diff (
174:             $this->mapFields ($this->attributeNames ($this->owner), $targetClass), 
175:             $targetClass::model ()->attributeNames ());
176:         if (count ($fieldDiff) > 0) {
177:             $potentialDataLoss = false;
178:             foreach ($fieldDiff as $name) {
179:                 $name = isset ($fieldMap[$name]) ? $fieldMap[$name] : $name;
180:                 if (isset ($this->owner->$name)) {
181:                     $potentialDataLoss = true;
182:                 }
183:             }
184:             if ($potentialDataLoss) return false;
185:         }
186: 
187:         // don't convert if any of this model's fields and the targetModel's fields 
188:         // have the same name but a different type
189:         $sharedAttrs = array_intersect (
190:             $this->mapFields ($this->attributeNames ($this->owner), $targetClass), 
191:             $targetModel->attributeNames ());
192: 
193:         foreach ($sharedAttrs as $name) {
194:             // get this model's field name from converted field name, if field name is in the map
195:             $sourceFieldName = isset ($fieldMap[$name]) ? $fieldMap[$name] : $name;
196: 
197:             $sourceField = $this->owner->getField ($sourceFieldName);
198:             $targetModelField = $targetModel->getField ($name);
199: 
200:             if (!$sourceField instanceof Fields || !$targetModelField instanceof Fields) {
201:                 continue;
202:             }
203: 
204:             if ($sourceField->type !== $targetModelField->type) {
205:                 return false;
206:             }
207:         }
208: 
209:         return true;
210:     }
211: 
212:     /**
213:      * Uses the attributes of this model to generate a model of another type. This model is deleted.
214:      * @param bool $force If true, model will be converted even if there is potential
215:      *  for data loss
216:      * @return CModel|false 
217:      */
218:     public function convert ($targetClass, $force=false) {
219:         $attributes = $this->mapFields ($this->owner->getAttributes (), $targetClass, true);
220:         unset ($attributes['id']);
221:         unset ($attributes['nameId']);
222:         unset ($attributes['createDate']);
223:         if ($this->convertedField)
224:             unset ($attributes[$this->convertedField]);
225:         if ($this->conversionDateField)
226:             unset ($attributes[$this->conversionDateField]);
227:         if ($this->convertedToTypeField)
228:             unset ($attributes[$this->convertedToTypeField]);
229:         if ($this->convertedToIdField)
230:             unset ($attributes[$this->convertedToIdField]);
231: 
232:         $targetModel = new $targetClass ();
233: 
234:         if (!$force && !$this->checkConversionCompatibility ($targetClass)) { 
235:             return false;
236:         }
237: 
238:         $targetModel->setAttributes ($attributes, false);
239: 
240:         // don't create a targetModel creation notification or event
241:         $targetModel->disableBehavior('changelog'); 
242:         if ($targetModel->save ()) {
243:             $targetModel->mergeRelatedRecords ($this->owner);
244:             $changeLogBehavior = $this->owner->asa ('changelog');
245:             $changeLogBehavior->createEvent = false; // don't create a lead deletion event
246:             if ($this->deleteConvertedRecord) {
247:                 $this->owner->delete ();
248:             } else {
249:                 $updated = array ();
250:                 if ($this->convertedField) {
251:                     $convertedField = $this->convertedField;
252:                     $this->owner->$convertedField = true;
253:                     $updated[] = $convertedField;
254:                 }
255:                 if ($this->conversionDateField) {
256:                     $conversionDateField = $this->conversionDateField;
257:                     $this->owner->$conversionDateField = time ();
258:                     $updated[] = $conversionDateField;
259:                 }
260:                 if ($this->convertedToTypeField) {
261:                     $convertedToTypeField = $this->convertedToTypeField;
262:                     $this->owner->$convertedToTypeField = get_class ($targetModel);
263:                     $updated[] = $convertedToTypeField;
264:                 }
265:                 if ($this->convertedToIdField) {
266:                     $convertedToIdField = $this->convertedToIdField;
267:                     $this->owner->$convertedToIdField = $targetModel->id;
268:                     $updated[] = $convertedToIdField;
269:                 }
270:                 if ($updated)
271:                     $this->owner->update ($updated);
272: 
273:             }
274:             return $targetModel;
275:         }
276:         $this->errorModel = $targetModel;
277:         return $targetModel;
278:     }
279: 
280:     /**
281:      * @return <array of strings> Incompatibility warnings to be presented to the user before
282:      *  they convert this model to the target model.
283:      */
284:     public function getConversionIncompatibilityWarnings ($targetClass) {
285:         $warnings = array ();
286:         $targetModel = $targetClass::model ();
287:         $attributeNames = $this->mapFields ($this->attributeNames ($this->owner), $targetClass);
288:         $leadsAttrs = array_diff (
289:             $attributeNames, $targetClass::model()->attributeNames ());
290:         $fieldMap = $this->getFieldMap ($targetClass, true);
291: 
292:         // look for fields which aren't in the target model
293:         foreach ($leadsAttrs as $name) {
294:             $name = isset ($fieldMap[$name]) ? $fieldMap[$name] : $name;
295:             // if field isn't set, there's no risk of data loss
296:             if (!isset ($this->owner->$name)) continue;
297:             $warnings[] = 
298:                 Yii::t('app', 
299:                     'A field {fieldName} is in Leads but not in {targetModel}.',
300:                     array (
301:                         '{fieldName}' => $name,
302:                         '{targetModel}' => X2Model::getModelTitle ($targetClass),
303:                     )
304:                 );
305:         }
306: 
307:         // look for type mismatches
308:         $sharedAttrs = array_intersect (
309:             $attributeNames, $targetModel->attributeNames ());
310:         foreach ($sharedAttrs as $name) {
311:             $originalName = $name;
312:             $sourceFieldName = isset ($fieldMap[$name]) ? $fieldMap[$name] : $name;
313:             $sourceField = $this->owner->getField ($sourceFieldName);
314:             $targetModelField = $targetModel->getField ($name);
315: 
316:             if (!$sourceField instanceof Fields || !$targetModelField instanceof Fields) {
317:                 continue;
318:             }
319: 
320:             if ($sourceField->type !== $targetModelField->type) {
321:                 if (isset ($fieldMap[$originalName])) {
322:                     $warnings[] = 
323:                         Yii::t('app', 
324:                             'The {model} field {fieldName} maps to the {targetModel} field '.
325:                             '{targetField} but the fields have different types.', 
326:                             array (
327:                                 '{fieldName}' => $sourceFieldName,
328:                                 '{targetField}' => $name,
329:                                 '{model}' => X2Model::getModelTitle (get_class ($this->owner)),
330:                                 '{targetModel}' => X2Model::getModelTitle ($targetClass),
331:                             )
332:                         );
333:                 } else {
334:                     $warnings[] = 
335:                         Yii::t('app', 
336:                             'A field {fieldName} is in both {model} and {targetModel} but the fields
337:                              have different types.', 
338:                             array (
339:                                 '{fieldName}' => $name,
340:                                 '{model}' => X2Model::getModelTitle (get_class ($this->owner)),
341:                                 '{targetModel}' => X2Model::getModelTitle ($targetClass),
342:                             )
343:                         );
344:                 }
345:             }
346:         }
347: 
348:         return $warnings;
349:     }
350: 
351:     /**
352:      * Renders error summary containing compatibility warnings 
353:      * @return string
354:      */
355:     public function errorSummary ($targetModelClass, $convertMultiple=false) {
356:         $sourceModelClass = get_class ($this->owner);
357:         $sourceModelTitle = X2Model::getModelTitle ($sourceModelClass, true);
358:         $targetModelTitle = X2Model::getModelTitle ($targetModelClass, true);
359:         $sourceModelTitlePlural = X2Model::getModelTitle ($sourceModelClass, false);
360:         $targetModelTitlePlural = X2Model::getModelTitle ($targetModelClass, false);
361: 
362:         $html = '<p>';
363:         $html .= CHtml::encode (!$convertMultiple ?
364:             Yii::t('app', 'Converting this {model} to {article} {targetModel} could result in data 
365:                 from your {model} being lost. '.
366:                 'The following field incompatibilities have been detected: ',
367:                 array (
368:                     '{model}' => $sourceModelTitle,
369:                     '{targetModel}' => $targetModelTitle,
370:                     '{article}' => preg_match ('/^[aeiouAEIOU]/', $targetModelTitle) ? 'an' : 'a',
371:                 )) : 
372:             Yii::t('app', 'Converting these {model} to {targetModel} could result in data 
373:                 from your {model} being lost. '.
374:                 'The following field incompatibilities have been detected: ',
375:                 array (
376:                     '{model}' => $sourceModelTitlePlural,
377:                     '{targetModel}' => $targetModelTitlePlural,
378:                 )));
379:         $html .= "
380:         </p>
381:         <ul class='errorSummary'>
382:         ";
383:         foreach ($this->owner->getConversionIncompatibilityWarnings ($targetModelClass) as 
384:             $message) {
385:             
386:             $html .= '<li>'.CHtml::encode ($message).'</li>';
387:         }
388:         $html .= "
389:         </ul>
390:         <p>
391:         ";
392:         $html .= CHtml::encode (
393:             Yii::t('app', 'To resolve these incompatibilities, make sure that every '.
394:             '{model} field has a corresponding {targetModel} field of the same name and type.', 
395:             array (
396:                 '{model}' => $sourceModelTitlePlural,
397:                 '{targetModel}' => $targetModelTitlePlural,
398:             )));
399:         $html .= '</p>';
400:         return $html;
401:     }
402: }
403: 
404: ?>
405: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0