[FEATURE] Upgrade Analysis in Install Tool 21/47621/66
authorAnja Leichsenring <aleichsenring@ab-softlab.de>
Sat, 8 Oct 2016 07:42:09 +0000 (09:42 +0200)
committerJan Helke <typo3@helke.de>
Sat, 8 Oct 2016 11:07:49 +0000 (13:07 +0200)
Noteworthy core changes come with ReST files explaining details since
core version 7. The patch adds a module to the install tool to render
those files.

This patch is the first part of a larger feature that will end up with
automated code migration and upgrade support based on code analysis.

Resolves: #75691
Releases: master
Change-Id: Ib74f4b806db8fbb215963838bbb1812a94503164
Reviewed-on: https://review.typo3.org/47621
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Jan Helke <typo3@helke.de>
Tested-by: Jan Helke <typo3@helke.de>
17 files changed:
Build/Gruntfile.js
Build/Resources/Public/Less/install.less
Build/package.json
typo3/sysext/backend/Resources/Public/Css/backend.css
typo3/sysext/core/Documentation/Changelog/Howto.rst
typo3/sysext/core/Documentation/Changelog/master/Feature-75691-UpgradeAnalysis-ProvideListingOfDocumentationFiles.rst [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/Action/Tool/UpgradeAnalysis.php [new file with mode: 0644]
typo3/sysext/install/Classes/Controller/ToolController.php
typo3/sysext/install/Classes/UpgradeAnalysis/DocumentationFile.php [new file with mode: 0644]
typo3/sysext/install/Resources/Private/Partials/Action/Ajax/UpgradeAnalysis/ListDocumentation.html [new file with mode: 0644]
typo3/sysext/install/Resources/Private/Partials/Action/Common/Headers.html
typo3/sysext/install/Resources/Private/Partials/Action/Common/Left.html
typo3/sysext/install/Resources/Private/Templates/Action/Tool/UpgradeAnalysis.html [new file with mode: 0644]
typo3/sysext/install/Resources/Public/Css/install.css
typo3/sysext/install/Resources/Public/JavaScript/Install.js
typo3/sysext/install/Resources/Public/JavaScript/tagsort.min.js [new file with mode: 0644]
typo3/sysext/install/Tests/Unit/UpgradeAnalysis/DocumentationFileTest.php [new file with mode: 0644]

index 76c6d5e..e304b5b 100644 (file)
@@ -41,8 +41,10 @@ module.exports = function(grunt) {
                        t3editor  : '<%= paths.sysext %>t3editor/Resources/',
                        workspaces: '<%= paths.sysext %>workspaces/Resources/',
                        core      : '<%= paths.sysext %>core/Resources/',
-                       flags     : 'bower_components/region-flags/svg/',
-                       t3icons   : 'bower_components/wmdbsystems-typo3-icons/dist/'
+                       bower     : 'bower_components/',
+                       flags     : '<%= paths.bower %>region-flags/svg/',
+                       t3icons   : '<%= paths.bower %>wmdbsystems-typo3-icons/dist/',
+                       npm       : 'node_modules/'
                },
                less: {
                        options: {
@@ -181,6 +183,11 @@ module.exports = function(grunt) {
                                        { dest: '<%= paths.sysext %>viewpage/Resources/Public/Icons/module-viewpage.svg', src: '<%= paths.t3icons %>module/module-viewpage.svg' },
                                        { dest: '<%= paths.sysext %>workspaces/Resources/Public/Icons/module-workspaces.svg', src: '<%= paths.t3icons %>module/module-workspaces.svg' }
                                ]
+                       },
+                       npm: {
+                               files: [
+                                       {dest: '<%= paths.install %>Public/JavaScript/tagsort.min.js', src: '<%= paths.npm %>tagsort/tagsort.js'}
+                               ]
                        }
                },
                bowercopy: {
@@ -261,7 +268,8 @@ module.exports = function(grunt) {
                                        "<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/resizable.js": ["<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/resizable.js"],
                                        "<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/selectable.js": ["<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/selectable.js"],
                                        "<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/sortable.js": ["<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/sortable.js"],
-                                       "<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/widget.js": ["<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/widget.js"]
+                                       "<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/widget.js": ["<%= paths.core %>Public/JavaScript/Contrib/jquery-ui/widget.js"],
+                                       "<%= paths.install %>Public/JavaScript/tagsort.min.js": ["<%= paths.install %>Public/JavaScript/tagsort.min.js"]
                                }
                        }
                },
