[TASK] Prepare for release 91/47591/2
authorFrancois Suter <francois@typo3.org>
Tue, 12 Apr 2016 08:27:36 +0000 (10:27 +0200)
committerFrancois Suter <francois@typo3.org>
Tue, 12 Apr 2016 08:36:52 +0000 (10:36 +0200)
Update and verify version numbers, complete composer manifest,
update extension icon.

Reformatted code with PSR-2.

Resolves: #75532
Releases: 3.0
Change-Id: I04c8c6e5ef20a27cfa3013a48e8a19acd1c2aaf8
Reviewed-on: https://review.typo3.org/47591
Reviewed-by: Francois Suter <francois@typo3.org>
Tested-by: Francois Suter <francois@typo3.org>
Classes/OverlayEngine.php
Documentation/Index.rst
Documentation/Settings.yml
LICENSE.txt [new file with mode: 0644]
composer.json
ext_emconf.php
ext_icon.png

index e1f6304..5226f2f 100644 (file)
@@ -24,763 +24,789 @@ namespace Cobweb\Overlays;
  * @package TYPO3
  * @subpackage tx_overlays
  */
-final class OverlayEngine {
-       static public $tableFields = array();
-
-       /**
-        * Gets all the records from a given table, properly overlaid with versions and translations.
-        *
-        * Its parameters are the same as t3lib_db::exec_SELECTquery().
-        * A small difference is that it will take only a single table.
-        * The big difference is that it returns an array of properly overlaid records and not a result pointer.
-        *
-        * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
-        * @param string $fromTable Table from which to select. This is what comes right after "FROM ...". Required value.
-        * @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!
-        * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
-        * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
-        * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string.
-        * @param string $indexField Optional name of a field to use as an index for the result array. Must be included in the list of select fields.
-        * @return array Fully overlaid recordset
-        */
-       public static function getAllRecordsForTable($selectFields, $fromTable, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '', $indexField = '') {
-               // SQL WHERE clause is the base clause passed to the function
-               $where = $whereClause;
-               // Add language condition
-               $condition = self::getLanguageCondition($fromTable);
-               if (!empty($condition)) {
-                       if (!empty($where)) {
-                               $where .= ' AND ';
-                       }
-                       $where .= '(' . $condition . ')';
-               }
-               // Add enable fields condition
-               $condition = self::getEnableFieldsCondition($fromTable);
-               if (!empty($condition)) {
-                       if (!empty($where)) {
-                               $where .= ' AND ';
-                       }
-                       $where .= '(' . $condition . ')';
-               }
-               // Add workspace condition
-               $condition = self::getVersioningCondition($fromTable);
-               if (!empty($condition)) {
-                       if (!empty($where)) {
-                               $where .= ' AND ';
-                       }
-                       $where .= '(' . $condition . ')';
-               }
-
-               // If the language is not default, prepare for overlays
-               $doOverlays = FALSE;
-               if ($GLOBALS['TSFE']->sys_language_content > 0) {
-                       // Make sure the list of selected fields includes the necessary language fields
-                       // so that language overlays can be gotten properly
-                       try {
-                               $selectFields = self::selectOverlayFields($fromTable, $selectFields);
-                               $doOverlays = TRUE;
-                       } catch (\Exception $e) {
-                               // If the language fields could not be gotten, avoid overlay process
-                               $doOverlays = FALSE;
-                       }
-               }
-               // If versioning preview is on, prepare for version overlays
-               $doVersioning = FALSE;
-               if ($GLOBALS['TSFE']->sys_page->versioningPreview) {
-                       try {
-                               $selectFields = self::selectVersioningFields($fromTable, $selectFields);
-                               $doVersioning = TRUE;
-                       } catch (\Exception $e) {
-                               $doVersioning = FALSE;
-                       }
-               }
-
-               // Add base fields (uid, pid) if translations or versioning are activated
-               if ($doOverlays || $doVersioning) {
-                       try {
-                               $selectFields = self::selectBaseFields($fromTable, $selectFields);
-                       } catch (\Exception $e) {
-                               // Neither translations nor versioning can happen without uid and pid
-                               $doOverlays = FALSE;
-                               $doVersioning = FALSE;
-                       }
-               }
-
-               // Execute the query itself
-               $records = self::getDatabaseConnection()->exec_SELECTgetRows(
-                       $selectFields,
-                       $fromTable,
-                       $where,
-                       $groupBy,
-                       $orderBy,
-                       $limit,
-                       $indexField
-               );
-               // Perform version overlays, if needed
-               if ($doVersioning) {
-                       foreach ($records as $index => $aRecord) {
-                               $GLOBALS['TSFE']->sys_page->versionOL($fromTable, $aRecord);
-                                       // The versioned record may actually be FALSE if it is meant to be deleted
-                                       // in the workspace. To be really clean, unset it.
-                               if ($aRecord === FALSE) {
-                                       unset($records[$index]);
-                               }
-                       }
-               }
-
-               // If we have both a uid and a pid field, we can proceed with overlaying the records
-               if ($doOverlays) {
-                       $records = self::overlayRecordSet(
-                               $fromTable,
-                               $records,
-                               $GLOBALS['TSFE']->sys_language_content,
-                               $GLOBALS['TSFE']->sys_language_contentOL,
-                               $doVersioning
-                       );
-               }
-               return $records;
-       }
-
-       /**
-        * Retrieves a single record from the given table, properly overlaid for version and language.
-        *
-        * NOTE: this method first collects all records and then returns the first one, which makes it possible
-        * to filter out records which may not have a translation, for example. However this is not perfect as the
-        * sorting is still done on the SQL side, so that the first record in a translated language may not be correct
-        * for alphabetical ordering. A sorting on the PHP side could be included in the future, similar to what is
-        * done class tx_dataquery_wrapper (EXT:dataquery).
-        *
-        * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
-        * @param string $fromTable Table from which to select. This is what comes right after "FROM ...". Required value.
-        * @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!
-        * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
-        * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
-        * @return array
-        * @throws \Exception
-        */
-       public static function getSingleRecordForTable($selectFields, $fromTable, $whereClause = '', $groupBy = '', $orderBy = '') {
-               $records = self::getAllRecordsForTable($selectFields, $fromTable, $whereClause, $groupBy, $orderBy);
-               if (count($records) > 0) {
-                       return array_shift($records);
-               } else {
-                       throw new \Exception('No record found', 1397483854);
-               }
-       }
-
-       /**
-        * This method gets the SQL condition to apply for fetching the proper language
-        * depending on the localization settings in the TCA
-        *
-        * @param string $table True name of the table to assemble the condition for
-        * @param string $alias Alias to use for the table instead of its true name
-        * @return string SQL to add to the WHERE clause (without "AND")
-        */
-       public static function getLanguageCondition($table, $alias = '') {
-               $languageCondition = '';
-               if (empty($alias)) {
-                       $alias = $table;
-               }
-
-               // First check if there's actually a TCA for the given table
-               if (isset($GLOBALS['TCA'][$table]['ctrl'])) {
-                       $tableCtrlTCA = $GLOBALS['TCA'][$table]['ctrl'];
-
-                       // Assemble language condition only if a language field is defined
-                       if (!empty($tableCtrlTCA['languageField'])) {
-                               if (isset($GLOBALS['TSFE']->sys_language_contentOL) && isset($tableCtrlTCA['transOrigPointerField'])) {
-                                       // Default language and "all" language
-                                       $languageCondition = $alias . '.' . $tableCtrlTCA['languageField'] . ' IN (0,-1)';
-
-                                       // If current language is not default, select elements that exist only for current language
-                                       // That means elements that exist for current language but have no parent element
-                                       if ($GLOBALS['TSFE']->sys_language_content > 0) {
-                                               $languageCondition .= ' OR (' . $alias . '.' . $tableCtrlTCA['languageField'] . " = '" . $GLOBALS['TSFE']->sys_language_content . "' AND " . $alias . '.' . $tableCtrlTCA['transOrigPointerField'] . " = '0')";
-                                       }
-                               } else {
-                                       $languageCondition = $alias . '.' . $tableCtrlTCA['languageField'] . " = '" . $GLOBALS['TSFE']->sys_language_content . "'";
-                               }
-                       }
-               }
-               return $languageCondition;
-       }
-
-       /**
-        * Returns the condition on enable fields for the given table.
-        *
-        * Basically it calls on the method provided by \TYPO3\CMS\Frontend\Page\PageRepository, but without the " AND " in front.
-        *
-        * @param string $table Name of the table to build the condition for
-        * @param boolean $showHidden Set to TRUE to force the display of hidden records
-        * @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
-        * @return string SQL to add to the WHERE clause (without "AND")
-        * @see \TYPO3\CMS\Frontend\Page\PageRepository::enableFields()
-        */
-       public static function getEnableFieldsCondition($table, $showHidden = FALSE, $ignoreArray = array()) {
-               try {
-                       $showHidden = $showHidden ? $showHidden : ($table == 'pages' ? $GLOBALS['TSFE']->showHiddenPage : $GLOBALS['TSFE']->showHiddenRecords);
-                       $enableCondition = $GLOBALS['TSFE']->sys_page->enableFields($table, $showHidden , $ignoreArray);
-                       // If an enable clause was returned, strip the first ' AND '
-                       if (!empty($enableCondition)) {
-                               $enableCondition = substr($enableCondition, strlen(' AND '));
-                       }
-               }
-               catch (\Exception $e) {
-                       $enableCondition = '';
-               }
-               return $enableCondition;
-       }
-
-       /**
-        * Assembles the proper condition with regards to versioning/workspaces.
-        *
-        * Explanations on parameter $getOverlaysDirectly:
-        * -----------------------------------------------
-        * The base condition assembled by this method will get the placeholders for new or modified records.
-        * This is normally the right way to do things, since those records are overlaid with their workspace version afterwards.
-        * However if you want this condition as part of a more complicated query implying JOINs,
-        * selecting placeholders will not work as the relationships are normally built with the version overlays
-        * and not the placeholders. In this case it is desirable to select the overlays directly,
-        * which can be achieved by setting $getOverlaysDirectly to TRUE
-        *
-        * NOTE: if this all sounds like gibberish, try reading more about workspaces in "Core API"
-        * (there's also quite some stuff in "Inside TYPO3", but part of it is badly outdated)
-        *
-        * @param string $table True name of the table to build the condition for
-        * @param string $alias Alias to use for the table instead of its true name
-        * @param boolean $getOverlaysDirectly Flag to choose original/placeholder records or overlays (see explanations above)
-        * @return string SQL to add to the WHERE clause (without "AND")
-        */
-       public static function getVersioningCondition($table, $alias = '', $getOverlaysDirectly = FALSE) {
-               $workspaceCondition = '';
-               if (empty($alias)) {
-                       $alias = $table;
-               }
-
-               // If the table has some TCA definition, check workspace handling
-               if (isset($GLOBALS['TCA'][$table]['ctrl']) && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
-
-                       // Base condition to get only live records
-                       // (they get overlaid afterwards in case of preview)
-                       $workspaceCondition .= '(' . $alias . '.t3ver_state <= 0 AND ' . $alias . '.t3ver_oid = 0)';
-
-                       // Additional conditions when previewing a workspace
-                       if ($GLOBALS['TSFE']->sys_page->versioningPreview) {
-                               $workspace = intval($GLOBALS['BE_USER']->workspace);
-                               // Condition for records that are unmodified but whose parent was modified
-                               // (when a parent record is modified, copies of its children are made that refer to the modified parent)
-                               $workspaceCondition .= ' OR (' . $alias . '.t3ver_state = 0 AND ' . $alias . '.t3ver_wsid = ' . $workspace . ')';
-                               // Choose the version state of records to select based on the $getOverlaysDirectly flag
-                               // (see explanations in the phpDoc comment above)
-                               $modificationPlaceholderState = 1;
-                               $movePlaceholderState = 3;
-                               if ($getOverlaysDirectly) {
-                                       $modificationPlaceholderState = -1;
-                                       $movePlaceholderState = 4;
-                               }
-                               // Select new records (which exist only in the workspace)
-                               // This is achieved by selecting the placeholders, which will be overlaid
-                               // with the actual content later when calling t3lib_page::versionOL()
-                               $workspaceCondition .= ' OR (' . $alias . '.t3ver_state = ' . $modificationPlaceholderState . ' AND ' . $alias . '.t3ver_wsid = ' . $workspace . ')';
-                               // Move-to placeholder
-                               $workspaceCondition .= ' OR (' . $alias . '.t3ver_state = ' . $movePlaceholderState . ' AND ' . $alias . '.t3ver_wsid = ' . $workspace . ')';
-                       }
-               }
-               return $workspaceCondition;
-       }
-
-       /**
-        * Gets all fields for a given table and stores that list into an internal cache array,
-        * then returns the list of fields.
-        *
-        * @param string $table Name of the table to fetch the fields for
-        * @return array List of fields for the given table
-        */
-       public static function getAllFieldsForTable($table) {
-               if (!isset(self::$tableFields[$table])) {
-                       self::$tableFields[$table] = self::getDatabaseConnection()->admin_get_fields($table);
-               }
-               return self::$tableFields[$table];
-       }
-
-       /**
-        * Makes sure that base fields such as uid and pid are included
-        * in the list of selected fields.
-        *
-        * These fields are absolutely necessary when
-        * translations or versioning overlays are being made.
-        * The method throws an \Exception if the fields are not available.
-        *
-        * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
-        * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
-        * @throws \Exception
-        * @return string The modified SELECT string
-        */
-       public static function selectBaseFields($table, $selectFields) {
-               $select = $selectFields;
-               // Don't bother if all fields are selected anyway
-               if ($selectFields != '*') {
-                       $hasUidField = FALSE;
-                       $hasPidField = FALSE;
-                       // Get the list of fields for the given table
-                       $tableFields = self::getAllFieldsForTable($table);
-
-                       // Add the fields, if available
-                       // NOTE: this may add the fields twice if they are already
-                       // in the list of selected fields, but that doesn't hurt
-                       // It doesn't seem worth making a very precise parsing of the list
-                       // of selected fields just to avoid duplicates
-                       if (isset($tableFields['uid'])) {
-                               $select .= ', ' . $table . '.uid';
-                               $hasUidField = TRUE;
-                       }
-                       if (isset($tableFields['pid'])) {
-                               $select .= ', ' . $table . '.pid';
-                               $hasPidField = TRUE;
-                       }
-                       // If one of the fields is still missing after that, throw an \Exception
-                       if ($hasUidField === FALSE || $hasPidField === FALSE) {
-                               throw new \Exception('Not all base fields are available.', 1284463019);
-                       }
-               }
-               return $select;
-       }
-
-       /**
-        * Makes sure that all the fields necessary for proper overlaying are included
-        * in the list of selected fields and exist in the table being queried.
-        *
-        * If not, it lets the \Exception thrown by tx_context::selectOverlayFieldsArray() bubble up.
-        *
-        * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
-        * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
-        * @return string Possibly modified list of fields to select
-        */
-       public static function selectOverlayFields($table, $selectFields) {
-               $additionalFields = self::selectOverlayFieldsArray($table, $selectFields);
-               if (count($additionalFields) > 0) {
-                       foreach ($additionalFields as $aField) {
-                               $selectFields .= ', ' . $table . '.' . $aField;
-                       }
-               }
-               return $selectFields;
-       }
-
-       /**
-        * Checks which fields need to be added to the given list of SELECTed fields
-        * so that language overlays can take place properly.
-        *
-        * If some information is missing, it throws an \Exception.
-        *
-        * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
-        * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
-        * @throws \Exception
-        * @return array List of fields to add
-        */
-       public static function selectOverlayFieldsArray($table, $selectFields) {
-               $additionalFields = array();
-
-               // If all fields are selected anyway, no need to worry
-               if ($selectFields != '*') {
-                       // Check if the table indeed has a TCA
-                       if (isset($GLOBALS['TCA'][$table]['ctrl'])) {
-
-                               // Continue only if table is not using foreign tables for translations
-                               // (in this case no additional field is needed) and has a language field
-                               if (empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) && !empty($GLOBALS['TCA'][$table]['ctrl']['languageField'])) {
-                                       $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
-
-                                       // In order to be properly overlaid, a table has to have a given languageField
-                                       $hasLanguageField = strpos($selectFields, $languageField);
-                                       if ($hasLanguageField === FALSE) {
-                                               // Get the list of fields for the given table
-                                               $tableFields = self::getAllFieldsForTable($table);
-                                               if (isset($tableFields[$languageField])) {
-                                                       $additionalFields[] = $languageField;
-                                                       $hasLanguageField = TRUE;
-                                               }
-                                       }
-                                       // If language field is still missing after that, throw an \Exception
-                                       if ($hasLanguageField === FALSE) {
-                                               throw new \Exception('Language field not available.', 1284463837);
-                                       }
-                               }
-
-                       // The table has no TCA, throw an \Exception
-                       } else {
-                               throw new \Exception('No TCA for table, cannot add overlay fields.', 1284474025);
-                       }
-               }
-               return $additionalFields;
-       }
-
-       /**
-        * Makes sure that all the fields necessary for proper versioning overlays are included
-        * in the list of selected fields and exist in the table being queried.
-        *
-        * If not, it lets the \Exception thrown by tx_context::selectVersioningFieldsArray() bubble up.
-        *
-        * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
-        * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
-        * @return string Possibly modified list of fields to select
-        */
-       public static function selectVersioningFields($table, $selectFields) {
-               $additionalFields = self::selectVersioningFieldsArray($table, $selectFields);
-               if (count($additionalFields) > 0) {
-                       foreach ($additionalFields as $aField) {
-                               $selectFields .= ', ' . $table . '.' . $aField;
-                       }
-               }
-               return $selectFields;
-       }
-
-       /**
-        * Checks which fields need to be added to the given list of SELECTed fields
-        * so that versioning overlays can take place properly.
-        *
-        * If some information is missing, it throws an \Exception.
-        *
-        * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
-        * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
-        * @throws \Exception
-        * @return array List of fields to add
-        */
-       public static function selectVersioningFieldsArray($table, $selectFields) {
-               $additionalFields = array();
-
-               // If all fields are selected anyway, no need to worry
-               if ($selectFields != '*') {
-                       // Check if the table indeed has a TCA and versioning information
-                       if (isset($GLOBALS['TCA'][$table]['ctrl']) && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
-
-                               // In order for versioning to work properly, the version state field is needed
-                               $stateField = 't3ver_state';
-                               $hasStateField = strpos($selectFields, $stateField);
-                               if ($hasStateField === FALSE) {
-                                       // Get the list of fields for the given table
-                                       $tableFields = self::getAllFieldsForTable($table);
-                                       if (isset($tableFields[$stateField])) {
-                                               $additionalFields[] = $stateField;
-                                               $hasStateField = TRUE;
-                                       }
-                               }
-                               // If state field is still missing after that, throw an \Exception
-                               if ($hasStateField === FALSE) {
-                                       throw new \Exception('Fields for versioning were not all available.', 1284473941);
-                               }
-
-                       // The table has no TCA, throw an \Exception
-                       } else {
-                               throw new \Exception('No TCA for table, cannot add versioning fields.', 1284474016);
-                       }
-               }
-               return $additionalFields;
-       }
-
-       /**
-        * Creates language-overlay for records in general (where translation is found in records from the same table).
-        *
-        * This is originally copied from \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay()
-        *
-        * @param string $table Table name
-        * @param array $recordset Full recordset to overlay. Must contain uid, pid and $TCA[$table]['ctrl']['languageField']
-        * @param integer $currentLanguage Uid of the currently selected language in the FE
-        * @param string $overlayMode Overlay mode. If "hideNonTranslated" then records without translation will not be returned un-translated but removed instead.
-        * @param boolean $doVersioning True if workspace preview is on
-        * @return array Returns the full overlaid recordset. If $overlayMode is "hideNonTranslated" then some records may be missing if no translation was found.
-        * @see \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay()
-        */
-       public static function overlayRecordSet($table, $recordset, $currentLanguage, $overlayMode = '', $doVersioning = FALSE) {
-
-               // Test with the first row if uid and pid fields are present
-               $firstRecord = current($recordset);
-               if ($firstRecord !== FALSE && !empty($firstRecord['uid']) && !empty($firstRecord['pid'])) {
-
-                       // Test if the table has a TCA definition
-                       if (isset($GLOBALS['TCA'][$table])) {
-                               $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
-
-                               // Test if the TCA definition includes translation information for the same table
-                               if (isset($tableCtrl['languageField']) && isset($tableCtrl['transOrigPointerField'])) {
-
-                                       // Test with the first row if languageField is present
-                                       if (isset($firstRecord[$tableCtrl['languageField']])) {
-
-                                               // Filter out records that are not in the default or [ALL] language, should there be any
-                                               $filteredRecordset = array();
-                                               foreach ($recordset as $index => $row) {
-                                                       if ($row[$tableCtrl['languageField']] <= 0) {
-                                                               $filteredRecordset[$index] = $row;
-                                                       }
-                                               }
-                                               // Will try to overlay a record only if the sys_language_content value is larger than zero,
-                                               // that is, it is not default or [ALL] language
-                                               if ($currentLanguage > 0) {
-                                                       // Assemble a list of uid's for getting the overlays,
-                                                       // but only from the filtered recordset
-                                                       $uidList = array();
-                                                       foreach ($filteredRecordset as $row) {
-                                                               $uidList[] = $row['uid'];
-                                                       }
-
-                                                       // Get all overlay records
-                                                       $overlays = self::getLocalOverlayRecords($table, $uidList, $currentLanguage, $doVersioning);
-
-                                                       // Now loop on the filtered recordset and try to overlay each record
-                                                       $overlaidRecordset = array();
-                                                       foreach ($recordset as $index => $row) {
-                                                               // If record is already in the right language, keep it as is
-                                                               if ($row[$tableCtrl['languageField']] == $currentLanguage) {
-                                                                       $overlaidRecordset[$index] = $row;
-
-                                                               // Else try to apply an overlay
-                                                               } elseif (isset($overlays[$row['uid']][$row['pid']])) {
-                                                                       $overlaidRecordset[$index] = self::overlaySingleRecord($table, $row, $overlays[$row['uid']][$row['pid']]);
-
-                                                               // No overlay exists, apply relevant translation rules
-                                                               } else {
-                                                                       // Take original record, only if non-translated are not hidden, or if language is [All]
-                                                                       if ($overlayMode != 'hideNonTranslated' || $row[$tableCtrl['languageField']] == -1) {
-                                                                               $overlaidRecordset[$index] = $row;
-                                                                       }
-                                                               }
-                                                       }
-                                                       // Return the overlaid recordset
-                                                       return $overlaidRecordset;
-
-                                               } else {
-                                                       // When default language is displayed, we never want to return a record carrying another language!
-                                                       // Return the filtered recordset
-                                                       return $filteredRecordset;
-                                               }
-
-                                       // Provided recordset does not contain languageField field, return recordset unchanged
-                                       } else {
-                                               return $recordset;
-                                       }
-
-                               // Test if the TCA definition includes translation information for a foreign table
-                               } elseif (isset($tableCtrl['transForeignTable'])) {
-                                       // The foreign table has a TCA structure. We can proceed.
-                                       if (isset($GLOBALS['TCA'][$tableCtrl['transForeignTable']])) {
-                                               $foreignCtrl = $GLOBALS['TCA'][$tableCtrl['transForeignTable']]['ctrl'];
-                                               // Check that the foreign table is indeed the appropriate translation table
-                                               // and also check that the foreign table has all the necessary TCA definitions
-                                               if (!empty($foreignCtrl['transOrigPointerTable']) && $foreignCtrl['transOrigPointerTable'] == $table && !empty($foreignCtrl['transOrigPointerField']) && !empty($foreignCtrl['languageField'])) {
-                                                       // Assemble a list of all uid's of records to translate
-                                                       $uidList = array();
-                                                       foreach ($recordset as $row) {
-                                                               $uidList[] = $row['uid'];
-                                                       }
-
-                                                       // Get all overlay records
-                                                       $overlays = self::getForeignOverlayRecords($tableCtrl['transForeignTable'], $uidList, $currentLanguage, $doVersioning);
-
-                                                       // Now loop on the filtered recordset and try to overlay each record
-                                                       $overlaidRecordset = array();
-                                                       foreach ($recordset as $index => $row) {
-                                                               // An overlay exists, apply it
-                                                               if (isset($overlays[$row['uid']])) {
-                                                                       $overlaidRecordset[$index] = self::overlaySingleRecord($table, $row, $overlays[$row['uid']]);
-
-                                                               // No overlay exists
-                                                               } else {
-                                                                       // Take original record, only if non-translated are not hidden
-                                                                       if ($overlayMode != 'hideNonTranslated') {
-                                                                               $overlaidRecordset[$index] = $row;
-                                                                       }
-                                                               }
-                                                       }
-                                                       // Return the overlaid recordset
-                                                       return $overlaidRecordset;
-
-                                       // The foreign table definition is incomplete, don't perform overlays and
-                                       // return recordset as is
-                                               } else {
-                                                       return $recordset;
-                                               }
-
-                                       // The foreign table has no TCA definition, it's impossible to perform overlays
-                                       // Return recordset as is
-                                       } else {
-                                               return $recordset;
-                                       }
-
-                               // No appropriate language fields defined in TCA, return recordset unchanged
-                               } else {
-                                       return $recordset;
-                               }
-
-                       // No TCA for table, return recordset unchanged
-                       } else {
-                               return $recordset;
-                       }
-               }
-               // Recordset did not contain uid or pid field, return recordset unchanged
-               else {
-                       return $recordset;
-               }
-       }
-
-       /**
-        * Wraps around getLocalOverlayRecords() and getForeignOverlayRecords().
-        *
-        * It makes it possible to use the same call whether translations are in the same table or
-        * in a foreign table. This method dispatches accordingly.
-        *
-        * @param string $table Name of the table for which to fetch the records
-        * @param array $uids Array of all uid's of the original records for which to fetch the translation
-        * @param integer $currentLanguage Uid of the system language to translate to
-        * @param boolean $doVersioning True if versioning overlay must be performed
-        * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
-        */
-       public static function getOverlayRecords($table, $uids, $currentLanguage, $doVersioning = FALSE) {
-               if (is_array($uids) && count($uids) > 0) {
-                       if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
-                               return self::getForeignOverlayRecords($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'], $uids, $currentLanguage, $doVersioning);
-                       } else {
-                               return self::getLocalOverlayRecords($table, $uids, $currentLanguage, $doVersioning);
-                       }
-               } else {
-                       return array();
-               }
-       }
-
-       /**
-        * Retrieves all the records for overlaying other records
-        * when those records are stored in the same table as the originals.
-        *
-        * @param string $table Name of the table for which to fetch the records
-        * @param array $uids Array of all uid's of the original records for which to fetch the translation
-        * @param integer $currentLanguage Uid of the system language to translate to
-        * @param boolean $doVersioning True if versioning overlay must be performed
-        * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
-        */
-       public static function getLocalOverlayRecords($table, $uids, $currentLanguage, $doVersioning = FALSE) {
-               $overlays = array();
-               if (is_array($uids) && count($uids) > 0) {
-                       $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
-                       // Select overlays for all records
-                       $where = $tableCtrl['languageField'] . ' = ' . intval($currentLanguage) .
-                               ' AND ' . $tableCtrl['transOrigPointerField'] . ' IN (' . implode(', ', $uids) . ')';
-                       $enableCondition = self::getEnableFieldsCondition($table);
-                       if (!empty($enableCondition)) {
-                               $where .= ' AND ' . $enableCondition;
-                       }
-                       $rows = self::getDatabaseConnection()->exec_SELECTgetRows(
-                               '*',
-                               $table,
-                               $where
-                       );
-                       // Arrange overlay records according to transOrigPointerField, so that it's easy to relate them to the originals
-                       // This structure is actually a 2-dimensional array, with the pid as the second key
-                       // Because of versioning, there may be several overlays for a given original and matching the pid too
-                       // ensures that we are referring to the correct overlay
-                       foreach ($rows as $row) {
-                               // Perform version overlays, if needed
-                               if ($doVersioning) {
-                                       $GLOBALS['TSFE']->sys_page->versionOL($table, $row);
-                               }
-                               // The versioned record may actually be FALSE if it is meant to be deleted
-                               // in the workspace. To be really clean, unset it.
-                               if ($row !== FALSE) {
-                                       if (!isset($overlays[$row[$tableCtrl['transOrigPointerField']]])) {
-                                               $overlays[$row[$tableCtrl['transOrigPointerField']]] = array();
-                                       }
-                                       $overlays[$row[$tableCtrl['transOrigPointerField']]][$row['pid']] = $row;
-                               }
-                       }
-               }
-               return $overlays;
-       }
-
-       /**
-        * This method is used to retrieve all the records for overlaying other records
-        * when those records are stored in a different table than the originals
-        *
-        * @param string $table Name of the table for which to fetch the records
-        * @param array $uids Array of all uid's of the original records for which to fetch the translation
-        * @param integer $currentLanguage Uid of the system language to translate to
-        * @param boolean $doVersioning True if versioning overlay must be performed
-        * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
-        */
-       public static function getForeignOverlayRecords($table, $uids, $currentLanguage, $doVersioning = FALSE) {
-               $overlays = array();
-               if (is_array($uids) && count($uids) > 0) {
-                       $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
-                       // Select overlays for all records
-                       $where = $tableCtrl['languageField'] . ' = ' . intval($currentLanguage) .
-                               ' AND ' . $tableCtrl['transOrigPointerField'] . ' IN (' . implode(', ', $uids) . ')';
-                       $enableCondition = self::getEnableFieldsCondition($table);
-                       if (!empty($enableCondition)) {
-                               $where .= ' AND ' . $enableCondition;
-                       }
-                       $rows = self::getDatabaseConnection()->exec_SELECTgetRows(
-                               '*',
-                               $table,
-                               $where
-                       );
-                       // Arrange overlay records according to transOrigPointerField, so that it's easy to relate them to the originals
-                       foreach ($rows as $row) {
-                               // Perform version overlays, if needed
-                               if ($doVersioning) {
-                                       $GLOBALS['TSFE']->sys_page->versionOL($table, $row);
-                               }
-                               // The versioned record may actually be FALSE if it is meant to be deleted
-                               // in the workspace. To be really clean, unset it.
-                               if ($row !== FALSE) {
-                                       $overlays[$row[$tableCtrl['transOrigPointerField']]] = $row;
-                               }
-                       }
-               }
-               return $overlays;
-       }
-
-       /**
-        * Takes a record and its overlay and performs the overlay according to active translation rules.
-        *
-        * This piece of code is extracted from \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay().
-        *
-        * @param string $table Name of the table for which the operation is taking place
-        * @param array $record Record to overlay
-        * @param array $overlay Overlay of the record
-        * @return array Overlaid record
-        * @see \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay()
-        */
-       public static function overlaySingleRecord($table, $record, $overlay) {
-               $overlaidRecord = $record;
-               $overlaidRecord['_LOCALIZED_UID'] = $overlay['uid'];
-               foreach ($record as $key => $value) {
-                       if ($key != 'uid' && $key != 'pid' && isset($overlay[$key])) {
-                               $l10mMode = self::getL10nModeForColumn($table, $key);
-                               if (empty($l10mMode)) {
-                                       $overlaidRecord[$key] = $overlay[$key];
-                               } else {
-                                       if ($l10mMode != 'exclude' && ($l10mMode != 'mergeIfNotBlank' || strcmp(trim($overlay[$key]), ''))) {
-                                               $overlaidRecord[$key] = $overlay[$key];
-                                       }
-                               }
-                       }
-               }
-               return $overlaidRecord;
-       }
-
-       /**
-        * Gets the l10n_mode value.
-        *
-        * @param string $table Name of the table being looked up
-        * @param string $column Name of the column being looked up
-        * @return string Value of the l10n_mode property, a blank string if undefined
-        */
-       public static function getL10nModeForColumn($table, $column) {
-               return (isset($GLOBALS['TCA'][$table]['columns'][$column]['l10n_mode'])) ? $GLOBALS['TCA'][$table]['columns'][$column]['l10n_mode'] : '';
-       }
-
-       /**
-        * Returns the global database object.
-        *
-        * @return \TYPO3\CMS\Core\Database\DatabaseConnection
-        */
-       protected static function getDatabaseConnection() {
-               return $GLOBALS['TYPO3_DB'];
-       }
+final class OverlayEngine
+{
+    static public $tableFields = array();
+
+    /**
+     * Gets all the records from a given table, properly overlaid with versions and translations.
+     *
+     * Its parameters are the same as t3lib_db::exec_SELECTquery().
+     * A small difference is that it will take only a single table.
+     * The big difference is that it returns an array of properly overlaid records and not a result pointer.
+     *
+     * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
+     * @param string $fromTable Table from which to select. This is what comes right after "FROM ...". Required value.
+     * @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!
+     * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
+     * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
+     * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string.
+     * @param string $indexField Optional name of a field to use as an index for the result array. Must be included in the list of select fields.
+     * @return array Fully overlaid recordset
+     */
+    public static function getAllRecordsForTable($selectFields, $fromTable, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '', $indexField = '') {
+        // SQL WHERE clause is the base clause passed to the function
+        $where = $whereClause;
+        // Add language condition
+        $condition = self::getLanguageCondition($fromTable);
+        if (!empty($condition)) {
+            if (!empty($where)) {
+                $where .= ' AND ';
+            }
+            $where .= '(' . $condition . ')';
+        }
+        // Add enable fields condition
+        $condition = self::getEnableFieldsCondition($fromTable);
+        if (!empty($condition)) {
+            if (!empty($where)) {
+                $where .= ' AND ';
+            }
+            $where .= '(' . $condition . ')';
+        }
+        // Add workspace condition
+        $condition = self::getVersioningCondition($fromTable);
+        if (!empty($condition)) {
+            if (!empty($where)) {
+                $where .= ' AND ';
+            }
+            $where .= '(' . $condition . ')';
+        }
+
+        // If the language is not default, prepare for overlays
+        $doOverlays = false;
+        if ($GLOBALS['TSFE']->sys_language_content > 0) {
+            // Make sure the list of selected fields includes the necessary language fields
+            // so that language overlays can be gotten properly
+            try {
+                $selectFields = self::selectOverlayFields($fromTable, $selectFields);
+                $doOverlays = true;
+            } catch (\Exception $e) {
+                // If the language fields could not be gotten, avoid overlay process
+                $doOverlays = false;
+            }
+        }
+        // If versioning preview is on, prepare for version overlays
+        $doVersioning = false;
+        if ($GLOBALS['TSFE']->sys_page->versioningPreview) {
+            try {
+                $selectFields = self::selectVersioningFields($fromTable, $selectFields);
+                $doVersioning = true;
+            } catch (\Exception $e) {
+                $doVersioning = false;
+            }
+        }
+
+        // Add base fields (uid, pid) if translations or versioning are activated
+        if ($doOverlays || $doVersioning) {
+            try {
+                $selectFields = self::selectBaseFields($fromTable, $selectFields);
+            } catch (\Exception $e) {
+                // Neither translations nor versioning can happen without uid and pid
+                $doOverlays = false;
+                $doVersioning = false;
+            }
+        }
+
+        // Execute the query itself
+        $records = self::getDatabaseConnection()->exec_SELECTgetRows(
+                $selectFields,
+                $fromTable,
+                $where,
+                $groupBy,
+                $orderBy,
+                $limit,
+                $indexField
+        );
+        // Perform version overlays, if needed
+        if ($doVersioning) {
+            foreach ($records as $index => $aRecord) {
+                $GLOBALS['TSFE']->sys_page->versionOL($fromTable, $aRecord);
+                // The versioned record may actually be FALSE if it is meant to be deleted
+                // in the workspace. To be really clean, unset it.
+                if ($aRecord === false) {
+                    unset($records[$index]);
+                }
+            }
+        }
+
+        // If we have both a uid and a pid field, we can proceed with overlaying the records
+        if ($doOverlays) {
+            $records = self::overlayRecordSet(
+                    $fromTable,
+                    $records,
+                    $GLOBALS['TSFE']->sys_language_content,
+                    $GLOBALS['TSFE']->sys_language_contentOL,
+                    $doVersioning
+            );
+        }
+        return $records;
+    }
+
+    /**
+     * Retrieves a single record from the given table, properly overlaid for version and language.
+     *
+     * NOTE: this method first collects all records and then returns the first one, which makes it possible
+     * to filter out records which may not have a translation, for example. However this is not perfect as the
+     * sorting is still done on the SQL side, so that the first record in a translated language may not be correct
+     * for alphabetical ordering. A sorting on the PHP side could be included in the future, similar to what is
+     * done class tx_dataquery_wrapper (EXT:dataquery).
+     *
+     * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
+     * @param string $fromTable Table from which to select. This is what comes right after "FROM ...". Required value.
+     * @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!
+     * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
+     * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
+     * @return array
+     * @throws \Exception
+     */
+    public static function getSingleRecordForTable($selectFields, $fromTable, $whereClause = '', $groupBy = '', $orderBy = '') {
+        $records = self::getAllRecordsForTable($selectFields, $fromTable, $whereClause, $groupBy, $orderBy);
+        if (count($records) > 0) {
+            return array_shift($records);
+        } else {
+            throw new \Exception('No record found', 1397483854);
+        }
+    }
+
+    /**
+     * This method gets the SQL condition to apply for fetching the proper language
+     * depending on the localization settings in the TCA
+     *
+     * @param string $table True name of the table to assemble the condition for
+     * @param string $alias Alias to use for the table instead of its true name
+     * @return string SQL to add to the WHERE clause (without "AND")
+     */
+    public static function getLanguageCondition($table, $alias = '')
+    {
+        $languageCondition = '';
+        if (empty($alias)) {
+            $alias = $table;
+        }
+
+        // First check if there's actually a TCA for the given table
+        if (isset($GLOBALS['TCA'][$table]['ctrl'])) {
+            $tableCtrlTCA = $GLOBALS['TCA'][$table]['ctrl'];
+
+            // Assemble language condition only if a language field is defined
+            if (!empty($tableCtrlTCA['languageField'])) {
+                if (isset($GLOBALS['TSFE']->sys_language_contentOL) && isset($tableCtrlTCA['transOrigPointerField'])) {
+                    // Default language and "all" language
+                    $languageCondition = $alias . '.' . $tableCtrlTCA['languageField'] . ' IN (0,-1)';
+
+                    // If current language is not default, select elements that exist only for current language
+                    // That means elements that exist for current language but have no parent element
+                    if ($GLOBALS['TSFE']->sys_language_content > 0) {
+                        $languageCondition .= ' OR (' . $alias . '.' . $tableCtrlTCA['languageField'] . " = '" . $GLOBALS['TSFE']->sys_language_content . "' AND " . $alias . '.' . $tableCtrlTCA['transOrigPointerField'] . " = '0')";
+                    }
+                } else {
+                    $languageCondition = $alias . '.' . $tableCtrlTCA['languageField'] . " = '" . $GLOBALS['TSFE']->sys_language_content . "'";
+                }
+            }
+        }
+        return $languageCondition;
+    }
+
+    /**
+     * Returns the condition on enable fields for the given table.
+     *
+     * Basically it calls on the method provided by \TYPO3\CMS\Frontend\Page\PageRepository, but without the " AND " in front.
+     *
+     * @param string $table Name of the table to build the condition for
+     * @param boolean $showHidden Set to TRUE to force the display of hidden records
+     * @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
+     * @return string SQL to add to the WHERE clause (without "AND")
+     * @see \TYPO3\CMS\Frontend\Page\PageRepository::enableFields()
+     */
+    public static function getEnableFieldsCondition($table, $showHidden = false, $ignoreArray = array())
+    {
+        try {
+            $showHidden = $showHidden ? $showHidden : ($table == 'pages' ? $GLOBALS['TSFE']->showHiddenPage : $GLOBALS['TSFE']->showHiddenRecords);
+            $enableCondition = $GLOBALS['TSFE']->sys_page->enableFields($table, $showHidden, $ignoreArray);
+            // If an enable clause was returned, strip the first ' AND '
+            if (!empty($enableCondition)) {
+                $enableCondition = substr($enableCondition, strlen(' AND '));
+            }
+        } catch (\Exception $e) {
+            $enableCondition = '';
+        }
+        return $enableCondition;
+    }
+
+    /**
+     * Assembles the proper condition with regards to versioning/workspaces.
+     *
+     * Explanations on parameter $getOverlaysDirectly:
+     * -----------------------------------------------
+     * The base condition assembled by this method will get the placeholders for new or modified records.
+     * This is normally the right way to do things, since those records are overlaid with their workspace version afterwards.
+     * However if you want this condition as part of a more complicated query implying JOINs,
+     * selecting placeholders will not work as the relationships are normally built with the version overlays
+     * and not the placeholders. In this case it is desirable to select the overlays directly,
+     * which can be achieved by setting $getOverlaysDirectly to TRUE
+     *
+     * NOTE: if this all sounds like gibberish, try reading more about workspaces in "Core API"
+     * (there's also quite some stuff in "Inside TYPO3", but part of it is badly outdated)
+     *
+     * @param string $table True name of the table to build the condition for
+     * @param string $alias Alias to use for the table instead of its true name
+     * @param boolean $getOverlaysDirectly Flag to choose original/placeholder records or overlays (see explanations above)
+     * @return string SQL to add to the WHERE clause (without "AND")
+     */
+    public static function getVersioningCondition($table, $alias = '', $getOverlaysDirectly = false)
+    {
+        $workspaceCondition = '';
+        if (empty($alias)) {
+            $alias = $table;
+        }
+
+        // If the table has some TCA definition, check workspace handling
+        if (isset($GLOBALS['TCA'][$table]['ctrl']) && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
+
+            // Base condition to get only live records
+            // (they get overlaid afterwards in case of preview)
+            $workspaceCondition .= '(' . $alias . '.t3ver_state <= 0 AND ' . $alias . '.t3ver_oid = 0)';
+
+            // Additional conditions when previewing a workspace
+            if ($GLOBALS['TSFE']->sys_page->versioningPreview) {
+                $workspace = intval($GLOBALS['BE_USER']->workspace);
+                // Condition for records that are unmodified but whose parent was modified
+                // (when a parent record is modified, copies of its children are made that refer to the modified parent)
+                $workspaceCondition .= ' OR (' . $alias . '.t3ver_state = 0 AND ' . $alias . '.t3ver_wsid = ' . $workspace . ')';
+                // Choose the version state of records to select based on the $getOverlaysDirectly flag
+                // (see explanations in the phpDoc comment above)
+                $modificationPlaceholderState = 1;
+                $movePlaceholderState = 3;
+                if ($getOverlaysDirectly) {
+                    $modificationPlaceholderState = -1;
+                    $movePlaceholderState = 4;
+                }
+                // Select new records (which exist only in the workspace)
+                // This is achieved by selecting the placeholders, which will be overlaid
+                // with the actual content later when calling t3lib_page::versionOL()
+                $workspaceCondition .= ' OR (' . $alias . '.t3ver_state = ' . $modificationPlaceholderState . ' AND ' . $alias . '.t3ver_wsid = ' . $workspace . ')';
+                // Move-to placeholder
+                $workspaceCondition .= ' OR (' . $alias . '.t3ver_state = ' . $movePlaceholderState . ' AND ' . $alias . '.t3ver_wsid = ' . $workspace . ')';
+            }
+        }
+        return $workspaceCondition;
+    }
+
+    /**
+     * Gets all fields for a given table and stores that list into an internal cache array,
+     * then returns the list of fields.
+     *
+     * @param string $table Name of the table to fetch the fields for
+     * @return array List of fields for the given table
+     */
+    public static function getAllFieldsForTable($table)
+    {
+        if (!isset(self::$tableFields[$table])) {
+            self::$tableFields[$table] = self::getDatabaseConnection()->admin_get_fields($table);
+        }
+        return self::$tableFields[$table];
+    }
+
+    /**
+     * Makes sure that base fields such as uid and pid are included
+     * in the list of selected fields.
+     *
+     * These fields are absolutely necessary when
+     * translations or versioning overlays are being made.
+     * The method throws an \Exception if the fields are not available.
+     *
+     * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
+     * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
+     * @throws \Exception
+     * @return string The modified SELECT string
+     */
+    public static function selectBaseFields($table, $selectFields)
+    {
+        $select = $selectFields;
+        // Don't bother if all fields are selected anyway
+        if ($selectFields != '*') {
+            $hasUidField = false;
+            $hasPidField = false;
+            // Get the list of fields for the given table
+            $tableFields = self::getAllFieldsForTable($table);
+
+            // Add the fields, if available
+            // NOTE: this may add the fields twice if they are already
+            // in the list of selected fields, but that doesn't hurt
+            // It doesn't seem worth making a very precise parsing of the list
+            // of selected fields just to avoid duplicates
+            if (isset($tableFields['uid'])) {
+                $select .= ', ' . $table . '.uid';
+                $hasUidField = true;
+            }
+            if (isset($tableFields['pid'])) {
+                $select .= ', ' . $table . '.pid';
+                $hasPidField = true;
+            }
+            // If one of the fields is still missing after that, throw an \Exception
+            if ($hasUidField === false || $hasPidField === false) {
+                throw new \Exception('Not all base fields are available.', 1284463019);
+            }
+        }
+        return $select;
+    }
+
+    /**
+     * Makes sure that all the fields necessary for proper overlaying are included
+     * in the list of selected fields and exist in the table being queried.
+     *
+     * If not, it lets the \Exception thrown by tx_context::selectOverlayFieldsArray() bubble up.
+     *
+     * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
+     * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
+     * @return string Possibly modified list of fields to select
+     */
+    public static function selectOverlayFields($table, $selectFields)
+    {
+        $additionalFields = self::selectOverlayFieldsArray($table, $selectFields);
+        if (count($additionalFields) > 0) {
+            foreach ($additionalFields as $aField) {
+                $selectFields .= ', ' . $table . '.' . $aField;
+            }
+        }
+        return $selectFields;
+    }
+
+    /**
+     * Checks which fields need to be added to the given list of SELECTed fields
+     * so that language overlays can take place properly.
+     *
+     * If some information is missing, it throws an \Exception.
+     *
+     * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
+     * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
+     * @throws \Exception
+     * @return array List of fields to add
+     */
+    public static function selectOverlayFieldsArray($table, $selectFields)
+    {
+        $additionalFields = array();
+
+        // If all fields are selected anyway, no need to worry
+        if ($selectFields != '*') {
+            // Check if the table indeed has a TCA
+            if (isset($GLOBALS['TCA'][$table]['ctrl'])) {
+
+                // Continue only if table is not using foreign tables for translations
+                // (in this case no additional field is needed) and has a language field
+                if (empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) && !empty($GLOBALS['TCA'][$table]['ctrl']['languageField'])) {
+                    $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
+
+                    // In order to be properly overlaid, a table has to have a given languageField
+                    $hasLanguageField = strpos($selectFields, $languageField);
+                    if ($hasLanguageField === false) {
+                        // Get the list of fields for the given table
+                        $tableFields = self::getAllFieldsForTable($table);
+                        if (isset($tableFields[$languageField])) {
+                            $additionalFields[] = $languageField;
+                            $hasLanguageField = true;
+                        }
+                    }
+                    // If language field is still missing after that, throw an \Exception
+                    if ($hasLanguageField === false) {
+                        throw new \Exception('Language field not available.', 1284463837);
+                    }
+                }
+
+                // The table has no TCA, throw an \Exception
+            } else {
+                throw new \Exception('No TCA for table, cannot add overlay fields.', 1284474025);
+            }
+        }
+        return $additionalFields;
+    }
+
+    /**
+     * Makes sure that all the fields necessary for proper versioning overlays are included
+     * in the list of selected fields and exist in the table being queried.
+     *
+     * If not, it lets the \Exception thrown by tx_context::selectVersioningFieldsArray() bubble up.
+     *
+     * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
+     * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
+     * @return string Possibly modified list of fields to select
+     */
+    public static function selectVersioningFields($table, $selectFields)
+    {
+        $additionalFields = self::selectVersioningFieldsArray($table, $selectFields);
+        if (count($additionalFields) > 0) {
+            foreach ($additionalFields as $aField) {
+                $selectFields .= ', ' . $table . '.' . $aField;
+            }
+        }
+        return $selectFields;
+    }
+
+    /**
+     * Checks which fields need to be added to the given list of SELECTed fields
+     * so that versioning overlays can take place properly.
+     *
+     * If some information is missing, it throws an \Exception.
+     *
+     * @param string $table Table from which to select. This is what comes right after "FROM ...". Required value.
+     * @param string $selectFields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
+     * @throws \Exception
+     * @return array List of fields to add
+     */
+    public static function selectVersioningFieldsArray($table, $selectFields)
+    {
+        $additionalFields = array();
+
+        // If all fields are selected anyway, no need to worry
+        if ($selectFields != '*') {
+            // Check if the table indeed has a TCA and versioning information
+            if (isset($GLOBALS['TCA'][$table]['ctrl']) && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
+
+                // In order for versioning to work properly, the version state field is needed
+                $stateField = 't3ver_state';
+                $hasStateField = strpos($selectFields, $stateField);
+                if ($hasStateField === false) {
+                    // Get the list of fields for the given table
+                    $tableFields = self::getAllFieldsForTable($table);
+                    if (isset($tableFields[$stateField])) {
+                        $additionalFields[] = $stateField;
+                        $hasStateField = true;
+                    }
+                }
+                // If state field is still missing after that, throw an \Exception
+                if ($hasStateField === false) {
+                    throw new \Exception('Fields for versioning were not all available.', 1284473941);
+                }
+
+                // The table has no TCA, throw an \Exception
+            } else {
+                throw new \Exception('No TCA for table, cannot add versioning fields.', 1284474016);
+            }
+        }
+        return $additionalFields;
+    }
+
+    /**
+     * Creates language-overlay for records in general (where translation is found in records from the same table).
+     *
+     * This is originally copied from \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay()
+     *
+     * @param string $table Table name
+     * @param array $recordset Full recordset to overlay. Must contain uid, pid and $TCA[$table]['ctrl']['languageField']
+     * @param integer $currentLanguage Uid of the currently selected language in the FE
+     * @param string $overlayMode Overlay mode. If "hideNonTranslated" then records without translation will not be returned un-translated but removed instead.
+     * @param boolean $doVersioning True if workspace preview is on
+     * @return array Returns the full overlaid recordset. If $overlayMode is "hideNonTranslated" then some records may be missing if no translation was found.
+     * @see \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay()
+     */
+    public static function overlayRecordSet(
+            $table,
+            $recordset,
+            $currentLanguage,
+            $overlayMode = '',
+            $doVersioning = false
+    ) {
+
+        // Test with the first row if uid and pid fields are present
+        $firstRecord = current($recordset);
+        if ($firstRecord !== false && !empty($firstRecord['uid']) && !empty($firstRecord['pid'])) {
+
+            // Test if the table has a TCA definition
+            if (isset($GLOBALS['TCA'][$table])) {
+                $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
+
+                // Test if the TCA definition includes translation information for the same table
+                if (isset($tableCtrl['languageField']) && isset($tableCtrl['transOrigPointerField'])) {
+
+                    // Test with the first row if languageField is present
+                    if (isset($firstRecord[$tableCtrl['languageField']])) {
+
+                        // Filter out records that are not in the default or [ALL] language, should there be any
+                        $filteredRecordset = array();
+                        foreach ($recordset as $index => $row) {
+                            if ($row[$tableCtrl['languageField']] <= 0) {
+                                $filteredRecordset[$index] = $row;
+                            }
+                        }
+                        // Will try to overlay a record only if the sys_language_content value is larger than zero,
+                        // that is, it is not default or [ALL] language
+                        if ($currentLanguage > 0) {
+                            // Assemble a list of uid's for getting the overlays,
+                            // but only from the filtered recordset
+                            $uidList = array();
+                            foreach ($filteredRecordset as $row) {
+                                $uidList[] = $row['uid'];
+                            }
+
+                            // Get all overlay records
+                            $overlays = self::getLocalOverlayRecords($table, $uidList, $currentLanguage, $doVersioning);
+
+                            // Now loop on the filtered recordset and try to overlay each record
+                            $overlaidRecordset = array();
+                            foreach ($recordset as $index => $row) {
+                                // If record is already in the right language, keep it as is
+                                if ($row[$tableCtrl['languageField']] == $currentLanguage) {
+                                    $overlaidRecordset[$index] = $row;
+
+                                    // Else try to apply an overlay
+                                } elseif (isset($overlays[$row['uid']][$row['pid']])) {
+                                    $overlaidRecordset[$index] = self::overlaySingleRecord($table, $row,
+                                            $overlays[$row['uid']][$row['pid']]);
+
+                                    // No overlay exists, apply relevant translation rules
+                                } else {
+                                    // Take original record, only if non-translated are not hidden, or if language is [All]
+                                    if ($overlayMode != 'hideNonTranslated' || $row[$tableCtrl['languageField']] == -1) {
+                                        $overlaidRecordset[$index] = $row;
+                                    }
+                                }
+                            }
+                            // Return the overlaid recordset
+                            return $overlaidRecordset;
+
+                        } else {
+                            // When default language is displayed, we never want to return a record carrying another language!
+                            // Return the filtered recordset
+                            return $filteredRecordset;
+                        }
+
+                        // Provided recordset does not contain languageField field, return recordset unchanged
+                    } else {
+                        return $recordset;
+                    }
+
+                    // Test if the TCA definition includes translation information for a foreign table
+                } elseif (isset($tableCtrl['transForeignTable'])) {
+                    // The foreign table has a TCA structure. We can proceed.
+                    if (isset($GLOBALS['TCA'][$tableCtrl['transForeignTable']])) {
+                        $foreignCtrl = $GLOBALS['TCA'][$tableCtrl['transForeignTable']]['ctrl'];
+                        // Check that the foreign table is indeed the appropriate translation table
+                        // and also check that the foreign table has all the necessary TCA definitions
+                        if (!empty($foreignCtrl['transOrigPointerTable']) && $foreignCtrl['transOrigPointerTable'] == $table && !empty($foreignCtrl['transOrigPointerField']) && !empty($foreignCtrl['languageField'])) {
+                            // Assemble a list of all uid's of records to translate
+                            $uidList = array();
+                            foreach ($recordset as $row) {
+                                $uidList[] = $row['uid'];
+                            }
+
+                            // Get all overlay records
+                            $overlays = self::getForeignOverlayRecords($tableCtrl['transForeignTable'], $uidList,
+                                    $currentLanguage, $doVersioning);
+
+                            // Now loop on the filtered recordset and try to overlay each record
+                            $overlaidRecordset = array();
+                            foreach ($recordset as $index => $row) {
+                                // An overlay exists, apply it
+                                if (isset($overlays[$row['uid']])) {
+                                    $overlaidRecordset[$index] = self::overlaySingleRecord($table, $row,
+                                            $overlays[$row['uid']]);
+
+                                    // No overlay exists
+                                } else {
+                                    // Take original record, only if non-translated are not hidden
+                                    if ($overlayMode != 'hideNonTranslated') {
+                                        $overlaidRecordset[$index] = $row;
+                                    }
+                                }
+                            }
+                            // Return the overlaid recordset
+                            return $overlaidRecordset;
+
+                            // The foreign table definition is incomplete, don't perform overlays and
+                            // return recordset as is
+                        } else {
+                            return $recordset;
+                        }
+
+                        // The foreign table has no TCA definition, it's impossible to perform overlays
+                        // Return recordset as is
+                    } else {
+                        return $recordset;
+                    }
+
+                    // No appropriate language fields defined in TCA, return recordset unchanged
+                } else {
+                    return $recordset;
+                }
+
+                // No TCA for table, return recordset unchanged
+            } else {
+                return $recordset;
+            }
+        } // Recordset did not contain uid or pid field, return recordset unchanged
+        else {
+            return $recordset;
+        }
+    }
+
+    /**
+     * Wraps around getLocalOverlayRecords() and getForeignOverlayRecords().
+     *
+     * It makes it possible to use the same call whether translations are in the same table or
+     * in a foreign table. This method dispatches accordingly.
+     *
+     * @param string $table Name of the table for which to fetch the records
+     * @param array $uids Array of all uid's of the original records for which to fetch the translation
+     * @param integer $currentLanguage Uid of the system language to translate to
+     * @param boolean $doVersioning True if versioning overlay must be performed
+     * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
+     */
+    public static function getOverlayRecords($table, $uids, $currentLanguage, $doVersioning = false)
+    {
+        if (is_array($uids) && count($uids) > 0) {
+            if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
+                return self::getForeignOverlayRecords($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'], $uids,
+                        $currentLanguage, $doVersioning);
+            } else {
+                return self::getLocalOverlayRecords($table, $uids, $currentLanguage, $doVersioning);
+            }
+        } else {
+            return array();
+        }
+    }
+
+    /**
+     * Retrieves all the records for overlaying other records
+     * when those records are stored in the same table as the originals.
+     *
+     * @param string $table Name of the table for which to fetch the records
+     * @param array $uids Array of all uid's of the original records for which to fetch the translation
+     * @param integer $currentLanguage Uid of the system language to translate to
+     * @param boolean $doVersioning True if versioning overlay must be performed
+     * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
+     */
+    public static function getLocalOverlayRecords($table, $uids, $currentLanguage, $doVersioning = false)
+    {
+        $overlays = array();
+        if (is_array($uids) && count($uids) > 0) {
+            $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
+            // Select overlays for all records
+            $where = $tableCtrl['languageField'] . ' = ' . intval($currentLanguage) .
+                    ' AND ' . $tableCtrl['transOrigPointerField'] . ' IN (' . implode(', ', $uids) . ')';
+            $enableCondition = self::getEnableFieldsCondition($table);
+            if (!empty($enableCondition)) {
+                $where .= ' AND ' . $enableCondition;
+            }
+            $rows = self::getDatabaseConnection()->exec_SELECTgetRows(
+                    '*',
+                    $table,
+                    $where
+            );
+            // Arrange overlay records according to transOrigPointerField, so that it's easy to relate them to the originals
+            // This structure is actually a 2-dimensional array, with the pid as the second key
+            // Because of versioning, there may be several overlays for a given original and matching the pid too
+            // ensures that we are referring to the correct overlay
+            foreach ($rows as $row) {
+                // Perform version overlays, if needed
+                if ($doVersioning) {
+                    $GLOBALS['TSFE']->sys_page->versionOL($table, $row);
+                }
+                // The versioned record may actually be FALSE if it is meant to be deleted
+                // in the workspace. To be really clean, unset it.
+                if ($row !== false) {
+                    if (!isset($overlays[$row[$tableCtrl['transOrigPointerField']]])) {
+                        $overlays[$row[$tableCtrl['transOrigPointerField']]] = array();
+                    }
+                    $overlays[$row[$tableCtrl['transOrigPointerField']]][$row['pid']] = $row;
+                }
+            }
+        }
+        return $overlays;
+    }
+
+    /**
+     * This method is used to retrieve all the records for overlaying other records
+     * when those records are stored in a different table than the originals
+     *
+     * @param string $table Name of the table for which to fetch the records
+     * @param array $uids Array of all uid's of the original records for which to fetch the translation
+     * @param integer $currentLanguage Uid of the system language to translate to
+     * @param boolean $doVersioning True if versioning overlay must be performed
+     * @return array All overlay records arranged per original uid and per pid, so that they can be checked (this is related to workspaces)
+     */
+    public static function getForeignOverlayRecords($table, $uids, $currentLanguage, $doVersioning = false)
+    {
+        $overlays = array();
+        if (is_array($uids) && count($uids) > 0) {
+            $tableCtrl = $GLOBALS['TCA'][$table]['ctrl'];
+            // Select overlays for all records
+            $where = $tableCtrl['languageField'] . ' = ' . intval($currentLanguage) .
+                    ' AND ' . $tableCtrl['transOrigPointerField'] . ' IN (' . implode(', ', $uids) . ')';
+            $enableCondition = self::getEnableFieldsCondition($table);
+            if (!empty($enableCondition)) {
+                $where .= ' AND ' . $enableCondition;
+            }
+            $rows = self::getDatabaseConnection()->exec_SELECTgetRows(
+                    '*',
+                    $table,
+                    $where
+            );
+            // Arrange overlay records according to transOrigPointerField, so that it's easy to relate them to the originals
+            foreach ($rows as $row) {
+                // Perform version overlays, if needed
+                if ($doVersioning) {
+                    $GLOBALS['TSFE']->sys_page->versionOL($table, $row);
+                }
+                // The versioned record may actually be FALSE if it is meant to be deleted
+                // in the workspace. To be really clean, unset it.
+                if ($row !== false) {
+                    $overlays[$row[$tableCtrl['transOrigPointerField']]] = $row;
+                }
+            }
+        }
+        return $overlays;
+    }
+
+    /**
+     * Takes a record and its overlay and performs the overlay according to active translation rules.
+     *
+     * This piece of code is extracted from \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay().
+     *
+     * @param string $table Name of the table for which the operation is taking place
+     * @param array $record Record to overlay
+     * @param array $overlay Overlay of the record
+     * @return array Overlaid record
+     * @see \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay()
+     */
+    public static function overlaySingleRecord($table, $record, $overlay)
+    {
+        $overlaidRecord = $record;
+        $overlaidRecord['_LOCALIZED_UID'] = $overlay['uid'];
+        foreach ($record as $key => $value) {
+            if ($key != 'uid' && $key != 'pid' && isset($overlay[$key])) {
+                $l10mMode = self::getL10nModeForColumn($table, $key);
+                if (empty($l10mMode)) {
+                    $overlaidRecord[$key] = $overlay[$key];
+                } else {
+                    if ($l10mMode != 'exclude' && ($l10mMode != 'mergeIfNotBlank' || strcmp(trim($overlay[$key]),
+                                            ''))
+                    ) {
+                        $overlaidRecord[$key] = $overlay[$key];
+                    }
+                }
+            }
+        }
+        return $overlaidRecord;
+    }
+
+    /**
+     * Gets the l10n_mode value.
+     *
+     * @param string $table Name of the table being looked up
+     * @param string $column Name of the column being looked up
+     * @return string Value of the l10n_mode property, a blank string if undefined
+     */
+    public static function getL10nModeForColumn($table, $column)
+    {
+        return (isset($GLOBALS['TCA'][$table]['columns'][$column]['l10n_mode'])) ? $GLOBALS['TCA'][$table]['columns'][$column]['l10n_mode'] : '';
+    }
+
+    /**
+     * Returns the global database object.
+     *
+     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
+     */
+    protected static function getDatabaseConnection()
+    {
+        return $GLOBALS['TYPO3_DB'];
+    }
 }
