[FEATURE] Allow exclusion of hidden records on export 60/45960/9
authorLudwig Rafelsberger <ludwig.rafelsberger@gmx.at>
Fri, 15 Jan 2016 17:37:31 +0000 (18:37 +0100)
committerMorton Jonuschat <m.jonuschat@mojocode.de>
Sun, 17 Jan 2016 10:29:08 +0000 (11:29 +0100)
Allow editors to exclude all disabled records during export
preparation (EXT:impexp).

When preparing to export a page tree, users can now:
- choose to exclude all disabled records recursively. This is the new
  default behaviour
- toggle all disabled records which are scheduled to be exported
  (convenience method, this was already possible by manually checking
  all these records.

Resolves: #19157
Releases: master
Change-Id: Ibb3534151a9d08cf4a60b54430678563d6feb5be
Reviewed-on: https://review.typo3.org/45960
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Reinhard Führicht <rf@typoheads.at>
Tested-by: Reinhard Führicht <rf@typoheads.at>
Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de>
typo3/sysext/core/Documentation/Changelog/master/Feature-19157-impexpCouldHaveAnOptionToExcludeAllHiddenRecords.rst [new file with mode: 0644]
typo3/sysext/impexp/Classes/Controller/ImportExportController.php
typo3/sysext/impexp/Classes/Export.php
typo3/sysext/impexp/Classes/ImportExport.php
typo3/sysext/impexp/Resources/Private/Language/locallang.xlf
typo3/sysext/impexp/Resources/Private/Partials/ContentOverview.html
typo3/sysext/impexp/Resources/Private/Partials/Export/Configuration.html
typo3/sysext/impexp/Resources/Public/JavaScript/ImportExport.js

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-19157-impexpCouldHaveAnOptionToExcludeAllHiddenRecords.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-19157-impexpCouldHaveAnOptionToExcludeAllHiddenRecords.rst
new file mode 100644 (file)
index 0000000..7cb0916
--- /dev/null
@@ -0,0 +1,14 @@
+========================================================================
+Feature: #19157 - Add option to exclude all hidden records in EXT:impexp
+========================================================================
+
+Description
+===========
+
+The export configuration of EXT:impexp has been extended to allow to
+completely deactivate exporting of hidden/deactivated records. This
+behaviour can be controlled via a new option which is checked by default.
+
+Furthermore, if the inclusion of hidden records is activated (which is
+now an explicit choice), then an additional button is shown, allowing
+users to preselect all hidden records for manual exclusion.
index df846ca..1a9f380 100644 (file)
@@ -34,12 +34,12 @@ use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\File\ExtendedFileUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
+use TYPO3\CMS\Fluid\View\StandaloneView;
 use TYPO3\CMS\Impexp\Domain\Repository\PresetRepository;
 use TYPO3\CMS\Impexp\Export;
 use TYPO3\CMS\Impexp\Import;
 use TYPO3\CMS\Impexp\View\ExportPageTreeView;
 use TYPO3\CMS\Lang\LanguageService;
-use TYPO3\CMS\Fluid\View\StandaloneView;
 
 /**
  * Main script class for the Import / Export facility
@@ -127,6 +127,11 @@ class ImportExportController extends BaseScriptClass
     protected $standaloneView = null;
 
     /**
+     * @var bool
+     */
+    protected $excludeDisabledRecords = false;
+
+    /**
      * Constructor
      */
     public function __construct()
@@ -185,6 +190,10 @@ class ImportExportController extends BaseScriptClass
 
         // Input data grabbed:
         $inData = GeneralUtility::_GP('tx_impexp');
+        if (!array_key_exists('excludeDisabled', $inData)) {
+            // flag doesn't exist initially; state is on by default
+            $inData['excludeDisabled'] = 1;
+        }
         $this->standaloneView->assign('moduleUrl', BackendUtility::getModuleUrl('xMOD_tximpexp'));
         $this->standaloneView->assign('id', $this->id);
         $this->standaloneView->assign('inData', $inData);
@@ -343,6 +352,8 @@ class ImportExportController extends BaseScriptClass
         $this->export->extensionDependencies = (array)$inData['extension_dep'];
         $this->export->showStaticRelations = $inData['showStaticRelations'];
         $this->export->includeExtFileResources = !$inData['excludeHTMLfileResources'];
+        $this->excludeDisabledRecords = (bool)$inData['excludeDisabled'];
+        $this->export->setExcludeDisabledRecords($this->excludeDisabledRecords);
 
         // Static tables:
         if (is_array($inData['external_static']['tables'])) {
@@ -396,6 +407,9 @@ class ImportExportController extends BaseScriptClass
             $idH = null;
             if ($inData['pagetree']['levels'] == -1) {
                 $pagetree = GeneralUtility::makeInstance(ExportPageTreeView::class);
+                if ($this->excludeDisabledRecords) {
+                    $pagetree->init(BackendUtility::BEenableFields('pages'));
+                }
                 $tree = $pagetree->ext_tree($inData['pagetree']['id'], $this->filterPageIds($this->export->excludeMap));
                 $this->treeHTML = $pagetree->printTree($tree);
                 $idH = $pagetree->buffer_idH;
@@ -416,7 +430,11 @@ class ImportExportController extends BaseScriptClass
                 if (is_array($sPage)) {
                     $pid = $inData['pagetree']['id'];
                     $tree = GeneralUtility::makeInstance(PageTreeView::class);
-                    $tree->init('AND ' . $this->perms_clause . $this->filterPageIds($this->export->excludeMap));
+                    $initClause = 'AND ' . $this->perms_clause . $this->filterPageIds($this->export->excludeMap);
+                    if ($this->excludeDisabledRecords) {
+                        $initClause .= BackendUtility::BEenableFields('pages');
+                    }
+                    $tree->init($initClause);
                     $HTML = $this->iconFactory->getIconForRecord('pages', $sPage, Icon::SIZE_SMALL)->render();
                     $tree->tree[] = array('row' => $sPage, 'HTML' => $HTML);
                     $tree->buffer_idH = array();
@@ -539,7 +557,10 @@ class ImportExportController extends BaseScriptClass
         $this->standaloneView->assign('errors', $this->export->errorLog);
 
         // Generate overview:
-        $this->standaloneView->assign('contentOverview', $this->export->displayContentOverview());
+        $this->standaloneView->assign(
+            'contentOverview',
+            $this->export->displayContentOverview()
+        );
     }
 
     /**
@@ -583,10 +604,15 @@ class ImportExportController extends BaseScriptClass
         $orderBy = $GLOBALS['TCA'][$table]['ctrl']['sortby']
             ? 'ORDER BY ' . $GLOBALS['TCA'][$table]['ctrl']['sortby']
             : $GLOBALS['TCA'][$table]['ctrl']['default_sortby'];
+
+        $whereClause = 'pid=' . (int)$pid . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table);
+        if ($this->excludeDisabledRecords) {
+            $whereClause .= BackendUtility::BEenableFields($table);
+        }
         $res = $db->exec_SELECTquery(
             '*',
             $table,
-            'pid=' . (int)$pid . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table),
+            $whereClause,
             '',
             $db->stripOrderBy($orderBy),
             $limit
index 66ad7c6..1ebfaee 100644 (file)
@@ -307,6 +307,9 @@ class Export extends ImportExport
     public function export_addRecord($table, $row, $relationLevel = 0)
     {
         BackendUtility::workspaceOL($table, $row);
+        if ($this->excludeDisabledRecords && !$this->isActive($table, $row['uid'])) {
+            return;
+        }
         if ((string)$table !== '' && is_array($row) && $row['uid'] > 0 && !$this->excludeMap[$table . ':' . $row['uid']]) {
             if ($this->checkPID($table === 'pages' ? $row['uid'] : $row['pid'])) {
                 if (!isset($this->dat['records'][$table . ':' . $row['uid']])) {
index 2ba411c..4f75bcb 100644 (file)
@@ -253,6 +253,14 @@ abstract class ImportExport
     protected $iconFactory;
 
     /**
+     * Flag to control whether all disabled records and their children are excluded (true) or included (false). Defaults
+     * to the old behaviour of including everything.
+     *
+     * @var bool
+     */
+    protected $excludeDisabledRecords = false;
+
+    /**
      * The constructor
      */
     public function __construct()
@@ -305,7 +313,7 @@ abstract class ImportExport
             if (is_array($this->dat['header']['pagetree'])) {
                 reset($this->dat['header']['pagetree']);
                 $lines = array();
-                $this->traversePageTree($this->dat['header']['pagetree'], $lines);
+                $this->traversePageTree($this->dat['header']['pagetree'], $lines, '');
 
                 $viewData['dat'] = $this->dat;
                 $viewData['update'] = $this->update;
@@ -353,6 +361,11 @@ abstract class ImportExport
     public function traversePageTree($pT, &$lines, $preCode = '')
     {
         foreach ($pT as $k => $v) {
+            if ($this->excludeDisabledRecords === true && !$this->isActive('pages', $k)) {
+                $this->excludePageAndRecords($k, $v);
+                continue;
+            }
+
             // Add this page:
             $this->singleRecordLines('pages', $k, $lines, $preCode);
             // Subrecords:
@@ -374,6 +387,53 @@ abstract class ImportExport
     }
 
     /**
+     * Test whether a record is active (i.e. not hidden)
+     *
+     * @param string $table Name of the records' database table
+     * @param int $uid Database uid of the record
+     * @return bool true if the record is active, false otherwise
+     */
+    protected function isActive($table, $uid)
+    {
+        return
+            !isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'])
+            || !(bool)$this->dat['records'][$table . ':' . $uid]['data'][
+                $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['disabled']
+            ];
+    }
+
+    /**
+     * Exclude a page, its sub pages (recursively) and records placed in them from this import/export
+     *
+     * @param int $pageUid Uid of the page to exclude
+     * @param array $pageTree Page tree array with uid/subrow (from ->dat[header][pagetree]
+     * @return void
+     */
+    protected function excludePageAndRecords($pageUid, $pageTree)
+    {
+        // Prevent having this page appear in "remaining records" table
+        unset($this->remainHeader['records']['pages'][$pageUid]);
+
+        // Subrecords
+        if (is_array($this->dat['header']['pid_lookup'][$pageUid])) {
+            foreach ($this->dat['header']['pid_lookup'][$pageUid] as $table => $recordData) {
+                if ($table != 'pages') {
+                    foreach (array_keys($recordData) as $uid) {
+                        unset($this->remainHeader['records'][$table][$uid]);
+                    }
+                }
+            }
+            unset($this->remainHeader['pid_lookup'][$pageUid]);
+        }
+        // Subpages excluded recursively
+        if (is_array($pageTree['subrow'])) {
+            foreach ($pageTree['subrow'] as $subPageUid => $subPageTree) {
+                $this->excludePageAndRecords($subPageUid, $subPageTree);
+            }
+        }
+    }
+
+    /**
      * Go through remaining pages (not in tree)
      *
      * @param array $pT Page tree array with uid/subrow (from ->dat[header][pagetree]
@@ -466,6 +526,9 @@ abstract class ImportExport
             $pInfo['msg'] = 'UNKNOWN TABLE \'' . $pInfo['ref'] . '\'';
             $pInfo['title'] = '<em>' . htmlspecialchars($record['title']) . '</em>';
         } else {
+            // prepare data attribute telling whether the record is active or hidden, allowing frontend bulk selection
+            $pInfo['active'] = $this->isActive($table, $uid) ? 'active' : 'hidden';
+
             // Otherwise, set table icon and title.
             // Import Validation (triggered by $this->display_import_pid_record) will show messages if import is not possible of various items.
             if (is_array($this->display_import_pid_record) && !empty($this->display_import_pid_record)) {
@@ -805,7 +868,7 @@ abstract class ImportExport
     {
         if ($this->mode === 'export') {
             if ($r['type'] === 'record') {
-                return '<input type="checkbox" name="tx_impexp[exclude][' . $r['ref'] . ']" id="checkExclude' . $r['ref'] . '" value="1" /> <label for="checkExclude' . $r['ref'] . '">' . $this->getLanguageService()->getLL('impexpcore_singlereco_exclude', true) . '</label>';
+                return '<input type="checkbox" class="t3js-exclude-checkbox" name="tx_impexp[exclude][' . $r['ref'] . ']" id="checkExclude' . $r['ref'] . '" value="1" /> <label for="checkExclude' . $r['ref'] . '">' . $this->getLanguageService()->getLL('impexpcore_singlereco_exclude', true) . '</label>';
             } else {
                 return  $r['type'] == 'softref' ? $this->softrefSelector($r['_softRefInfo']) : '';
             }
@@ -1182,6 +1245,19 @@ abstract class ImportExport
         }
     }
 
+    /**
+     * Set flag to control whether disabled records and their children are excluded (true) or included (false). Defaults
+     * to the old behaviour of including everything.
+     *
+     * @param bool $excludeDisabledRecords Set to true if if all disabled records should be excluded, false otherwise
+     * @return \TYPO3\CMS\Impexp\ImportExport $this for fluent calls
+     */
+    public function setExcludeDisabledRecords($excludeDisabledRecords = false)
+    {
+        $this->excludeDisabledRecords = $excludeDisabledRecords;
+        return $this;
+    }
+
     /*****************************
      * Error handling
      *****************************/
@@ -1230,5 +1306,4 @@ abstract class ImportExport
     {
         return $GLOBALS['LANG'];
     }
-
 }
index 4cbd983..818ae8d 100644 (file)
@@ -45,6 +45,9 @@
                        <trans-unit id="execlistqu_structureToBeExported">
                                <source>Structure to be exported:</source>
                        </trans-unit>
+                       <trans-unit id="impexpcore_toggle_all_disabled_records">
+                               <source>Toggle disabled records</source>
+                       </trans-unit>
                        <trans-unit id="execlistqu_maxNumberLimit">
                                <source>Max number limit!</source>
                        </trans-unit>
                        <trans-unit id="makeconfig_excludeElements">
                                <source>Exclude elements:</source>
                        </trans-unit>
-                       <trans-unit id="makeconfig_clearAllExclusions">
-                               <source>Clear all exclusions:</source>
+                       <trans-unit id="makeconfig_clearAllManualExclusions">
+                               <source>Clear all manual exclusions:</source>
+                       </trans-unit>
+                       <trans-unit id="makeconfig_noManuallyExcludedElementsYet">
+                               <source>No manually excluded elements yet. Exclude by setting checkboxes below in the element display.</source>
                        </trans-unit>
-                       <trans-unit id="makeconfig_noExcludedElementsYet">
-                               <source>No excluded elements yet. Exclude by setting checkboxes below in the element display.</source>
+                       <trans-unit id="makeconfig_excludeDisabledElements">
+                               <source>Exclude disabled elements</source>
                        </trans-unit>
                        <trans-unit id="makeadvanc_update">
                                <source>Update</source>
index 0026384..d877488 100644 (file)
        </f:else>
 </f:if>
 <div>
+       <f:if condition="{inData.excludeDisabled}">
+               <f:else>
+                       <f:if condition="{inData.action} == 'export'">
+                               <f:form.button class="btn btn-default t3js-impexp-toggledisabled" type="button">
+                                       <f:translate key="impexpcore_toggle_all_disabled_records" />
+                               </f:form.button>
+                       </f:if>
+               </f:else>
+       </f:if>
        <f:if condition="{contentOverview.dat.header.pagetree -> f:count()} > 0">
                <h3><f:translate key="impexpcore_displaycon_insidePagetree" /></h3>
-               <table class="table table-striped table-hover">
+               <table class="table table-striped table-hover t3js-impexp-preview">
                        <tbody>
                                <tr>
                                        <th><f:translate key="impexpcore_displaycon_controls" /></th>
@@ -37,7 +46,7 @@
                                        </f:if>
                                </tr>
                                <f:for each="{contentOverview.pagetreeLines}" as="line">
-                                       <tr>
+                                       <tr data-active="{line.active}">
                                                <td><f:format.raw>{line.controls}</f:format.raw></td>
                                                <td class="col-nowrap"><f:format.raw>{line.preCode}{line.title}</f:format.raw></td>
                                                <td class="col-nowrap"><f:format.raw>{line.fileSize}</f:format.raw></td>
@@ -56,9 +65,9 @@
        </f:if>
        <f:if condition="{contentOverview.remainingRecords -> f:count()} > 0">
                <h3><f:translate key="impexpcore_singlereco_outsidePagetree" /></h3>
-               <table class="table table-striped table-hover">
+               <table class="table table-striped table-hover t3js-impexp-preview">
                        <tbody>
-                               <tr>
+                               <tr data-active="{line.active}">
                                        <th><f:translate key="impexpcore_displaycon_controls" /></th>
                                        <th><f:translate key="impexpcore_displaycon_title" /></th>
                                        <th><f:translate key="impexpcore_displaycon_size" /></th>
index b8deaff..db45732 100644 (file)
                                </label>
                        </f:then>
                        <f:else>
-                                       <f:translate key="makeconfig_noExcludedElementsYet" />
+                                       <f:translate key="makeconfig_noManuallyExcludedElementsYet" />
                        </f:else>
                </f:if>
        </p>
+       <p class="form-control-static">
+               <label for="checkExcludeDisabled">
+                       <f:form.checkbox name="tx_impexp[excludeDisabled]" id="checkExcludeDisabled" value="1" checked="{inData.excludeDisabled}" />
+                       <f:translate key="makeconfig_excludeDisabledElements" />
+               </label>
+       </p>
 </div>
 
 <div class="form-group">
index 8ab3de8..cc5f843 100644 (file)
@@ -33,5 +33,10 @@ define(['jquery', 'TYPO3/CMS/Backend/Modal'], function ($, Modal) {
                                        Modal.currentModal.trigger('modal-dismiss');
                                });
                });
+
+               $('.t3js-impexp-toggledisabled').on('click', function() {
+                       var $checkboxes = $('table.t3js-impexp-preview tr[data-active="hidden"] input.t3js-exclude-checkbox');
+                       $checkboxes.prop('checked', !$checkboxes.get(0).checked);
+               });
        });
 });