index b0c197f..a17e8d4 100644 (file)
 //
 @import "_variables.less";
 
+//
+// Include tagsort CSS file
+//
+@import (inline) "../../../node_modules/tagsort/tagsort.css";
+
 @grid-float-breakpoint: @screen-md-min;
 
 .content-area {
index d982b8f..ec74372 100644 (file)
@@ -32,6 +32,7 @@
     "karma-requirejs": "^1.0.0",
     "karma-safari-launcher": "^1.0.0",
     "phantomjs-prebuilt": "^2.1.7",
-    "requirejs": "^2.2.0"
+    "requirejs": "^2.2.0",
+    "tagsort": "1.4.0"
   }
 }
index f25377d..b7405a7 100644 (file)
@@ -6885,6 +6885,7 @@ button.close {
 @media all and (transform-3d), (-webkit-transform-3d) {
   .carousel-inner > .item {
     transition: transform 0.6s ease-in-out;
+    -webkit-backface-visibility: hidden;
     backface-visibility: hidden;
     perspective: 1000px;
   }
index ad50814..17e675c 100644 (file)
@@ -27,12 +27,14 @@ teams or projects:
 
 This structure replaces the old `NEWS.md` file.
 
+
 Different changelog types
 =========================
 
 A changelog handles one of these change types:
 
-- Breaking change: A patch moved or removed a specific part of core functionality that may break extensions if they use this part. Removal of deprecated code or an interface change are good examples of this type.
+- Breaking change: A patch moved or removed a specific part of core functionality that may break extensions if they use
+  this part. Removal of deprecated code or an interface change are good examples of this type.
 
 - Deprecation: A patch deprecates a certain core functionality for a planned removal.
 
@@ -66,10 +68,59 @@ Like other documentation, changelog files are done in ReST, see `TYPO3 wiki ReST
 
 - All types contain a "Description" section that should give a short summary on which core part was affected by the change.
 
-- All types contain an "Impact" section that describes the possible impact of a change. An example is "Frontend output may change", "Configuration of xy is easier" or "Backend will throw a fatal error".
+- All types contain an "Impact" section that describes the possible impact of a change. An example is "Frontend output
+  may change", "Configuration of xy is easier" or "Backend will throw a fatal error".
 
-- Types "Deprecation" and "Breaking" contain an "Affected installations" section that describes when and if a TYPO3 instance is affected by a change. Example: "Extension xy is in use" or "TypoScript functionality xy is used" or "System is based on PHP 5.3".
+- Types "Deprecation" and "Breaking" contain an "Affected installations" section that describes when and if a TYPO3 instance
+  is affected by a change. Example: "Extension xy is in use" or "TypoScript functionality xy is used" or "System is based on PHP 5.3".
 
 - Types "Deprecation" and "Breaking" contain a "Migration" section to describe best practices on how to cope with a specific change.
 
+- All types contain a list of tags, see below.
+
 .. _TYPO3 wiki ReST syntax: http://wiki.typo3.org/ReST_Syntax
+
+
+Tagging
+=======
+
+To provide the possibility to filter ReST files by topics, it is mandatory to equip every RST file with at least two tags.
+As a rule of thumb a file should have no more than five tags. Please limit yourself to the list provided below. If you
+are in dearly need to introduce a new tag, you must also add it to the list (and explain it) in this file as a reference
+for everyone.
+
+The tag list should be located at the end of a ReST file prefixed with the index keyword,
+example:: ``.. index:: Backend, JavaScript``.
+
+List of all possible tags:
+
+- TypoScript - Changes that imply or introduce alterations to some TypoScript settings or modify the behavior of TypoScript
+  itself. Frontend TypoScript only.
+
+- TSConfig - Changes or modifications on the PageTS or UserTS or the behavior of this field.
+
+- TCA - Every change related to TCA.
+
+- FlexForm - Changes affecting FlexForm functionality.
+
+- LocalConfiguration - Changes that affect the LocalConfiguration.php or the subordinated AdditionalConfiguration.php
+
+- Fluid - Changes that alter behavior of Fluid like introducing new tags or modifying already established ones.
+
+- FAL - Changes to File Abstraction Layer.
+
+- Database - Changes that modify behavior of the database abstraction or introduces or removes new fields.
+
+- JavaScript - Modifying or introducing JavaScript.
+
+- PHP-API - Implementations of mandatory changes of the PHP-API.
+
+- Frontend - Changes that will affect the behavior or rendering of the TYPO3 Frontend.
+
+- Backend - Changes that will affect the behavior or rendering of the TYPO3 Backend.
+
+- CLI - Changes affecting CLI functionality.
+
+- RTE - Changes to RTE functionality.
+
+- ext:xyz - Changes on extension xyz. Please refer to this tag only when changing system extensions.
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-75691-UpgradeAnalysis-ProvideListingOfDocumentationFiles.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-75691-UpgradeAnalysis-ProvideListingOfDocumentationFiles.rst
new file mode 100644 (file)
index 0000000..3465af5
--- /dev/null
@@ -0,0 +1,23 @@
+.. include:: ../../Includes.txt
+
+===========================================================================
+Feature: #75691 - Upgrade Analysis - Provide listing of documentation files
+===========================================================================
+
+See :forge:`75691`
+
+Description
+===========
+
+The install now shows all the documentation files that were delivered with the core
+in the section `Upgrade analysis`. All files can be read inline, but there is no
+parsing, plain `.rst` is shown to the user.
+
+
+Impact
+======
+
+The install tool features a new main entry point that lists the documentation files shipped with
+the core. Filtering by tags provided in the documentation files helps to find interesting changes.
+
+.. index:: Backend
diff --git a/typo3/sysext/install/Classes/Controller/Action/Tool/UpgradeAnalysis.php b/typo3/sysext/install/Classes/Controller/Action/Tool/UpgradeAnalysis.php
new file mode 100644 (file)
index 0000000..a409027
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+namespace TYPO3\CMS\Install\Controller\Action\Tool;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
+use TYPO3\CMS\Install\Controller\Action\AbstractAction;
+use TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile;
+
+/**
+ * Run code analysis based on changelog documentation
+ */
+class UpgradeAnalysis extends AbstractAction
+{
+
+    /**
+     * Executes the action upon click in the Install Tool Menu
+     *
+     * All available documentation files are aggregated and
+     * passed to the frontend to be displayed as a list of entries.
+     *
+     * All following actions are handled via Ajax.
+     *
+     * @return string Rendered content
+     */
+    protected function executeAction()
+    {
+        $documentationFileService = new DocumentationFile();
+        $documentationFiles = $documentationFileService->findDocumentationFiles(
+            PATH_site . ExtensionManagementUtility::siteRelPath('core') . 'Documentation/Changelog'
+        );
+
+        $this->view->assign('files', $documentationFiles);
+        return $this->view->render();
+    }
+}
index c31a4d7..efabeb9 100644 (file)
@@ -34,6 +34,7 @@ class ToolController extends AbstractController
         'folderStructure',
         'testSetup',
         'upgradeWizard',
+        'upgradeAnalysis',
         'allConfiguration',
         'cleanUp',
         'loadExtensions',
diff --git a/typo3/sysext/install/Classes/UpgradeAnalysis/DocumentationFile.php b/typo3/sysext/install/Classes/UpgradeAnalysis/DocumentationFile.php
new file mode 100644 (file)
index 0000000..7cef249
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Install\UpgradeAnalysis;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\StringUtility;
+
+/**
+ * Provide information about documentation files
+ */
+class DocumentationFile
+{
+
+    /**
+     * @var array Unified array of used tags
+     */
+    protected $tagsTotal = [];
+
+    /**
+     * Traverse given directory, select files
+     *
+     * @param string $path
+     * @return array file details of affected documentation files
+     */
+    public function findDocumentationFiles(string $path): array
+    {
+        $documentationFiles = [];
+        $versionDirectories = scandir($path);
+
+        $fileInfo = pathinfo($path);
+        $absolutePath = $fileInfo['dirname'] . DIRECTORY_SEPARATOR . $fileInfo['basename'];
+        foreach ($versionDirectories as $version) {
+            $directory = $absolutePath . DIRECTORY_SEPARATOR . $version;
+            $documentationFiles += $this->getDocumentationFilesForVersion($directory, $version);
+        }
+        $this->tagsTotal = $this->collectTagTotal($documentationFiles);
+
+        return $documentationFiles;
+    }
+
+    /**
+     * True if file should be considered
+     *
+     * @param array $fileInfo
+     * @return bool
+     */
+    protected function isRelevantFile(array $fileInfo): bool
+    {
+        return $fileInfo['extension'] === 'rst' && $fileInfo['filename'] !== 'Index';
+    }
+
+    /**
+     * Add tags from file
+     *
+     * @param array $file file content, each line is an array item
+     * @return array
+     */
+    protected function extractTags(array $file): array
+    {
+        $tags = $this->extractTagsFromFile($file);
+        // Headline starting with the category like Breaking, Important or Feature
+        $tags[] = $this->extractCategoryFromHeadline($file);
+
+        return $tags;
+    }
+
+    /**
+     * Files must contain an index entry, detailing any number of manual tags
+     * each of these tags is extracted and added to the general tag structure for the file
+     *
+     * @param array $file file content, each line is an array item
+     * @return array extracted tags
+     */
+    protected function extractTagsFromFile(array $file): array
+    {
+        foreach ($file as $line) {
+            if (StringUtility::beginsWith($line, '.. index::')) {
+                $tagString = substr($line, strlen('.. index:: '));
+                return GeneralUtility::trimExplode(',', $tagString, true);
+            }
+        }
+
+        return [];
+    }
+
+    /**
+     * Files contain a headline (provided as input parameter,
+     * it starts with the category string.
+     * This will used as a tag
+     *
+     * @param array $lines
+     * @return string
+     */
+    protected function extractCategoryFromHeadline(array $lines): string
+    {
+        $headline = $this->extractHeadline($lines);
+        if (strpos($headline, ':') !== false) {
+            return 'cat:' . substr($headline, 0, strpos($headline, ':'));
+        }
+
+        return '';
+    }
+
+    /**
+     * First line is headline mark, skip it
+     * second line is headline
+     *
+     * @param array $lines
+     * @return string
+     */
+    protected function extractHeadline(array $lines): string
+    {
+        $index = 0;
+        while (StringUtility::beginsWith($lines[$index], '..') || StringUtility::beginsWith($lines[$index], '==')) {
+            $index++;
+        }
+        return trim($lines[$index]);
+    }
+
+    /**
+     * Get issue number from headline
+     *
+     * @param string $headline
+     * @return int
+     */
+    protected function extractIssueNumber(string $headline): int
+    {
+        return (int)substr($headline, strpos($headline, '#') + 1, 5);
+    }
+
+    /**
+     * Get main information from a .rst file
+     *
+     * @param string $file
+     * @return array
+     */
+    protected function getListEntry(string $file): array
+    {
+        $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+        $headline = $this->extractHeadline($lines);
+        $entry['headline'] = $headline;
+        $entry['filepath'] = $file;
+        $entry['tags'] = $this->extractTags($lines);
+        $entry['tagList'] = implode(',', $entry['tags']);
+        $entry['content'] = file_get_contents($file);
+        $issueNumber = $this->extractIssueNumber($headline);
+
+        return [$issueNumber => $entry];
+    }
+
+    /**
+     * True if files within directory should be considered
+     *
+     * @param string $versionDirectory
+     * @param string $version
+     * @return bool
+     */
+    protected function isRelevantDirectory(string $versionDirectory, string $version): bool
+    {
+        return is_dir($versionDirectory) && $version !== '.' && $version !== '..';
+    }
+
+    /**
+     * Handle a single directory
+     *
+     * @param string $docDirectory
+     * @param string $version
+     * @return array
+     */
+    protected function getDocumentationFilesForVersion(
+        string $docDirectory,
+        string $version
+    ): array {
+        $documentationFiles = [];
+        if ($this->isRelevantDirectory($docDirectory, $version)) {
+            $documentationFiles[$version] = [];
+            $absolutePath = dirname($docDirectory) . DIRECTORY_SEPARATOR . $version;
+            $rstFiles = scandir($docDirectory);
+            foreach ($rstFiles as $file) {
+                $fileInfo = pathinfo($file);
+                if ($this->isRelevantFile($fileInfo)) {
+                    $filePath = $absolutePath . DIRECTORY_SEPARATOR . $fileInfo['basename'];
+                    $documentationFiles[$version] += $this->getListEntry($filePath);
+                }
+            }
+        }
+
+        return $documentationFiles;
+    }
+
+    /**
+     * Merge tag list
+     *
+     * @param $documentationFiles
+     * @return array
+     */
+    protected function collectTagTotal($documentationFiles): array
+    {
+        $tags = [];
+        foreach ($documentationFiles as $versionArray) {
+            foreach ($versionArray as $fileArray) {
+                $tags = array_merge(array_unique($tags), $fileArray['tags']);
+            }
+        }
+
+        return array_unique($tags);
+    }
+
+    /**
+     * Return full tag list
+     *
+     * @return array
+     */
+    public function getTagsTotal(): array
+    {
+        return $this->tagsTotal;
+    }
+}
diff --git a/typo3/sysext/install/Resources/Private/Partials/Action/Ajax/UpgradeAnalysis/ListDocumentation.html b/typo3/sysext/install/Resources/Private/Partials/Action/Ajax/UpgradeAnalysis/ListDocumentation.html
new file mode 100644 (file)
index 0000000..92af230
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
+         xmlns:f="http://xsd.helhum.io/ns/typo3/cms-fluid/master/ViewHelpers">
+<head>
+       <title>Partials: ListDocumentation</title>
+</head>
+<body>
+<f:section name="Main">
+       <f:for each="{files}" as="versionArray" key="version">
+               <f:if condition="{versionArray -> f:count()} > 0">
+                       <h2>{version}</h2>
+                       <div class="panel-group" role="tablist" aria-multiselectable="false">
+                               <f:for each="{versionArray}" as="fileArray" key="issueNumber">
+
+                                       <div class="panel panel-default upgrade_analysis_item_to_filter"
+                                                        data-item-tags="{fileArray.tagList}" id="heading{issueNumber}">
+                                               <div class="panel-heading" role="tab">
+                                                       <h3 class="panel-title">
+                                                               <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse{issueNumber}" aria-expanded="true" aria-controls="collapse{issueNumber}" class="collapsed">
+                                                                       <span class="caret"></span>
+                                                                       <strong >
+                                                                               {fileArray.headline}</strong>
+                                                               </a>
+                                                       </h3>
+                                               </div>
+                                               <div id="collapse{issueNumber}" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading{issueNumber}">
+                                                       <pre>{fileArray.content}</pre>
+                                               </div>
+                                       </div>
+
+                               </f:for>
+                       </div>
+               </f:if>
+       </f:for>
+</f:section>
+</body>
+</html>
index c84649d..17eebf9 100644 (file)
@@ -5,4 +5,5 @@
 <script type="text/javascript" src="../../core/Resources/Public/JavaScript/Contrib/jquery/jquery-2.2.3.min.js?{time}"></script>
 <script type="text/javascript" src="../../backend/Resources/Public/JavaScript/jquery.clearable.js?{time}"></script>
 <script type="text/javascript" src="../Resources/Public/JavaScript/bootstrap.min.js?{time}"></script>
-<script type="text/javascript" src="../Resources/Public/JavaScript/Install.js?{time}"></script>
\ No newline at end of file
+<script type="text/javascript" src="../Resources/Public/JavaScript/tagsort.min.js?{time}"></script>
+<script type="text/javascript" src="../Resources/Public/JavaScript/Install.js?{time}"></script>
index 94f6a90..eda951f 100644 (file)
@@ -4,6 +4,7 @@
                <f:render partial="Action/Common/MenuLink" arguments="{action: action, context: context, action_name: 'configuration', label: 'Configuration Presets'}" />
                <f:render partial="Action/Common/MenuLink" arguments="{action: action, context: context, action_name: 'allConfiguration', label: 'All configuration'}" />
                <f:render partial="Action/Common/MenuLink" arguments="{action: action, context: context, action_name: 'upgradeWizard', label: 'Upgrade Wizard'}" />
+               <f:render partial="Action/Common/MenuLink" arguments="{action: action, context: context, action_name: 'upgradeAnalysis', label: 'Upgrade Analysis'}"/>
                <f:render partial="Action/Common/MenuLink" arguments="{action: action, context: context, action_name: 'systemEnvironment', label: 'System environment'}" />
                <f:render partial="Action/Common/MenuLink" arguments="{action: action, context: context, action_name: 'folderStructure', label: 'Folder structure'}" />
                <f:render partial="Action/Common/MenuLink" arguments="{action: action, context: context, action_name: 'testSetup', label: 'Test setup'}" />
diff --git a/typo3/sysext/install/Resources/Private/Templates/Action/Tool/UpgradeAnalysis.html b/typo3/sysext/install/Resources/Private/Templates/Action/Tool/UpgradeAnalysis.html
new file mode 100644 (file)
index 0000000..441f3fb
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
+                       xmlns:f="http://xsd.helhum.io/ns/typo3/cms-fluid/master/ViewHelpers">
+<head>
+       <title>Template: Action/Tool/UpgradeAnalysis</title>
+</head>
+<body>
+<f:layout name="ToolAuthenticated"/>
+
+<f:section name="Content">
+       <h1>Upgrade Analysis</h1>
+
+       <div id="tagsort_tags_container"></div>
+       <div id="upgradeAnalysis_output">
+               <f:render partial="Action/Ajax/UpgradeAnalysis/ListDocumentation" section="Main" arguments="{_all}"/>
+       </div>
+</f:section>
+</body>
+</html>
index 126479b..490782a 100644 (file)
@@ -6876,6 +6876,7 @@ button.close {
 @media all and (transform-3d), (-webkit-transform-3d) {
   .carousel-inner > .item {
     transition: transform 0.6s ease-in-out;
+    -webkit-backface-visibility: hidden;
     backface-visibility: hidden;
     perspective: 1000px;
   }
@@ -8346,6 +8347,33 @@ fieldset[disabled] .navbar-inverse .btn-link:hover,
 fieldset[disabled] .navbar-inverse .btn-link:focus {
   color: #4d4d4d;
 }
+/* NOT REQUIRED */
+/* Just some example CSS */
+.tagsort-tags-container span {
+       display: inline-block;
+       border: 2px solid #CCC;
+       color: #AAA;
+       font-size: 12px;
+       line-height: 10px;
+       padding: 5px 9px;
+       margin: 5px;
+       cursor: pointer;
+       border-radius: 12px 12px 12px 12px;
+       -moz-border-radius: 12px 12px 12px 12px;
+       -webkit-border-radius: 12px 12px 12px 12px;
+       transition: all 0.2s ease-in-out;
+}
+.tagsort-tags-container span:hover {
+       border: 2px solid #000;
+       color: #FFF;
+       background-color: #000;
+
+}
+.tagsort-tags-container span.tagsort-active {
+       border: 2px solid #000;
+       color: #000;
+       background-color: transparent;
+}
 .content-area {
   padding-bottom: 35px;
 }
index d7cc4d5..c43a46f 100644 (file)
@@ -82,7 +82,7 @@ TYPO3.Install.ExtensionChecker = {
                                if (data === 'OK') {
                                        self.checkExtensionsCompatibility(true);
                                } else {
-                                       if(data === 'unauthorized') {
+                                       if (data === 'unauthorized') {
                                                location.reload();
                                        }
                                        // workaround for xdebug returning 200 OK on fatal errors
@@ -119,10 +119,10 @@ TYPO3.Install.ExtensionChecker = {
                                        );
                                        var extensions = data.split(',');
                                        var unloadButtonWrapper = $('<fieldset class="t3-install-form-submit"></fieldset>');
-                                       for(var i=0; i<extensions.length; i++) {
+                                       for (var i = 0; i < extensions.length; i++) {
                                                var extension = extensions[i];
                                                var unloadButton = $('<button />', {
-                                                       text: 'Uninstall '+ $.trim(extension),
+                                                       text: 'Uninstall ' + $.trim(extension),
                                                        'class': 't3-js-uninstallSingle',
                                                        'data-extension': $.trim(extension)
                                                });
@@ -137,7 +137,7 @@ TYPO3.Install.ExtensionChecker = {
                                                });
                                        }
                                        var unloadAllButton = $('<button />', {
-                                               text: 'Uninstall all incompatible extensions: '+ data,
+                                               text: 'Uninstall all incompatible extensions: ' + data,
                                                click: function(e) {
                                                        $('.alert-loading', '#checkExtensions').show();
                                                        self.uninstallExtension(data);
@@ -197,7 +197,7 @@ TYPO3.Install.ExtensionChecker = {
                                if (data === 'OK') {
                                        self.handleCheckExtensionsSuccess();
                                } else {
-                                       if(data === 'unauthorized') {
+                                       if (data === 'unauthorized') {
                                                location.reload();
                                        }
                                        // workaround for xdebug returning 200 OK on fatal errors
@@ -562,7 +562,7 @@ $(function() {
 
                if (value.length === 0) {
                        $(this).attr('style', 'background-color:#FBB19B; border:1px solid #DC4C42');
-               } else if (!enoughRegex.test(value)) {
+               } else  if (!enoughRegex.test(value)) {
                        $(this).attr('style', 'background-color:#FBB19B; border:1px solid #DC4C42');
                } else if (strongRegex.test(value)) {
                        $(this).attr('style', 'background-color:#CDEACA; border:1px solid #58B548');
@@ -625,7 +625,7 @@ $(function() {
        }
        // This makes jquerys "contains" work case-insensitive
        jQuery.expr[':'].contains = jQuery.expr.createPseudo(function(arg) {
-               return function( elem ) {
+               return function(elem) {
                        return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
                };
        });
@@ -660,7 +660,7 @@ $(function() {
                $menuListGroup.width($(this).parent().width());
        });
        $menuListGroup.width($menuWrapper.parent().width());
-       $(window).resize(function(){
+       $(window).resize(function() {
                $menuListGroup.width($('#menuWrapper').parent().width());
        });
        var $collapse = $('.collapse');
@@ -686,4 +686,19 @@ $(function() {
        $('.t3js-custom-preset').on('input', function() {
                $('#' + $(this).data('radio')).prop('checked', true);
        });
+
+       TYPO3.Install.upgradeAnalysis.initialize();
 });
+
+
+TYPO3.Install.upgradeAnalysis = {
+       provideTags: function() {
+               $('#tagsort_tags_container').tagSort({
+                       selector: '.upgrade_analysis_item_to_filter'
+               });
+       },
+
+       initialize: function() {
+               TYPO3.Install.upgradeAnalysis.provideTags();
+       }
+};
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/tagsort.min.js b/typo3/sysext/install/Resources/Public/JavaScript/tagsort.min.js
new file mode 100644 (file)
index 0000000..179b30e
--- /dev/null
@@ -0,0 +1 @@
+!function(a){a.fn.tagSort=function(b){var c={selector:".item-tagsort",tagWrapper:"span",tagClassPrefix:!1,displaySelector:!1,displaySeperator:" ",sortType:"exclusive",fadeTime:200};b=a.extend(c,b);var d={generateTags:function(c){var e={},f={elements:[],tags:[]},g=a(document.createElement(b.tagWrapper));return c.each(function(c){$element=a(this);var h=$element.data("item-tags"),i=h.match(/,\s+/)?h.split(", "):h.split(",");a.each(i,function(a,c){var f=c.toLowerCase();e[f]||(e[f]=[],d.container.append(b.tagClassPrefix!==!1?g.clone().text(c).addClass((b.tagClassPrefix+c.toLowerCase()).replace(/[!\"#$%&'\(\)\*\+,\.\/:;<=>\?\@\[\\\]\^`\{\|\}~]/g,"")):g.clone().text(c))),b.displaySelector!==!1&&$element.find(b.displaySelector).append(a>0?b.displaySeperator+c:c),e[f].push($element)}),f.elements.push($element),f.tags.push(i)}),"inclusive"==b.sortType?e:"exclusive"==b.sortType?f:"single"==b.sortType?e:void 0},exclusiveSort:function(b,c){var e=[[],[]];return a.each(b.elements,function(c,f){var g=!0;d.container.find(".tagsort-active").each(function(d){-1==b.tags[c].indexOf(a(this).text())&&(g=!1,e[0].push(f))}),1==g&&e[1].push(f)}),e},inclusiveSort:function(b,c){var e=[[],[]];return d.container.find(".tagsort-active").each(function(c){a.each(b[a(this).text().toLowerCase()],function(a,b){e[1].push(b)})}),e},showElements:function(c){a.each(c,function(a,c){c.is("visible")||c.fadeIn(b.fadeTime)})},hideElements:function(c){a.each(c,function(a,c){c.is("visible")&&c.fadeOut(b.fadeTime)})},inititalize:function(c){d.container=c,d.container.addClass("tagsort-tags-container");var e=a(b.selector);d.tags=d.generateTags(e,d.container);var f=d.container.find(b.tagWrapper);f.click(function(){var c=f.hasClass("tagsort-active");if(c||e.fadeIn(b.fadeTime),e.fadeOut(b.fadeTime),"single"==b.sortType)if(a(this).hasClass("tagsort-active"))e.fadeIn(b.fadeTime),a(this).removeClass("tagsort-active");else{a(".tagsort-active").removeClass("tagsort-active"),a(this).toggleClass("tagsort-active");var g=d.inclusiveSort(d.tags,e)}else{a(this).toggleClass("tagsort-active");var g="inclusive"==b.sortType?d.inclusiveSort(d.tags,e):d.exclusiveSort(d.tags,e)}g[0].length>0&&d.hideElements(g[0]),g[1].length>0&&d.showElements(g[1])})}};return d.inititalize(this),a(this)}}(jQuery);
\ No newline at end of file
diff --git a/typo3/sysext/install/Tests/Unit/UpgradeAnalysis/DocumentationFileTest.php b/typo3/sysext/install/Tests/Unit/UpgradeAnalysis/DocumentationFileTest.php
new file mode 100644 (file)
index 0000000..eed321c
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Install\Tests\Unit\UpgradeAnalysis;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use org\bovigo\vfs\vfsStream;
+use org\bovigo\vfs\vfsStreamDirectory;
+use TYPO3\CMS\Core\Tests\UnitTestCase;
+use TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile;
+
+class DocumentationFileTest extends UnitTestCase
+{
+
+    /**
+     * @var DocumentationFile
+     */
+    protected $documentationFileService;
+
+    /**
+     * @var  vfsStreamDirectory
+     */
+    protected $docRoot;
+
+    /**
+     * set up test environment
+     */
+    public function setUp()
+    {
+        $content_12345 = [
+            '====',
+            'Breaking: #12345 - Issue',
+            '====',
+            '',
+            'some text content',
+        ];
+        $content_45678 = [
+            '====',
+            'Important: #45678 - Issue',
+            '====',
+            '',
+            'Some more text content',
+        ];
+
+        $content_98574 = [
+            '====',
+            'Important: #98574 - Issue',
+            '====',
+            '',
+            'Something else',
+            '',
+            '.. index:: unittest'
+        ];
+        $content_13579 = [
+            '====',
+            'Breaking: #13579 - Issue',
+            '====',
+            '',
+            'Some more content'
+        ];
+
+        $structure = [
+            'Changelog' => [
+                '1.2' => [
+                    'Breaking-12345-Issue.rst' => implode("\n", $content_12345),
+                    'Important-45678-Issue.rst' => implode("\n", $content_45678),
+
+                ],
+                '2.0' => [
+                    'Important-98574-Issue.rst' => implode("\n", $content_98574),
+                ],
+                'master' => [
+                    'Breaking-13579-Issue.rst' => implode("\n", $content_13579),
+                    'Index.rst' => '',
+                ],
+            ],
+        ];
+
+        $this->docRoot = vfsStream::setup('root', null, $structure);
+        $this->documentationFileService = new DocumentationFile();
+    }
+
+    /**
+     * @test
+     */
+    public function findDocumentationFilesReturnsArrayOfFiles()
+    {
+        $expected = [
+            '1.2' => [],
+            '2.0' => [],
+            'master' => [],
+        ];
+
+        $result = $this->documentationFileService->findDocumentationFiles(vfsStream::url('root/Changelog'));
+        self::assertEquals(array_keys($expected), array_keys($result));
+    }
+
+    /**
+     * @test
+     */
+    public function extractingTagsProvidesTagsAsDesired()
+    {
+        $expected = [
+            'unittest',
+            'cat:Important',
+        ];
+        $result = $this->documentationFileService->findDocumentationFiles(vfsStream::url('root/Changelog'));
+        self::assertEquals($expected, $result['2.0'][98574]['tags']);
+    }
+}