index c3bf7e6..c3adbe7 100644 (file)
@@ -28,7 +28,7 @@ Improved overlays
         overlays, language, versioning, api
 
     :Copyright:
-        2008-2014
+        2008-2016
 
     :Author:
         Fran├žois Suter
index a99f978..b0c43b2 100644 (file)
@@ -4,8 +4,8 @@
 
 ---
 conf.py:
-  copyright: 2008-2015
+  copyright: 2008-2016
   project: Improved overlays
   version: 3.0
-  release: 3.0.0-dev
+  release: 3.0.0
 ...
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..95d36a7
--- /dev/null
@@ -0,0 +1,345 @@
+Some icons used in the TYPO3 project are retrieved from the "Silk" icon set of
+Mark James, which can be found at http://famfamfam.com/lab/icons/silk/. This
+set is distributed under a Creative Commons Attribution 2.5 License. The
+license can be found at http://creativecommons.org/licenses/by/2.5/.
+---------------------------------
+
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
index d516e8b..b909644 100644 (file)
@@ -1,13 +1,25 @@
 {
        "name": "cobweb/overlays",
        "type": "typo3-cms-extension",
-       "description": "A library for bringing into extensions something like TypoScript's getText function.",
-       "license": ["GPL-2.0+"],
+       "description": "This extension provides an easy to use API for retrieving properly localized records. It also takes care of keeping the DB calls to a minimum.",
+       "license": [
+               "GPL-2.0+"
+       ],
+       "authors": [
+               {
+                       "name": "Francois Suter",
+                       "role": "Developer"
+               }
+       ],
        "autoload": {
                "psr-4": {
                        "Cobweb\\Overlays\\": "Classes/"
                }
        },
+       "version": "3.0.0",
+       "require": {
+               "typo3/cms-core": ">=7.0.0,<8.0"
+       },
        "replace": {
                "overlays": "self.version",
                "typo3-ter/overlays": "self.version"
index 6205b89..1ae1ed0 100644 (file)
@@ -1,12 +1,14 @@
 <?php
 
-/*********************************************************************
-* Extension configuration file for ext "overlays".
-*
-* Generated by ext 09-05-2014 15:56 UTC
-*
-* https://github.com/t3elmar/Ext
-*********************************************************************/
+/***************************************************************
+ * Extension Manager/Repository config file for ext "overlays".
+ *
+ * Auto generated 12-04-2016 10:30
+ *
+ * Manual updates:
+ * Only the data in the array - everything else is removed by next
+ * writing. "version" and "dependencies" must not be touched!
+ ***************************************************************/
 
 $EM_CONF[$_EXTKEY] = array (
   'title' => 'Improved overlays',
@@ -14,39 +16,27 @@ $EM_CONF[$_EXTKEY] = array (
   'category' => 'fe',
   'author' => 'Francois Suter (Cobweb)',
   'author_email' => 'typo3@cobweb.ch',
-  'shy' => '',
-  'dependencies' => '',
-  'conflicts' => '',
-  'priority' => '',
-  'module' => '',
   'state' => 'stable',
-  'internal' => '',
   'uploadfolder' => 0,
   'createDirs' => '',
-  'modify_tables' => '',
   'clearCacheOnLoad' => 0,
-  'lockType' => '',
   'author_company' => '',
-  'version' => '3.0.0-dev',
-  'constraints' =>
+  'version' => '3.0.0',
+  'constraints' => 
   array (
-    'depends' =>
+    'depends' => 
     array (
-      'typo3' => '6.2.0-7.99.99',
+      'typo3' => '7.0.0-7.99.99',
     ),
-    'conflicts' =>
+    'conflicts' => 
     array (
     ),
-    'suggests' =>
+    'suggests' => 
     array (
     ),
   ),
-  '_md5_values_when_last_written' => 'a:7:{s:9:"ChangeLog";s:4:"6aac";s:21:"class.tx_overlays.php";s:4:"192f";s:16:"ext_autoload.php";s:4:"ad6d";s:12:"ext_icon.gif";s:4:"73e0";s:10:"README.txt";s:4:"b729";s:14:"doc/manual.pdf";s:4:"f295";s:14:"doc/manual.sxw";s:4:"622c";}',
-  'suggests' =>
-  array (
-  ),
-  'comment' => 'Verified compatibility with TYPO3 CMS 6.2; added API for getting single record.',
+  '_md5_values_when_last_written' => 'a:17:{s:9:"ChangeLog";s:4:"ee59";s:11:"LICENSE.txt";s:4:"6404";s:10:"README.txt";s:4:"b729";s:13:"composer.json";s:4:"1f84";s:12:"ext_icon.png";s:4:"2caf";s:25:"Classes/OverlayEngine.php";s:4:"5c01";s:26:"Documentation/Includes.txt";s:4:"6d5f";s:23:"Documentation/Index.rst";s:4:"01ed";s:26:"Documentation/Settings.yml";s:4:"f93a";s:33:"Documentation/Developer/Index.rst";s:4:"5edb";s:37:"Documentation/Developer/Api/Index.rst";s:4:"7cd9";s:46:"Documentation/Developer/Introduction/Index.rst";s:4:"ab74";s:48:"Documentation/Developer/OverlayRecords/Index.rst";s:4:"308e";s:36:"Documentation/Installation/Index.rst";s:4:"9002";s:36:"Documentation/Introduction/Index.rst";s:4:"5c7e";s:37:"Documentation/KnownProblems/Index.rst";s:4:"d9c8";s:35:"Documentation/Limitations/Index.rst";s:4:"249b";}',
+  'comment' => 'Verified compatibility with TYPO3 CMS 7 LTS; moved to namespaces.',
   'user' => 'francois',
 );
 
-?>
\ No newline at end of file
index f47a929..e8d79e5 100644 (file)
Binary files a/ext_icon.png and b/ext_icon.png differ