1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
36:
37: 38: 39:
40:
41: class X2ModelConversionBehavior extends CActiveRecordBehavior {
42:
43: 44: 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,
57: 'lastName' => null,
58: ),
59: )
60: );
61:
62: public $deleteConvertedRecord = true;
63:
64: 65: 66:
67: public $convertedField;
68:
69: 70: 71:
72: public $conversionDateField;
73:
74: 75: 76:
77: public $convertedToTypeField;
78:
79: 80: 81:
82: public $convertedToIdField;
83:
84: 85: 86:
87: public $conversionFailed = false;
88:
89: 90: 91: 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: 104: 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: 127: 128: 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: 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: 167:
168: public function checkConversionCompatibility ($targetClass) {
169: $targetModel = new $targetClass ();
170: $fieldMap = $this->getFieldMap ($targetClass, true);
171:
172:
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:
188:
189: $sharedAttrs = array_intersect (
190: $this->mapFields ($this->attributeNames ($this->owner), $targetClass),
191: $targetModel->attributeNames ());
192:
193: foreach ($sharedAttrs as $name) {
194:
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: 214: 215: 216: 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:
241: $targetModel->disableBehavior('changelog');
242: if ($targetModel->save ()) {
243: $targetModel->mergeRelatedRecords ($this->owner);
244: $changeLogBehavior = $this->owner->asa ('changelog');
245: $changeLogBehavior->createEvent = false;
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: 282: 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:
293: foreach ($leadsAttrs as $name) {
294: $name = isset ($fieldMap[$name]) ? $fieldMap[$name] : $name;
295:
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:
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: 353: 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: