Split versioning overlays API into 2 methods
[TYPO3CMS/Extensions/overlays.git] / class.tx_overlays.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2008-2010 Francois Suter (Cobweb) <typo3@cobweb.ch>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24
25
26 /**
27 * Main library of the 'overlays' extension.
28 * It aims to improve on the performance of the original overlaying mechanism provided by t3lib_page
29 * and to provide a more useful API for developers
30 *
31 * @author Francois Suter (Cobweb) <typo3@cobweb.ch>
32 * @package TYPO3
33 * @subpackage tx_overlays
34 *
35 * $Id$
36 */
37 final class tx_overlays {
38 static public $tableFields = array();
39
40 /**
41 * This method is designed to get all the records from a given table, properly overlaid with versions and translations
42 * Its parameters are the same as t3lib_db::exec_SELECTquery()
43 * A small difference is that it will take only a single table
44 * The big difference is that it returns an array of properly overlaid records and not a result pointer
45 *
46 * @param string $selectFields: List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
47 * @param string $fromTable: Table from which to select. This is what comes right after "FROM ...". Required value.
48 * @param string $whereClause: Optional additional WHERE clauses put in the end of the query. NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself! DO NOT PUT IN GROUP BY, ORDER BY or LIMIT!
49 * @param string $groupBy: Optional GROUP BY field(s), if none, supply blank string.
50 * @param string $orderBy: Optional ORDER BY field(s), if none, supply blank string.
51 * @param string $limit: Optional LIMIT value ([begin,]max), if none, supply blank string.
52 * @return array Fully overlaid recordset
53 */
54 public static function getAllRecordsForTable($selectFields, $fromTable, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '') {
55 // SQL WHERE clause is the base clause passed to the function
56 $where = $whereClause;
57 // Add language condition
58 $condition = self::getLanguageCondition($fromTable);
59 if (!empty($condition)) {
60 if (!empty($where)) {
61 $where .= ' AND ';
62 }
63 $where .= '(' . $condition . ')';
64 }
65 // Add enable fields condition
66 $condition = self::getEnableFieldsCondition($fromTable);
67 if (!empty($condition)) {
68 if (!empty($where)) {
69 $where .= ' AND ';
70 }
71 $where .= '(' . $condition . ')';
72 }
73 // Add workspace condition
74 $condition = self::getWorkspaceCondition($fromTable);
75 if (!empty($condition)) {
76 if (!empty($where)) {
77 $where .= ' AND ';
78 }
79 $where .= '(' . $condition . ')';
80 }
81
82 // If the language is not default, prepare for overlays
83 $doOverlays = FALSE;
84 if ($GLOBALS['TSFE']->sys_language_content > 0) {
85 // Make sure the list of selected fields includes "uid", "pid" and language fields so that language overlays can be gotten properly
86 // If these do not exist in the queried table, the recordset is returned as is, without overlay
87 try {
88 $selectFields = self::selectOverlayFields($fromTable, $selectFields);
89 $doOverlays = TRUE;
90 } catch (Exception $e) {
91 $doOverlays = FALSE;
92 }
93 }
94 // If versioning preview is on, prepare for version overlays
95 $doVersioning = FALSE;
96 if ($GLOBALS['TSFE']->sys_page->versioningPreview) {
97 try {
98 $selectFields = self::selectVersioningFields($fromTable, $selectFields);
99 $doVersioning = TRUE;
100 } catch (Exception $e) {
101 $doVersioning = FALSE;
102 }
103 }
104
105 // Add base fields (uid, pid) if translations or versioning are activated
106 if ($doOverlays || $doVersioning) {
107 try {
108 $selectFields = self::selectBaseFields($fromTable, $selectFields);
109 } catch (Exception $e) {
110 // Neither translations nor versioning can happen without uid and pid
111 $doOverlays = FALSE;
112 $doVersioning = FALSE;
113 }
114 }
115
116 // Execute the query itself
117 //t3lib_div::debug($GLOBALS['TYPO3_DB']->SELECTquery($selectFields, $fromTable, $where, $groupBy, $orderBy, $limit));
118 $records = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows($selectFields, $fromTable, $where, $groupBy, $orderBy, $limit);
119 //t3lib_div::debug($records, 'before versioning');
120 // Perform version overlays, if needed
121 if ($doVersioning) {
122 $numRecords = count($records);
123 for ($i = 0; $i < $numRecords; $i++) {
124 $GLOBALS['TSFE']->sys_page->versionOL($fromTable, $records[$i]);
125 // The versioned record may actually be FALSE if it is meant to be deleted
126 // in the workspace. To be really clean, unset it.
127 if ($records[$i] === FALSE) {
128 unset($records[$i]);
129 }
130 }
131 }
132 //t3lib_div::debug($records, 'after versioning');
133
134 // If we have both a uid and a pid field, we can proceed with overlaying the records
135 if ($doOverlays) {
136 $records = self::overlayRecordSet($fromTable, $records, $GLOBALS['TSFE']->sys_language_content, $GLOBALS['TSFE']->sys_language_contentOL, $doVersioning);
137 }
138 return $records;
139 }
140
141 /**
142 * This method gets the SQL condition to apply for fetching the proper language
143 * depending on the localization settings in the TCA
144 *
145 * @param string $table: name of the table to assemble the condition for
146 * @return string SQL to add to the WHERE clause (without "AND")
147 */
148 public static function getLanguageCondition($table) {
149 $languageCondition = '';
150
151 // First check if there's actually a TCA for the given table
152 if (isset($GLOBALS['TCA'][$table]['ctrl'])) {
153 $tableCtrlTCA = $GLOBALS['TCA'][$table]['ctrl'];
154
155 // Assemble language condition only if a language field is defined
156 if (!empty($tableCtrlTCA['languageField'])) {
157 if (isset($GLOBALS['TSFE']->sys_language_contentOL) && isset($tableCtrlTCA['transOrigPointerField'])) {
158 $languageCondition = $table . '.' . $tableCtrlTCA['languageField'] . ' IN (0,-1)'; // Default language and "all" language
159
160 // If current language is not default, select elements that exist only for current language
161 // That means elements that exist for current language but have no parent element
162 if ($GLOBALS['TSFE']->sys_language_content > 0) {
163 $languageCondition .= ' OR (' . $table . '.' . $tableCtrlTCA['languageField'] . " = '" . $GLOBALS['TSFE']->sys_language_content . "' AND " . $table . '.' . $tableCtrlTCA['transOrigPointerField'] . " = '0')";
164 }
165 } else {
166 $languageCondition = $table . '.' . $tableCtrlTCA['languageField'] . " = '" . $GLOBALS['TSFE']->sys_language_content . "'";
167 }
168 }
169 }
170 return $languageCondition;
171 }
172
173 /**
174 * This method returns the condition on enable fields for the given table
175 * Basically it calls on the method provided by t3lib_page, but without the " AND " in front
176 *
177 * @param string $table: name of the table to build the condition for
178 * @param boolean $showHidden: set to TRUE to force the display of hidden records
179 * @param array $ignoreArray: use keys like "disabled", "starttime", "endtime", "fe_group" (i.e. keys from "enablefields" in TCA) and set values to TRUE to exclude corresponding conditions from WHERE clause
180 * @return string SQL to add to the WHERE clause (without "AND")
181 */
182 public static function getEnableFieldsCondition($table, $showHidden = FALSE, $ignoreArray = array()) {
183 $enableCondition = '';
184 // First check if table has a TCA ctrl section, otherwise t3lib_page::enableFields() will die() (stupid thing!)
185 // NOTE: since TYPO3 4.5, an execption is thrown, so this method could eventually be adapted
186 if (isset($GLOBALS['TCA'][$table]['ctrl'])) {
187 $showHidden = $showHidden ? $showHidden : ($table == 'pages' ? $GLOBALS['TSFE']->showHiddenPage : $GLOBALS['TSFE']->showHiddenRecords);
188 $enableCondition = $GLOBALS['TSFE']->sys_page->enableFields($table, $showHidden , $ignoreArray);
189 // If an enable clause was returned, strip the first ' AND '
190 if (!empty($enableCondition)) {
191 $enableCondition = substr($enableCondition, strlen(' AND '));
192 }
193 }
194 // TODO: throw an exception if the given table has no TCA? (t3lib_page::enableFields() used a die)
195 return $enableCondition;
196 }
197
198 /**
199 * This method assembles the proper condition with regards to versioning/workspaces
200 *
201 * NOTE: this is not complete yet! It just makes sure that only live records
202 * are shown in the FE, but workspace preview does not work yet.
203 *
204 * @param string $table: name of the table to build the condition for
205 * @return string
206 */
207 public static function getWorkspaceCondition($table) {
208 $workspaceCondition = '';
209 // If the table has some TCA definition, check workspace handling
210 if (isset($GLOBALS['TCA'][$table]['ctrl']) && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
211
212 // Base condition to get only live records
213 // (they get overlaid afterwards in case of preview)
214 $workspaceCondition .= ' (' . $table . '.t3ver_state <= 0 AND ' . $table . '.t3ver_oid = 0)';
215
216 // Additional conditions when previewing a workspace
217 if ($GLOBALS['TSFE']->sys_page->versioningPreview) {
218 // Select new records (which exist only in the workspace)
219 // This is achieved by selecting the placeholders, which will be overlaid
220 // with the actual content later when calling t3lib_page::versionOL()
221 $workspaceCondition .= ' OR (' . $table . '.t3ver_state = 1 AND ' . $table . '.t3ver_wsid = ' . intval($GLOBALS['BE_USER']->workspace) . ')';
222 }
223 }
224 return $workspaceCondition;
225 }
226
227 /**
228 * This method gets all fields for a given table and stores that list
229 * into an internal cache array
230 *
231 * @param string $table: name of the table to fetch the fields for
232 * @return void
233 */
234 public static function getAllFieldsForTable($table) {
235 self::$tableFields[$table] = $GLOBALS['TYPO3_DB']->admin_get_fields($table);
236 }
237
238 /**
239 * This method makes sure that base fields such as uid and pid are included
240 * in the list of selected fields. These fields are absolutely necessary when
241 * translations or versioning overlays are being made.
242 * The method throws an exception if the fields are not available.
243 *
244 * @param string $table: Table from which to select. This is what comes right after "FROM ...". Required value.
245 * @param string $selectFields: List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
246 * @return string The modified SELECT string
247 */
248 public static function selectBaseFields($table, $selectFields) {
249 $select = $selectFields;
250 // Don't bother if all fields are selected anyway
251 if ($selectFields != '*') {
252 $hasUidField = FALSE;
253 $hasPidField = FALSE;
254 // Make sure we have the list of fields for the given table
255 if (!isset(self::$tableFields[$table])) {
256 self::getAllFieldsForTable($table);
257 }
258 // Add the fields, if available
259 // NOTE: this may add the fields twice if they are already
260 // in the list of selected fields, but that doesn't hurt
261 // It doesn't seem worth making a very precise parsing of the list
262 // of selected fields just to avoid duplicates
263 if (isset(self::$tableFields[$table]['uid'])) {
264 $select .= ', ' . $table . '.uid';
265 $hasUidField = TRUE;
266 }
267 if (isset(self::$tableFields[$table]['pid'])) {
268 $select .= ', ' . $table . '.pid';
269 $hasPidField = TRUE;
270 }
271 // If one of the fields is still missing after that, throw an exception
272 if ($hasUidField === FALSE || $hasPidField === FALSE) {
273 throw new Exception('Not all base fields are available.', 1284463019);
274 }
275 }
276 return $select;
277 }
278
279 /**
280 * This method makes sure that all the fields necessary for proper overlaying are included
281 * in the list of selected fields and exist in the table being queried
282 * If not, it lets the exception thrown by tx_context::selectOverlayFieldsArray() bubble up
283 *
284 * @param string $table: Table from which to select. This is what comes right after "FROM ...". Required value.
285 * @param string $selectFields: List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
286 * @return string Possibly modified list of fields to select
287 */
288 public static function selectOverlayFields($table, $selectFields) {
289 $additionalFields = self::selectOverlayFieldsArray($table, $selectFields);
290 if (count($additionalFields) > 0) {
291 foreach ($additionalFields as $aField) {
292 $selectFields .= ', ' . $table . '.' . $aField;
293 }
294 }
295 return $selectFields;
296 }
297
298 /**
299 * This method checks which fields need to be added to the given list of SELECTed fields
300 * so that language overlays can take place properly
301 * If some information is missing, it throws an exception
302 *
303 * @param string $table: Table from which to select. This is what comes right after "FROM ...". Required value.
304 * @param string $selectFields: List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
305 * @return array List of fields to add
306 */
307 public static function selectOverlayFieldsArray($table, $selectFields) {
308 $additionalFields = array();
309
310 // If all fields are selected anyway, no need to worry
311 if ($selectFields != '*') {
312 // Check if the table indeed has a TCA
313 if (isset($GLOBALS['TCA'][$table]['ctrl'])) {
314
315 // Continue only if table is not using foreign tables for translations
316 // (in this case no additional field is needed) and has a language field
317 if (empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) && !empty($GLOBALS['TCA'][$table]['ctrl']['languageField'])) {
318 $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
319
320 // In order to be properly overlaid, a table has to have a given languageField
321 $hasLanguageField = strpos($selectFields, $languageField);
322 if ($hasLanguageField === FALSE) {
323 // Make sure we have the list of fields for the given table
324 if (!isset(self::$tableFields[$table])) {
325 self::getAllFieldsForTable($table);
326 }
327 if (isset(self::$tableFields[$table][$languageField])) {
328 $additionalFields[] = $languageField;
329 $hasLanguageField = TRUE;
330 }
331 }
332 // If language field is still missing after that, throw an exception
333 if ($hasLanguageField === FALSE) {
334 throw new Exception('Language field not available.', 1284463837);
335 }
336 }
337
338 // The table has no TCA, throw an exception
339 } else {
340 throw new Exception('No TCA for table, cannot add overlay fields.', 1284474025);
341 }
342 }
343 return $additionalFields;
344 }
345
346 /**
347 * This method makes sure that all the fields necessary for proper versioning overlays are included
348 * in the list of selected fields and exist in the table being queried
349 * If not, it lets the exception thrown by tx_context::selectVersioningFieldsArray() bubble up
350 *
351 * @param string $table: Table from which to select. This is what comes right after "FROM ...". Required value.
352 * @param string $selectFields: List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
353 * @return string Possibly modified list of fields to select
354 */
355 public static function selectVersioningFields($table, $selectFields) {
356 $additionalFields = self::selectVersioningFieldsArray($table, $selectFields);
357 if (count($additionalFields) > 0) {
358 foreach ($additionalFields as $aField) {
359 $selectFields .= ', ' . $table . '.' . $aField;
360 }
361 }
362 return $selectFields;
363 }
364
365 /**
366 * This method checks which fields need to be added to the given list of SELECTed fields
367 * so that versioning overlays can take place properly
368 * If some information is missing, it throws an exception
369 *
370 * @param string $table: Table from which to select. This is what comes right after "FROM ...". Required value.
371 * @param string $selectFields: List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
372 * @return array List of fields to add
373 */
374 public static function selectVersioningFieldsArray($table, $selectFields) {
375 $additionalFields = array();
376
377 // If all fields are selected anyway, no need to worry
378 if ($selectFields != '*') {
379 // Check if the table indeed has a TCA and versioning information
380 if (isset($GLOBALS['TCA'][$table]['ctrl']) && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
381
382 // In order for versioning to work properly, the version state field is needed
383 $stateField = 't3ver_state';
384 $hasStateField = strpos($selectFields, $stateField);
385 if ($hasStateField === FALSE) {
386 // Make sure we have the list of fields for the given table
387 if (!isset(self::$tableFields[$table])) {
388 self::getAllFieldsForTable($table);
389 }
390 if (isset(self::$tableFields[$table][$stateField])) {
391 $additionalFields[] = $stateField;
392 $hasStateField = TRUE;
393 }
394 }
395 // If state field is still missing after that, throw an exception
396 if ($hasStateField === FALSE) {
397 throw new Exception('Fields for versioning were not all available.', 1284473941);
398 }
399
400 // The table has no TCA, throw an exception
401 } else {
402 throw new Exception('No TCA for table, cannot add versioning fields.', 1284474016);
403 }
404 }
405 return $additionalFields;
406 }
407
408 /**
409 * Creates language-overlay for records in general (where translation is found in records from the same table)
410 * This is originally copied from t3lib_page::getRecordOverlay()
411 *
412 * @param string $table: Table name
413 * @param array $recordset: Full recordset to overlay. Must containt uid, pid and $TCA[$table]['ctrl']['languageField']
414 * @param integer $currentLanguage: Uid of the currently selected language in the FE
415 * @param string $overlayMode: Overlay mode. If "hideNonTranslated" then records without translation will not be returned un-translated but removed instead.
416 * @param boolean $doVersioning: true if workspace preview is on
417 * @return array Returns the full overlaid recordset. If $overlayMode is "hideNonTranslated" then some records may be missing if no translation was found.
418 */
419 public static function overlayRecordSet($table, $recordset, $currentLanguage, $overlayMode = '', $doVersioning = FALSE) {
420
421 // Test with the first row if uid and pid fields are present
422 if (!empty($recordset[0]['uid']) && !empty($recordset[0]['pid'])) {
423
424 // Test if the table has a TCA definition
425 if (isset($GLOBALS['TCA'][$table])) {
426 $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
427
428 // Test if the TCA definition includes translation information for the same table
429 if (isset($tableCtrl['languageField']) && isset($tableCtrl['transOrigPointerField'])) {
430
431 // Test with the first row if languageField is present
432 if (isset($recordset[0][$tableCtrl['languageField']])) {
433
434 // Filter out records that are not in the default or [ALL] language, should there be any
435 $filteredRecordset = array();
436 foreach ($recordset as $row) {
437 if ($row[$tableCtrl['languageField']] <= 0) {
438 $filteredRecordset[] = $row;
439 }
440 }
441 // Will try to overlay a record only if the sys_language_content value is larger than zero,
442 // that is, it is not default or [ALL] language
443 if ($currentLanguage > 0) {
444 // Assemble a list of uid's for getting the overlays,
445 // but only from the filtered recordset
446 $uidList = array();
447 foreach ($filteredRecordset as $row) {
448 $uidList[] = $row['uid'];
449 }
450
451 // Get all overlay records
452 $overlays = self::getLocalOverlayRecords($table, $uidList, $currentLanguage, $doVersioning);
453
454 // Now loop on the filtered recordset and try to overlay each record
455 $overlaidRecordset = array();
456 foreach ($recordset as $row) {
457 // If record is already in the right language, keep it as is
458 if ($row[$tableCtrl['languageField']] == $currentLanguage) {
459 $overlaidRecordset[] = $row;
460
461 // Else try to apply an overlay
462 } elseif (isset($overlays[$row['uid']][$row['pid']])) {
463 $overlaidRecordset[] = self::overlaySingleRecord($table, $row, $overlays[$row['uid']][$row['pid']]);
464
465 // No overlay exists, apply relevant translation rules
466 } else {
467 // Take original record, only if non-translated are not hidden, or if language is [All]
468 if ($overlayMode != 'hideNonTranslated' || $row[$tableCtrl['languageField']] == -1) {
469 $overlaidRecordset[] = $row;
470 }
471 }
472 }
473 // Return the overlaid recordset
474 return $overlaidRecordset;
475
476 } else {
477 // When default language is displayed, we never want to return a record carrying another language!
478 // Return the filtered recordset
479 return $filteredRecordset;
480 }
481
482 // Provided recordset does not contain languageField field, return recordset unchanged
483 } else {
484 return $recordset;
485 }
486
487 // Test if the TCA definition includes translation information for a foreign table
488 } elseif (isset($tableCtrl['transForeignTable'])) {
489 // The foreign table has a TCA structure. We can proceed.
490 if (isset($GLOBALS['TCA'][$tableCtrl['transForeignTable']])) {
491 $foreignCtrl = $GLOBALS['TCA'][$tableCtrl['transForeignTable']]['ctrl'];
492 // Check that the foreign table is indeed the appropriate translation table
493 // and also check that the foreign table has all the necessary TCA definitions
494 if (!empty($foreignCtrl['transOrigPointerTable']) && $foreignCtrl['transOrigPointerTable'] == $table && !empty($foreignCtrl['transOrigPointerField']) && !empty($foreignCtrl['languageField'])) {
495 // Assemble a list of all uid's of records to translate
496 $uidList = array();
497 foreach ($recordset as $row) {
498 $uidList[] = $row['uid'];
499 }
500
501 // Get all overlay records
502 $overlays = self::getForeignOverlayRecords($tableCtrl['transForeignTable'], $uidList, $currentLanguage);
503
504 // Now loop on the filtered recordset and try to overlay each record
505 $overlaidRecordset = array();
506 foreach ($recordset as $row) {
507 // An overlay exists, apply it
508 if (isset($overlays[$row['uid']])) {
509 $overlaidRecordset[] = self::overlaySingleRecord($table, $row, $overlays[$row['uid']][$row['pid']]);
510
511 // No overlay exists
512 } else {
513 // Take original record, only if non-translated are not hidden
514 if ($overlayMode != 'hideNonTranslated') {
515 $overlaidRecordset[] = $row;
516 }
517 }
518 }
519 // Return the overlaid recordset
520 return $overlaidRecordset;
521 }
522
523 // The foreign table has no TCA definition, it's impossible to perform overlays
524 // Return recordset as is
525 } else {
526 return $recordset;
527 }
528
529 // No appropriate language fields defined in TCA, return recordset unchanged
530 } else {
531 return $recordset;
532 }
533
534 // No TCA for table, return recordset unchanged
535 } else {
536 return $recordset;
537 }
538 }
539 // Recordset did not contain uid or pid field, return recordset unchanged
540 else {
541 return $recordset;
542 }
543 }
544
545 /**
546 * This method is a wrapper around getLocalOverlayRecords() and getForeignOverlayRecords().
547 * It makes it possible to use the same call whether translations are in the same table or
548 * in a foreign table. This method dispatches accordingly.
549 *
550 * @param string $table: name of the table for which to fetch the records
551 * @param array $uids: array of all uid's of the original records for which to fetch the translation
552 * @param integer $currentLanguage: uid of the system language to translate to
553 * @param boolean $doVersioning: true if versioning overlay must be performed
554 * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
555 */
556 public static function getOverlayRecords($table, $uids, $currentLanguage, $doVersioning) {
557 if (is_array($uids) && count($uids) > 0) {
558 if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
559 return self::getForeignOverlayRecords($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'], $uids, $currentLanguage);
560 } else {
561 return self::getLocalOverlayRecords($table, $uids, $currentLanguage, $doVersioning);
562 }
563 } else {
564 return array();
565 }
566 }
567
568 /**
569 * This method is used to retrieve all the records for overlaying other records
570 * when those records are stored in the same table as the originals
571 *
572 * @param string $table: name of the table for which to fetch the records
573 * @param array $uids: array of all uid's of the original records for which to fetch the translation
574 * @param integer $currentLanguage: uid of the system language to translate to
575 * @param boolean $doVersioning: true if versioning overlay must be performed
576 * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
577 */
578 public static function getLocalOverlayRecords($table, $uids, $currentLanguage, $doVersioning) {
579 $overlays = array();
580 if (is_array($uids) && count($uids) > 0) {
581 $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
582 // Select overlays for all records
583 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
584 '*',
585 $table,
586 $tableCtrl['languageField'].' = '.intval($currentLanguage).
587 ' AND ' . $tableCtrl['transOrigPointerField'] . ' IN (' . implode(', ', $uids) . ')' .
588 ' AND ' . self::getEnableFieldsCondition($table)
589 );
590 // Arrange overlay records according to transOrigPointerField, so that it's easy to relate them to the originals
591 // This structure is actually a 2-dimensional array, with the pid as the second key
592 // Because of versioning, there may be several overlays for a given original and matching the pid too
593 // ensures that we are referring to the correct overlay
594 while (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
595 // Perform version overlays, if needed
596 if ($doVersioning) {
597 $GLOBALS['TSFE']->sys_page->versionOL($table, $row);
598 }
599 // The versioned record may actually be FALSE if it is meant to be deleted
600 // in the workspace. To be really clean, unset it.
601 if ($row !== FALSE) {
602 if (!isset($overlays[$row[$tableCtrl['transOrigPointerField']]])) {
603 $overlays[$row[$tableCtrl['transOrigPointerField']]] = array();
604 }
605 $overlays[$row[$tableCtrl['transOrigPointerField']]][$row['pid']] = $row;
606 }
607 }
608 $GLOBALS['TYPO3_DB']->sql_free_result($res);
609 }
610 return $overlays;
611 }
612
613 /**
614 * This method is used to retrieve all the records for overlaying other records
615 * when those records are stored in a different table than the originals
616 *
617 * @param string $table: name of the table for which to fetch the records
618 * @param array $uids: array of all uid's of the original records for which to fetch the translation
619 * @param integer $currentLanguage: uid of the system language to translate to
620 * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
621 */
622 public static function getForeignOverlayRecords($table, $uids, $currentLanguage) {
623 $overlays = array();
624 if (is_array($uids) && count($uids) > 0) {
625 $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
626 // Select overlays for all records
627 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
628 '*',
629 $table,
630 $tableCtrl['languageField'].' = '.intval($currentLanguage).
631 ' AND ' . $tableCtrl['transOrigPointerField'] . ' IN (' . implode(', ', $uids) . ')' .
632 ' AND ' . self::getEnableFieldsCondition($table)
633 );
634 // Arrange overlay records according to transOrigPointerField, so that it's easy to relate them to the originals
635 while (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
636 $overlays[$row[$tableCtrl['transOrigPointerField']]] = $row;
637 }
638 $GLOBALS['TYPO3_DB']->sql_free_result($res);
639 }
640 return $overlays;
641 }
642
643 /**
644 * This method takes a record and its overlay and performs the overlay according to active translation rules
645 * This piece of code is extracted from t3lib_page::getRecordOverlay()
646 *
647 * @param string $table: name of the table for which the operation is taking place
648 * @param array $record: record to overlay
649 * @param array $overlay: overlay of the record
650 * @return array Overlaid record
651 */
652 public static function overlaySingleRecord($table, $record, $overlay) {
653 $overlaidRecord = $record;
654 $overlaidRecord['_LOCALIZED_UID'] = $overlay['uid'];
655 foreach ($record as $key => $value) {
656 if ($key != 'uid' && $key != 'pid' && isset($overlay[$key])) {
657 if (isset($GLOBALS['TSFE']->TCAcachedExtras[$table]['l10n_mode'][$key])) {
658 if ($GLOBALS['TSFE']->TCAcachedExtras[$table]['l10n_mode'][$key] != 'exclude'
659 && ($GLOBALS['TSFE']->TCAcachedExtras[$table]['l10n_mode'][$key] != 'mergeIfNotBlank' || strcmp(trim($overlay[$key]), ''))) {
660 $overlaidRecord[$key] = $overlay[$key];
661 }
662 } else {
663 $overlaidRecord[$key] = $overlay[$key];
664 }
665 }
666 }
667 return $overlaidRecord;
668 }
669 }
670 ?>