67065615f8bde32dc7e6ff2358610ad58f27ee6f
[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 throws an exception
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 $select = $selectFields;
357
358 // If all fields are selected anyway, no need to worry
359 if ($selectFields != '*') {
360 // Check if the table indeed has a TCA and versioning information
361 if (isset($GLOBALS['TCA'][$table]['ctrl']) && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
362
363 // In order for versioning to work properly, the version state field is needed
364 $stateField = 't3ver_state';
365 $hasStateField = strpos($selectFields, $stateField);
366 if ($hasStateField === FALSE) {
367 // Make sure we have the list of fields for the given table
368 if (!isset(self::$tableFields[$table])) {
369 self::getAllFieldsForTable($table);
370 }
371 if (isset(self::$tableFields[$table][$stateField])) {
372 $select .= ', ' . $table . '.'.$stateField;
373 $hasStateField = TRUE;
374 }
375 }
376 // If state field is still missing after that, throw an exception
377 if ($hasStateField === FALSE) {
378 throw new Exception('Fields for versioning were not all available.', 1284473941);
379 }
380
381 // The table has no TCA, throw an exception
382 } else {
383 throw new Exception('No TCA for table, cannot add versioning fields.', 1284474016);
384 }
385 }
386 return $select;
387 }
388
389 /**
390 * Creates language-overlay for records in general (where translation is found in records from the same table)
391 * This is originally copied from t3lib_page::getRecordOverlay()
392 *
393 * @param string $table: Table name
394 * @param array $recordset: Full recordset to overlay. Must containt uid, pid and $TCA[$table]['ctrl']['languageField']
395 * @param integer $currentLanguage: Uid of the currently selected language in the FE
396 * @param string $overlayMode: Overlay mode. If "hideNonTranslated" then records without translation will not be returned un-translated but removed instead.
397 * @param boolean $doVersioning: true if workspace preview is on
398 * @return array Returns the full overlaid recordset. If $overlayMode is "hideNonTranslated" then some records may be missing if no translation was found.
399 */
400 public static function overlayRecordSet($table, $recordset, $currentLanguage, $overlayMode = '', $doVersioning = FALSE) {
401
402 // Test with the first row if uid and pid fields are present
403 if (!empty($recordset[0]['uid']) && !empty($recordset[0]['pid'])) {
404
405 // Test if the table has a TCA definition
406 if (isset($GLOBALS['TCA'][$table])) {
407 $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
408
409 // Test if the TCA definition includes translation information for the same table
410 if (isset($tableCtrl['languageField']) && isset($tableCtrl['transOrigPointerField'])) {
411
412 // Test with the first row if languageField is present
413 if (isset($recordset[0][$tableCtrl['languageField']])) {
414
415 // Filter out records that are not in the default or [ALL] language, should there be any
416 $filteredRecordset = array();
417 foreach ($recordset as $row) {
418 if ($row[$tableCtrl['languageField']] <= 0) {
419 $filteredRecordset[] = $row;
420 }
421 }
422 // Will try to overlay a record only if the sys_language_content value is larger than zero,
423 // that is, it is not default or [ALL] language
424 if ($currentLanguage > 0) {
425 // Assemble a list of uid's for getting the overlays,
426 // but only from the filtered recordset
427 $uidList = array();
428 foreach ($filteredRecordset as $row) {
429 $uidList[] = $row['uid'];
430 }
431
432 // Get all overlay records
433 $overlays = self::getLocalOverlayRecords($table, $uidList, $currentLanguage, $doVersioning);
434
435 // Now loop on the filtered recordset and try to overlay each record
436 $overlaidRecordset = array();
437 foreach ($recordset as $row) {
438 // If record is already in the right language, keep it as is
439 if ($row[$tableCtrl['languageField']] == $currentLanguage) {
440 $overlaidRecordset[] = $row;
441
442 // Else try to apply an overlay
443 } elseif (isset($overlays[$row['uid']][$row['pid']])) {
444 $overlaidRecordset[] = self::overlaySingleRecord($table, $row, $overlays[$row['uid']][$row['pid']]);
445
446 // No overlay exists, apply relevant translation rules
447 } else {
448 // Take original record, only if non-translated are not hidden, or if language is [All]
449 if ($overlayMode != 'hideNonTranslated' || $row[$tableCtrl['languageField']] == -1) {
450 $overlaidRecordset[] = $row;
451 }
452 }
453 }
454 // Return the overlaid recordset
455 return $overlaidRecordset;
456
457 } else {
458 // When default language is displayed, we never want to return a record carrying another language!
459 // Return the filtered recordset
460 return $filteredRecordset;
461 }
462
463 // Provided recordset does not contain languageField field, return recordset unchanged
464 } else {
465 return $recordset;
466 }
467
468 // Test if the TCA definition includes translation information for a foreign table
469 } elseif (isset($tableCtrl['transForeignTable'])) {
470 // The foreign table has a TCA structure. We can proceed.
471 if (isset($GLOBALS['TCA'][$tableCtrl['transForeignTable']])) {
472 $foreignCtrl = $GLOBALS['TCA'][$tableCtrl['transForeignTable']]['ctrl'];
473 // Check that the foreign table is indeed the appropriate translation table
474 // and also check that the foreign table has all the necessary TCA definitions
475 if (!empty($foreignCtrl['transOrigPointerTable']) && $foreignCtrl['transOrigPointerTable'] == $table && !empty($foreignCtrl['transOrigPointerField']) && !empty($foreignCtrl['languageField'])) {
476 // Assemble a list of all uid's of records to translate
477 $uidList = array();
478 foreach ($recordset as $row) {
479 $uidList[] = $row['uid'];
480 }
481
482 // Get all overlay records
483 $overlays = self::getForeignOverlayRecords($tableCtrl['transForeignTable'], $uidList, $currentLanguage);
484
485 // Now loop on the filtered recordset and try to overlay each record
486 $overlaidRecordset = array();
487 foreach ($recordset as $row) {
488 // An overlay exists, apply it
489 if (isset($overlays[$row['uid']])) {
490 $overlaidRecordset[] = self::overlaySingleRecord($table, $row, $overlays[$row['uid']][$row['pid']]);
491
492 // No overlay exists
493 } else {
494 // Take original record, only if non-translated are not hidden
495 if ($overlayMode != 'hideNonTranslated') {
496 $overlaidRecordset[] = $row;
497 }
498 }
499 }
500 // Return the overlaid recordset
501 return $overlaidRecordset;
502 }
503
504 // The foreign table has no TCA definition, it's impossible to perform overlays
505 // Return recordset as is
506 } else {
507 return $recordset;
508 }
509
510 // No appropriate language fields defined in TCA, return recordset unchanged
511 } else {
512 return $recordset;
513 }
514
515 // No TCA for table, return recordset unchanged
516 } else {
517 return $recordset;
518 }
519 }
520 // Recordset did not contain uid or pid field, return recordset unchanged
521 else {
522 return $recordset;
523 }
524 }
525
526 /**
527 * This method is a wrapper around getLocalOverlayRecords() and getForeignOverlayRecords().
528 * It makes it possible to use the same call whether translations are in the same table or
529 * in a foreign table. This method dispatches accordingly.
530 *
531 * @param string $table: name of the table for which to fetch the records
532 * @param array $uids: array of all uid's of the original records for which to fetch the translation
533 * @param integer $currentLanguage: uid of the system language to translate to
534 * @param boolean $doVersioning: true if versioning overlay must be performed
535 * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
536 */
537 public static function getOverlayRecords($table, $uids, $currentLanguage, $doVersioning) {
538 if (is_array($uids) && count($uids) > 0) {
539 if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
540 return self::getForeignOverlayRecords($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'], $uids, $currentLanguage);
541 } else {
542 return self::getLocalOverlayRecords($table, $uids, $currentLanguage, $doVersioning);
543 }
544 } else {
545 return array();
546 }
547 }
548
549 /**
550 * This method is used to retrieve all the records for overlaying other records
551 * when those records are stored in the same table as the originals
552 *
553 * @param string $table: name of the table for which to fetch the records
554 * @param array $uids: array of all uid's of the original records for which to fetch the translation
555 * @param integer $currentLanguage: uid of the system language to translate to
556 * @param boolean $doVersioning: true if versioning overlay must be performed
557 * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
558 */
559 public static function getLocalOverlayRecords($table, $uids, $currentLanguage, $doVersioning) {
560 $overlays = array();
561 if (is_array($uids) && count($uids) > 0) {
562 $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
563 // Select overlays for all records
564 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
565 '*',
566 $table,
567 $tableCtrl['languageField'].' = '.intval($currentLanguage).
568 ' AND ' . $tableCtrl['transOrigPointerField'] . ' IN (' . implode(', ', $uids) . ')' .
569 ' AND ' . self::getEnableFieldsCondition($table)
570 );
571 // Arrange overlay records according to transOrigPointerField, so that it's easy to relate them to the originals
572 // This structure is actually a 2-dimensional array, with the pid as the second key
573 // Because of versioning, there may be several overlays for a given original and matching the pid too
574 // ensures that we are referring to the correct overlay
575 while (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
576 // Perform version overlays, if needed
577 if ($doVersioning) {
578 $GLOBALS['TSFE']->sys_page->versionOL($table, $row);
579 }
580 // The versioned record may actually be FALSE if it is meant to be deleted
581 // in the workspace. To be really clean, unset it.
582 if ($row !== FALSE) {
583 if (!isset($overlays[$row[$tableCtrl['transOrigPointerField']]])) {
584 $overlays[$row[$tableCtrl['transOrigPointerField']]] = array();
585 }
586 $overlays[$row[$tableCtrl['transOrigPointerField']]][$row['pid']] = $row;
587 }
588 }
589 $GLOBALS['TYPO3_DB']->sql_free_result($res);
590 }
591 return $overlays;
592 }
593
594 /**
595 * This method is used to retrieve all the records for overlaying other records
596 * when those records are stored in a different table than the originals
597 *
598 * @param string $table: name of the table for which to fetch the records
599 * @param array $uids: array of all uid's of the original records for which to fetch the translation
600 * @param integer $currentLanguage: uid of the system language to translate to
601 * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
602 */
603 public static function getForeignOverlayRecords($table, $uids, $currentLanguage) {
604 $overlays = array();
605 if (is_array($uids) && count($uids) > 0) {
606 $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
607 // Select overlays for all records
608 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
609 '*',
610 $table,
611 $tableCtrl['languageField'].' = '.intval($currentLanguage).
612 ' AND ' . $tableCtrl['transOrigPointerField'] . ' IN (' . implode(', ', $uids) . ')' .
613 ' AND ' . self::getEnableFieldsCondition($table)
614 );
615 // Arrange overlay records according to transOrigPointerField, so that it's easy to relate them to the originals
616 while (($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))) {
617 $overlays[$row[$tableCtrl['transOrigPointerField']]] = $row;
618 }
619 $GLOBALS['TYPO3_DB']->sql_free_result($res);
620 }
621 return $overlays;
622 }
623
624 /**
625 * This method takes a record and its overlay and performs the overlay according to active translation rules
626 * This piece of code is extracted from t3lib_page::getRecordOverlay()
627 *
628 * @param string $table: name of the table for which the operation is taking place
629 * @param array $record: record to overlay
630 * @param array $overlay: overlay of the record
631 * @return array Overlaid record
632 */
633 public static function overlaySingleRecord($table, $record, $overlay) {
634 $overlaidRecord = $record;
635 $overlaidRecord['_LOCALIZED_UID'] = $overlay['uid'];
636 foreach ($record as $key => $value) {
637 if ($key != 'uid' && $key != 'pid' && isset($overlay[$key])) {
638 if (isset($GLOBALS['TSFE']->TCAcachedExtras[$table]['l10n_mode'][$key])) {
639 if ($GLOBALS['TSFE']->TCAcachedExtras[$table]['l10n_mode'][$key] != 'exclude'
640 && ($GLOBALS['TSFE']->TCAcachedExtras[$table]['l10n_mode'][$key] != 'mergeIfNotBlank' || strcmp(trim($overlay[$key]), ''))) {
641 $overlaidRecord[$key] = $overlay[$key];
642 }
643 } else {
644 $overlaidRecord[$key] = $overlay[$key];
645 }
646 }
647 }
648 return $overlaidRecord;
649 }
650 }
651 ?>