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: class TranslateBehavior extends ModelBehavior {
 35:  36:  37: 
 38:     var $runtime = array();
 39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55: 
 56:     function setup(&$model, $config = array()) {
 57:         $db =& ConnectionManager::getDataSource($model->useDbConfig);
 58:         if (!$db->connected) {
 59:             trigger_error(
 60:                 sprintf(__('Datasource %s for TranslateBehavior of model %s is not connected', true), $model->useDbConfig, $model->alias),
 61:                 E_USER_ERROR
 62:             );
 63:             return false;
 64:         }
 65: 
 66:         $this->settings[$model->alias] = array();
 67:         $this->runtime[$model->alias] = array('fields' => array());
 68:         $this->translateModel($model);
 69:         return $this->bindTranslation($model, $config, false);
 70:     }
 71:  72:  73:  74:  75:  76: 
 77:     function cleanup(&$model) {
 78:         $this->unbindTranslation($model);
 79:         unset($this->settings[$model->alias]);
 80:         unset($this->runtime[$model->alias]);
 81:     }
 82:  83:  84:  85:  86:  87:  88: 
 89:     function beforeFind(&$model, $query) {
 90:         $locale = $this->_getLocale($model);
 91:         if (empty($locale)) {
 92:             return $query;
 93:         }
 94:         $db =& ConnectionManager::getDataSource($model->useDbConfig);
 95:         $RuntimeModel =& $this->translateModel($model);
 96:         if (!empty($RuntimeModel->tablePrefix)) {
 97:             $tablePrefix = $RuntimeModel->tablePrefix;
 98:         } else {
 99:             $tablePrefix = $db->config['prefix'];
100:         }
101: 
102:         if (is_string($query['fields']) && 'COUNT(*) AS '.$db->name('count') == $query['fields']) {
103:             $query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count';
104:             $query['joins'][] = array(
105:                 'type' => 'INNER',
106:                 'alias' => $RuntimeModel->alias,
107:                 'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
108:                 'conditions' => array(
109:                     $model->alias . '.' . $model->primaryKey => $db->identifier($RuntimeModel->alias.'.foreign_key'),
110:                     $RuntimeModel->alias.'.model' => $model->name,
111:                     $RuntimeModel->alias.'.locale' => $locale
112:                 )
113:             );
114:             return $query;
115:         }
116:         $autoFields = false;
117: 
118:         if (empty($query['fields'])) {
119:             $query['fields'] = array($model->alias.'.*');
120: 
121:             $recursive = $model->recursive;
122:             if (isset($query['recursive'])) {
123:                 $recursive = $query['recursive'];
124:             }
125: 
126:             if ($recursive >= 0) {
127:                 foreach (array('hasOne', 'belongsTo') as $type) {
128:                     foreach ($model->{$type} as $key => $value) {
129: 
130:                         if (empty($value['fields'])) {
131:                             $query['fields'][] = $key.'.*';
132:                         } else {
133:                             foreach ($value['fields'] as $field) {
134:                                 $query['fields'][] = $key.'.'.$field;
135:                             }
136:                         }
137:                     }
138:                 }
139:             }
140:             $autoFields = true;
141:         }
142:         $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
143:         $addFields = array();
144:         if (is_array($query['fields'])) {
145:             foreach ($fields as $key => $value) {
146:                 $field = (is_numeric($key)) ? $value : $key;
147: 
148:                 if (in_array($model->alias.'.*', $query['fields']) || $autoFields || in_array($model->alias.'.'.$field, $query['fields']) || in_array($field, $query['fields'])) {
149:                     $addFields[] = $field;
150:                 }
151:             }
152:         }
153: 
154:         if ($addFields) {
155:             foreach ($addFields as $field) {
156:                 foreach (array($field, $model->alias.'.'.$field) as $_field) {
157:                     $key = array_search($_field, $query['fields']);
158: 
159:                     if ($key !== false) {
160:                         unset($query['fields'][$key]);
161:                     }
162:                 }
163: 
164:                 if (is_array($locale)) {
165:                     foreach ($locale as $_locale) {
166:                         $query['fields'][] = 'I18n__'.$field.'__'.$_locale.'.content';
167:                         $query['joins'][] = array(
168:                             'type' => 'LEFT',
169:                             'alias' => 'I18n__'.$field.'__'.$_locale,
170:                             'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
171:                             'conditions' => array(
172:                                 $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
173:                                 'I18n__'.$field.'__'.$_locale.'.model' => $model->name,
174:                                 'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field,
175:                                 'I18n__'.$field.'__'.$_locale.'.locale' => $_locale
176:                             )
177:                         );
178:                     }
179:                 } else {
180:                     $query['fields'][] = 'I18n__'.$field.'.content';
181:                     $query['joins'][] = array(
182:                         'type' => 'LEFT',
183:                         'alias' => 'I18n__'.$field,
184:                         'table' => $db->name($tablePrefix . $RuntimeModel->useTable),
185:                         'conditions' => array(
186:                             $model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"),
187:                             'I18n__'.$field.'.model' => $model->name,
188:                             'I18n__'.$field.'.'.$RuntimeModel->displayField => $field
189:                         )
190:                     );
191: 
192:                     if (is_string($query['conditions'])) {
193:                         $query['conditions'] = $db->conditions($query['conditions'], true, false, $model) . ' AND '.$db->name('I18n__'.$field.'.locale').' = \''.$locale.'\'';
194:                     } else {
195:                         $query['conditions'][$db->name("I18n__{$field}.locale")] = $locale;
196:                     }
197:                 }
198:             }
199:         }
200:         if (is_array($query['fields'])) {
201:             $query['fields'] = array_merge($query['fields']);
202:         }
203:         $this->runtime[$model->alias]['beforeFind'] = $addFields;
204:         return $query;
205:     }
206: 207: 208: 209: 210: 211: 212: 213: 
214:     function afterFind(&$model, $results, $primary) {
215:         $this->runtime[$model->alias]['fields'] = array();
216:         $locale = $this->_getLocale($model);
217: 
218:         if (empty($locale) || empty($results) || empty($this->runtime[$model->alias]['beforeFind'])) {
219:             return $results;
220:         }
221:         $beforeFind = $this->runtime[$model->alias]['beforeFind'];
222: 
223:         foreach ($results as $key => $row) {
224:             $results[$key][$model->alias]['locale'] = (is_array($locale)) ? @$locale[0] : $locale;
225: 
226:             foreach ($beforeFind as $field) {
227:                 if (is_array($locale)) {
228:                     foreach ($locale as $_locale) {
229:                         if (!isset($results[$key][$model->alias][$field]) && !empty($results[$key]['I18n__'.$field.'__'.$_locale]['content'])) {
230:                             $results[$key][$model->alias][$field] = $results[$key]['I18n__'.$field.'__'.$_locale]['content'];
231:                         }
232:                         unset($results[$key]['I18n__'.$field.'__'.$_locale]);
233:                     }
234: 
235:                     if (!isset($results[$key][$model->alias][$field])) {
236:                         $results[$key][$model->alias][$field] = '';
237:                     }
238:                 } else {
239:                     $value = '';
240:                     if (!empty($results[$key]['I18n__'.$field]['content'])) {
241:                         $value = $results[$key]['I18n__'.$field]['content'];
242:                     }
243:                     $results[$key][$model->alias][$field] = $value;
244:                     unset($results[$key]['I18n__'.$field]);
245:                 }
246:             }
247:         }
248:         return $results;
249:     }
250: 251: 252: 253: 254: 255: 
256:     function beforeValidate(&$model) {
257:         $locale = $this->_getLocale($model);
258:         if (empty($locale)) {
259:             return true;
260:         }
261:         $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
262:         $tempData = array();
263: 
264:         foreach ($fields as $key => $value) {
265:             $field = (is_numeric($key)) ? $value : $key;
266: 
267:             if (isset($model->data[$model->alias][$field])) {
268:                 $tempData[$field] = $model->data[$model->alias][$field];
269:                 if (is_array($model->data[$model->alias][$field])) {
270:                     if (is_string($locale) && !empty($model->data[$model->alias][$field][$locale])) {
271:                         $model->data[$model->alias][$field] = $model->data[$model->alias][$field][$locale];
272:                     } else {
273:                         $values = array_values($model->data[$model->alias][$field]);
274:                         $model->data[$model->alias][$field] = $values[0];
275:                     }
276:                 }
277:             }
278:         }
279:         $this->runtime[$model->alias]['beforeSave'] = $tempData;
280:         return true;
281:     }
282: 283: 284: 285: 286: 287: 288: 
289:     function afterSave(&$model, $created) {
290:         if (!isset($this->runtime[$model->alias]['beforeSave'])) {
291:             return true;
292:         }
293:         $locale = $this->_getLocale($model);
294:         $tempData = $this->runtime[$model->alias]['beforeSave'];
295:         unset($this->runtime[$model->alias]['beforeSave']);
296:         $conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
297:         $RuntimeModel =& $this->translateModel($model);
298: 
299:         foreach ($tempData as $field => $value) {
300:             unset($conditions['content']);
301:             $conditions['field'] = $field;
302:             if (is_array($value)) {
303:                 $conditions['locale'] = array_keys($value);
304:             } else {
305:                 $conditions['locale'] = $locale;
306:                 if (is_array($locale)) {
307:                     $value = array($locale[0] => $value);
308:                 } else {
309:                     $value = array($locale => $value);
310:                 }
311:             }
312:             $translations = $RuntimeModel->find('list', array('conditions' => $conditions, 'fields' => array($RuntimeModel->alias . '.locale', $RuntimeModel->alias . '.id')));
313:             foreach ($value as $_locale => $_value) {
314:                 $RuntimeModel->create();
315:                 $conditions['locale'] = $_locale;
316:                 $conditions['content'] = $_value;
317:                 if (array_key_exists($_locale, $translations)) {
318:                     $RuntimeModel->save(array($RuntimeModel->alias => array_merge($conditions, array('id' => $translations[$_locale]))));
319:                 } else {
320:                     $RuntimeModel->save(array($RuntimeModel->alias => $conditions));
321:                 }
322:             }
323:         }
324:     }
325: 326: 327: 328: 329: 330: 
331:     function afterDelete(&$model) {
332:         $RuntimeModel =& $this->translateModel($model);
333:         $conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
334:         $RuntimeModel->deleteAll($conditions);
335:     }
336: 337: 338: 339: 340: 341: 
342:     function _getLocale(&$model) {
343:         if (!isset($model->locale) || is_null($model->locale)) {
344:             if (!class_exists('I18n')) {
345:                 App::import('Core', 'i18n');
346:             }
347:             $I18n =& I18n::getInstance();
348:             $I18n->l10n->get(Configure::read('Config.language'));
349:             $model->locale = $I18n->l10n->locale;
350:         }
351: 
352:         return $model->locale;
353:     }
354: 355: 356: 357: 358: 359: 
360:     function &translateModel(&$model) {
361:         if (!isset($this->runtime[$model->alias]['model'])) {
362:             if (!isset($model->translateModel) || empty($model->translateModel)) {
363:                 $className = 'I18nModel';
364:             } else {
365:                 $className = $model->translateModel;
366:             }
367: 
368:             if (PHP5) {
369:                 $this->runtime[$model->alias]['model'] = ClassRegistry::init($className, 'Model');
370:             } else {
371:                 $this->runtime[$model->alias]['model'] =& ClassRegistry::init($className, 'Model');
372:             }
373:         }
374:         if (!empty($model->translateTable) && $model->translateTable !== $this->runtime[$model->alias]['model']->useTable) {
375:             $this->runtime[$model->alias]['model']->setSource($model->translateTable);
376:         } elseif (empty($model->translateTable) && empty($model->translateModel)) {
377:             $this->runtime[$model->alias]['model']->setSource('i18n');
378:         }
379:         $model =& $this->runtime[$model->alias]['model'];
380:         return $model;
381:     }
382: 383: 384: 385: 386: 387: 388: 389: 390: 
391:     function bindTranslation(&$model, $fields, $reset = true) {
392:         if (is_string($fields)) {
393:             $fields = array($fields);
394:         }
395:         $associations = array();
396:         $RuntimeModel =& $this->translateModel($model);
397:         $default = array('className' => $RuntimeModel->alias, 'foreignKey' => 'foreign_key');
398: 
399:         foreach ($fields as $key => $value) {
400:             if (is_numeric($key)) {
401:                 $field = $value;
402:                 $association = null;
403:             } else {
404:                 $field = $key;
405:                 $association = $value;
406:             }
407: 
408:             if (array_key_exists($field, $this->settings[$model->alias])) {
409:                 unset($this->settings[$model->alias][$field]);
410:             } elseif (in_array($field, $this->settings[$model->alias])) {
411:                 $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field)));
412:             }
413: 
414:             if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) {
415:                 unset($this->runtime[$model->alias]['fields'][$field]);
416:             } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) {
417:                 $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field)));
418:             }
419: 
420:             if (is_null($association)) {
421:                 if ($reset) {
422:                     $this->runtime[$model->alias]['fields'][] = $field;
423:                 } else {
424:                     $this->settings[$model->alias][] = $field;
425:                 }
426:             } else {
427:                 if ($reset) {
428:                     $this->runtime[$model->alias]['fields'][$field] = $association;
429:                 } else {
430:                     $this->settings[$model->alias][$field] = $association;
431:                 }
432: 
433:                 foreach (array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany') as $type) {
434:                     if (isset($model->{$type}[$association]) || isset($model->__backAssociation[$type][$association])) {
435:                         trigger_error(
436:                             sprintf(__('Association %s is already binded to model %s', true), $association, $model->alias),
437:                             E_USER_ERROR
438:                         );
439:                         return false;
440:                     }
441:                 }
442:                 $associations[$association] = array_merge($default, array('conditions' => array(
443:                     'model' => $model->alias,
444:                     $RuntimeModel->displayField => $field
445:                 )));
446:             }
447:         }
448: 
449:         if (!empty($associations)) {
450:             $model->bindModel(array('hasMany' => $associations), $reset);
451:         }
452:         return true;
453:     }
454: 455: 456: 457: 458: 459: 460: 461: 
462:     function unbindTranslation(&$model, $fields = null) {
463:         if (empty($fields)) {
464:             return $this->unbindTranslation($model, $this->settings[$model->alias]);
465:         }
466: 
467:         if (is_string($fields)) {
468:             $fields = array($fields);
469:         }
470:         $RuntimeModel =& $this->translateModel($model);
471:         $associations = array();
472: 
473:         foreach ($fields as $key => $value) {
474:             if (is_numeric($key)) {
475:                 $field = $value;
476:                 $association = null;
477:             } else {
478:                 $field = $key;
479:                 $association = $value;
480:             }
481: 
482:             if (array_key_exists($field, $this->settings[$model->alias])) {
483:                 unset($this->settings[$model->alias][$field]);
484:             } elseif (in_array($field, $this->settings[$model->alias])) {
485:                 $this->settings[$model->alias] = array_merge(array_diff_assoc($this->settings[$model->alias], array($field)));
486:             }
487: 
488:             if (array_key_exists($field, $this->runtime[$model->alias]['fields'])) {
489:                 unset($this->runtime[$model->alias]['fields'][$field]);
490:             } elseif (in_array($field, $this->runtime[$model->alias]['fields'])) {
491:                 $this->runtime[$model->alias]['fields'] = array_merge(array_diff_assoc($this->runtime[$model->alias]['fields'], array($field)));
492:             }
493: 
494:             if (!is_null($association) && (isset($model->hasMany[$association]) || isset($model->__backAssociation['hasMany'][$association]))) {
495:                 $associations[] = $association;
496:             }
497:         }
498: 
499:         if (!empty($associations)) {
500:             $model->unbindModel(array('hasMany' => $associations), false);
501:         }
502:         return true;
503:     }
504: }
505: if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
506: 507: 508: 509: 
510:     class I18nModel extends AppModel {
511:         var $name = 'I18nModel';
512:         var $useTable = 'i18n';
513:         var $displayField = 'field';
514:     }
515: }
516: ?>